diff options
author | Andrew John Hughes <gnu_andrew@member.fsf.org> | 2006-09-05 20:47:38 +0000 |
---|---|---|
committer | Andrew John Hughes <gnu_andrew@member.fsf.org> | 2006-09-05 20:47:38 +0000 |
commit | 228fc33f661314b6723b691635085ee4ff6a69b2 (patch) | |
tree | 50e78696593f8df21fe9330258dff98537d39f65 | |
parent | f090c35d5776db64813ccf19d8327cf0ae756ac7 (diff) | |
download | classpath-228fc33f661314b6723b691635085ee4ff6a69b2.tar.gz |
2006-09-05 Andrew John Hughes <gnu_andrew@member.fsf.org>
* Merge of HEAD-->generics from 2006/08/12 to 2006/09/03.
218 files changed, 18188 insertions, 7153 deletions
@@ -1,3 +1,1700 @@ +2006-09-03 Audrius Meskauskas <AudriusA@Bioinformatics.org> + + * gnu/javax/swing/text/html/parser/HTML_401F.java (defineElements): + Disallow H1 - H6 in the paragraphs. + * gnu/javax/swing/text/html/parser/support/textPreProcessor.java + (preprocess): Leave at most one leading and/or trailing space. + * javax/swing/text/html/HTMLDocument.java (HTMLReader.handleText): + Do not add any text after closing the HTML tag. + +2006-09-02 Roman Kennke <kennke@aicas.com> + + PR 28928 + * javax/swing/plaf/basic/BasicTextUI.java + (RootView.getPreferredSpan): Default to 10 when there is no + real view. + (RootView.getMinimumSpan): Forward to view and default to 10 + when there is no real view. + (RootView.getMaximumSpan): Return Integer.MAX_VALUE. + (getMaximumSize): Check for overflow. + * javax/swing/text/FieldView.java + (getResizeWeight): Removed unneeded assignment. + +2006-09-01 Francis Kung <fkung@redhat.com> + * java/awt/image/ColorConvertOp.java + (copyImage): Updated javadoc and comments. + (copyRaster): Add javadoc. + (createCompatibleColorModel): Add javadocs and comments. + (createCompatibleDestImage): Use correct transfer type. + (createCompatibleDestRaster): Add new parameter for transfer type. + (filter): Use correct transfer type. + * java/awt/image/ConvolveOp.java: Updated javadocs. + (createCompatibleDestImage): Set new image properties correctly. + (filter(BufferedImage, BufferedImage): Correct handling of premultiplication. + (filter(WritableRaster, Raster): Clip sample values to [0-255]. + +2006-09-01 Robert Schuster <robertschuster@fsfe.org> + + * javax/swing/table/DefaultTableModel.java: + (checkSize): Added null check for dataVector. + +2006-09-01 Robert Schuster <robertschuster@fsfe.org> + + * javax/swing/plaf/basic/BasicSplitPaneUI.java: + (BasicHorizontalLayout.getAlignmentX): Return fixed value. + (BasicHorizontalLayout.getAlignmentY): Return fixed value. + +2006-09-01 Robert Schuster <robertschuster@fsfe.org> + + * javax/swing/plaf/metal/MetalCheckBoxIcon.java: + (paintIcon): Removed unused import statements, lowered cast requirement + from JCheckBox to AbstractButton. + +2006-09-01 Robert Schuster <robertschuster@fsfe.org> + + * javax/swing/plaf/basic/BasicLookAndFeel.java: + (initComponentDefaults): Added, changed and removed some + tabbed pane properties. + +2006-09-01 Robert Schuster <robertschuster@fsfe.org> + + * examples/gnu/classpath/examples/swing/TabbedPaneDemo.java: + (createContent): Changed menu item name and tab naming. + +2006-09-01 Roman Kennke <kennke@aicas.com> + + PR 28922 + * javax/swing/plaf/basic/BasicHTML.java + (HTMLRootView.getAttributes): Overridden to return null. + (HTMLRootView.getElement): Overridden to return the view's + element. + +2006-09-01 Robert Schuster <robertschuster@fsfe.org> + + * javax/swing/plaf/basic/BasicTabbedPaneUI.java: + (calculateTabAreaHeight): Use getTabRunOverlay method instead + of accessing variable directly. + (calculateTabAreaWidth): Dito. + +2006-08-31 Keith Seitz <keiths@redhat.com> + + * include/jvmti.h: Include jvmti_md.h. + +2006-08-31 Keith Seitz <keiths@redhat.com> + + From Martin Platter <motse@complang.tuwien.ac.at>: + * Makefile.am (include_HEADERS): Include jvmti.h. + * include/jvmti.h (jvmtiEnv) [!__cplusplus]: Add missing '*'. + (jvmtiError): Remove superfluous comma after last entry. + (jvmtiEvent): It's "BREAKPOINT" not "BERAKPOINT". + (_Jv_jvmtiEnv.StopThread): Add missing exception parameter. + (_Jv_jvmtiEnv.RawMonitorWait): Add missing millis parameter. + (_Jv_jvmtiEnv.GetSourceFileName): source_name_ptr is pointer to + character pointer. + (_Jv_JVMTIEnv::StopThread): Add missing exception parameter. + (_Jv_JVMTIEnv::RawMonitorWait): Add missing millis parameter. + (_Jv_JVMTIEnv::GetSourceFileName): source_name_ptr is pointer to + character pointer. + +2006-08-31 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/BoxView.java + (getWidth): Return the width with insets added, not with one + added and one removed. + (getHeight): Return the height with insets added, not with one + added and one removed. + * javax/swing/text/GlyphView.java + (DefaultGlyphPainter.viewToModel): Need to add the start offset. + * javax/swing/text/ParagraphView.java + (Row.getAlignment): Adjust alignment with respect to + the justification attribute. + (Row.getLeftInset): Overridden to adjust for firstLineIndent + attribute. + * javax/swing/text/html/CSS.java + (getValue): Convert length values. + * javax/swing/text/html/Paragraph.java + (painter): New field. + (paint): Implemented to delegate painting to the BoxPainter too. + (setPropertiesFromAttributes): Implemented to load attributes + from CSS. + * javax/swing/text/html/StyleSheet.java + (BoxPainter.as): Removed field. + (BoxPainter.leftInset): New field. + (BoxPainter.bottomInset): New field. + (BoxPainter.rightInset): New field. + (BoxPainter.topInset): New field. + (BoxPainter.BoxPainter): Implemented to load the insets from + CSS. + (BoxPainter.getInset): Implemented. + * gnu/javax/swing/text/html/Length.java: New class. + Converts CSS length units to usable values. + +2006-08-31 Andreas Tobler <a.tobler@schweiz.ch> + + * configure.ac: Add check for gethostbyname_r. + Add check for MSG_NOSIGNAL and SO_NOSIGPIPE. + * native/jni/native-lib/cpnet.c (SOCKET_NOSIGNAL): Define + SOCKET_NOSIGNAL according to the configure check. + (cpnet_send): Use SOCKET_NOSIGNAL. + (cpnet_sendTo): Likewise. + (cpnet_getHostByName): Use gethostbyname in case gethostbyname_r is not + defined. + * native/jni/native-lib/cpio.c: Define O_SYNC and O_DSYNC in case they + are not available. + + * lib/Makefile.am (cssfiles): Add new rule to install css files. + +2006-08-31 Roman Kennke <kennke@aicas.com> + + * javax/swing/JEditorPane.java + (getPreferredSize): Replace preferred size with minimum + UI size only if the scrollable does _not_ track the viewport + size and only if the viewport's size is smaller than the + scrollable's size. + (getScrollableTracksViewportWidth): Avoid unnecessary multiple + method calls. + * javax/swing/plaf/basic/BasicTextUI.java + (getPreferredSize): Read-lock the document to avoid + concurrency problems. + (getMaximumSize): Return maximum size of the view. + Read-lock the document to avoid concurrency problems. + (getMinimumSize): Return minimum size of the view. + Read-lock the document to avoid concurrency problems. + +2006-08-31 Gary Benson <gbenson@redhat.com> + + * java/net/SocketPermission.java + (maybeBracketIPv6Address): Renamed to processHostport. + (processHostport): Also translate "" to "localhost". + (setHostPort): Remove special cases for empty hostport and for + extra colons in hostport (processHostport handles these now). + +2006-08-31 Mark Wielaard <mark@klomp.org> + + * javax/swing/text/ZoneView.java (Zone): Make static class. + Constructor takes axis parameter. + (createZone): Create Zone with getAxis() as major axis. + +2006-08-30 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/ZoneView.java + (loadChildren): Implemented. + (getViewIndexAtPosition): Implemented. + (checkZoneAt): New helper method. + (splitZone): New helper method. + (getPreferredZoneEnd): New helper method. + +2006-08-30 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/ZoneView.java: New class. + +2006-08-30 Roman Kennke <kennke@aicas.com> + + * javax/swing/JMenu.java + (getMenu): Removed unneeded cast. + (getPopupMenuOrigin): Made positioning algorithm better respect + the screen bounds. + (setMenuLocation): Also set the location on the popup if it's + not null. + (setModel): Use menuChangeListener so that we don't override + the changeListener field from AbstractButton. + (setPopupMenuVisible): Use custom location if set, otherwise + fallback to getPopupMenuOrigin(). + +2006-08-29 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/InternationalFormatter.java + (stringToValue): Fixed bounds check. + * javax/swing/text/MaskFormatter.java + (MaskFormatter): Don't explicitly set allosInvalid property. + (convertStringToValue): New helper method. + (convertValueToString): New helper method. + (convertValue): Removed. Replaced by the 2 convert* methods + above. + (getPadCharAt): Removed. + (isCharValid): Removed. + (pad): Removed. + (stringToValue): Fixed stringToValue conversion. + (stripLiterals): Removed. + (valueToString): Fixed valueToString conversion. + * javax/swing/text/DefaultFormatter.java + (DefaultFormatter): Default to commitsOnValidEdit = false. + +2006-08-29 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/TextAction.java + (getTextComponent): Check event for null and return null in + this case. + (augmentList): Augment Actions based on their names. + * javax/swing/text/DefaultEditorKit.java + (BeginAction.actionPerformed): Check target for null. + (BeginLineAction.actionPerformed): Check target for null. + (CopyAction.actionPerformed): Check target for null. + (CutAction.actionPerformed): Check target for null. + (EndAction.actionPerformed): Check target for null. + (EndLineAction.actionPerformed): Check target for null. + (InsertBreakAction.actionPerformed): Check target for null. + (InsertTabAction.actionPerformed): Check target for null. + (PasteAction.actionPerformed): Check target for null. + (SelectAllAction.actionPerformed): Check target for null. + (SelectionBeginAction.actionPerformed): Check target for null. + (SelectionBeginLineAction.actionPerformed): Check target for null. + (SelectionEndAction.actionPerformed): Check target for null. + (SelectionEndLineAction.actionPerformed): Check target for null. + (SelectLineAction.actionPerformed): Check target for null. + (SelectWordAction.actionPerformed): Check target for null. + +2006-08-29 Roman Kennke <kennke@aicas.com> + + * javax/swing/plaf/basic/BasicTextUI.java + (FocusHandler): New class. This is moved from the anonymous + inner focus listener class to a static member class, and + is now shared between components. + (DocumentHandler): This class is combined with the PropertyHandler + into the Handler class. + (PropertyChangeHandler): This class is combined with the + DocumentHandler into the Handler class. + (Handler): New class. This combines the Property and Document + handler into one class. + (RootView.changedUpdate): Only forward if real view != null. + (RootView.insertUpdate): Only forward if real view != null. + (RootView.removeUpdate): Only forward if real view != null. + (documentHandler): Removed field and replaced by handler. + (focuslistener): Made field static and renamed to focusListener. + (handler): New field. + (kit): Lazily initialize field. + (rootView): Lazily initialize field. + (updateHandler): Removed and replaced by handler. + (getEditorKit): Lazily instantiate field. + (installDefaults): Don't set margin twice. Install correct + property for disabledTextColor. Moved caret and highlighter + initialization to installFixedDefaults. + (installFixedDefaults): New method. Installs defaults that + can't be overridden by subclasses. + (installListeners): Only install focus handler when new + system property gnu.swing.text.no-xlike-clipboard is not set. + Lazily initialize focus handler. + (installUI): Lazily initialize rootView. Install handler + both for property and document changes. + (uninstallDefaults): Uninstall the UI defaults. + (uninstallFixedDefaults): New method. Uninstalls the fixed + defaults. + (installListeners): Only uninstall focus handler when not null. + (uninstallUI): Uninstall property and document listener here. + +2006-08-29 Gary Benson <gbenson@redhat.com> + + * java/net/SocketPermission.java + (maybeBracketIPv6Address): New method. + (<init>): Pass the hostport argument through the above. + + * java/net/NetworkInterface.java (getInetAddresses): + Don't bracket IPv6 addresses. + +2006-08-28 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/BoxView.java + (calculateMinorAxisRequirements): Initialize max size + with Integer.MAX_VALUE. + * javax/swing/text/Utilities.java + (getBreakLocation): For simple chars, scan the text directly. + * javax/swing/text/WrappedPlainView.java + (tabBase): New field. + (tabSize): New field. + (calculateBreakPosition): Use Utilities. Fixed for correct + break calculation. + (changedUpdate): Update children directly. + (insertUpdate): Update children directly. Notify children. + (removeUpdate): Update children directly. Notify children. + (updateChildren): New helper method. + (nextTabStop): Fixed to return correct results. + (paint): Update tabBase. + (updateMetrics): Update tab size. + +2006-08-28 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/Position.java + (Bias.Forward): Initialize with 'Forward' rather then 'forward'. + (Bias.Backward): Initialize with 'Backward' rather then 'backward'. + +2006-08-28 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/View.java + (height): Removed unneeded field. + (width): Removed unneeded field. + (getBreakWeight): Return GoodBreakWeight when pos is after + the view's span. + (getToolTipText): Check view index more carefully. Avoid + Rectangle creation. + (insertUpdate): Only execute method body if view count > 0. + When updateChildren returns false, clear the ec variable. + (updateChildren): Added null checks. + (viewToModel): Initialize bias array correctly. + * javax/swing/text/CompositeView.java + (children): Made private. + (numChildren): New field. + (loadChildren): Check factory for null. Don't load children + when factory is null. + (replace): Removed null check. Nullify removed children. Made + growing the array more efficient. + (getViewCount): Return numChildren rather then the real array + size. + * javax/swing/text/BoxView.java + (getViewAtPoint): Fixed algorithm for finding the view. + (replace): Made array growing more efficient. + (replaceLayoutArray): New helper method for growing/patching + the layout arrays. + (viewToModel): Make sure we have a valid layout. + +2006-08-28 Tania Bento <tbento@redhat.com> + + * java/awt/MenuShortcut.java + (MenuShortcut (int, boolean)): Set keyName. + (toString): Modified string output. + (setKeyName): New private method. + +2006-08-28 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/GapContent.java + (Mark.getOffset): Made assert less strict, include boundary. + (search): Made package private to avoid accessor method. + +2006-08-28 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/StringContent.java + (InsertUndo.positions): New field. + (InsertUndo.redo): Update the undo positions. + (InsertUndo.undo): Fetch the undo positions. + (Mark): New class. Layer of indirection to allow Positions + to be GC'ed while we still hold references to the Mark. + (RemoveUndo.len): New field. + (RemoveUndo.positions): New field. + (RemoveUndo.RemoveUndo): Fetch undo positions. + (RemoveUndo.redo): Re-fetch positions and string. + (RemoveUndo.undo): Update undo positions. + (StickyPosition.mark): New field. + (StickyPosition.offset): Removed field. + (StickyPosition.StickyPosition): Create new Mark. Register + Position in queueOfDeath. Update reference count on mark. + (StickyPosition.getOffset): Return offset stored in mark. + (StickyPosition.setOffset): Removed unneeded method. + (UndoPosRef): New class. Handles undo/redo on positions/marks. + (EMPTY): New field. + (marks): New field. Stores the marks. + (positions): Removed field. + (queueOfDeath): New field. Used for GCing the positions. + (StringContent): Initialize queueOfDeath. + (createPosition): Lazily create marks vector. + (garbageCollect): New helper method. Collects positions + to be GCed and updates their marks. + (getChars): Fixed bounds check. + (getPositionsInRange): When v == null, create new Vector, + otherwise use v. Store UndoPosRefs in vector. + (getString): Added comment about bug in RI. + (insertString): Use new helper method for replacing the array. + Correctly update positions. + (length): Removed this qualifier. + (remove): Use new helper method for replacing the array. + Correctly update positions. + (replace): New helper method for growing or patching the array. + (updateUndoPositions): Implemented. Updates the positions + for undo/redo operations. + +2006-08-27 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/StyleContext.java + (NamedStyle.attributes): Made field transient. + (NamedStyle.changeEvent): Made field transient. + (NamedStyle.name): Removed field. The name is stored as + attribute. + (NamedStyle.NamedStyle(String,Style)): Call setName() for + storing the name and check for null name and resolveParent. + Don't initialize changeEvent. + (NamedStyle.copyAttributes): Return a new NamedStyle, + rather than a plain copy of the attributes field. + (NamedStyle.fireStateChange): Lazily create changeEvent + field. + (NamedStyle.getName): Fetch name from attributes. + (NamedStyle.setName): Store name from attributes. + (NamedStyle.readObject): Implemented for correct + deserialization. + (NamedStyle.writeObject): Implemented for correct + serialization. + (NamedStyle.setResolveParent): When new parent is null, + remove resolveParent attribute. Use addAttribute() method + rather than StyleContext addAttribute(). + (NamedStyle.toString): Fixed to produce output equal to the + RI. + (SmallAttributeSet.resolveParent): New field. + (SmallAttributeSet.SmallAttributeSet(AttributeSet)): Update + the resolveParent field correctly. + (SmallAttributeSet.SmallAttributeSet(Object[])): Don't copy + array but store it directly. Update + the resolveParent field correctly. + (SmallAttributeSet.clone): Return this as the object is + immutable. + (SmallAttributeSet.containsAttributes): Make sure that keys + and values are the same. + (SmallAttributeSet.containsAttribute): Make sure that keys + and values are the same. + (SmallAttributeSet.copyAttributes): Return this as the object is + immutable. + (SmallAttributeSet.equals): Fixed comparison. Two AttributeSet + are equal if they have the same number of attributes and + one contains the other. + (SmallAttributeSet.getAttribute): Improved lookup of + resolveParent. + (SmallAttributeSet.getResolveParent): Improved lookup of + resolveParent. + (SmallAttributeSet.isEqual): When comparing object is a + SmallAttributeSet, consider them equal only if they are the + same object. + (SmallAttributeSet.toString): Fixed to produce output equal to the + RI. + (attributeSetPool): New field. + (defaultStyleContext): Initialize lazily. + (defaultStyle): Removed field. This is stored in the style context + as attribute. + (listenerList): Removed field. The NamedStyle stores the + listeners. + (readAttributeKeys): New static field. Used for looking up + the serialization mappings when reading. + (search): New field. Used as search key. + (staticAttributeKeys): Replaced by read/writeAttributeKeys. + (styles): New field. Stores the styles and listeners. + (styleTable): Removed field. Replaced by styles field. + (writeAttributeKeys): New static field. Used for looking up + the serialization mappings when writing. + (static_initializer): Register mappings for all keys in + StyleConstants. + (StyleContext): Initialize styles correctly. + (addAttributes): Fixed caching of immutable attributes. + (addAttribute): Fixed caching of immutable attributes. + (removeAttributes): Fixed caching of immutable attributes. + (removeAttribute): Fixed caching of immutable attributes. + (addChangeListener): Add listener to styles field. + (removeChangeListener): Remove listener from styles field. + (getChangeListeners): Fetch listeners from styles field. + (addStyle): Add style to styles field. + (cleanupPool): New method. + (getDefaultStyleContext): Lazily create context. + (getEmptySet): Simply return SimpleAttributeSet.EMPTY. + (getMutableAttributeSet): New helper method. Used for + caching. + (getStaticAttribute): Fetch key from readAttributeKeys. + (getStyleNames): Return names from styles field. + (getStyle): Lookup style in styles field. + (removeStyle): Remove style from styles field. + (readAttributeSet): Fixed deserialization. + (writeAttributeSet): Fixed serialization. + (readObject): Fixed deserialization. + (writeObject): Fixed serialization. + (reclaim): Simply cleanup the pool. + (registerStaticAttributeKey): Store mapping in both ways. + (searchImmutableSet): New helper method for caching. + (toString): Fixed for output like the RI. + * javax/swing/text/StyleConstants.java + (keys): New field. Stores all known keys. + (StyleConstants): Store created key in keys list. + * javax/swing/event/EventListenerList.java + (readObject): Fixed deserialization. + (writeObject): Fixed serialization. + +2006-08-25 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/CompositeView.java + (insets): Removed. Replaced by single short fields. + (top): New field. Replaces insets. + (bottom): New field. Replaces insets. + (left): New field. Replaces insets. + (right): New field. Replaces insets. + (CompositeView): Initialize insets fields. + (createDefaultLocation): Removed unneeded method. + (getBottomInset): Return field directly. + (getTopInset): Return field directly. + (getLeftInset): Return field directly. + (getRightInset): Return field directly. + (getInsideAllocation): Adjusted to work on new insets fields. + (getViewIndex): Fixed check. + (loadChildren): Don't replace the old children. + (replace): Make sure that there is an array to operate on. + Only set parent to null, when it is this View. + (setInsets): Adjusted to work with new insets fields. + (setParagraphInsets): Fixed to pull insets directly from + StyleConstants. + +2006-08-25 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/ComponentView.java + (Interceptor): New inner helper class. Used to propagate + invalidate requests and cache component layout sizes. + (interceptor): New field. + (getAlignment): Fetch alignment from interceptor container. + (getComponent): Don't create component here. This is done + in setParent(). + (getMaximumSpan): Fetch layout info from interceptor. Check + for illegal axis. + (getMinimumSpan): Fetch layout info from interceptor. Check + for illegal axis. + (getPreferredSpan): Fetch layout info from interceptor. Check + for illegal axis. + (modelToView): Fixed model to view mapping. + (viewToModel): Fixed view to model mapping. + (paint): Check for null. Set bounds on interceptor rather + then component. + (setParentImpl): Install interceptor between component + and hosting container. + (setParent): Call super.setParent() immediately. + +2006-08-25 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/LabelView.java + (setPropertiesFromAttributes): Only set background when + the corresponding attribute is actually defined, otherwise + set to null, as the StyleConstants would return black. + * javax/swing/text/DefaultStyledDocument.java + (ElementBuffer.documentEvent): Removed obsolete field. + (ElementBuffer.change): Do prepareEdits() and finishEdits() + to correctly update the element structure. + (ElementBuffer.insertContentTag): Removed unused statement. + (ElementBuffer.recreateAfterFracture): Removed + unused obsolete method. + (setCharacterAttributes): Removed unused statement. + +2006-08-25 Roman Kennke <kennke@aicas.com> + + * examples/gnu/classpath/examples/swing/Demo.java + (LaterMain.run): Removed unused local variable. + (Demo): Don't put desktop in scrollpane. + (addChildren): Removed unused method. + (mkButtonBar): Added HTML demo. + (mkMenuBar): Added HTML demo. + (mkPanel): Removed unused method. + (mkScrollPane): Removed unused method. + (mkTree): Removed unused method. + (valign2str): Removed unused method. + * examples/gnu/classpath/examples/swing/HtmlDemo.java: + Initialize text field with some HTML that already works. + (DEBUG): New field. Set to true for debugging output. + (createContent): Dump element tree after parsing. + +2006-08-25 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/html/CSS.java + (getValue): Added color value conversion. + * javax/swing/text/html/HTMLDocument.java + (HTMLReader.ConvertAction): New class, converts HTML style tags + to CSS attributes. + (HTMLReader.initTags): Register ConvertAction for <font> tag. + * javax/swing/text/html/InlineView.java + (setPropertiesFromAttributes): Implemented to fetch + CSS character attributes. + * javax/swing/text/html/StyleSheet.java + (addCSSAttribute): Convert value. + (getBackground): Implemented to fetch CSS background color + attribute. + (getForeground): Implemented to fetch CSS color + attribute. + (getFont): Adjust font size for superscript and subscript. + (translateHTMLToCSS): Rudimentary implementation that + copies the original attributes, so that any CSS attributes in + there are preserved. + (stringToColor): Use CSSColor for conversion. + * gnu/javax/swing/text/html/css/CSSColor.java: + New class. Converts CSS color values to RGB color values. + * gnu/javax/swing/text/html/CharacterAttributeTranslator.java: + Removed. This is more or less replaced by CSSColor and the + ConvertAction in HTMLReader. + +2006-08-25 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/BoxView.java + (childReqs): Removed obsolete field. + (baselineLayout): Reimplemented for correct baseline layout. + (baselineRequirements): Reimplemented for correct baseline + layout. + (updateChildRequirements): Removed obsolete method. + * javax/swing/text/GlyphView.java + (DefaultGlyphPainter.getSpan): Removed unused statement. + (DefaultGlyphPainter.paint): Dont paint subscript/superscript + specially. The subscript/superscript layout is performed + via the alignment, the font is supplied by the StyleContext. + (breakView): Removed unused statements. + (getAlignment): Adjust alignment according to the + superscript/subscript setting. + (getFont): Reimplemented to fetch the font from the style + context, or from the document if the stylecontext is not + available. + (getPreferredSpan): Adjust span for superscript. Use switch + instead of if-else. + * javax/swing/text/LabelView.java + (setPropertiesFromAttributes): Fetch background and foreground + from document / style context. + (isSubscript): Resync properties if needed. + * javax/swing/text/ParagraphView.java + (Row.calculateMinorAxisRequirements): Overridden to perform + a baseline layout. + (Row.layoutMinorAxis): Overridden to perform a baseline layout. + +2006-08-24 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/Utilities.java + (BUF_LENGTH): Removed unused field. + (drawTabbedText): Removed unneeded cast. + (getBreakLocation): Removed unneeded cast. + Fixed offset to account for Segments not starting at 0. + +2006-08-24 Roman Kennke <kennke@aicas.com> + + * javax/swing/plaf/basic/BasicTabbedPaneUI.java + (TabbedPaneLayout.normalizeTabRuns): Removed unused statement. + (TabbedPaneScrollLayout.layoutContainer): Likewise. + (ScrollingPane.updateUI): Likewise. + (calculateTabWidth): Rewritten to correctly and efficiently + layout the tab width. + (layoutLabel): Call SwingUtilities method with the tabPane + as argument. + (paintContentBorderLeftEdge): Removed unused statement. + (paintContentBorderRightEdge): Removed unused statement. + (paintContentBorder): Removed unused statement. + +2006-08-24 Roman Kennke <kennke@aicas.com> + + * javax/swing/plaf/basic/BasicTextUI.java + (RootView.getAttributes): Overridden to return null, + as the RootView has no parent. + +2006-08-24 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/html/CSSParser.java: Removed. + * javax/swing/text/html/CSS.java + (getValue): New helper method. Returns special converter + instances for certain kinds of property values. + * javax/swing/text/html/HTMLDocument.java + (HTMLReader.CharacterAction.start): Don't translate tags + here. Instead, store the attributes directly with the tag + as key. + (content): Removed field. The Content object is handled + by AbstractDocument. + (styleSheet): Removed field. The styleSheet is the styleContext + of this document and handled by the DefaultStyledDocument already. + (HTMLDocument(Content,StyleSheet): Simply call super here. + The super classes already handle the content and styleContext. + (HTMLDocument()): Call this() with a default GapContent and + StyleSheet. + (getStyleSheet): Return the styleContext here. + (insertUpdate): New method. Overridden to add the + CONTENT dummy tag to the element's attributes. + (setBase): Set the base on the styleContext. + * javax/swing/text/html/HTMLEditorKit.java + (styleContext): Removed unneeded field. + (styleSheet): Made field private. + (HTMLEditorKit): Do nothing here. The StyleSheet is + created lazily in getStyleSheet(). A styleContext is not + needed here. + (getStyleSheet): Create StyleSheet correctly. + (insertHTML): Removed unneeded cast. + * javax/swing/text/html/InlineView.java + (attributes): New field. + (changedUpdate): Reload attributes. Trigger preferenceChanged. + (getAttributes): Implemented to fetch the attributes from + the stylesheet. + * javax/swing/text/html/MultiAttributeSet.java: New class. + Multiplexes between several AttributeSets. + * javax/swing/text/html/MultiStyle.java: New class. + Multiplexes between several Styles. + * javax/swing/text/html/ParagraphView.java + (attributes): New field. + (getAttributes): Implemented to fetch the attributes from + the stylesheet. + * javax/swing/text/html/StyleSheet.java + (CssParser): Removed inner class. + (CSSStyle): New inner class. Represents a style defined + by a CSS rule. + (CSSStyleSheetParserCallback): New class, for parsing + CSS stylesheets. + (css): New field. Stores the CSS rules. + (resolvedStyles): New field. Stores resolved styles. + (StyleSheet): Initialize resolvedStyles map. + (addRule): Removed bogus impl. + (getFont): Implemented to fetch font, based on CSS rules. + (getResolvedStyle): New helper method. Looks up resolved + styles, and resolves a style if necessary. + (resolveStyle): New pair of helper methods. Resolves + CSS style rules. + (getRule(String)): Provide rudimentary implementation. + (getRule(Tag,Element)): Implemented. + (getViewAttributes): Implemented. + (loadRules): Implemented. + (translateHTMLToCSS): Tagged as not implemented. + * javax/swing/text/html/ViewAttributeSet.java: New class. + +2006-08-24 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/FlowView.java: + (LogicalView.getAttributes): New method. Overrides super + impl to return the attributes of the FlowView instance. + * javax/swing/text/LabelView.java: + (setPropertiesFromAttributes): Fetch attributes from + View, rather then from the Element. (In the HTML + package the getAttributes() method is overridden to + return different attributes). Fetch font from the StyledDocument. + +2006-08-24 Roman Kennke <kennke@aicas.com> + + * javax/swing/text/DefaultEditorKit.java: + (DefaultKeyTypedAction.actionPerform): Also filter + ALT and CTRL modifiers. + +2006-08-24 Roman Kennke <kennke@aicas.com> + + * gnu/javax/swing/text/html/css/FontSize.java, + * gnu/javax/swing/text/html/css/FontStyle.java, + * gnu/javax/swing/text/html/css/FontWeight.java: + New classes. Used to convert CSS font attributes to AWT/Swing + Font constants. + +2006-08-24 Francis Kung <fkung@redhat.com> + * gnu/java/awt/color/PyccConverter.java: Throw UnsupportedOperationExceptions. + * java/awt/image/ColorConvertOp.java: Updated javadocs. + (srccs, dstcs, rasterValid): Variables removed. + (ColorConvertOp(RenderingHints)): Initialize spaces to empty array. + (copyRaster): Check for null rendering hints + (createCompatibleColorModel): New private method. + (createCompatibleDestImage): Re-implemented. + (createCompatibleDestRaster(Raster, ColorSpace, boolean)): New private method. + (createCompatibleDestRaster(Raster)): Re-implemented. + (filter(BufferedImage, BufferedImage)): Add checks; fix temp image creation. + (filter(Raster, WritableRaster)): Add checks; fix temp raster creation. + (getPoint2D): Clean up formatting. + * java/awt/image/ComponentColorModel.java + (constructor): use findBits method instead of passing null. + (findBits): New method. + +2006-08-24 Gary Benson <gbenson@redhat.com> + + * java/net/NetworkInterface.java (getInetAddresses): Bracket IPv6 + addresses. + +2006-08-24 Jeroen Frijters <jeroen@frijters.net> + + * java/lang/ref/Reference.java + (queue, nextOnQueue): Made volatile. + (enqueue): Made thread safe. + * java/lang/ref/ReferenceQueue.java + (lock): New field. + (poll): Removed synchronized. + (enqueue): Changed to synchronize on lock object, to update Reference + state and return success status. + (dequeue, remove): Synchronize on lock object. + +2006-08-24 Jeroen Frijters <jeroen@frijters.net> + + * java/security/SecureClassLoader.java + (protectionDomainCache): Changed to HashMap. + (SecureClassLoader): Removed redundant security check. + (defineClass(String,byte[],int,int,CodeSource): Moved + protection domain lookup/construction to new method. + (defineClass(String,ByteBuffer,CodeSource): New method. + (getProtectionDomain): New method. + +2006-08-23 Roman Kennke <kennke@aicas.com> + + * javax/swing/JComponent.java + (isRepainting): Made package private. + (paintChild): New field. + (findOpaqueParent): Removed method. This is now in + paintImmediately(). + (findOverlapFreeParent): Removed method. This is now + in paintImmediately2(). + (findPaintRoot): Removed method. This is now + in paintImmediately2(). + (isCompletelyObscured): Changed to take rectangle as single + ints as argument. + (isPaintingDoubleBuffered): Removed method. This is now + in paintImmediately2(). + (isPartiallyObscured): New helper method. + (onTop): New helper method for optimization. + (paintChildren): Paint only to specific child when + requested like this from paintImmediately2(). + (paintDoubleBuffered): Changed to take rectangle as single int + arguments. + (paintImmediately2): Changed to take rectangle as single int + arguments. Optimized determination of paint root. + (paintImmediately(Rectangle)): Change to delegate to + paintImmediately(int,int,int,int). + (paintImmediately(int,int,int,int)): Look for opaque ancestor + and start painting there. + (paint): Call paintDoubleBuffered() with int arguments. Only + paint component, when not completely occupied by opaque child. + (processKeyBinding): Removed unnecessary cast. + (isOccupiedByChild): New helper method. + * javax/swing/RepaintManager.java + (repaintUnderway): Removed obsolete field. + (commitRequests): Removed obsolete field. + (RepaintManager): Removed initialization of obsolete fields. + (addDirtyRegion): Removed unused statement. + (commitBuffer): Changed to take plain ints as argument. + (compileRepaintRoots): Optimized to avoid use of Rectangle. + Compute offsets in place, rather than using SwingUtilities. + (paintDirtyRegions): Removed unused field. + * javax/swing/JMenuItem.java + (onTop): Return true when not descendant of JInternalFrame. + * javax/swing/JPopupMenu.java + (onTop): Return true. + * javax/swing/JToolTip.java + (onTop): Return true. + * javax/swing/JViewport.java + (paintImmediately2): Change signature to match the + corresponding JComponent method. + +2006-08-23 Tania Bento <tbento@redhat.com> + + * java/awt/Color.java + (brighter): Modified algorithm to correctly determine the + new brighter colour. + +2006-08-23 Roman Kennke <kennke@aicas.com> + + * java/awt/Container.java + (maxSize): Removed field. This is already declared in Component. + (validateTree): Check for ContainerPeer. Don't addNotify here. + Only validate Component instances if they are invalid. + +2006-08-22 Roman Kennke <kennke@aicas.com> + + * javax/swing/JComponent.java + (preferredSize): Removed field. + (maximumSize): Removed field. + (minimumSize): Removed field. + (getMaximumSize): Adjusted to delegate to Component, rather + then managing the size in JComponent. + (getMinimumSize): Adjusted to delegate to Component, rather + then managing the size in JComponent. + (getPreferredSize): Adjusted to delegate to Component, rather + then managing the size in JComponent. + (isMaximumSizeSet): Removed. + (isMinimumSizeSet): Removed. + (isPreferredSizeSet): Removed. + (setMaximumSize): Removed. + (setMinimumSize): Removed + (setPreferredSize): Removed. + +2006-08-22 Roman Kennke <kennke@aicas.com> + + * javax/swing/AbstractButton.java + (ButtonChangeListener.stateChanged): Delegate to combined + handler. + (EventHandler): New inner class. Handles all three types + of events on the model. + (eventHandler): New field. Stores the combined event + handler. + (AbstractButton): Moved listener initialization to + setModel(). + (createActionListener): Return combined handler. + (createChangeListener): Return combined handler. + (createItemListener): Return combined handler. + (getEventHandler): New helper method for creating the combined + handler. + (setModel): Initialize listeners here. + * javax/swing/plaf/basic/BasicButtonListener.java + (ButtonAction): New class. Implements the keyboard action + for buttons. + (checkOpacity): Implemented. + (createDefaultActionMap): New helper method. + (installKeyboardActions): Rewritten to install InputMap + and ActionMap according to 'new' keyboard input method. + (mouseClicked): Commented as no-op. + (mouseDragged): Commented as no-op. + (mouseMoved): Commented as no-op. + (propertyChange): Check for contentAreaFilled change and + update opacity. Pull handling of HTLM in font and text handler. + (stateChanged): Repaint button. + (uninstallKeyboardActions): Properly uninstall keyboard actions. + * javax/swing/plaf/basic/BasicButtonUI.java + (listener): Removed. + (sharedListener): New static field. Stores the shared listener. + (sharedUI): New static field. Stores the shared UI. + (createButtonListener): Return shared instance here. + (createUI): Return shared instance here. + (getButtonListener): New helper method. Looks for the + BasicButtonListener installed on a button and returns it. + (installDefaults): Correctly install rollover property here. + Fetch defaultTextShiftOffset. Initialize opaqueness correctly. + (installKeyboardActions): Fetch listener with new helper method. + (installListeners): Don't use removed field. Check for null. + (installUI): Added comment about order of method invocations. + (uninstallDefaults): Don't uninstall non-uninstallable properties. + (uninstallKeyboardActions): Fetch listener with new helper method. + (uninstallListeners): Fetch listener with new helper method. + (paintIcon): Paint icon offset when pressed and armed. + * javax/swing/plaf/metal/MetalButtonListener.java: Removed. + * javax/swing/plaf/metal/MetalButtonUI.java + (sharedUI): New field. Stores the shared UI. + (MetalButtonUI): Don't initialize fields here. + (createButtonListener): Removed method. Use super impl. + (createUI): Return shared instance. + (getDisabledTextColor): Update field here. + (getFocusColor): Update field here. + (getSelectColor): Update field here. + (installDefaults): Don't handle rollover property here. + (uninstallDefaults): Don't handle rollover property here. + (paintButtonPressed): Use accessor method to update the + field value. + +2006-08-21 Mark Wielaard <mark@klomp.org> + + Merge NATIVE_LAYER branch. + + 2006-08-20 Mark Wielaard <mark@klomp.org> + + * doc/tools.texinfo: Add file from trunk. + * native/jni/Makefile.am (DIST_SUBDIRS): Add native-lib. + * native/jni/java-io/java_io_VMFile.c: Include lstat and readlink + headers. + * native/jni/java-lang/java_lang_VMProcess.c + (Java_java_lang_VMProcess_nativeSpawn): Remove redirect argument. + * native/jni/java-net/java_net_VMInetAddress.c + (Java_java_net_VMInetAddress_getHostByName): Remove unused variable. + * native/jni/native-lib/Makefile.am: Remove empty and nonexisting + files. + * native/jni/native-lib/cpio.c (cpio_setFileReadonly): Use correct + mask. + * native/jni/native-lib/cpnet.c (cpnet_connect): Removed unused + theaddr. + * native/jni/native-lib/cpnet.h (cpnet_freeAddresses): Moved from + cpnet.h. + * native/jni/native-lib/cpnet.h (cpnet_freeAddresses): Declare, + don't implement. + * vm/reference/java/lang/VMProcess.java: Removed unused redirect + argument. + * include/java_lang_VMProcess.h: Regenerated. + + 2006-07-09 Guilhem Lavaux <guilhem@kaffe.org> + + * native/jni/java-net/gnu_java_net_VMPlainDatagramSocketImpl.c + (nativeReceive): Fixed the type of the arrays (use java types). + (nativeSendTo): Force throwing an exception if port is 0. + + * native/jni/java-net/javanet.c: + (_javanet_accept): Throw SocketTimeoutException if ETIMEDOUT is + returned. + (_javanet_recvfrom): Likewise. + (_javanet_sendto): Throw a NullPointerException if the socket is + not connected and no address is given. + + * native/jni/java-net/javanet.h + (NULL_EXCEPTION): Defined. + + 2006-06-16 Guilhem Lavaux <guilhem@kaffe.org> + + * native/jni/java-net/java_net_VMInetAddress.c + (Java_java_net_VMInetAddress_getHostByName): Fix detection of + error. + + * native/jni/java-net/javanet.c + (_javanet_accept): Fixed bogus call to TARGET. + (_javanet_create_inetaddress): Fixed address generation. Fixed + bogus memory free. + (_javanet_bind): set "Reuse address" flag. + + * native/jni/native-lib/cpio.c + (cpio_getModificationTime): Fixed type. + (cpio_removeFile): Use rmdir too. + + * native/jni/native-lib/cpnet.c + (cpnet_getHostByName): Fixed error detection. + + * native/jni/native-lib/cpnet.h + (cpnet_newIPV4address, cpnet_newIPV6address): Put zero in the + memory. + (cpnet_IPV4AddressToBytes): Fixed types. + + 2006-06-10 Guilhem Lavaux <guilhem@kaffe.org> + + * native/jni/native-lib/cpio.c + (cpio_openDir, cpio_closeDir, cpio_readDir): Implemented. + + * native/jni/native-lib/cpnet.h: + (cpnet_bytesToIPV4Address): Fixed type casting to avoid being + messed by signs in jbyte. + + * native/jni/native-lib/cpproc.h + (CPIO_EXEC_NUM_PIPES): Compilation fix. + + 2006-05-09 Guilhem Lavaux <guilhem@kaffe.org> + + * native/jni/native-lib/cpnet.c + (cpnet_getSocketTimeout, cpnet_setSocketTimeout): Reimplemented. + (waitForWritable, waitForReadable): New functions. + (socketTimeouts): New static global table to hold timeouts for all + socket fds. + (cpnet_accept,cpnet_bind,cpnet_sendTo,cpnet_recv,cpnet_recvFrom): + Added waitForXXXX safeguards to handle socket timeouts. + + * native/jni/java-net/javanet.c + (_javanet_accept): Check for the right error value when a timeout + occurs. + + 2006-03-25 Guilhem Lavaux <guilhem@kaffe.org> + + * native/jni/java-io/Makefile.am, + native/jni/java-lang/Makefile.am, + native/jni/java-net/Makefile.am, + native/jni/java-nio/Makefile.am: Link to libclasspathnative.la now. + + * native/jni/native-lib/Makefile.am: Added cpproc.c + + * native/jni/native-lib/cpio.c: Implemented missing functions for + CPIO. + + * native/jni/native-lib/cpnet.c + (cpnet_getHostByName): Fixed address array initialization. + + * native/jni/native-lib/cpproc.c: Implemented. + + 2006-02-19 Guilhem Lavaux <guilhem@kaffe.org> + + * configure.ac: Invoke GCC_ATTRIBUTE_UNUSED. + + * m4/gcc_attribute.m4: New file from ac_archive. + + * native/jni/java-net/javanet.c: Adapted to cpnet API + modification. + + * native/jni/native-lib/cpnet.c: Implemented. + + * native/jni/native-lib/cpnet.h + (cpnet_openSocketDatagram, + cpnet_openSocketStream): These calls need an address family now. + (cpnet_IPV4AddressToBytes, + cpnet_bytesToIPV4Address): Convert the address to network order. + + 2006-02-19 Guilhem Lavaux <guilhem@kaffe.org> + + * native/jni/java-io/java_io_VMFile.c, + native/jni/java-nio/gnu_java_nio_channels_FileChannelImpl.c, + native/jni/midi-dssi/dssi_data.h, + native/jni/native-lib/cpio.c, + native/jni/native-lib/cpmath.h: Removed cpmath + dependency. Fixed coding style. + + 2006-02-18 Guilhem Lavaux <guilhem@kaffe.org> + + * native/jni/java-lang/java_lang_VMProcess.c: Removed TARGET + dependency. Simplified the JNI code by moving some part into the + native layer. + + * native/jni/native-lib/cpproc.h: New interface to handle processes. + + 2006-02-18 Guilhem Lavaux <guilhem@kaffe.org> + + * native/jni/java-net/java_net_VMInetAddress.c: Fixed compilation + errors. Removed any remaining TARGET invocations. + + * native/jni/java-net/javanet.c + (_javanet_create_inetaddress): Removed spurious arr and + octets. Fixed compilation errors. + + * native/jni/native-lib/cpnet.h + (cpnet_getHostname, cpnet_getHostByName, cpnet_getHostByAddr, + cpnet_setIPV4Any, cpnet_freeAddresses, cpnet_isIPV6Address, + cpnet_isIPV4Address): New functions. + (cpnet_bytesToIPV4Address): Fixed interface to be consistent with + the rest. + + 2006-01-28 Guilhem Lavaux <guilhem@kaffe.org> + + * native/jni/java-io/java_io_VMFile.c, + native/jni/java-net/gnu_java_net_VMPlainDatagramSocketImpl.c, + native/jni/java-net/gnu_java_net_VMPlainSocketImpl.c, + native/jni/java-net/javanet.c, + native/jni/java-net/javanet.h: Adapted the VM layer code + to the new native layer. + + * native/jni/native-lib/cpnet.h + (cpnet_addMembership, + cpnet_dropMembership, + cpnet_getAvailableBytes): Added the declarations of + some new functions. + (cpnet_newIPV6Address, + cpnet_IPV6AddressToBytes, + cpnet_bytesToIPV6Address): Implemented. + (cpnet_newIPV4Address): Initialize the sin_family field. + + 2006-01-28 Guilhem Lavaux <guilhem@kaffe.org> + + * native/target: Removed. + + * configure.ac: Removed target from CLASSPATH_INCLUDES and + Makefile generation. + + * native/jni/native-lib/Makefile.am, + native/jni/native-lib/cpnet.h, + native/jni/native-lib/cpnet.c + native/jni/native-lib/cpio.h, + native/jni/native-lib/cpio.c, + native/jni/native-lib/cpmath.h: Imported new native compatibility + layer. + +2006-08-21 Roman Kennke <kennke@aicas.com> + + * gnu/javax/swing/text/html/css/CSSParser.java: + New class. + * gnu/javax/swing/text/html/css/CSSParserCallback.java: + New interface. + * gnu/javax/swing/text/html/css/CSSParserException.java: + New exception. + * gnu/javax/swing/text/html/css/CSSScanner.java: + Adjusted API comments. Made all constants package private. + (EOF): New constant field. + (parseBuffer): Made package private. + (tokenEnd): Made package private. + (CSSScanner): Initialize lookahead buffer with -1. + (main): Print out to System.out rather then System.err. + (nextToken): Push back character after IDENT. + +2006-08-21 Jeroen Frijters <jeroen@frijters.net> + + * java/io/File.java (normalizePath): Fixed handling of "//" and "\\". + +2006-08-21 Roman Kennke <kennke@aicas.com> + + * gnu/javax/swing/text/html/css/CSSScanner.java + (main): Use buffered input stream. + (nextToken): Removed 65536 workaround. Use int value directly + without cast to char. + (readComment): Use int value directly without cast to char. + Cast to char only when putting the character into the buffer. + (readEscape): Likewise. + (readIdent): Likewise. + (readName): Likewise. + (readNum): Likewise. + (readString): Likewise. + (readWhitespace): Likewise. + +2006-08-21 Ingo Proetel <proetel@aicas.com> + + * java/io/InputStreamReader.java + (bytesCache): New field. + (cacheLock): New field. + (read(byte[],int,int): Avoid allocations of new byte + array on every call and reuse cached byte array if possible. + +2006-08-21 Roman Kennke <kennke@aicas.com> + + * gnu/java/net/local/LocalSocketImpl.java + Only load native lib if this is supported by runtime. + * native/jni/java-net/local.c + Include config.h unconditionally. + * native/jni/java-net/gnu_java_net_local_LocalSocketImpl.c + Include config.h unconditionally. + +2006-08-21 Friedjof Siebert <siebert@aicas.com> + + * java/io/ObjectInputStream.java + (objectLookupTable): Changed to be a Vector. + (ObjectInputStream): Initialize objectLookupTable as Vector. + (assignNewHandle): Store Object using handle index rather than + Hashtable, using the new rememberHandle() method. + (hierarchy): New method. This replaces inputGetObjectStreamClasses() + with a caching in ObjectStreamClass. + (inputGetObjectStreamClass): Replaced by hierarchy(). + (lookupHandle): New method. Looks up an object by it's handle + index. + (parseContent): Avoid creating of Integer objects. Use + hierarchy() method for looking up the class hierarchy. + (processResolution): Use rememberHandle() to store + handle per index, rather than Hashtabling the object. + (readFields): + (rememberHandle): New method. + * java/io/ObjectOutputStream.java + (OIDLookupTable): Use ObjectIdentityMap2Int instead of + Hashtable for improved lookup performance. + (ObjectOutputStream): Initialize OIDLookupTable as + ObjectIdentityMap2Int. + (assignNewHandle): Change to use ObjectIdentityMap2Int. + (findHandle): Change to use ObjectIdentityMap2Int. + (getBooleanField): Removed. + (getByteField): Removed. + (getCharField): Removed. + (getDoubleField): Removed. + (getField): Removed. + (getFloatField): Removed. + (getIntField): Removed. + (getLongField): Removed. + (getObjectField): Removed. + (writeFields(Object,ObjectStreamClass)): Use new helper method. + (writeFields(Object,ObjectStreamField)): New helper method. + Use switch rather then if-else cascade. + (writeObject): Use int handle, rather then Integer. + * java/io/ObjectStreamClass.java + (hierarchy): New field. Caches the class hierarchy. + (methodCache): New field. Caches methods. + (readObjectSignature): New field. Stores the read signature. + (uidCache): New field. Caches UIDs. + (writeObjectSignature): New field. Stores the write signature. + (cacheMethods): Cache methods in methodCache. + (calculateClassID): Outsourced from getClassUID() + for computing the UIDs. + (getClassUIDFromField): Outsourced from getClassUID() for + fetching the UID from the class field. + (getClassUID): Use cached uid if possible. Use new helper + methods for fetching the UID from the field or computing + from scratch. + (getObjectStreamClasses): Removed. Replaced by more + efficient hierarchy() method, that also caches the result. + (hierarchy): Replaces getObjectStreamClasses() for caching + the result. + (loadedByBootOrApplicationClassLoader): New helper method. + (setClass): Invalidate hierarchy cache. + (setSuperclass): Invalidate hierarchy cache. + * java/io/ObjectStreamField.java + (field): Made field package private for access from other + classes. + * gnu/java/io/ObjectIdentityWrapper.java: Removed. + * gnu/java/io/ObjectIdentityMap2Int.java: Efficient + hashtable for mapping objects to ints. + +2006-08-21 Roman Kennke <kennke@aicas.com> + + * java/io/File.java + (getAbsolutePath): Fetch absolute path from + VMFile.getAbsolutePath(). Moved actual impl to there. + (isAbsolute): Let VMFile determine the absoluteness. + (toURL): Let VMFile convert the filename. + * vm/reference/java/io/VMFile.java + (getAbsolutePath): New method. + (isAbsolute): New method. + (toURL): New method. + +2006-08-21 Jeroen Frijters <jeroen@frijters.net> + + * NEWS: Added note about updated VM interface. + +2006-08-20 Andrew John Hughes <gnu_andrew@member.fsf.org> + + * java/lang/management/ManagementFactory.java: + Updated documentation. + +2006-08-20 Ito Kazumitsu <kaz@maczuka.gcd.org> + + Fixes bug #28412 + * gnu/java/util/regex/CharIndexed.java(move1, setHitEnd, hitEnd): + New methods. + * gnu/java/util/regex/CharIndexedCharSequence.java, + gnu/java/util/regex/CharIndexedInputStream.java: Implemented the + new methods above. + * gnu/java/util/regex/RE.java(REG_FIX_STARTING_POSITION): New flag, + (match): call the new method setHitEnd of the input, + (getMatchImpl): Handle the new flag REG_FIX_STARTING_POSITION, + Some optimization commented out, Use CharIndexed#move1 instead of move. + * gnu/java/util/regex/REMatch.java: Made some debugging methods public. + * gnu/java/util/regex/REToken.java(match): The method body has been + moved to an internal private method, (matchFake): New method, + (setHitEnd): New method. + * gnu/java/util/regex/RETokenChar.java(matchThis): Call setHitEnd + if the match is not complete, (matchOneString): Count the number of + characters which matched the pattern. + * gnu/java/util/regex/RETokenEnd.java(fake): New field, + (setFake): New method, (match): Call super.match or super.matchFake. + * gnu/java/util/regex/RETokenEndSub.java(setHitEnd): New method. + * gnu/java/util/regex/RETokenOneOf.java(match): call the new method + setHitEnd of the input, + * gnu/java/util/regex/RETokenRepeated.java(match): Likewise. + * java/util/regex/Matcher.java(lookingAt, match): Use the new flag + RE.REG_FIX_STARTING_POSITION, (hitEnd, toString): New methods. + +2006-08-18 Tom Tromey <tromey@redhat.com> + + * gnu/javax/swing/text/html/css/CSSScanner.java (readWhitespace): Push + the 'int', not the cast char. + +2006-08-18 Roger Sayle <roger@eyesopen.com> + + * scripts/check_jni_methods.sh: Don't use the "set -C" command + which isn't available in all shells. + +2006-08-18 Roger Sayle <roger@eyesopen.com> + + * lib/Makefile.am (resources): Fix some shell portability issues. + +2006-08-18 Thomas Fitzsimmons <fitzsim@redhat.com> + + * configure.ac (tool-wrappers): Check for ltdl support when tool + wrapper binaries are enabled. + * tools/Makefile.am (LIBJVM): Remove variable. + (AM_CPPFLAGS): Add LIBJVM define. + (gappletviewer_LDFLAGS, gjarsigner_LDFLAGS, gkeytool_LDFLAGS, + gjar_LDFLAGS, gnative2ascii_LDFLAGS, gserialver_LDFLAGS, + gjavah_LDFLAGS): Remove variables. + * tools/toolwrapper.c (main): Use dlopen to load libjvm library. + +2006-08-18 Tom Tromey <tromey@redhat.com> + + * gnu/java/util/prefs/NodeWriter.java (writeParents): Removed + debugging prints. + * gnu/java/util/prefs/NodeReader.java (readNodes): Removed debugging + print. + (readEntries): Likewise. + +2006-08-17 Roman Kennke <kennke@aicas.com> + + * javax/swing/plaf/basic/BasicTabbedPaneUI.java + (layoutLabel): Reset the text and icon rectangles. + +2006-08-17 Thomas Fitzsimmons <fitzsim@redhat.com> + + PR classpath/28537 + * native/plugin/Makefile.am (libgcjwebplugin_la_CXXFLAGS): + Transform gappletviewer name using program_transform_name. + +2006-08-17 Jeroen Frijters <jeroen@frijters.net> + + * javax/swing/filechooser/FileSystemView.java + (getFileSystemView): Always return UnixFileSystemView, since + that's the only one we got. Marked with NotImplementedException. + * javax/swing/plaf/basic/BasicFileChooserUI.java + (mouseClicked, installUI): Don't parse path by hand. + +2006-08-17 Roman Kennke <kennke@aicas.com> + + * javax/swing/JComponent.java + (scrollRectToVisible): Handle intermediate non-JComponents + more gracefully. + +2006-08-17 Roman Kennke <kennke@aicas.com> + + * javax/swing/RepaintManager.java + (blitBuffer): Removed. This is now done in commitBuffer(). + (commitBuffer): Always paint on the root window or applet. + No need to look for intermediate heavyweights. Optimized + rectangle translation. + (commitRemainingBuffers): Removed. Not needed anymore. + (getHeavyweightParent): Removed. Not needed anymore. + (getOffscreenBuffer): Fetch offscreen image from the + actual root component. + (paintDirtyRegions): Don't call commitRemainingBuffers(). + +2006-08-17 Roman Kennke <kennke@aicas.com> + + * javax/swing/SwingUtilities.java + (clipString): New helper method for trimming strings. + (layoutCompoundLabelImpl): Fixed algorithm to conform + testsuites. Trim text if it's too long. Avoid creating + new Rectangles. Optimized for performance. + (layoutCompoundLabel): Use switch rather then if-else-chain. + * javax/swing/plaf/basic/BasicButtonUI.java + (viewR): New field. + (iconR): New field. + (textR): New field. + (paint): Reset and use cached rectangles. Only call paintIcon() + if icon is not null. Don't call paintButtonPressed() when + button is selected, only when it is both armed and pressed. + * javax/swing/plaf/basic/BasicGraphicsUtils.java + (getPreferredButtonSize): Reused cached rectangles rather + then creating new ones. Don't create new Rectangle via + Rectangle.union(). + * javax/swing/plaf/basic/BasicLabelUI.java + (getPreferredSize): Correctly reset cached rectangles. Especially + the view rect must have a big size to give it room for layouting. + Short cut layout when text == null. + (paint): Correctly reset cached rectangles. + * javax/swing/plaf/basic/BasicMenuItemUI.java + (resetRectangles): New helper method. + (getPreferredMenuItemSize): Correctly reset the cached rectangles. + (paintMenuItem): Correctly reset the cached rectangles. + * javax/swing/plaf/basic/BasicRadioButtonUI.java + (getPreferredSize): Use cached Rectangle objects and initialize + them correctly. + (paint): Use cached Rectangle objects and initialize + them correctly. + +2006-08-17 David Gilbert <david.gilbert@object-refinery.com> + + * java/util/Calendar.java: API doc additions. + +2006-08-17 Robert Schuster <robertschuster@fsfe.org> + + * javax/swing/DefaultButtonModel.java: + (setRollover): Simplified statement. + +2006-08-17 Jeroen Frijters <jeroen@frijters.net> + + * gnu/java/rmi/server/RMIClassLoaderImpl.java + (loadClass): Rewritten to use getClassLoader. + (loadProxyClass): Implemented. + (getClassLoader): Fixed support for null or empty codebase. + * gnu/java/rmi/server/RMIObjectInputStream.java + (resolveClass): Use user class loader as default class loader. + (resolveProxyClass): Delegate to RMIClassLoader.loadProxyClass. + * gnu/javax/rmi/CORBA/UtilDelegateImpl.java + (loadClass): Simplified and use user class loader instead of + context class loader as default. + * java/io/ObjectInputStream.java + (currentLoader): Use VMStackWalker.firstNonNullClassLoader(). + * vm/reference/gnu/classpath/VMStackWalker.java + (firstNonNullClassLoader): New method. + * vm/reference/java/io/VMObjectInputStream.java + (loaderAction, currentClassLoader): Removed. + +2006-08-17 Robert Schuster <robertschuster@fsfe.org> + + * javax/swing/plaf/basic/BasicTabbedPaneUI.java: + (getTabBounds(JTabbedPane, int)): Added code to shift rectangle + by current scroll offset, added method documention. + (getTabBounds(int, Rectangle)): Added method documentation. + * javax/swing/plaf/metal/MetalTabbedPaneUI.java: + (paintContentBorderLeftEdge): Changed y to 1. + +2006-08-17 Robert Schuster <robertschuster@fsfe.org> + + * javax/swing/plaf/basic/BasicTabbedPaneUI.java: + (MouseHandler.mouseReleased): Implemented. + (MouseHandler.mousePressed): Added delegation to tabbed pane. + (MouseHandler.mouseEntered): Dito. + (MouseHandler.mouseExited): Dito. + (MouseHandler.mouseMoved): Dito. + (MouseHandler.redispatchEvent): New method. + (PropertyChangeHandler.propertyChange): Added extra block level, + added code to handle tab placement changes, added comment. + (updateViewPosition): Set unneeded coordinate to 0, added comment. + +2006-08-16 Roman Kennke <kennke@aicas.com> + + * gnu/javax/swing/text/html/css/CSSScanner.java: New file. + * gnu/javax/swing/text/html/css/CSSLexicalException.java: + New file. + +2006-08-16 Mark Wielaard <mark@klomp.org> + + * java/awt/Component.java (orientation): Renamed to + componentOrientation. + (setComponentOrientation): Use new field name. + (getComponentOrientation): Likewise. + +2006-08-16 Roman Kennke <kennke@aicas.com> + + PR 28750 + * javax/swing/plaf/basic/BasicTreeUI.java + (CellEditorHandler.editingCancelled): Call completeEditing + directly. + (CellEditorHandler.editingStopped): Call completeEditing + directly. + (NodeDimensionHandler.getNodeDimensions): Rewritten + to use the preferred sizes of the renderer and editor. + (TreeExpansionHandler.treeCollapsed): Complete editing + here. + (TreeSelectionHandler.valueChanged): Complete editing + here. + (cancelEditing): Call completeEditing with false, false and + false. Don't call finish (removed method). + (completeEditing(boolean,boolean,boolean): Only do something when + stopEditingInCompleteEditing is true. Nullify editingComponent + and editingPath. Remove editingComponent from tree. Update + the layout when necessary and repaint. + (completeEditing): Stop editing when necessary. + (editorRequestFocus): New helper method. Request focus + on the actual editor. + (finish) Removed. This is now done in completeEditing(). + (prepareForUIInstall): Set stopEditingInCompleteEditing to true. + (setLargeModel): Complete editing here. + (setRootVisible): Complete editing here. + (setRowHeight): Complete editing here. + (setSelectionModel): Complete editing here. + (startEditing): Correctly initialize and start editing. + (updateExpandedDescendants): Complete editing here. + * javax/swing/tree/DefaultTreeCellEditor.java + (DefaultTextField): Fetch size from super and use renderer's height + if appropriate. + (EditorContainer.EditorContainer): Set layout to null, just + to make sure. + (EditorContainer.doLayout): Layout so that the editor + is offset to the right of the icon. + (EditorContainer.getPreferredSize): Implemented to + provide a reasonable preferred size. + (EditorContainer.paint): Position icon in the middle. + Also paint border if appropriate. + (EditorContainer.setBounds): Removed. + (RealEditorListener): Removed. + (DefaultTreeCellEditor): Set correct border. + (cancelCellEditing): Message real editor. Call finish(). + (createCellEditor): Don't add listener. + (determineOffset): Correctly determine offset, and update + the icon. + (finish): New helper method. + (getTreeCellEditorComponent): Set correct font. Call + prepareForEditing() and determineOffset() to correctly initialize + the state. + (stopCellEditing): Messsage realEditor to stop editing. Call + finish to clean up. + (stopEditingTimer): Removed. + (valueChanged): Correctly reset lastPath. + * javax/swing/tree/DefaultTreeCellRenderer.java + (getPreferredSize): Return super plus some extra space for + better readability. + +2006-08-16 Roman Kennke <kennke@aicas.com> + + * javax/swing/plaf/metal/MetalTreeUI.java + (LineStyleListener): New property listener, that updates + the line style setting if the corresponding property + changes. + (lineStyleListener): New field. + (lineStyle): New field. + (LINE_STYLE_ANGLED): New constant field. + (LINE_STYLE_HORIZONTAL): New constant field. + (LINE_STYLE_NONE): New constant field. + (LINE_STYLE_VALUE_ANGLED): New constant field. + (LINE_STYLE_VALUE_HORIZONTAL): New constant field. + (LINE_STYLE_VALUE_NONE): New constant field. + (LINE_STYLE_PROPERTY): New constant field. + (decodeLineStyle): Implemented. + (installUI): Install line style listener. Set initial + lineStyle. + (uninstallUI): Uninstall line style listener. + (paintHorizontalPartOfLeg): Only call super for angled + lineStyle. + (paintVerticalPartOfLeg): Only call super for angled + lineStyle. + (paintHorizontalSeparators): Implemented. + (paint): If lineStyle==HORIZONTAL, call + paintHorizontalSeparators(). + * examples/gnu/classpath/examples/swing/TreeDemo.java + (createContent): Add panel for selecting line styles. + +2006-08-16 Robert Schuster <robertschuster@fsfe.org> + + * examples/gnu/classpath/demo/swing/TabbedPaneDemo.java: + (createContent): Rewritten. + (createPlacementChangingMenuItem): New method. + (createLayoutPolicyChangingMenuItem): New method. + (createTabbedPane): New method. + (createTabContent): New method. + +2006-08-16 Robert Schuster <robertschuster@fsfe.org> + + * javax/swing/plaf/basic/BasicTabbedPaneUI.java: + (MouseHandler.mousePressed): Fixed indentation, intercept clicks on + disabled tabs, do proper revalidation in WRAP_TAB_LAYOUT mode. + +2006-08-16 Robert Schuster <robertschuster@fsfe.org> + + * javax/swing/text/WrappedPlainView.java: + (WrappedLine.modelToView): Provide variable pos as argument and not a fixed value. + (calculateBreakPosition): Add p0 to return value. + +2006-08-15 Roman Kennke <kennke@aicas.com> + + * javax/swing/plaf/basic/BasicTreeUI.java + (MouseHandler.selectedOnPress): New field. + (MouseHandler.handleEvent): New helper method for handling + selection and start/stop editing for mouse events. + (MouseHandler.mouseDragged): Commented as no-op method. + (MouseHandler.mouseMoved): Commented as no-op method. + (MouseHandler.mousePressed): Use handleEvent() to handle + selection and editing handling. + (MouseHandler.mouseReleased): Use handleEvent() to handle + selection and editing handling. + (MouseInputHandler.MouseInputHandler): Register itself + as mouse listener on source. Redispatch event to + destination. + (MouseInputHandler.dispatch): New helper method. + (MouseInputHandler.mouseClicked): Dispatch event. + (MouseInputHandler.mouseDragged): Dispatch event. + (MouseInputHandler.mouseEntered): Stop dispatching + if dragging stopped. + (MouseInputHandler.mouseExited): Stop dispatching + if dragging stopped. + (MouseInputHandler.mouseMoved): Stop dispatching. + (MouseInputHandler.mousePressed): Marked as no-op. + (MouseInputHandler.mouseReleased): Dispatch and stop + dispatching afterwards. + (MouseInputHandler.removeFromSource): Implemented. + (PropertyChangeHandler.propertyChange): Also handle + editable property changes by calling setEditable(). + (SelectionModelPropertyChangeHandler.propertyChange): + Reset row selection. + (startEditTimer): Removed. + (setCellEditor): Call updateEditor(). + (setEditable): Call updateEditor(). + (startEditingAtPath): Make path fully visible before starting + editing. + (startEditing): Maybe cancel previous edit session. Add + editing component itself, not its parent container. + Register MouseInputHandler for correctly redispatching + initial events. + (stopEditing): Message cellEditor and only completeEditing() + when approved by cell editor. + (updateCellEditor): Complete editing before updating + the cell editor. Get cell editor from JTree if possible, + otherwise create default editor. Update the listeners + on the editor. + * javax/swing/tree/DefaultTreeCellEditor.java + (CLICK_COUNT_TO_START): Removed. + (DefaultTreeCellEditor): Install correct border. Let setTree() + update the listeners. Don't initialize lastPath and font yet. + (actionPerformed): Implemented to start editing. + (createTreeCellEditor): Set click count to start to 1, rather than + 3. + (isCellEditable): Prepare editor here. Determine if we can + start immediately, or if we trigger a timer to do so. + (prepareForEditing): Don't removeAll() (not necessary), + check editingComponent to be non-null. + (setTree): Update listeners. + (shouldStartEditingTimer): Check for left mouse button. + (startEditingTimer): Lazily create timer. + +2006-08-15 Lillian Angel <langel@redhat.com> + + * java/awt/dnd/DropTargetDragEvent.java + (getTransferable): Implemented. + +2006-08-15 Roman Kennke <kennke@aicas.com> + + * java/util/Vector.java + (removeAll): Added comment about NPE. + (retainAll): Added comment about NPE. + +2006-08-15 Roman Kennke <kennke@aicas.com> + + * java/util/zip/ZipFile.java + (UTF8DECODER): Removed. + (UTF8CHARSET): New constant field. Stores the UTF8 charset. + (utf8Decoder): New instance field. + (decodeChars): Lazily create UTF8 decoder. Use instance + field rather than a static field to avoid corruption. + +2006-08-15 Roman Kennke <kennke@aicas.com> + + * java/io/PrintStream.java + (line_separator): Provide default for system property. + * java/io/FileDescriptor.java + (valid): Create local copy of channel field for better + threading safetly. + +2006-08-15 Ingo Proetel <proetel@aicas.com> + + * java/util/zip/ZipFile.java + (PartialInputStream.UTF8DECODER): New constant field, used + for decoding UTF8 strings. + (readLeShort): Access buffer directly if it has enough bytes + available. + (readLeInt): Access buffer directly if it has enough bytes + available. + (decodeChars): New helper method for decoding UTF8 strings. + (readString): Avoid NIO charset decoder if possible. + +2006-08-15 Roman Kennke <kennke@aicas.com> + + * java/util/Vector.java + (removeAll): Don't explicitly null-check here. The RI allows + null arguments when Vector is empty. In other cases we + implicitly throw an NPE. + (retainAll): Don't explicitly null-check here. The RI allows + null arguments when Vector is empty. In other cases we + implicitly throw an NPE. + 2006-08-14 Casey Marshall <csm@gnu.org> Merge in ssl-nio-branch work. See `ChangeLog-ssl-nio' for a record @@ -155,6 +1852,408 @@ * gnu/javax/net/ssl/provider/SynchronizedRandom.java * gnu/javax/net/ssl/provider/XMLSessionContext.java +2006-08-14 Roman Kennke <kennke@aicas.com> + + * javax/swing/plaf/basic/BasicSplitPaneDivider.java + (DividerLayout.layoutContainer): Removed debug output. + * javax/swing/plaf/basic/BasicSplitPaneUI.java + (BasicHorizontalLayoutManager.axis): New field. + (BasicHorizontalLayoutManager.BasicHorizontalLayoutManager(int)): + New constructor. + (BasicHorizontalLayoutManager.BasicHorizontalLayoutManager()): + Call new axis constructor. + (BasicHorizontalLayoutManager.getAvailableSize): Refactored to + handle direction. + (BasicHorizontalLayoutManager.getInitialLocation): Refactored to + handle direction. + (BasicHorizontalLayoutManager.getPreferredSizeOfComponent): + Refactored to handle direction. + (BasicHorizontalLayoutManager.getSizeOfComponent): Refactored + to handle direction. + (BasicHorizontalLayoutManager.minimumLayoutSize): Refactored to + handle direction. + (BasicHorizontalLayoutManager.preferredLayoutSize): Refactored + to handle direction. + (BasicHorizontalLayoutManager.minimumSizeOfComponent): Refactored + to handle direction. + (BasicHorizontalLayoutManager.setComponentToSize): Refactored + to handle direction. + (BasicHorizontalLayoutManager.updateComponents): Don't reset + divider size. + (BasicVerticalLayoutManager.BasicVerticalLayoutManager): + New explicit constructor. Calls super with vertical axis. + (BasicVerticalLayoutManager.getAvailableSize): Functionality moved + to BasicHorizontalLayoutManager. + (BasicVerticalLayoutManager.getInitialLocation): Functionality + moved to BasicHorizontalLayoutManager. + (BasicVerticalLayoutManager.getPreferredSizeOfComponent): + Functionality moved to BasicHorizontalLayoutManager. + (BasicVerticalLayoutManager.getSizeOfComponent): Functionality + moved to BasicHorizontalLayoutManager. + (BasicVerticalLayoutManager.minimumLayoutSize): Functionality + moved to BasicHorizontalLayoutManager. + (BasicVerticalLayoutManager.minimumSizeOfComponent): + Functionality moved to BasicHorizontalLayoutManager. + (BasicVerticalLayoutManager.preferredLayoutSize): Functionality + moved to BasicHorizontalLayoutManager. + (BasicVerticalLayoutManager.setComponentToSize): Functionality + moved to BasicHorizontalLayoutManager. + * javax/swing/plaf/metal/MetalSplitPaneDivider.java + (BUTTON_SPRITE): Renamed to BUTTON_SPRITE_L. + (BUTTON_SPRITE_R): New constant field. + (MetalOneTouchButton.paint): Paint R sprite for right buttons, + L sprite for left buttons. + +2006-08-14 Andreas Tobler <a.tobler@schweiz.ch> + + * native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c + (query_formats): g_free 'name' after usage. + g_strfreev 'ch', the gdk_pixbuf_format_get_extensions instance. + g_strfreev 'ch', the gdk_pixbuf_format_get_mime_types instance. + +2006-08-14 Thomas Fitzsimmons <fitzsim@redhat.com> + + PR classpath/27723 + * configure.ac (MOZILLA_FOUND): Add pkg-config check for + seamonkey-plugin. + +2006-08-14 Francis Kung <fkung@redhat.com> + + PR 28694 + * java/awt/image/ColorModel.java + (coerceData): Added check for non-transparent images. + +2006-08-14 Francis Kung <fkung@redhat.com> + + * java/awt/image/BandCombineOp.java + (BandCombineOp): Perform checks on validity of matrix. + (createCompatibleDestRaster): Add checks and choose raster type dynamically. + (filter): Updated to work with new matrix storage. + (getMatrix): Updated javadoc. + (getPoint2D): Formatting change. + +2006-08-14 Francis Kung <fkung@redhat.com> + + * java/awt/image/AffineTransformOp.java + (AffineTransformOp): Updated javadoc. + (createCompatibleDestImage): Match behaviour of reference implementation. + (createCompatibleDestRaster): Formatting changes. + (filter(BufferedImage, BufferedImage)): Create compatible destination image. + (filter(Raster, WritableRaster)): Re-implemented. + (filterBicubic): New private method. + (filterBilinear): New private method. + (filterNearest): New private method. + (getBounds2D): No longer fixed around one point for rotations. + (getInterpolationType): Add support for bicubic interpolation. + +2006-08-14 Roman Kennke <kennke@aicas.com> + + * javax/swing/plaf/metal/MetalLookAndFeel.java + (MetalLookAndFeel): Moved theme initialization to + getDefaults(). + (createDefaultTheme): Forward to getCurrentTheme(). + (getDefaults): Initialize theme before doing anything else. + (getCurrentTheme): Recognize swing.metalTheme property. + +2006-08-14 Roman Kennke <kennke@aicas.com> + + * javax/swing/JTable + (getScrollableUnitIncrement): Expose partially exposed + row in scrolling direction. + +2006-08-14 Audrius Meskauskas <AudriusA@Bioinformatics.org> + + * javax/swing/JTable (getScrollableUnitIncrement): + Removing my name as the whole method body have been + recently completely replaced. + +2006-08-14 Roman Kennke <kennke@aicas.com> + + PR 28028 + * javax/swing/text/Utilities.java + (getTabbedTextOffset): Don't add p0 here. + +2006-08-14 Roman Kennke <kennke@aicas.com> + + PR 28719 + * javax/swing/plaf/basic/BasicScrollPaneUI.java + (MouseWheelHandler.mouseWheelMoved): Scroll negative delta + when wheel is going up. + +2006-08-14 Roman Kennke <kennke@aicas.com> + + PR 28693 + * javax/swing/plaf/basic/BasicSplitPaneDivider.java + (BasicOneTouchButton): New inner class. + (DividerLayout.changeButtonOrientation): Removed. + (DividerLayout.positionButtons): Moved into layoutContainer. + (DividerLayout.layoutContainer): Reworked for correct layout. + (OneTouchAction): New inner class. + (centerOneTouchButtons): New field. + (BasicSplitPaneDivider): Initialize centerOneTouchButton from + UIManager. + (createLeftOneTouchButton): Reimplemented to return + BasicOneTouchButton. + (createRightOneTouchButton): Reimplemented to return + BasicOneTouchButton. + (getPreferredSize): Reimplemented to return fixed preferredSize. + (oneTouchExpandableChanged): Add OneTouchAction action to + buttons. Don't install mouse listeners. + (MouseHandler.mousePressed): Removed handling of one touch buttons. + (paint): Don't trigger extra paint for buttons. + (propertyChange): Revalidate splitPane when orientation is changed. + (setBasicSplitPaneUI): Call oneTouchExpandableChanged only when + oneTouchExpandable is true. + * javax/swing/plaf/basic/BasicSplitPaneUI.java + (installDefaults): Install dividerSize on the divider too. + * javax/swing/plaf/metal/MetalSplitPaneDivider.java + (MetalDividerLayout): Removed. Functionality is already + in BasicSplitPaneDivider.DividerLayout. + (MetalOneTouchButton): New inner class. + (BUTTON_SPRITE): New constant field. + (MetalSplitPaneDivider): Don't change layout. + (createLeftOneTouchButton): Overridden to return custom button + for Metal. + (createRightOneTouchButton): Overridden to return custom button + for Metal. + (paint): Don't trigger button painting. Call super instead. + +2006-08-13 Roman Kennke <kennke@aicas.com> + + * javax/swing/plaf/basic/BasicTableHeaderUI.java + (installKeyboardAction): Unmarked as stub. Added comment + explaining that the RI seems to do nothing here. + (uninstallKeyboardAction): Unmarked as stub. Added comment + explaining that the RI seems to do nothing here. + +2006-08-13 Roman Kennke <kennke@aicas.com> + + PR 28135 + * javax/swing/ScrollPaneLayout.java + (layoutContainer): Consider the viewportBorder of the + JScrollPane. + (minimumLayoutSize): Consider the viewportBorder of the + JScrollPane. + (preferredLayoutSize): Consider the viewportBorder of the + JScrollPane. + * javax/swing/plaf/basic/BasicScrollPaneUI.java + (installDefaults): Also install viewportBorder if specified. + (paint): Paint viewportBorder if present. + (uninstallDefaults): Uninstall viewportBorder if appropriate. + Don't nullify foreground, background and font. Uninstall + border via LookAndFeel helper method to avoid uninstall + user set border. + +2006-08-13 Roman Kennke <kennke@aicas.com> + + PR 28696 + * javax/swing/plaf/basic/BasicHTML.java + (HTMLRootView.HTMLRootView): Trigger initial layout. + (HTMLRootView.setSize): Overridden to forward to real view. + * javax/swing/plaf/basic/BasicToolTipUI.java + (PropertyChangeHandler): New inner class. Updates the HTML + renderer. + (propertyChangeHandler): New field. + (getMaximumSize): Add HTML width delta. + (getMinimumSize): Add HTML width delta. + (getPreferredSize): Reimplemented to use HTML view for size + calculation if appropriate, otherwise use simple stringWidth() + measurement. + (installListeners): Install propertyChangeHandler. + (uninstallListeners): Uninstall propertyChangeHandler. + (installUI): Update HTML renderer. + (uninstallUI): Update HTML renderer. + (paint): Reimplemented to use HTML view for rendering if + appropriate, simple drawString otherwise. + * javax/swing/plaf/metal/MetalToolTipUI.java + (getPreferredSize): Call super and add accelerator delta. + (paint): Simply call super. + +2006-08-13 Roman Kennke <kennke@aicas.com> + + * javax/swing/JMenu.java + (changeListener): Renamed to menuChangeListener to avoid + shadowing changeListener field from AbstractButton. + +2006-08-13 Roman Kennke <kennke@aicas.com> + + * javax/swing/JTree.java + (getScrollableUnitIncrement): Fixed direction. + (getScrollableBlockIncrement): Implemented to scroll one + page. + * javax/swing/tree/VariableHeightLayoutCache.java + (distance): Consider y + height already outside the node. + +2006-08-13 Roman Kennke <kennke@aicas.com> + + * javax/swing/JTable.java + (getScrollableUnitIncrement): Fixed direction. Make it behave + like the RI. + (getScrollableBlockIncrement): Fixed direction. Make it behave + like the RI. + +2006-08-13 Roman Kennke <kennke@aicas.com> + + * javax/swing/JList.java + (getScrollableUnitIncrement): Fixed direction. Implemented + horizontal scrolling. Improved usability. + (getScrollableBlockIncrement): Fixed direction. Improved usability. + +2006-08-13 Roman Kennke <kennke@aicas.com> + + * javax/swing/plaf/basic/BasicScrollBarUI.java + (scrollByUnit): Scroll by -unit when direction is not positive + and +unit otherwise. + (scrollByBlock): Scroll by -unit when direction is not positive + and +unit otherwise. + +2006-08-13 Roman Kennke <kennke@aicas.com> + + PR 28028 + * javax/swing/text/PlainView.java + (paint): Limit painted area to the lines inside the clip + and allocation. + +2006-08-13 Roman Kennke <kennke@aicas.com> + + * javax/swing/plaf/basic/BasicTextUI.java + (uninstallListeners): Unregister document listener. + +2006-08-13 Sven de Marothy <sven@physto.se> + + * java/util/Locale.java + (hashcodeCache): New field. + (hashCode): use the above field instead of the serialized one + (writeObject): Removed method. + (readObject): Intern strings. + (equals): Revert to previous method. + +2006-08-13 Roman Kennke <kennke@aicas.com> + + * javax/swing/JTabbedPane.java + (JTabbedPane): Call setModel() here and let this install the + change listener correctly. + (setModel): Correctly uninstall and reinstall ChangeListener when + model changes. + +2006-08-13 Raif S. Naffah <raif@swiftdsl.com.au> + + PR Classpath/23952 + * java/util/ResourceBundle.java (CACHE_SIZE): New constant. + (bundleCache): Replaced with an LRU of CACHE_SIZE elements. + (lastDefaultLocale): Removed. + (emptyLocale): Likewise. + (BundleKey.defaultLocale): New field. + (BundleKey.BundleKey): Add a Locale (as a 1st positional) argument. + (BundleKey.set): Likewise. + (BundleKey.equals): Take defaultLocal field into consideration. + (getBundle(String, Locale, ClassLoader)): Use updated BundleKey and LRU. + +2006-08-13 Roman Kennke <kennke@aicas.com> + + * javax/swing/JMenu.java + (MenuChangeListener): New inner class, helps firing menu events. + (changeListener): New field. + (add(text)): Create new JMenuItem here and call add(JMenuItem). + (add(Action)): Create Action using createActionComponent() + and add via add(Component). + (setModel): Install and uninstall MenuChangeListener here. + +2006-08-13 Raif S. Naffah <raif@swiftdsl.com.au> + + PR Classpath/27372 + * java/math/BigInteger.java: Updated copyright year. + (init): Consume as little bytes as possible. + (BigInteger(int, int, Random)): Ensure bitLength bits are used. + (valueOf(String, int)): Throw NumberFormatException for malformed strings + as per RI's documentation. + +2006-08-13 Sven de Marothy <sven@physto.se> + + * java/util/Locale.java + (hashcode): Is a serialized field, not transient. + (equals): Should NOT compare strings by reference. + (readObject/writeObject): Use the default methods and handle the hash + seperately. + +2006-08-13 Raif S. Naffah <raif@swiftdsl.com.au> + + PR Classpath/28678 + * gnu/java/security/Engine.java (getInstance(String, String, Provider)): + Updated documentation. + Formatting. + (getInstance(String, String, Provider, Object[])): Likewise. + Separate checks for null and empty string arguments. + Include as much information as possible in the exception's message. + Do not swallow original exception; instead use it as the cause of the + resulting exception. + * gnu/javax/security/auth/callback/AbstractCallbackHandler.java + (getInstance(String)): Updated documentation. + Formatting. + Store last exception caught when iterating through all providers. + If no implementation found, raise last exception if one was caught. + (getInstance(String, String)): Updated documentation. + Formatting. + Check for null or empty provider as per RI-5's documentation. + (getInstance(String, Provider)): Updated documentation. + Formatting. + Use as much information as possible in the exception message. + Do not swallow original exception; instead use it as the cause for the + ultimate raised exception(s). + * java/security/cert/CertificateFactory.java: Likewise. + * java/security/cert/CertPathBuilder.java: Likewise. + * java/security/cert/CertPathValidator.java: Likewise. + * java/security/cert/CertStore.java: Likewise. + * java/security/AlgorithmParameterGenerator.java: Likewise. + * java/security/AlgorithmParameters.java: Likewise. + * java/security/KeyFactory.java: Likewise. + * java/security/KeyPairGenerator.java: Likewise. + * java/security/KeyStore.java: Likewise. + * java/security/MessageDigest.java: Likewise. + * java/security/SecureRandom.java: Likewise. + * java/security/Signature.java: Likewise. + * javax/crypto/Cipher.java: Likewise. + * javax/crypto/ExemptionMechanism.java: Likewise. + * javax/crypto/KeyAgreement.java: Likewise. + * javax/crypto/KeyGenerator.java: Likewise. + * javax/crypto/Mac.java: Likewise. + * javax/crypto/SecretKeyFactory.java: Likewise. + * javax/net/ssl/KeyManagerFactory.java: Likewise. + * javax/net/ssl/SSLContext.java: Likewise. + * javax/net/ssl/TrustManagerFactory.java: Likewise. + +2006-08-13 Roman Kennke <kennke@aicas.com> + + * javax/swing/JEditorPane.java + (getScrollableTracksViewportHeight): Also check maximum size. + * javax/swing/JTextPane.java + (insertIcon): Use input attributes for adding the icon + attribute. + * javax/swing/plaf/basic/BasicTextUI.java + (RootView.setSize): Overridden to forward to real view. + (getPreferredSize): Trigger setSize() on the view. + (viewToModel(JTextComponent,Point)): Pass Position.Bias array + to viewToModel() call, rather then null. + * javax/swing/text/ParagraphView.java + (changedUpdate): Invalide layout. Call super. + * javax/swing/text/SimpleAttributeSet.java + (clone): Use super's clone method to create clone. + * javax/swing/text/StyleConstants.java + (setIcon): Also set element name attribute. + * javax/swing/text/StyledEditorKit.java + (BoldAction.actionPerformed): Actually set the bold attribute, + not italic. + (setCharacterAttributes): Replaced with more straightforward + impl. + * javax/swing/text/TextAction.java + (getFocusedComponent): Implemented. + * javax/swing/text/Utilities.java + (getNextVisualPositionFrom): Pass Position.Bias arrays instead + of null. + * javax/swing/text/View.java + (changedUpdate): Nullify element change when updateChildren + says so. + 2006-08-11 Andrew John Hughes <gnu_andrew@member.fsf.org> * vm/reference/gnu/java/lang/management/VMMemoryMXBeanImpl.java: @@ -301,6 +2400,12 @@ Avoid a duplicated AccessController.getContext() call. 2006-08-09 Mark Wielaard <mark@klomp.org> + + * doc/www.gnu.org/newsitems.txt: Add 0.92. + * doc/www.gnu.org/downloads/downloads.wml: Likewise. + * doc/www.gnu.org/announce/20060809.wml: New file. + +2006-08-09 Mark Wielaard <mark@klomp.org> * configure.ac (VERSION): Set to 0.92-generics. * NEWS: Add updates for 0.92 release. @@ -5,6 +5,11 @@ New in release 0.93 (UNRELEASED) option to configure * Added the rmi and corbaname URL context factories for JNDI. * Fixes in the JNDI InitialContext now allows to plug-in user implementation. +* Removed currentClassLoader method from + vm/reference/java/io/ObjectInputStream.java. +* Added firstNonNullClassLoader method to + vm/reference/gnu/classpath/VMStackWalker.java. VMs are encouraged to + provide a more efficient implementation. New in release 0.92 (Aug 9, 2006) diff --git a/configure.ac b/configure.ac index 93a42659b..d31bb91b7 100644 --- a/configure.ac +++ b/configure.ac @@ -287,7 +287,11 @@ dnl ----------------------------------------------------------- AC_ARG_ENABLE([tool-wrappers], [AS_HELP_STRING(--enable-tool-wrappers,create tool wrapper binaries [default=no])], [case x"${enableval}" in - xyes) COMPILE_WRAPPERS=yes ;; + xyes) + COMPILE_WRAPPERS=yes; + AC_CHECK_HEADERS([ltdl.h],, [AC_MSG_ERROR(cannot find ltdl.h)]) + AC_CHECK_LIB(ltdl, lt_dlopen,, [AC_MSG_ERROR(cannot find libltdl)]) + ;; xno) COMPILE_WRAPPERS=no ;; x) COMPILE_WRAPPERS=yes ;; *) COMPILE_WRAPPERS=yes ;; @@ -329,6 +333,8 @@ if test "x${COMPILE_COLLECTIONS}" = xyes; then fi if test "x${COMPILE_JNI}" = xyes; then + GCC_ATTRIBUTE_UNUSED + AC_HEADER_STDC dnl Checking sizeof void * is needed for fdlibm to work properly on ppc64, @@ -362,7 +368,7 @@ if test "x${COMPILE_JNI}" = xyes; then lseek fstat read write htonl memset htons connect \ getsockname getpeername bind listen accept \ recvfrom send sendto setsockopt getsockopt time mktime \ - localtime_r \ + gethostbyname_r localtime_r \ strerror_r \ fcntl \ mmap munmap mincore msync madvise getpagesize sysconf \ @@ -544,6 +550,28 @@ if test "x${COMPILE_JNI}" = xyes; then AC_SUBST(QT_CFLAGS) AC_SUBST(QT_LIBS) fi + dnl ********************************************************************** + dnl Check for MSG_NOSIGNAL + dnl ********************************************************************** + AC_MSG_CHECKING(for MSG_NOSIGNAL) + AC_TRY_COMPILE([#include <sys/socket.h>], + [ int f = MSG_NOSIGNAL; ], + [ AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_MSG_NOSIGNAL, 1, + [Define this symbol if you have MSG_NOSIGNAL]) ], + [ AC_MSG_RESULT(no)] + ) + dnl ********************************************************************** + dnl Check for SO_NOSIGPIPE (Darwin equivalent for MSG_NOSIGNAL) + dnl ********************************************************************** + AC_MSG_CHECKING(for SO_NOSIGPIPE ) + AC_TRY_COMPILE([#include <sys/socket.h>], + [ int f = SO_NOSIGPIPE; ], + [ AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_SO_NOSIGPIPE, 1, + [Define this symbol if you have SO_NOSIGPIPE]) ], + [ AC_MSG_RESULT(no)] + ) dnl Check for plugin support headers and libraries. if test "x${COMPILE_PLUGIN}" = xyes; then @@ -557,6 +585,9 @@ if test "x${COMPILE_JNI}" = xyes; then if test "x${MOZILLA_FOUND}" = xno; then PKG_CHECK_MODULES(MOZILLA, mozilla-firefox-plugin, [MOZILLA_FOUND=yes], [MOZILLA_FOUND=no]) fi + if test "x${MOZILLA_FOUND}" = xno; then + PKG_CHECK_MODULES(MOZILLA, seamonkey-plugin, [MOZILLA_FOUND=yes], [MOZILLA_FOUND=no]) + fi PKG_CHECK_MODULES(GLIB, glib-2.0) AC_SUBST(MOZILLA_CFLAGS) @@ -574,7 +605,7 @@ dnl ----------------------------------------------------------- dnl Add the include files for the native abstraction layer. dnl Used by AM_CPPFLAGS in the different modules. dnl ----------------------------------------------------------- -CLASSPATH_INCLUDES="-I\$(top_srcdir)/include -I\$(top_srcdir)/native/jni/classpath -I\$(top_srcdir)/native/target/Linux -I\$(top_srcdir)/native/target/generic" +CLASSPATH_INCLUDES="-I\$(top_srcdir)/include -I\$(top_srcdir)/native/jni/classpath -I\$(top_srcdir)/native/jni/native-lib" AC_SUBST(CLASSPATH_INCLUDES) dnl ----------------------------------------------------------- @@ -846,10 +877,8 @@ native/jni/qt-peer/Makefile native/jni/xmlj/Makefile native/jni/midi-alsa/Makefile native/jni/midi-dssi/Makefile +native/jni/native-lib/Makefile native/plugin/Makefile -native/target/Makefile -native/target/Linux/Makefile -native/target/generic/Makefile resource/Makefile resource/META-INF/services/java.util.prefs.PreferencesFactory scripts/Makefile diff --git a/doc/www.gnu.org/announce/20060809.wml b/doc/www.gnu.org/announce/20060809.wml new file mode 100644 index 000000000..680e3cd4e --- /dev/null +++ b/doc/www.gnu.org/announce/20060809.wml @@ -0,0 +1,225 @@ +#!wml --include=.. + +#use wml::std::page +#use wml::std::lang +#use wml::fmt::isolatin +#use wml::std::case global=upper + +<lang:star:slice:> + +<set-var last-modified-author="mjw"> + +#include <include/macros.wml> + +<header title="GNU Classpath 0.92 Announcement (2006-08-09)"> +<pre> +We are proud to announce the release of GNU Classpath 0.92 Bling! Bling! + +This is the first release that has a full graphics 2D implemenation +based on Cairo enabled by default. This enables the use of applications +like JEdit, FlickrBackup and JFreeChart out of the box. Screenshots of +CairoGraphics2D in action http://www.jfree.org/jfreechart/samples-gnu.html + +Also new in this release is the inclusion of an applet viewer +and plugin that can be embedded in webbrowsers or other applications. +It works on any platform supported by the various runtimes based on +GNU Classpath, including 64 bit architectures. + +Some other highlights in this release (more extensive list below): + + An alternative awt peer implementation based on Escher that uses the + X protocol directly. Various ImageIO providers for png, gif and bmp + images. Support for reading and writing midi files and reading .au + and .wav files have been added. Various tools and support classes + have been added for jar, native2ascii, serialver, keytool, jarsigner. + A GConf based util.peers backend has been added. Support for using + alternative root certificate authorities with the security and crypto + packages. Start of javax.management and runtime lang.managment support. + NIO channels now support scatter-gather operations. + +GNU Classpath, essential libraries for java, is a project to create +free core class libraries for use with runtimes, compilers and tools +for the java programming language. + +The GNU Classpath developer snapshot releases are not directly aimed +at the end user but are meant to be integrated into larger development +platforms. For example the GCC (gcj) and Kaffe projects will use the +developer snapshots as a base for future versions. More projects based +on GNU Classpath: http://www.gnu.org/software/classpath/stories.html + +Also released is classpath-generics-0.92 an experimental branch with +support for all the new 1.5 language features such as generics and +enumerations. ECJ, JamVM, IKVM and Cacao are known to support the +generics release. And you can use it to run Eclipse 3.1 with it to +develop programs that use the new 1.5 language and core library +additions. classpath-generics is a work in progress and not as +extensively tested as our regular releases. But please try it out if +you want to help us test the new 1.5 support of the core libraries. + +The GNU Classpath developers site http://developer.classpath.org/ +provides detailed information on how to start with helping the GNU +Classpath project and gives an overview of the core class library +packages currently provided. For each snapshot release generated +documentation is provided through the GNU Classpath Tools gjdoc +project. A documentation generation framework for java source +files used by the GNU project. Full documentation on the currently +implementated packages and classes can be found at: +http://developer.classpath.org/doc/ + +For more information about the project see also: + +- GNU Classpath home page: + http://www.gnu.org/software/classpath/ + +- Developer information (wiki): + http://developer.classpath.org/ + +- Full class documentation + http://developer.classpath.org/doc/ + +- GNU Classpath hackers: + http://planet.classpath.org/ + +- Autobuilder, current build status, build snapshots: + http://builder.classpath.org/ + +- Application test pages (wiki) + http://developer.classpath.org/mediation/Applets + http://developer.classpath.org/mediation/FreeAWTTestApps + http://developer.classpath.org/mediation/FreeSwingTestApps + http://developer.classpath.org/mediation/FreeSWTTestApps + +- GNU Classpath hacking with Eclipse (wiki) + http://developer.classpath.org/mediation/ClasspathHackingWithEclipse + +- GNU Classpath promotion banners: + http://developer.classpath.org/mediation/ClasspathBanners + +GNU Classpath 0.92 can be downloaded from +ftp://ftp.gnu.org/pub/gnu/classpath/ +or one of the ftp.gnu.org mirrors +http://www.gnu.org/order/ftp.html + +File: classpath-0.92.tar.gz +MD5sum: 4603ef3e593713d94788b919bc0b6c75 +SHA1sum: fab3d6d360f6e9d712fc999f3f085e9f9c8c641a + +File: classpath-0.92-generics.tar.gz (EXPERIMENTAL) +MD5sum: 3efacbefe0224dfe57d9049619095b32 +SHA1sum: 7db4d90e36e40ec676ac813f300265849e36e223 + +New in release 0.92 (Aug 9, 2006) +(See the ChangeLog file for a full list of changes.) + +* libjawtgnu.so has been renamed libjawt.so for binary compatibility. + libjawt.so should be installed in a VM-specific directory rather + than directly in /usr/lib. Proprietary VMs put their libjawt.so + implementations in VM-specific directories but search /usr/lib first. + If GNU Classpath's libjawt.so is installed in /usr/lib it will create + problems for people who use a proprietary VM to run AWT Native + Interface applications. +* The GdkGraphics2D backend has been made the default. There is no + longer an explicit dependency on Cairo, the --enable-gtk-cairo + configure option is gone, and GTK 2.8 or higher is now required to + build the GTK peers. +* A Mozilla plugin, 'gcjwebplugin', is now included. It introduces a + dependency on the Mozilla plugin support headers and libraries. +* New java implementations of png and gif imageio readers and writers. +* A tools.texinfo document has been created and now includes + documentation about: + * appletviewer + * gcjwebplugin + * jarsigner + * keytool +* Several new tools are now included: + * appletviewer + * jar + * native2ascii + * serialver + * keytool + * jarsigner + A new configure option --enable-tool-wrappers causes wrapper + binaries to be built for VMs that support the JNI Invocation API. +* javax.sound.midi providers have been added to read and + write standard MIDI files. +* A javax.sound.sampled .au and .wav file readers have been added. +* New Java Virtual Machine Tool Interface header, jvmti.h. +* AWT peers for X Windows based on Escher (a pure java X protocol + implementation) have been added. So far it supports AWT 1.1 style + Graphics, image loading via ImageIO (PNG, GIF and BMP images in this + release), top level components as well as mouse and keyboard input. + It is capable of running many Swing applications. Graphics2D and + AWT widgets are not yet supported with this peer set. +* GConf based util.peers backend (see the --enable-gconf-peer and + --enable-default-preferences-peer configure options). +* Support for batch importing trusted certificates for use with ssl + connections (see script/import-cacerts.sh). +* NIO scatter-gather channel support. + +Runtime interface changes: + +* A new class, VMURLConnection, is used to implement + URLConnection.guessContentTypeFromStream. The reference + implementation uses libmagic (and falls back to doing nothing if + libmagic is not available). +* The method gnu.java.io.PlatformHelper.toCanonicalForm() has been + replaced with a JNI implementation of VMFile.toCanonicalForm() for + GNU/Posix systems. +* A new class, VMRuntimeMXBeanImpl, is used to implement + the low-level support of the runtime management bean. + VMs should use it to supply the input arguments and start + time of the VM. In addition, one of sun.boot.class.path + or java.boot.class.path should be defined by the VM to + support the optional boot class path access functionality. +* The Unsafe class was moved back to the place expected by the JSR 166 + reference implementation. We've also added a couple other new VM + classes to support the JSR 166 code -- sun.reflect.Reflection and + sun.reflect.misc.ReflectUtil. +* Another new class, VMClassLoadingMXBeanImpl, is used to implement + the low-level support of the class loading management bean. + VMs need to supply it with information about how many classes + are currently loaded, how many have been unloaded and whether + verbose class loading output is on or off. Provision should also + be made for the latter to be toggled at runtime. +* VMThreadMXBeanImpl is used to implement the low-level support + of the thread management bean. Providing this interface requires + providing a fair amount of information about threads, including + optional time and contention monitoring, and instances of the + new ThreadInfo class in java.lang.management. getState() has also + been added to the VMThread interface; this is required by the bean + as well as java.lang.Thread. +* VMMemoryMXBeanImpl is used to implement the low-level support + of the memory management bean. Providing this interface requires + providing information about the levels of heap and non-heap memory, + and the number of objects eligible for garbage collection. +* VMCompilationMXBeanImpl is used to allow for optional compilation + time support for Just-In-Time compilers. +* VMMemoryPoolMXBeanImpl is used to implement the low-level support + of the memory pool beans. Providing this interface requires + providing memory usage statistics for each supported bean. +* VMManagementFactory provides the names of the memory pools, + memory managers and garbage collectors maintained by the virtual + machine. These are used to create the beans by the ManagementFactory. +* VMMemoryManagerMXBeanImpl and VMGarbageCollectorMXBeanImpl provide + low-level support for memory managers (including the specific subclass + of garbage collecting memory managers). The interfaces for these + require no more than enumerating the number of collections and the + time spent (for garbage collectors) and a relationship to the memory + pools (for all), along with a validity check. + +The following people helped with this release: + +Andreas Tobler, Andrew John Hughes, Anthony Balkissoon, Anthony Green, +Archie Cobbs, Audrius Meskauskas, Carsten Neumann, Casey Marshall, +Chris Burdess, Christian Thalinger, C. Scott Marshall, Dalibor Topic, +David Gilbert, Francis Kung, Gary Benson, Henrik Gulbrandsen, Ingo +Proetel, Ito Kazumitsu, Jeroen Frijters, Jim Huang, Kazuya Ujihara, +Keith Seitz, Kyle Galloway, Lillian Angel, Mario Torre, Mark Wielaard, +Martin Platter, Matthew Burgess, Matthew Wringe, Matt Wringe, Michael +Barker, Miriam Schuster, Olivier Jolly, Paul Jenner, Raif S. Naffah, +Robert Schuster, Roman Kennke, Sven de Marothy, Tania Bento, Thomas +Fitzsimmons, Thomas Minor, Tom Tromey and Vivek Lakshmanan + +We would also like to thank the numerous bug reporters and testers! +</pre> +<footer> diff --git a/doc/www.gnu.org/downloads/downloads.wml b/doc/www.gnu.org/downloads/downloads.wml index 838783472..a6b6c1c28 100644 --- a/doc/www.gnu.org/downloads/downloads.wml +++ b/doc/www.gnu.org/downloads/downloads.wml @@ -77,10 +77,10 @@ sub mylink { <download-block> <download - date="15 May 2006" - version="0.91" - url="ftp://ftp.gnu.org/gnu/classpath/classpath-0.91.tar.gz" - notes="http://www.gnu.org/software/classpath/announce/20060515.html" + date="09 Aug 2006" + version="0.92" + url="ftp://ftp.gnu.org/gnu/classpath/classpath-0.92.tar.gz" + notes="http://www.gnu.org/software/classpath/announce/20060809.html" > <!-- download @@ -100,6 +100,12 @@ sub mylink { <download-block> <download + date="15 May 2006" + version="0.91" + url="ftp://ftp.gnu.org/gnu/classpath/classpath-0.91.tar.gz" + notes="http://www.gnu.org/software/classpath/announce/20060515.html" +> +<download date="06 March 2006" version="0.90" url="ftp://ftp.gnu.org/gnu/classpath/classpath-0.90.tar.gz" diff --git a/doc/www.gnu.org/newsitems.txt b/doc/www.gnu.org/newsitems.txt index df0066500..5d5184ac4 100644 --- a/doc/www.gnu.org/newsitems.txt +++ b/doc/www.gnu.org/newsitems.txt @@ -1,3 +1,8 @@ +<newsitem date="09 Aug 2006"> +<createlink name="GNU Classpath 0.92" + url="announce/20060809.html"> +</newsitem> + <newsitem date="15 May 2006"> <createlink name="GNU Classpath 0.91" url="announce/20060515.html"> diff --git a/examples/gnu/classpath/examples/swing/Demo.java b/examples/gnu/classpath/examples/swing/Demo.java index 19bc27c1d..6570cdbad 100644 --- a/examples/gnu/classpath/examples/swing/Demo.java +++ b/examples/gnu/classpath/examples/swing/Demo.java @@ -28,7 +28,6 @@ import java.awt.*; import java.awt.event.*; import javax.swing.*; -import javax.swing.tree.*; import javax.swing.plaf.basic.BasicLookAndFeel; import javax.swing.plaf.metal.DefaultMetalTheme; @@ -161,6 +160,8 @@ public class Demo NavigationFilterDemo.createDemoFactory()))); examples.add(new JMenuItem(new PopupAction("JNI Overhead", JNIOverhead.createDemoFactory()))); + examples.add(new JMenuItem(new PopupAction("HTML Demo", + HtmlDemo.createDemoFactory()))); final JMenuItem vmMenu; @@ -294,21 +295,6 @@ public class Demo return bar; } - private static String valign2str(int a) - { - switch (a) - { - case SwingConstants.CENTER: - return "Center"; - case SwingConstants.TOP: - return "Top"; - case SwingConstants.BOTTOM: - return "Bottom"; - default: - return "Unknown"; - } - } - static String halign2str(int a) { switch (a) @@ -354,17 +340,6 @@ public class Demo return mkButton(null, i, -1, -1, -1, -1); } - - private static JScrollPane mkScrollPane(JComponent inner) - { - JScrollPane jsp; - jsp = new JScrollPane(inner, - JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, - JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - - return jsp; - } - public Demo() { frame = new JFrame("Swing Activity Board"); @@ -376,10 +351,7 @@ public class Demo JPanel main = new JPanel(); main.setLayout(new BoxLayout(main, BoxLayout.Y_AXIS)); desktop = createDesktop(); - - // Put the desktop in a scrollpane. The scrollbars may show then - // up when the them or LaF is changed. - main.add(new JScrollPane(desktop)); + main.add(desktop); main.add(mkButtonBar()); component.add(main, BorderLayout.CENTER); frame.pack(); @@ -391,7 +363,7 @@ public class Demo { public void run() { - Demo demo = new Demo(); + new Demo(); } } @@ -407,16 +379,6 @@ public class Demo return b; } - private static JPanel mkPanel(JComponent[] inners) - { - JPanel p = new JPanel(); - for (int i = 0; i < inners.length; ++i) - { - p.add(inners[i]); - } - return p; - } - static JButton mkDisposerButton(final JFrame c) { JButton close = mkBigButton("Close"); @@ -479,52 +441,6 @@ public class Demo } } - /** - * Create the tree. - * - * @return thr scroll pane, containing the tree. - */ - private static JComponent mkTree() - { - DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root node"); - - addChildren("Node", root, 12); - - JTree tree = new JTree(root); - tree.setLargeModel(true); - DefaultTreeSelectionModel dtsm = new DefaultTreeSelectionModel(); - dtsm.setSelectionMode(DefaultTreeSelectionModel.SINGLE_TREE_SELECTION); - tree.setSelectionModel(dtsm); - - // Make it editable. - tree.setEditable(true); - - JComponent t = mkScrollPane(tree); - t.setPreferredSize(new Dimension(200,200)); - return t; - } - - /** - * Add the specified number of children to this parent node. For each - * child, the method is called recursively adding the nChildren-3 number of - * grandchildren. - * - * @param parent the parent node - * @param nChildren the number of children - */ - private static void addChildren(String name, DefaultMutableTreeNode parent, - int nChildren) - { - for (int i = 0; i < nChildren; i++) - { - String child_name = parent+"."+i; - DefaultMutableTreeNode child = new DefaultMutableTreeNode - (child_name); - parent.add(child); - addChildren(child_name, child, nChildren-3); - } - } - private JPanel mkButtonBar() { JPanel panel = new JPanel(new GridLayout(3, 1, 5, 5)); @@ -558,6 +474,8 @@ public class Demo MetalThemeEditor.createDemoFactory()))); panel.add(new JButton(new PopupAction("JNI Overhead", JNIOverhead.createDemoFactory()))); + panel.add(new JButton(new PopupAction("HTML", + HtmlDemo.createDemoFactory()))); JButton exitDisposer = mkDisposerButton(frame); panel.add(exitDisposer); diff --git a/examples/gnu/classpath/examples/swing/HtmlDemo.java b/examples/gnu/classpath/examples/swing/HtmlDemo.java index 223ee07cd..d31d8cc9d 100644 --- a/examples/gnu/classpath/examples/swing/HtmlDemo.java +++ b/examples/gnu/classpath/examples/swing/HtmlDemo.java @@ -52,6 +52,7 @@ import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.JTextPane; import javax.swing.SwingUtilities; +import javax.swing.text.AbstractDocument; import javax.swing.text.Element; import javax.swing.text.html.HTMLDocument; @@ -62,16 +63,48 @@ import javax.swing.text.html.HTMLDocument; */ public class HtmlDemo extends JPanel { - + + /** + * Setting this to true causes the parsed element structure to be dumped. + */ + private static final boolean DEBUG = true; + JTextPane html = new JTextPane(); - JTextArea text = new JTextArea("<html><body>" + - "123456789HR!<hr>987654321"+ - "123456789BR!<br>987654321"+ - "<p id='insertHere'>Insertion target</p><p>"+ - "<font color=red>ma</font>"+ - "<sup>sup</sup>normal<sub>sub</sub>normal</p><p>Table:"+ - "<table><tr>a<td>b<td>c<tr>x<td>y<td>z</table></body></html>"); + JTextArea text = new JTextArea("<html><body>\n" + + + "<h1>H1 Headline</h1>\n" + + "<h2>H2 Headline</h2>\n" + + "<h3>H3 Headline</h3>\n" + + "<h4>H4 Headline</h3>\n" + + "<h5>H5 Headline</h5>\n" + + "<h6>H6 Headline</h6>\n" + + "<h1>CSS colors via font tag</h1>\n" + + "<p>" + + "<font color=\"maroon\">maroon</font>\n" + + "<font color=\"red\">red</font>\n" + + "<font color=\"orange\">orange</font>\n" + + "<font color=\"yellow\">yellow</font>\n" + + "<font color=\"olive\">olive</font>\n" + + "<font color=\"purple\">purlpe</font>\n" + + "<font color=\"fuchsia\">fuchsia</font>\n" + + "<font color=\"white\">white</font>\n" + + "<font color=\"lime\">lime</font>\n" + + "<font color=\"green\">green</font>\n" + + "<font color=\"navy\">navy</font>\n" + + "<font color=\"blue\">blue</font>\n" + + "<font color=\"aqua\">aqua</font>\n" + + "<font color=\"teal\">teal</font>\n" + + "<font color=\"black\">black</font>\n" + + "<font color=\"silver\">silver</font>\n" + + "<font color=\"gray\">gray</font>\n" + + "</p>" + + "<h1>Some HTML formatting tags</h1>\n" + + "<p>Normal <b>Bold</b> <i>Italic</i> <b><i>Bold + Italic</i></b></p>\n" + + "<p><big>Big</big> <em>Emphasized</em> <small>Small</small>\n" + + "<strike>Strike</strike> <strong>Strong</strong> <u>Underline</u></p>\n" + + "<p>Normal vs <sup>Superscript</sup> vs <sub>Subscript</sub> text</p>\n" + + "</body></html>\n"); JPanel buttons; @@ -111,7 +144,9 @@ public class HtmlDemo extends JPanel String t = text.getText(); System.out.println("HtmlDemo.java.createContent:Parsing started"); html.setText(t); - System.out.println("HtmlDemo.java.createContent:Parsing completed"); + System.out.println("HtmlDemo.java.createContent:Parsing completed"); + if (DEBUG) + ((AbstractDocument) html.getDocument()).dump(System.out); } }); diff --git a/examples/gnu/classpath/examples/swing/TabbedPaneDemo.java b/examples/gnu/classpath/examples/swing/TabbedPaneDemo.java index 527fe455e..bd0e6bf15 100644 --- a/examples/gnu/classpath/examples/swing/TabbedPaneDemo.java +++ b/examples/gnu/classpath/examples/swing/TabbedPaneDemo.java @@ -39,15 +39,24 @@ exception statement from your version. */ package gnu.classpath.examples.swing; import java.awt.BorderLayout; +import java.awt.Color; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JMenu; +import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JScrollPane; import javax.swing.JTabbedPane; +import javax.swing.JTextArea; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; @@ -55,6 +64,10 @@ public class TabbedPaneDemo extends JPanel implements ActionListener { + static Color[] colors = { Color.BLUE, Color.CYAN, Color.GRAY, Color.GREEN, + Color.MAGENTA, Color.ORANGE, Color.PINK, + Color.ORANGE, Color.RED, Color.BLUE, Color.YELLOW + }; TabbedPaneDemo() { super(); @@ -64,25 +77,123 @@ public class TabbedPaneDemo private void createContent() { JPanel p = new JPanel(); - p.setLayout(new GridLayout(2, 2)); - JTabbedPane tabs1 = new JTabbedPane(SwingConstants.TOP); - tabs1.add("Top Item 1", new JButton("Content: Top Item 1")); - tabs1.add("Top Item 2", new JButton("Content: Top Item 2")); - JTabbedPane tabs2 = new JTabbedPane(SwingConstants.LEFT); - tabs2.add("Left Item 1", new JButton("Content: Left Item 1")); - tabs2.add("Left Item 2", new JButton("Content: Left Item 2")); - JTabbedPane tabs3 = new JTabbedPane(SwingConstants.BOTTOM); - tabs3.add("Bottom Item 1", new JButton("Content: Bottom Item 1")); - tabs3.add("Bottom Item 2", new JButton("Content: Bottom Item 2")); - JTabbedPane tabs4 = new JTabbedPane(SwingConstants.RIGHT); - tabs4.add("Right Item 1", new JButton("Content: Right Item 1")); - tabs4.add("Right Item 2", new JButton("Content: Right Item 2")); - p.add(tabs1); - p.add(tabs2); - p.add(tabs3); - p.add(tabs4); + p.setLayout(new GridLayout(1, 1)); + + int COUNT = 25; + JTabbedPane tp = createTabbedPane(SwingConstants.TOP, "tab", COUNT); + p.add(tp); + + final JPopupMenu popup = new JPopupMenu(); + + JMenu menu = new JMenu("tab placement"); + menu.add(createPlacementChangingMenuItem("top", + SwingConstants.TOP, + tp)); + + menu.add(createPlacementChangingMenuItem("bottom", + SwingConstants.BOTTOM, + tp)); + + menu.add(createPlacementChangingMenuItem("left", + SwingConstants.LEFT, + tp)); + + menu.add(createPlacementChangingMenuItem("right", + SwingConstants.RIGHT, + tp)); + popup.add(menu); + + menu = new JMenu("tab layout"); + menu.add(createLayoutPolicyChangingMenuItem("wrapping tabs", + JTabbedPane.WRAP_TAB_LAYOUT, + tp)); + + menu.add(createLayoutPolicyChangingMenuItem("scrolling tabs", + JTabbedPane.SCROLL_TAB_LAYOUT, + tp)); + popup.add(menu); + + tp.addMouseListener(new MouseAdapter() + { + public void mousePressed(MouseEvent e) { + showPopup(e); + } + + public void mouseReleased(MouseEvent e) { + showPopup(e); + } + + void showPopup(MouseEvent e) { + if (e.isPopupTrigger()) { + popup.show(e.getComponent(), e.getX(), e.getY()); + } + } + }); + setLayout(new BorderLayout()); add(p, BorderLayout.CENTER); + + } + + private JMenuItem createPlacementChangingMenuItem(String t, + final int v, + final JTabbedPane dst) + { + JMenuItem item = new JMenuItem(t); + + item.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent ae) + { + dst.setTabPlacement(v); + } + }); + + return item; + } + + private JMenuItem createLayoutPolicyChangingMenuItem(String t, + final int v, + final JTabbedPane dst) + { + JMenuItem item = new JMenuItem(t); + + item.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent ae) + { + dst.setTabLayoutPolicy(v); + } + }); + + return item; + } + + private JTabbedPane createTabbedPane(int direction, String name, int count) + { + JTabbedPane pane = new JTabbedPane(direction); + + for(int i = 0; i< count; i++) + { + pane.addTab(name + " " + i, createTabContent(name + " " + i)); + if (Math.random() >= 0.75) + pane.setEnabledAt(i, false); + } + + return pane; + } + + private JPanel createTabContent(String name) + { + JTextArea ta; + JPanel panel = new JPanel(); + panel.add(new JLabel(name)); + panel.add(new JButton(name)); + panel.add(new JScrollPane(ta = new JTextArea(5, 5))); + + ta.setBackground(colors[(int) (Math.random() * colors.length)]); + + return panel; } public void actionPerformed(ActionEvent e) diff --git a/examples/gnu/classpath/examples/swing/TreeDemo.java b/examples/gnu/classpath/examples/swing/TreeDemo.java index 32f765f73..8da375071 100644 --- a/examples/gnu/classpath/examples/swing/TreeDemo.java +++ b/examples/gnu/classpath/examples/swing/TreeDemo.java @@ -39,17 +39,17 @@ exception statement from your version. */ package gnu.classpath.examples.swing; import java.awt.BorderLayout; -import java.awt.JobAttributes.DefaultSelectionType; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import javax.swing.DebugGraphics; +import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTree; import javax.swing.SwingUtilities; @@ -59,7 +59,6 @@ import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.TreePath; -import javax.swing.tree.TreeSelectionModel; public class TreeDemo extends JPanel @@ -222,11 +221,39 @@ public class TreeDemo p2.add(add); p2.add(cbSingle); p2.add(cbRoot); - + tree.getSelectionModel(). setSelectionMode(DefaultTreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); + // Panel for selecting line style. + ActionListener l = new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + JRadioButton b = (JRadioButton) e.getSource(); + tree.putClientProperty("JTree.lineStyle", b.getText()); + tree.repaint(); + } + }; + JPanel lineStylePanel = new JPanel(); + ButtonGroup buttons = new ButtonGroup(); + lineStylePanel.add(new JLabel("Line style: ")); + JRadioButton none = new JRadioButton("None"); + lineStylePanel.add(none); + buttons.add(none); + none.addActionListener(l); + JRadioButton angled = new JRadioButton("Angled"); + lineStylePanel.add(angled); + buttons.add(angled); + angled.addActionListener(l); + JRadioButton horizontal = new JRadioButton("Horizontal"); + lineStylePanel.add(horizontal); + buttons.add(horizontal); + horizontal.addActionListener(l); + p2.add(lineStylePanel); + add(p2, BorderLayout.NORTH); + add(new JScrollPane(tree), BorderLayout.CENTER); add(choice, BorderLayout.SOUTH); } diff --git a/gnu/java/awt/color/PyccConverter.java b/gnu/java/awt/color/PyccConverter.java index cd50d8776..77ea28a3e 100644 --- a/gnu/java/awt/color/PyccConverter.java +++ b/gnu/java/awt/color/PyccConverter.java @@ -37,7 +37,6 @@ exception statement from your version. */ package gnu.java.awt.color; - /** * PyccConverter - conversion routines for the PhotoYCC colorspace * @@ -52,21 +51,21 @@ public class PyccConverter implements ColorSpaceConverter { public float[] toRGB(float[] in) { - return null; + throw new UnsupportedOperationException(); } public float[] fromRGB(float[] in) { - return null; + throw new UnsupportedOperationException(); } public float[] toCIEXYZ(float[] in) { - return null; + throw new UnsupportedOperationException(); } public float[] fromCIEXYZ(float[] in) { - return null; + throw new UnsupportedOperationException(); } } diff --git a/gnu/java/io/ObjectIdentityMap2Int.java b/gnu/java/io/ObjectIdentityMap2Int.java new file mode 100644 index 000000000..08f089d79 --- /dev/null +++ b/gnu/java/io/ObjectIdentityMap2Int.java @@ -0,0 +1,292 @@ +/* ObjectIdentityMapToInt.java -- Helper class for faster serialization + 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 gnu.java.io; + +/** + * This class provides a map from Object to non-negative int values. + * Objects are considered equal only if their references are equal. + * + * This can be used to equip objects with an integer id. This class + * is implemented to use as little memory as possible, particularly + * not to create hashtable buckets and Integer instances for each + * mapping. + * + * @author Fridtjof Siebert (siebert@aicas.com) + */ +public class ObjectIdentityMap2Int +{ + + + /** + * Prime numbers used as size of array. We need the size to be a + * prime number since the delta used for conflict resulution must + * not have any common divisors with the length. + */ + private static final int[] PRIMES = { + 0x1f, + 0x3d, + 0x7f, + 0xfb, + 0x1fd, + 0x3fd, + 0x7f7, + 0xffd, + 0x1fff, + 0x3ffd, + 0x7fed, + 0xfff1, + 0x1ffff, + 0x3fffb, + 0x7ffff, + 0xffffd, + 0x1ffff7, + 0x3ffffd, + 0x7ffff1, + 0xfffffd, + 0x1ffffd9, + 0x3fffffb, + 0x7ffffd9, + 0xfffffc7, + 0x1ffffffd, + 0x3fffffdd, + 0x7fffffff}; + + + /** + * Object to be used instead of "null" + */ + private static final Object NIL = new Object(); + + + /** + * The objects in this map: + * + * invariant + * objectTable.size == PRIMES[cap] + */ + private Object[] objectTable; + + + /** + * The corresponding integer ids. + * + * invariant + * intTable.size == PRIMES[cap] + */ + private int[] intTable; + + + /** + * The number of entries in this map. + * + * invariant + * size < limit + */ + private int size = 0; + + + /** + * The index in primes of the size of the tables. + */ + private int cap = 0; + + + /** + * The limit for size at which the table size is increased. + * + * invariant + * limit = PRIMES[cap] / 4 * 3; + */ + private int limit = 0; + + + /** + * Constructs an empty <code>ObjectIdentityMap2Int</code>. + */ + public ObjectIdentityMap2Int() + { + alloc(0); + } + + + /** + * Helper function to alloc the object and int array for the given + * capacity. Set limit, reset size to 0. + * + * No elements will be stored in the newly allocated arrays. + * + * @param c the capacity: this is an index in PRIMES, PRIMES[c] + * gives the size of the arrays. + * + * @throws InternalError if c >= PRIMES.length (in this case, a + * normal Hashtable would throw an OutOfMemoryError or a + * NegativeArraySizeException since the array size exceeds the range + * of positive integers). + */ + private void alloc(int c) + { + if (c >= PRIMES.length) + throw new InternalError("Hash table size overflow"); + + cap = c; + int len = PRIMES[c]; + objectTable = new Object[len]; + intTable = new int[len]; + limit = len / 4 * 3; + + size = 0; + } + + + /** + * Add a mapping to this Map. + * + * ensures + * (get(o) == i); + * + * @param o object reference or null that is to be mapped. + * + * @param i the integer id to be associated with o + * + * @throws IllegalArgumentException if i<0 + * + * @throws InternalError if hash tables has grown to more then + * 0x7fffffff entries (ie., size >= 0x7fffffff*3/4). + */ + public void put(Object o, int i) + { + if (i < 0) + throw new IllegalArgumentException("int argument must be postive: "+i); + + o = (o == null) ? NIL : o; + int s = slot(o); + Object[] ot = objectTable; + intTable[s] = i; + if (objectTable[s] == null) + { + objectTable[s] = o; + size++; + if (size >= limit) + { + rehash(); + } + } + } + + + /** + * Helper function to find the index of a free or existing slot for + * object o + * + * ensure + * ((objectTable[result] != null) IMPLIES (objectTable[result] == o)); + * + * @param o an object, must not be null. + * + * @return an index of o + */ + private int slot(Object o) + { + Object[] ot = objectTable; + int hc = System.identityHashCode(o); + int len = ot.length; + int result = hc % len; + result = result < 0 ? -result : result; + int delta = 16 - (hc & 15); + Object existing = ot[result]; + while ((existing != null) && (existing != o)) + { + result += delta; + if (result >= len) + result -= len; + existing = ot[result]; + } + return result; + } + + + /** + * Helper function for put() to increaes the capacity of this table + * to the next size (approx. double the size). Keep the mapping and + * the size unchanged. + * + * ensure + * (cap == \old cap+1); + */ + private void rehash() + { + Object[] ot = objectTable; + int [] it = intTable; + alloc(cap + 1); + + for (int i = 0; i < ot.length; i++) + put(ot[i], it[i]); + } + + + /** + * Obtain an element from this map + * + * @param o an object or null + * + * @return the corresponding integer id for o or -1 if o has not + * been put into this map. + */ + public int get(Object o) + { + o = (o == null) ? NIL : o; + int s = slot(o); + return objectTable[s] == null ? -1 : intTable[s]; + } + + /** + * Clear this map + * + * ensures + * ((size == 0) && \forall Object o: get(o) == -1) + */ + public void clear() + { + Object[] ot = objectTable; + size = 0; + for (int i = 0; i < ot.length; i++) + ot[i] = null; + } + +} diff --git a/gnu/java/net/local/LocalSocketImpl.java b/gnu/java/net/local/LocalSocketImpl.java index f43305a80..f49b79947 100644 --- a/gnu/java/net/local/LocalSocketImpl.java +++ b/gnu/java/net/local/LocalSocketImpl.java @@ -38,6 +38,8 @@ exception statement from your version. */ package gnu.java.net.local; +import gnu.classpath.Configuration; + import java.io.FileDescriptor; import java.io.InputStream; import java.io.IOException; @@ -66,7 +68,10 @@ final class LocalSocketImpl extends SocketImpl { try { - System.loadLibrary ("javanet"); + if (Configuration.INIT_LOAD_LIBRARY) + { + System.loadLibrary ("javanet"); + } } catch (Exception x) { diff --git a/gnu/java/rmi/server/RMIClassLoaderImpl.java b/gnu/java/rmi/server/RMIClassLoaderImpl.java index 2e1e78055..82f0ff69e 100644 --- a/gnu/java/rmi/server/RMIClassLoaderImpl.java +++ b/gnu/java/rmi/server/RMIClassLoaderImpl.java @@ -1,5 +1,5 @@ /* RMIClassLoaderImpl.java -- FIXME: briefly describe file purpose - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,6 +38,7 @@ exception statement from your version. */ package gnu.java.rmi.server; +import java.lang.reflect.Proxy; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; @@ -186,6 +187,7 @@ public class RMIClassLoaderImpl extends RMIClassLoaderSpi { defaultClassLoader = new MyClassLoader (new URL[] { defaultCodebase }, null, defaultAnnotation); + // XXX using getContextClassLoader here *cannot* be right cacheLoaders.put (new CacheKey (defaultAnnotation, Thread.currentThread().getContextClassLoader()), defaultClassLoader); @@ -216,47 +218,53 @@ public class RMIClassLoaderImpl extends RMIClassLoaderSpi ClassLoader defaultLoader) throws MalformedURLException, ClassNotFoundException { - ClassLoader loader; - if (defaultLoader == null) - loader = Thread.currentThread().getContextClassLoader(); - else - loader = defaultLoader; - - //try context class loader first try { - return Class.forName(name, false, loader); + if (defaultLoader != null) + return Class.forName(name, false, defaultLoader); } catch (ClassNotFoundException e) { - // class not found in the local classpath + } + + return Class.forName(name, false, getClassLoader(codeBase)); + } + + public Class loadProxyClass(String codeBase, String[] interfaces, + ClassLoader defaultLoader) + throws MalformedURLException, ClassNotFoundException + { + Class clss[] = new Class[interfaces.length]; + + for (int i = 0; i < interfaces.length; i++) + { + clss[i] = loadClass(codeBase, interfaces[i], defaultLoader); } - if (codeBase.length() == 0) //=="" + // Chain all class loaders (they may differ). + ArrayList loaders = new ArrayList(clss.length); + ClassLoader loader = null; + for (int i = 0; i < clss.length; i++) { - loader = defaultClassLoader; + loader = clss[i].getClassLoader(); + if (! loaders.contains(loader)) + { + loaders.add(0, loader); + } } - else + if (loaders.size() > 1) { - loader = getClassLoader(codeBase); + loader = new CombinedClassLoader(loaders); } - if (loader == null) + try { - //do not throw NullPointerException - throw new ClassNotFoundException ("Could not find class (" + name + - ") at codebase (" + codeBase + ")"); + return Proxy.getProxyClass(loader, clss); + } + catch (IllegalArgumentException e) + { + throw new ClassNotFoundException(null, e); } - - return Class.forName(name, false, loader); - } - - public Class loadProxyClass(String codeBase, String[] interfaces, - ClassLoader defaultLoader) - throws MalformedURLException, ClassNotFoundException - { - // FIXME: Implement this. - return null; } /** @@ -272,6 +280,9 @@ public class RMIClassLoaderImpl extends RMIClassLoaderSpi public ClassLoader getClassLoader(String codebase) throws MalformedURLException { + if (codebase == null || codebase.length() == 0) + return Thread.currentThread().getContextClassLoader(); + ClassLoader loader; CacheKey loaderKey = new CacheKey (codebase, Thread.currentThread().getContextClassLoader()); diff --git a/gnu/java/rmi/server/RMIObjectInputStream.java b/gnu/java/rmi/server/RMIObjectInputStream.java index e76535447..75f4f1202 100644 --- a/gnu/java/rmi/server/RMIObjectInputStream.java +++ b/gnu/java/rmi/server/RMIObjectInputStream.java @@ -1,5 +1,5 @@ /* RMIObjectInputStream.java -- - Copyright (c) 1996, 1997, 1998, 1999, 2002, 2004 + Copyright (c) 1996, 1997, 1998, 1999, 2002, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,11 +39,11 @@ exception statement from your version. */ package gnu.java.rmi.server; +import gnu.classpath.VMStackWalker; import java.io.IOException; import java.io.InputStream; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; -import java.lang.reflect.Proxy; import java.net.MalformedURLException; import java.rmi.server.RMIClassLoader; import java.util.ArrayList; @@ -57,16 +57,14 @@ public RMIObjectInputStream(InputStream strm) throws IOException { } protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { - String annotation = (String)getAnnotation(); - try { - if(annotation == null) - return (RMIClassLoader.loadClass(desc.getName())); - else - return (RMIClassLoader.loadClass(annotation, desc.getName())); + return RMIClassLoader.loadClass( + (String)getAnnotation(), + desc.getName(), + VMStackWalker.firstNonNullClassLoader()); } - catch (MalformedURLException _) { - throw new ClassNotFoundException(desc.getName()); + catch (MalformedURLException x) { + throw new ClassNotFoundException(desc.getName(), x); } } @@ -81,45 +79,16 @@ protected Object getAnnotation() protected Class resolveProxyClass(String intfs[]) throws IOException, ClassNotFoundException { - String annotation = (String) getAnnotation(); - - Class clss[] = new Class[intfs.length]; - - for (int i = 0; i < intfs.length; i++) - { - if (annotation == null) - clss[i] = RMIClassLoader.loadClass(intfs[i]); - else - clss[i] = RMIClassLoader.loadClass(annotation, intfs[i]); - } - - ClassLoader loader; - - if (clss.length > 0) - { - // Chain all class loaders (they may differ). - ArrayList loaders = new ArrayList(intfs.length); - ClassLoader cx; - for (int i = 0; i < clss.length; i++) - { - cx = clss[i].getClassLoader(); - if (!loaders.contains(cx)) - { - loaders.add(0, cx); - } - } - loader = new CombinedClassLoader(loaders); - } - else - loader = ClassLoader.getSystemClassLoader(); - - try + try { - return Proxy.getProxyClass(loader, clss); + return RMIClassLoader.loadProxyClass( + (String)getAnnotation(), + intfs, + VMStackWalker.firstNonNullClassLoader()); } - catch (IllegalArgumentException e) + catch (MalformedURLException x) { - throw new ClassNotFoundException(null, e); + throw new ClassNotFoundException(null, x); } } diff --git a/gnu/java/security/Engine.java b/gnu/java/security/Engine.java index c6271e3f2..44318af8e 100644 --- a/gnu/java/security/Engine.java +++ b/gnu/java/security/Engine.java @@ -79,158 +79,170 @@ public final class Engine /** This class cannot be instantiated. */ private Engine() { } - // Class method. - // ------------------------------------------------------------------------ - /** - * Get the implementation for <i>algorithm</i> for service - * <i>service</i> from <i>provider</i>. The service is e.g. - * "Signature", and the algorithm "DSA". - * - * @param service The service name. + * Return the implementation for <i>algorithm</i> for service <i>service</i> + * from <i>provider</i>. The service is e.g. "Signature", and the algorithm + * "DSA". + * + * @param service The service name. * @param algorithm The name of the algorithm to get. - * @param provider The provider to get the implementation from. - * @return The engine class for the specified algorithm; the object - * returned is typically a subclass of the SPI class for that - * service, but callers should check that this is so. - * @throws NoSuchAlgorithmException If the implementation cannot be - * found or cannot be instantiated. - * @throws InvocationTargetException If the SPI class's constructor - * throws an exception. - * @throws IllegalArgumentException If any of the three arguments are null. + * @param provider The provider to get the implementation from. + * @return The engine class for the specified algorithm; the object returned + * is typically a subclass of the SPI class for that service, but + * callers should check that this is so. + * @throws NoSuchAlgorithmException If the implementation cannot be found or + * cannot be instantiated. + * @throws InvocationTargetException If the SPI class's constructor throws an + * exception. + * @throws IllegalArgumentException If any of the three arguments is null. */ public static Object getInstance(String service, String algorithm, Provider provider) - throws InvocationTargetException, NoSuchAlgorithmException + throws InvocationTargetException, NoSuchAlgorithmException { return getInstance(service, algorithm, provider, NO_ARGS); } /** - * Get the implementation for <i>algorithm</i> for service - * <i>service</i> from <i>provider</i>, passing <i>initArgs</i> to the - * SPI class's constructor (which cannot be null; pass a zero-length - * array if the SPI takes no arguments). The service is e.g. - * "Signature", and the algorithm "DSA". - * - * @param service The service name. + * Return the implementation for <i>algorithm</i> for service <i>service</i> + * from <i>provider</i>, passing <i>initArgs</i> to the SPI class's + * constructor (which cannot be null; pass a zero-length array if the SPI + * takes no arguments). The service is e.g. "Signature", and the algorithm + * "DSA". + * + * @param service The service name. * @param algorithm The name of the algorithm to get. - * @param provider The provider to get the implementation from. - * @param initArgs The arguments to pass to the SPI class's - * constructor (cannot be null). - * @return The engine class for the specified algorithm; the object - * returned is typically a subclass of the SPI class for that - * service, but callers should check that this is so. - * @throws NoSuchAlgorithmException If the implementation cannot be - * found or cannot be instantiated. - * @throws InvocationTargetException If the SPI class's constructor - * throws an exception. - * @throws IllegalArgumentException If any of the four arguments are null. + * @param provider The provider to get the implementation from. + * @param initArgs The arguments to pass to the SPI class's constructor + * (cannot be null). + * @return The engine class for the specified algorithm; the object returned + * is typically a subclass of the SPI class for that service, but + * callers should check that this is so. + * @throws NoSuchAlgorithmException If the implementation cannot be found or + * cannot be instantiated. + * @throws InvocationTargetException If the SPI class's constructor throws an + * exception. + * @throws IllegalArgumentException If any of the four arguments is + * <code>null</code> or if either <code>service</code>, or + * <code>algorithm</code> is an empty string. */ public static Object getInstance(String service, String algorithm, Provider provider, Object[] initArgs) - throws InvocationTargetException, NoSuchAlgorithmException + throws InvocationTargetException, NoSuchAlgorithmException { - if (service != null) - service = service.trim(); + if (service == null) + throw new IllegalArgumentException("service MUST NOT be null"); + service = service.trim(); + if (service.length() == 0) + throw new IllegalArgumentException("service MUST NOT be empty"); + if (algorithm == null) + throw new IllegalArgumentException("algorithm MUST NOT be null"); + algorithm = algorithm.trim(); + if (algorithm.length() == 0) + throw new IllegalArgumentException("algorithm MUST NOT be empty"); + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + if (initArgs == null) + throw new IllegalArgumentException("Constructor's parameters MUST NOT be null"); - if (algorithm != null) - algorithm = algorithm.trim(); - - if (service == null || service.length() == 0 - || algorithm == null || algorithm.length() == 0 - || provider == null || initArgs == null) - throw new IllegalArgumentException(); - - Enumeration enumer = provider.propertyNames(); String key; String alias; int count = 0; boolean algorithmFound = false; - + StringBuilder sb = new StringBuilder(); while (enumer.hasMoreElements()) { key = (String) enumer.nextElement(); - if (key.equalsIgnoreCase(service + "." + algorithm)) { // remove the service portion from the key algorithm = key.substring(service.length() + 1); - algorithmFound = true; break; - } else if (key.equalsIgnoreCase(ALG_ALIAS + service + "." + algorithm)) { - alias = (String) provider.getProperty(key); - if (! algorithm.equalsIgnoreCase(alias)) // does not refer to itself { algorithm = alias; if (count++ > MAX_ALIASES) - throw new NoSuchAlgorithmException("too many aliases"); - + { + sb.append("Algorithm [").append(algorithm) + .append("] of type [").append(service) + .append("] from provider [").append(provider) + .append("] has too many aliases"); + throw new NoSuchAlgorithmException(sb.toString()); + } // need to reset enumeration to now look for the alias enumer = provider.propertyNames(); } } } - + if (! algorithmFound) { - throw new NoSuchAlgorithmException(algorithm); + sb.append("Algorithm [").append(algorithm).append("] of type [") + .append(service).append("] from provider [") + .append(provider).append("] is not found"); + throw new NoSuchAlgorithmException(sb.toString()); } - - - // Find and instantiate the implementation. + + // Find and instantiate the implementation Class clazz = null; ClassLoader loader = provider.getClass().getClassLoader(); Constructor constructor = null; - String error = algorithm; - + String className = provider.getProperty(service + "." + algorithm); + sb.append("Class [").append(className).append("] for algorithm [") + .append(algorithm).append("] of type [").append(service) + .append("] from provider [").append(provider).append("] "); + Throwable cause = null; try { if (loader != null) - clazz = loader.loadClass(provider.getProperty(service+"."+algorithm)); + clazz = loader.loadClass(className); else - clazz = Class.forName(provider.getProperty(service+"."+algorithm)); + clazz = Class.forName(className); constructor = getCompatibleConstructor(clazz, initArgs); return constructor.newInstance(initArgs); } - catch (ClassNotFoundException cnfe) + catch (ClassNotFoundException x) { - error = "class not found: " + algorithm; + sb.append("cannot not be found"); + cause = x; } - catch (IllegalAccessException iae) + catch (IllegalAccessException x) { - error = "illegal access: " + iae.getMessage(); + sb.append("cannot be accessed"); + cause = x; } - catch (InstantiationException ie) + catch (InstantiationException x) { - error = "instantiation exception: " + ie.getMessage(); + sb.append("cannot be instantiated"); + cause = x; } - catch (ExceptionInInitializerError eiie) + catch (ExceptionInInitializerError x) { - error = "exception in initializer: " + eiie.getMessage(); + sb.append("cannot be initialized"); + cause = x; } - catch (SecurityException se) + catch (SecurityException x) { - error = "security exception: " + se.getMessage(); + sb.append("caused a security violation"); + cause = x; } - catch (NoSuchMethodException nsme) + catch (NoSuchMethodException x) { - error = "no appropriate constructor found"; + sb.append("does not have/expose an appropriate constructor"); + cause = x; } - throw new NoSuchAlgorithmException(error); + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Own methods. - // ------------------------------------------------------------------------ - /** * Find a constructor in the given class that can take the specified * argument list, allowing any of which to be null. diff --git a/gnu/java/util/prefs/NodeReader.java b/gnu/java/util/prefs/NodeReader.java index 4cd52e5db..ae5510e1c 100644 --- a/gnu/java/util/prefs/NodeReader.java +++ b/gnu/java/util/prefs/NodeReader.java @@ -124,7 +124,6 @@ public class NodeReader { skipTill("name=\""); String name = readTill("\""); Preferences subnode = node.node(name); - System.out.println("Found subnode: " + subnode.absolutePath()); readMap(subnode); readNodes(subnode); skipTill("</node>"); @@ -160,7 +159,6 @@ public class NodeReader { String key = readTill("\""); skipTill("value=\""); String value = readTill("\""); - System.out.println("Key: " + key + " Value: " + value); node.put(key, value); } } diff --git a/gnu/java/util/prefs/NodeWriter.java b/gnu/java/util/prefs/NodeWriter.java index 574ddc247..231c047da 100644 --- a/gnu/java/util/prefs/NodeWriter.java +++ b/gnu/java/util/prefs/NodeWriter.java @@ -203,9 +203,6 @@ public class NodeWriter { StringTokenizer st = new StringTokenizer(path); parents = st.countTokens(); - System.out.println("path: " + path); - System.out.println("parents: " + parents); - for (int i=0; i<parents; i++) { String name = st.nextToken(); indent(i+2); diff --git a/gnu/java/util/regex/CharIndexed.java b/gnu/java/util/regex/CharIndexed.java index 6cd857e3b..27e07b2f8 100644 --- a/gnu/java/util/regex/CharIndexed.java +++ b/gnu/java/util/regex/CharIndexed.java @@ -77,6 +77,13 @@ public interface CharIndexed { boolean move(int index); /** + * Shifts the input buffer by a given number of positions. Returns + * true if the new cursor position is valid or cursor position is at + * the end of input. + */ + boolean move1(int index); // I cannot think of a better name for this. + + /** * Returns true if the most recent move() operation placed the cursor * position at a valid position in the input. */ @@ -105,6 +112,16 @@ public interface CharIndexed { REMatch getLastMatch(); /** + * Sets the information used for hitEnd(). + */ + void setHitEnd(REMatch match); + + /** + * Returns whether the matcher has hit the end of input. + */ + boolean hitEnd(); + + /** * Returns the anchor. */ int getAnchor(); diff --git a/gnu/java/util/regex/CharIndexedCharSequence.java b/gnu/java/util/regex/CharIndexedCharSequence.java index 2eb753b0f..8a0578eb8 100644 --- a/gnu/java/util/regex/CharIndexedCharSequence.java +++ b/gnu/java/util/regex/CharIndexedCharSequence.java @@ -62,6 +62,10 @@ class CharIndexedCharSequence implements CharIndexed, Serializable { return ((anchor += index) < len); } + public boolean move1(int index) { + return ((anchor += index) <= len); + } + public CharIndexed lookBehind(int index, int length) { if (length > (anchor + index)) length = anchor + index; return new CharIndexedCharSequence(s, anchor + index - length); @@ -77,6 +81,15 @@ class CharIndexedCharSequence implements CharIndexed, Serializable { lastMatch.anchor = anchor; } public REMatch getLastMatch() { return lastMatch; } + + private int rightmostTriedPosition = 0; + public void setHitEnd(REMatch match) { + int pos = anchor + match.index; + if (pos > rightmostTriedPosition) rightmostTriedPosition = pos; + } + public boolean hitEnd() { return rightmostTriedPosition >= len; } + public int getAnchor() { return anchor; } public void setAnchor(int anchor) { this.anchor = anchor; } + } diff --git a/gnu/java/util/regex/CharIndexedInputStream.java b/gnu/java/util/regex/CharIndexedInputStream.java index 77cd1abd5..844fada51 100644 --- a/gnu/java/util/regex/CharIndexedInputStream.java +++ b/gnu/java/util/regex/CharIndexedInputStream.java @@ -166,6 +166,16 @@ class CharIndexedInputStream implements CharIndexed { "difficult to support getLastMatch for an input stream"); } + public void setHitEnd(REMatch match) { + throw new UnsupportedOperationException( + "difficult to support setHitEnd for an input stream"); + } + + public boolean hitEnd() { + throw new UnsupportedOperationException( + "difficult to support hitEnd for an input stream"); + } + public int getAnchor() { throw new UnsupportedOperationException( "difficult to support getAnchor for an input stream"); @@ -176,6 +186,10 @@ class CharIndexedInputStream implements CharIndexed { "difficult to support setAnchor for an input stream"); } + public boolean move1(int index) { + throw new UnsupportedOperationException( + "difficult to support move1 for an input stream"); + } } diff --git a/gnu/java/util/regex/RE.java b/gnu/java/util/regex/RE.java index 1aab3b781..94aa0142c 100644 --- a/gnu/java/util/regex/RE.java +++ b/gnu/java/util/regex/RE.java @@ -252,6 +252,13 @@ public class RE extends REToken { */ public static final int REG_ICASE_USASCII = 0x0800; + /** + * Execution flag. + * Do not move the position at which the search begins. If not set, + * the starting position will be moved until a match is found. + */ + public static final int REG_FIX_STARTING_POSITION = 0x1000; + /** Returns a string representing the version of the gnu.regexp package. */ public static final String version() { return VERSION; @@ -1643,6 +1650,7 @@ public class RE extends REToken { /* Implements abstract method REToken.match() */ boolean match(CharIndexed input, REMatch mymatch) { + input.setHitEnd(mymatch); if (firstToken == null) { return next(input, mymatch); } @@ -1720,15 +1728,23 @@ public class RE extends REToken { REMatch getMatchImpl(CharIndexed input, int anchor, int eflags, StringBuffer buffer) { boolean tryEntireMatch = ((eflags & REG_TRY_ENTIRE_MATCH) != 0); + boolean doMove = ((eflags & REG_FIX_STARTING_POSITION) == 0); RE re = (tryEntireMatch ? (RE) this.clone() : this); if (tryEntireMatch) { - re.chain(new RETokenEnd(0, null)); + RETokenEnd reEnd = new RETokenEnd(0, null); + reEnd.setFake(true); + re.chain(reEnd); } // Create a new REMatch to hold results REMatch mymatch = new REMatch(numSubs, anchor, eflags); do { + /* The following potimization is commented out because + the matching should be tried even if the length of + input is obviously too short in order that + java.util.regex.Matcher#hitEnd() may work correctly. // Optimization: check if anchor + minimumLength > length if (minimumLength == 0 || input.charAt(minimumLength-1) != CharIndexed.OUT_OF_BOUNDS) { + */ if (re.match(input, mymatch)) { REMatch best = mymatch; // We assume that the match that coms first is the best. @@ -1749,13 +1765,17 @@ public class RE extends REToken { input.setLastMatch(best); return best; } - } + /* End of the optimization commented out + } + */ mymatch.clear(++anchor); // Append character to buffer if needed if (buffer != null && input.charAt(0) != CharIndexed.OUT_OF_BOUNDS) { buffer.append(input.charAt(0)); } - } while (input.move(1)); + // java.util.regex.Matcher#hitEnd() requires that the search should + // be tried at the end of input, so we use move1(1) instead of move(1) + } while (doMove && input.move1(1)); // Special handling at end of input for e.g. "$" if (minimumLength == 0) { diff --git a/gnu/java/util/regex/REMatch.java b/gnu/java/util/regex/REMatch.java index 3ff5ad794..d89948293 100644 --- a/gnu/java/util/regex/REMatch.java +++ b/gnu/java/util/regex/REMatch.java @@ -307,12 +307,12 @@ public final class REMatch implements Serializable, Cloneable { } /* The following are used for debugging purpose - static String d(REMatch m) { + public static String d(REMatch m) { if (m == null) return "null"; else return "[" + m.index + "]"; } - String substringUptoIndex(CharIndexed input) { + public String substringUptoIndex(CharIndexed input) { StringBuffer sb = new StringBuffer(); for (int i = 0; i < index; i++) { sb.append(input.charAt(i)); diff --git a/gnu/java/util/regex/REToken.java b/gnu/java/util/regex/REToken.java index 155c01878..9affd4ee3 100644 --- a/gnu/java/util/regex/REToken.java +++ b/gnu/java/util/regex/REToken.java @@ -72,6 +72,16 @@ abstract class REToken implements Serializable, Cloneable { /** Returns true if the match succeeded, false if it failed. */ boolean match(CharIndexed input, REMatch mymatch) { + return match(input, mymatch, false); + } + boolean matchFake(CharIndexed input, REMatch mymatch) { + return match(input, mymatch, true); + } + + private boolean match(CharIndexed input, REMatch mymatch, boolean fake) { + if (!fake) { + setHitEnd(input, mymatch); + } REMatch m = matchThis(input, mymatch); if (m == null) return false; if (next(input, m)) { @@ -81,6 +91,11 @@ abstract class REToken implements Serializable, Cloneable { return false; } + /** Sets whether the matching occurs at the end of input */ + void setHitEnd(CharIndexed input, REMatch mymatch) { + input.setHitEnd(mymatch); + } + /** Returns true if the match succeeded, false if it failed. * The matching is done against this REToken only. Chained * tokens are not checked. diff --git a/gnu/java/util/regex/RETokenChar.java b/gnu/java/util/regex/RETokenChar.java index 92d3efcf8..b70e6b1d8 100644 --- a/gnu/java/util/regex/RETokenChar.java +++ b/gnu/java/util/regex/RETokenChar.java @@ -58,15 +58,20 @@ final class RETokenChar extends REToken { } REMatch matchThis(CharIndexed input, REMatch mymatch) { - int z = ch.length; if (matchOneString(input, mymatch.index)) { - mymatch.index += z; + mymatch.index += matchedLength; return mymatch; } + // java.util.regex.Matcher#hitEnd() requires that the length of + // partial match be counted. + mymatch.index += matchedLength; + input.setHitEnd(mymatch); return null; } - boolean matchOneString(CharIndexed input, int index) { + private int matchedLength; + private boolean matchOneString(CharIndexed input, int index) { + matchedLength = 0; int z = ch.length; char c; for (int i=0; i<z; i++) { @@ -74,6 +79,7 @@ final class RETokenChar extends REToken { if (! charEquals(c, ch[i])) { return false; } + ++matchedLength; } return true; } diff --git a/gnu/java/util/regex/RETokenEnd.java b/gnu/java/util/regex/RETokenEnd.java index 00efdb6a7..294e32085 100644 --- a/gnu/java/util/regex/RETokenEnd.java +++ b/gnu/java/util/regex/RETokenEnd.java @@ -45,6 +45,12 @@ final class RETokenEnd extends REToken { private String newline; private boolean check_java_line_terminators; + /** + * Indicates whether this token is a real one generated at compile time, + * or a fake one temporarily added by RE#getMatchImpl. + */ + private boolean fake = false; + RETokenEnd(int subIndex,String newline) { super(subIndex); this.newline = newline; @@ -57,10 +63,19 @@ final class RETokenEnd extends REToken { this.check_java_line_terminators = b; } + void setFake(boolean fake) { + this.fake = fake; + } + int getMaximumLength() { return 0; } + boolean match(CharIndexed input, REMatch mymatch) { + if (!fake) return super.match(input, mymatch); + return super.matchFake(input, mymatch); + } + REMatch matchThis(CharIndexed input, REMatch mymatch) { char ch = input.charAt(mymatch.index); if (ch == CharIndexed.OUT_OF_BOUNDS) diff --git a/gnu/java/util/regex/RETokenEndSub.java b/gnu/java/util/regex/RETokenEndSub.java index 57a146d03..b3a28a3e8 100644 --- a/gnu/java/util/regex/RETokenEndSub.java +++ b/gnu/java/util/regex/RETokenEndSub.java @@ -58,6 +58,10 @@ final class RETokenEndSub extends REToken { return super.findMatch(input, mymatch); } + void setHitEnd(CharIndexed input, REMatch mymatch) { + // Do nothing + } + void dump(StringBuffer os) { // handled by RE // But add something for debugging. diff --git a/gnu/java/util/regex/RETokenOneOf.java b/gnu/java/util/regex/RETokenOneOf.java index bccc78311..239c2201c 100644 --- a/gnu/java/util/regex/RETokenOneOf.java +++ b/gnu/java/util/regex/RETokenOneOf.java @@ -120,6 +120,7 @@ final class RETokenOneOf extends REToken { } boolean match(CharIndexed input, REMatch mymatch) { + setHitEnd(input, mymatch); if (matchesOneChar) return matchOneChar(input, mymatch); else return matchOneRE(input, mymatch); } diff --git a/gnu/java/util/regex/RETokenRepeated.java b/gnu/java/util/regex/RETokenRepeated.java index 531c4a311..b32a316c4 100644 --- a/gnu/java/util/regex/RETokenRepeated.java +++ b/gnu/java/util/regex/RETokenRepeated.java @@ -318,6 +318,7 @@ final class RETokenRepeated extends REToken { } boolean match(CharIndexed input, REMatch mymatch) { + setHitEnd(input, mymatch); REMatch m1 = findMatch(input, mymatch); if (m1 != null) { mymatch.assignFrom(m1); diff --git a/gnu/javax/rmi/CORBA/UtilDelegateImpl.java b/gnu/javax/rmi/CORBA/UtilDelegateImpl.java index 66a4e24ff..b9dc7a3d2 100644 --- a/gnu/javax/rmi/CORBA/UtilDelegateImpl.java +++ b/gnu/javax/rmi/CORBA/UtilDelegateImpl.java @@ -1,5 +1,5 @@ /* UtilDelegateImpl.java -- - Copyright (C) 2002, 2005 Free Software Foundation, Inc. + Copyright (C) 2002, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -38,6 +38,8 @@ exception statement from your version. */ package gnu.javax.rmi.CORBA; +import gnu.classpath.VMStackWalker; + import gnu.CORBA.Minor; import gnu.CORBA.ObjectCreator; import gnu.CORBA.Poa.ORB_1_4; @@ -70,6 +72,7 @@ import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.net.MalformedURLException; import java.rmi.AccessException; import java.rmi.MarshalException; import java.rmi.NoSuchObjectException; @@ -374,37 +377,24 @@ public class UtilDelegateImpl throws ClassNotFoundException { if (loader == null) - loader = Thread.currentThread().getContextClassLoader(); + loader = VMStackWalker.firstNonNullClassLoader(); String p_useCodebaseOnly = System.getProperty("java.rmi.server.useCodebaseOnly"); boolean useCodebaseOnly = p_useCodebaseOnly != null && p_useCodebaseOnly.trim().equalsIgnoreCase("true"); - try - { - if (remoteCodebase != null && !useCodebaseOnly) - return RMIClassLoader.loadClass(remoteCodebase, className); - } - catch (Exception e) - { - // This failed but try others. - } + if (useCodebaseOnly) + remoteCodebase = null; try { - if (remoteCodebase == null || useCodebaseOnly) - return RMIClassLoader.loadClass(remoteCodebase, className); + return RMIClassLoader.loadClass(remoteCodebase, className, loader); } - catch (Exception e) + catch (MalformedURLException x) { - // This failed but try others. + throw new ClassNotFoundException(className, x); } - - if (loader != null) - return Class.forName(className, true, loader); - - throw new ClassNotFoundException(className + " at " + remoteCodebase); } /** diff --git a/gnu/javax/security/auth/callback/AbstractCallbackHandler.java b/gnu/javax/security/auth/callback/AbstractCallbackHandler.java index eeedf2605..5144f58ea 100644 --- a/gnu/javax/security/auth/callback/AbstractCallbackHandler.java +++ b/gnu/javax/security/auth/callback/AbstractCallbackHandler.java @@ -83,65 +83,102 @@ public abstract class AbstractCallbackHandler implements CallbackHandler this.name = name; } - // Class methods. - // ------------------------------------------------------------------------- - + /** + * Create an instance of <code>CallbackHandler</code> of the designated + * <code>type</code> from the first Security Provider which offers it. + * + * @param type the type of callback handler to create. + * @return a newly created instance of <code>ClassbackHandler</code>. + * @throws NoSuchAlgorithmException if no security provider is found to offer + * an implementation of <code>CallbackHandler</code> of the + * designated <code>type</code>. + */ public static CallbackHandler getInstance(String type) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(type, p[i]); - } - catch (NoSuchAlgorithmException ignored) - { - } - } + try + { + return getInstance(type, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(type); } + /** + * Create an instance of <code>CallbackHandler</code> of the designated + * <code>type</code> from the named security <code>provider</code>. + * + * @param type the type of callback handler to create. + * @param provider a named security provider to use. + * @return a newly created instance of <code>ClassbackHandler</code>. + * @throws NoSuchAlgorithmException if no security provider is found to offer + * an implementation of <code>CallbackHandler</code> of the + * designated <code>type</code>. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>type</code> is an empty string. + */ public static CallbackHandler getInstance(String type, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) - { - throw new NoSuchProviderException(provider); - } + throw new NoSuchProviderException(provider); return getInstance(type, p); } + /** + * Create an instance of <code>CallbackHandler</code> of the designated + * <code>type</code> from the designated security <code>provider</code>. + * + * @param type the type of callback handler to create. + * @param provider a security provider to use. + * @return a newly created instance of <code>ClassbackHandler</code>. + * @throws NoSuchAlgorithmException if no security provider is found to offer + * an implementation of <code>CallbackHandler</code> of the + * designated <code>type</code>. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>type</code> is an empty string. + */ public static CallbackHandler getInstance(String type, Provider provider) throws NoSuchAlgorithmException { + StringBuilder sb = new StringBuilder("CallbackHandler of type [") + .append(type).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { return (CallbackHandler) Engine.getInstance(SERVICE, type, provider); } - catch (InvocationTargetException ite) + catch (InvocationTargetException x) { - Throwable cause = ite.getCause(); + cause = x.getCause(); if (cause instanceof NoSuchAlgorithmException) throw (NoSuchAlgorithmException) cause; - NoSuchAlgorithmException nsae = new NoSuchAlgorithmException(type); - if (cause != null) - nsae.initCause (cause); - throw nsae; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - NoSuchAlgorithmException nsae = new NoSuchAlgorithmException(type); - nsae.initCause (cce); - throw nsae; + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ------------------------------------------------------------------------- - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { diff --git a/gnu/javax/swing/text/html/css/CSSColor.java b/gnu/javax/swing/text/html/css/CSSColor.java new file mode 100644 index 000000000..381bcd5ed --- /dev/null +++ b/gnu/javax/swing/text/html/css/CSSColor.java @@ -0,0 +1,134 @@ +/* CSSColor.java -- Converts CSS color values + 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 gnu.javax.swing.text.html.css; + +import java.awt.Color; +import java.util.HashMap; + +/** + * Converts CSS color values into AWT Color values. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class CSSColor +{ + + private static final HashMap COLOR_MAP; + static + { + COLOR_MAP = new HashMap(); + COLOR_MAP.put("maroon", "#800000"); + COLOR_MAP.put("red", "#ff0000"); + COLOR_MAP.put("orange", "#ffa500"); + COLOR_MAP.put("yellow", "#ffff00"); + COLOR_MAP.put("olive", "#808000"); + COLOR_MAP.put("purple", "#800080"); + COLOR_MAP.put("fuchsia", "#ff00ff"); + COLOR_MAP.put("white", "#ffffff"); + COLOR_MAP.put("lime", "#00ff00"); + COLOR_MAP.put("green", "#008000"); + COLOR_MAP.put("navy", "#000080"); + COLOR_MAP.put("blue", "#0000ff"); + COLOR_MAP.put("aqua", "#00ffff"); + COLOR_MAP.put("teal", "#008080"); + COLOR_MAP.put("black", "#000000"); + COLOR_MAP.put("silver", "#c0c0c0"); + COLOR_MAP.put("gray", "#808080"); + } + + /** + * The CSS value. + */ + private String value; + + /** + * The converted color. + */ + private Color color; + + /** + * Creates a new instance. + * + * @param val the CSS value + */ + public CSSColor(String val) + { + value = val; + color = convertValue(value); + } + + /** + * Converts a CSS color value to an AWT color. + * + * @param value the CSS color value + * + * @return the converted color value + */ + public static Color convertValue(String value) + { + Color color; + String val1 = value.toLowerCase(); + if (val1.charAt(0) != '#') + val1 = (String) COLOR_MAP.get(val1); + if (val1 != null) + { + String hexVal = val1.substring(1); + int rgb = Integer.parseInt(hexVal, 16); + color = new Color(rgb); + } + else + color = null; + return color; + } + + /** + * Returns the converted color. + * + * @return the converted color + */ + public Color getValue() + { + return color; + } + + public String toString() + { + return value; + } +} diff --git a/gnu/javax/swing/text/html/css/CSSLexicalException.java b/gnu/javax/swing/text/html/css/CSSLexicalException.java new file mode 100644 index 000000000..13968e4d2 --- /dev/null +++ b/gnu/javax/swing/text/html/css/CSSLexicalException.java @@ -0,0 +1,60 @@ +/* CSSLexicalException.java -- Indicates a failure in the lexical analyser + 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 gnu.javax.swing.text.html.css; + +import java.io.IOException; + +/** + * Indicates a failure in the lexical analyser of the CSS parser. + */ +public class CSSLexicalException + extends IOException +{ + + public CSSLexicalException() + { + super(); + } + + public CSSLexicalException(String message) + { + super(message); + } + +} diff --git a/gnu/javax/swing/text/html/css/CSSParser.java b/gnu/javax/swing/text/html/css/CSSParser.java new file mode 100644 index 000000000..190c93eb4 --- /dev/null +++ b/gnu/javax/swing/text/html/css/CSSParser.java @@ -0,0 +1,470 @@ +/* CSSParser.java -- A parser for CSS stylesheets + 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 gnu.javax.swing.text.html.css; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +/** + * A parser for CSS stylesheets. + * + * This parser is based on the simple CSS grammar describe in + * + * http://www.w3.org/TR/CSS21/syndata.html . + * + * @author Roman Kennke (kennke@aicas.com) + */ +// TODO: Maybe use more restrictive grammar: +// http://www.w3.org/TR/CSS21/grammar.html#q1 +public class CSSParser +{ + + /** + * The scanner used to read the input streams into more usable tokens. + */ + private CSSScanner scanner; + + /** + * The parser callback. + */ + private CSSParserCallback callback; + + /** + * One lookahead token. + */ + private int lookahead; + + /** + * The parse error. + */ + private String error; + + /** + * Creates a new CSSParser that parses the specified input. + * + * @param in the source to parse + */ + public CSSParser(Reader in, CSSParserCallback cb) + { + scanner = new CSSScanner(in); + callback = cb; + lookahead = -1; + } + + /** + * Parses the input source specified in the constructor. + * + * @throws IOException if an IO or parse error occurs + */ + public void parse() + throws IOException + { + boolean success = parseStylesheet(); + if (! success) + { + throw new CSSParserException(error); + } + } + + /** + * Parses a stylesheet. + * + * @return <code>true</code> if the stylesheet could be parsed successfully, + * <code>false</code> otherwise + * + * @throws IOException if an IO or parse error occurs + */ + private boolean parseStylesheet() + throws IOException + { + int token = peekToken(); + while (token != CSSScanner.EOF && (token == CSSScanner.CDC + || token == CSSScanner.CDO || token == CSSScanner.S + || parseStatement())) + { + if (token == CSSScanner.CDC || token == CSSScanner.CDO + || token == CSSScanner.S) + readToken(); + token = peekToken(); + } + // Last token must be EOF for valid stylesheets, I'd think. + return token == CSSScanner.EOF; + } + + /** + * Parses a CSS statement. + * @return <code>true</code> if the stylesheet could be parsed successfully, + * <code>false</code> otherwise + * + * @throws IOException if an IO or parse error occurs + */ + private boolean parseStatement() + throws IOException + { + return parseRuleset() || parseAtRule(); + } + + /** + * Parses a CSS rule set. + * + * @return <code>true</code> if the ruleset could be parsed successfully, + * <code>false</code> otherwise + * + * @throws IOException if an IO or parse error occurs + */ + private boolean parseRuleset() + throws IOException + { + StringBuilder selector = new StringBuilder(); + parseSelector(selector); + callback.startStatement(selector.toString()); + // Read any number of whitespace. + int token; + do + { + token = readToken(); + } while (token == CSSScanner.S); + boolean ret = true; + + if (token == CSSScanner.CURLY_LEFT) + { + // Read any number of whitespace. + do + { + token = readToken(); + } while (token == CSSScanner.S); + lookahead = token; + + // Maybe read declaration. + boolean decl = parseDeclaration(); + token = peekToken(); + while (token == CSSScanner.SEMICOLON) + { + readToken(); // Read the semicolon. + // Read any number of whitespace. + do + { + token = readToken(); + } while (token == CSSScanner.S); + lookahead = token; + + // Maybe read declaration. + parseDeclaration(); + token = peekToken(); + } + if (token != CSSScanner.CURLY_RIGHT) + { + error = "Expected right curly brace"; + ret = false; + } + else + { + readToken(); + // Read any number of whitespace. + do + { + token = readToken(); + } while (token == CSSScanner.S); + lookahead = token; + callback.endStatement(); + } + } + else + { + ret = false; + error = "Expected left curly brace"; + } + return ret; + } + + /** + * Parses a CSS declaration. + * + * @return <code>true</code> if the ruleset could be parsed successfully, + * <code>false</code> otherwise + * + * @throws IOException if an IO or parse error occurs + */ + private boolean parseDeclaration() + throws IOException + { + // Maybe fetch one DELIM. + int token = readToken(); + if (token == CSSScanner.DELIM) + token = readToken(); + + boolean ret = true; + + // Parse property + String property = null; + if (token == CSSScanner.IDENT) + { + property = new String(scanner.parseBuffer, 0, scanner.tokenEnd); + // Read any number of whitespace. + do + { + token = readToken(); + } while (token == CSSScanner.S); + + // Read ':'. + if (token == CSSScanner.DELIM && scanner.parseBuffer[0] == ':') + { + // Read any number of whitespace. + do + { + token = readToken(); + } while (token == CSSScanner.S); + lookahead = token; + + StringBuilder value = new StringBuilder(); + if (parseValue(value)) + { + callback.declaration(property, value.toString()); + } + else + { + ret = false; + error = "Error while reading the property value"; + } + } + else + { + ret = false; + error = "Expected colon to separate property and value"; + } + + } + else + { + lookahead = token; + ret = false; + error = "Expected IDENT token for property"; + } + return ret; + } + + /** + * Parses a property value. + * + * @param s the string builder to read the value into + * + * @return <code>true</code> if the ruleset could be parsed successfully, + * <code>false</code> otherwise + * + * @throws IOException if an IO or parse error occurs + */ + private boolean parseValue(StringBuilder s) + throws IOException + { + // FIXME: Handle block and ATKEYWORD. + return parseAny(s); + } + + /** + * Parses a selector. + * + * @param sel the string buffer to put the selector into + * + * @return <code>true</code> if the ruleset could be parsed successfully, + * <code>false</code> otherwise + * + * @throws IOException if an IO or parse error occurs + */ + private boolean parseSelector(StringBuilder sel) + throws IOException + { + // At least one any needs to be parsed. + boolean ret = parseAny(sel); + if (ret) + { + while (parseAny(sel)); + } + return ret; + } + + /** + * Parses the any rule. If s is not null, then the contents of the + * tokens is appended verbatim. + * + * @param s the string builder to append to + * + * @return <code>true</code> if the ruleset could be parsed successfully, + * <code>false</code> otherwise + * + * @throws IOException if an IO or parse error occurs + */ + private boolean parseAny(StringBuilder s) + throws IOException + { + int token = peekToken(); + boolean ret = false; + if (token == CSSScanner.IDENT || token == CSSScanner.NUMBER + || token == CSSScanner.PERCENTAGE || token == CSSScanner.DIMENSION + || token == CSSScanner.STRING || token == CSSScanner.DELIM + || token == CSSScanner.URI || token == CSSScanner.HASH + || token == CSSScanner.UNICODE_RANGE || token == CSSScanner.INCLUDES + || token == CSSScanner.DASHMATCH) + { + if (s != null) + s.append(scanner.parseBuffer, 0, scanner.tokenEnd); + readToken(); + ret = true; + } + else if (token == CSSScanner.FUNCTION) + System.err.println("Implement parseAny for FUNCTION"); + else if (token == CSSScanner.PAREN_LEFT) + System.err.println("Implement parseAny for ("); + else if (token == CSSScanner.BRACE_LEFT) + System.err.println("Implement parseAny for ["); + + // Parse any following whitespace too. + token = peekToken(); + while (token == CSSScanner.S) + { + if (s != null) + s.append(scanner.parseBuffer, 0, scanner.tokenEnd); + readToken(); + token = peekToken(); + } + return ret; + } + + /** + * Parses a CSS at-rule. + * + * @return <code>true</code> if the at-rule could be parsed successfully, + * <code>false</code> otherwise + * + * @throws IOException if an IO or parse error occurs + */ + private boolean parseAtRule() + throws IOException + { + // FIXME: Implement. + return false; + } + + /** + * Reads the next token, and skips the comments. + * + * @return the next non-comment token + */ + private int readToken() + throws IOException + { + int token; + if (lookahead == -1) + { + do + { + token = scanner.nextToken(); + } while (token == CSSScanner.COMMENT); + } + else + { + token = lookahead; + lookahead = -1; + } + return token; + } + + /** + * Returns the next token to be read, without really reading it. The next + * call to readToken() will return the same token again. + * + * @return the next token to be read, without really reading it + */ + private int peekToken() + throws IOException + { + int token; + if (lookahead == -1) + { + do + { + token = scanner.nextToken(); + } while (token == CSSScanner.COMMENT); + lookahead = token; + } + else + token = lookahead; + return token; + } + + /** + * For testing, we read in the default.css in javax/swing/text/html + * + * @param args + */ + public static void main(String[] args) + { + try + { + String name = "/javax/swing/text/html/default.css"; + InputStream in = CSSScanner.class.getResourceAsStream(name); + BufferedInputStream bin = new BufferedInputStream(in); + InputStreamReader r = new InputStreamReader(bin); + CSSParserCallback cb = new CSSParserCallback() + { + public void startStatement(String selector) + { + System.out.println("startStatement: " + selector); + } + public void endStatement() + { + System.out.println("endStatement"); + } + public void declaration(String property, String value) + { + System.out.println("declaration: " + property + ", " + value); + } + }; + CSSParser p = new CSSParser(r, cb); + p.parse(); + } + catch (IOException ex) + { + ex.printStackTrace(); + } + } + +} diff --git a/gnu/javax/swing/text/html/css/CSSParserCallback.java b/gnu/javax/swing/text/html/css/CSSParserCallback.java new file mode 100644 index 000000000..60dc5488a --- /dev/null +++ b/gnu/javax/swing/text/html/css/CSSParserCallback.java @@ -0,0 +1,81 @@ +/* CSSParserCallback.java -- Callback for parsing CSS + 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 gnu.javax.swing.text.html.css; + +/** + * Defines the callback that is used by the CSSParser to notify the + * backend of the parsing process. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public interface CSSParserCallback +{ + + /** + * Signals the beginning of a statement. + * + * A CSS statement is build up like follows: + * <pre> + * <selector> { + * ... declarations... + * } + * </pre> + * + * After startStatement(), the callback will receive zero to n callbacks + * to declaration, followed by an endStatement() call. + * + * @param selector the selector of the statement. + */ + void startStatement(String selector); + + /** + * Signals the end of a statement. + */ + void endStatement(); + + /** + * Signals the parsing of one declaration, which defines a mapping + * from a property to a value. + * + * @param property the property + * @param value the value + */ + void declaration(String property, String value); + +} diff --git a/gnu/javax/swing/text/html/css/CSSParserException.java b/gnu/javax/swing/text/html/css/CSSParserException.java new file mode 100644 index 000000000..2328d5398 --- /dev/null +++ b/gnu/javax/swing/text/html/css/CSSParserException.java @@ -0,0 +1,62 @@ +/* CSSParserException.java -- The CSS parser exception + 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 gnu.javax.swing.text.html.css; + +import java.io.IOException; + +/** + * This exception is raised when the CSS parser hits a syntax error. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class CSSParserException + extends IOException +{ + + /** + * Creates a new CSSParserException. + * + * @param message the exception message + */ + public CSSParserException(String message) + { + super(message); + } + +} diff --git a/gnu/javax/swing/text/html/css/CSSScanner.java b/gnu/javax/swing/text/html/css/CSSScanner.java new file mode 100644 index 000000000..a402b9522 --- /dev/null +++ b/gnu/javax/swing/text/html/css/CSSScanner.java @@ -0,0 +1,717 @@ +/* CSSScanner.java -- A parser for CSS stylesheets + 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 gnu.javax.swing.text.html.css; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; + +/** + * A tokenizer for CSS stylesheets. This is based on the scanner definition + * from: + * + * http://www.w3.org/TR/CSS21/syndata.html#tokenization + * + * @author Roman Kennke (kennke@aicas.com) + */ +// TODO: Maybe implement more restrictive scanner: +// http://www.w3.org/TR/CSS21/grammar.html#q2 +class CSSScanner +{ + + // The tokens. This list is taken from: + // http://www.w3.org/TR/CSS21/syndata.html#tokenization + static final int IDENT = 1; + static final int ATKEYWORD = 2; + static final int STRING = 3; + static final int INVALID = 4; + static final int HASH = 5; + static final int NUMBER = 6; + static final int PERCENTAGE = 7; + static final int DIMENSION = 8; + static final int URI = 9; + static final int UNICODE_RANGE = 10; + static final int CDO = 11; + static final int CDC = 12; + static final int SEMICOLON = 13; + static final int CURLY_LEFT = 14; + static final int CURLY_RIGHT = 15; + static final int PAREN_LEFT = 16; + static final int PAREN_RIGHT = 17; + static final int BRACE_LEFT = 16; + static final int BRACE_RIGHT = 17; + static final int S = 18; + static final int COMMENT = 19; + static final int FUNCTION = 20; + static final int INCLUDES = 21; + static final int DASHMATCH = 22; + static final int DELIM = 23; + + // Additional tokens defined for convenience. + static final int EOF = -1; + + /** + * The input source. + */ + private Reader in; + + /** + * The parse buffer. + */ + char[] parseBuffer; + + /** + * The end index in the parseBuffer of the current token. + */ + int tokenEnd; + + /** + * The lookahead 'buffer'. + */ + private int[] lookahead; + + CSSScanner(Reader r) + { + lookahead = new int[2]; + lookahead[0] = -1; + lookahead[1] = -1; + parseBuffer = new char[2048]; + in = r; + } + + /** + * Fetches the next token. The actual character data is in the parseBuffer + * afterwards with the tokenStart at index 0 and the tokenEnd field + * pointing to the end of the token. + * + * @return the next token + */ + int nextToken() + throws IOException + { + tokenEnd = 0; + int token = -1; + int next = read(); + if (next != -1) + { + switch (next) + { + case ';': + parseBuffer[0] = (char) next; + tokenEnd = 1; + token = SEMICOLON; + break; + case '{': + parseBuffer[0] = (char) next; + tokenEnd = 1; + token = CURLY_LEFT; + break; + case '}': + parseBuffer[0] = (char) next; + tokenEnd = 1; + token = CURLY_RIGHT; + break; + case '(': + parseBuffer[0] = (char) next; + tokenEnd = 1; + token = PAREN_LEFT; + break; + case ')': + parseBuffer[0] = (char) next; + tokenEnd = 1; + token = PAREN_RIGHT; + break; + case '[': + parseBuffer[0] = (char) next; + tokenEnd = 1; + token = BRACE_LEFT; + break; + case ']': + parseBuffer[0] = (char) next; + tokenEnd = 1; + token = BRACE_RIGHT; + break; + case '@': + parseBuffer[0] = (char) next; + tokenEnd = 1; + readIdent(); + token = ATKEYWORD; + break; + case '#': + parseBuffer[0] = (char) next; + tokenEnd = 1; + readName(); + token = HASH; + break; + case '\'': + case '"': + lookahead[0] = next; + readString(); + token = STRING; + break; + case ' ': + case '\t': + case '\r': + case '\n': + case '\f': + lookahead[0] = next; + readWhitespace(); + token = S; + break; + // FIXME: Detecting an URI involves several characters lookahead. +// case 'u': +// lookahead[0] = ch; +// readURI(); +// token = URI; +// break; + case '<': + parseBuffer[0] = (char) next; + parseBuffer[1] = (char) read(); + parseBuffer[2] = (char) read(); + parseBuffer[3] = (char) read(); + if (parseBuffer[1] == '!' && parseBuffer[2] == '-' + && parseBuffer[3] == '-') + { + token = CDO; + tokenEnd = 4; + } + else + throw new CSSLexicalException("expected CDO token"); + break; + case '/': + lookahead[0] = next; + readComment(); + token = COMMENT; + break; + case '~': + parseBuffer[0] = (char) next; + parseBuffer[1] = (char) read(); + if (parseBuffer[1] == '=') + token = INCLUDES; + else + throw new CSSLexicalException("expected INCLUDES token"); + break; + case '|': + parseBuffer[0] = (char) next; + parseBuffer[1] = (char) read(); + if (parseBuffer[1] == '=') + token = DASHMATCH; + else + throw new CSSLexicalException("expected DASHMATCH token"); + break; + case '-': + int ch2 = read(); + if (ch2 == '-') + { + int ch3 = read(); + if (ch3 == '>') + { + parseBuffer[0] = (char) next; + parseBuffer[1] = (char) ch2; + parseBuffer[2] = (char) ch3; + tokenEnd = 3; + token = CDC; + } + else + throw new CSSLexicalException("expected CDC token"); + } + else + { + lookahead[0] = next; + lookahead[1] = ch2; + readIdent(); + int ch3 = read(); + if (ch3 == -1 || ch3 != '(') + { + lookahead[0] = ch3; + token = IDENT; + } + else + { + parseBuffer[tokenEnd] = (char) ch3; + tokenEnd++; + token = FUNCTION; + } + } + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + lookahead[0] = next; + readNum(); + int ch3 = read(); + if (ch3 == '%') + { + parseBuffer[tokenEnd] = (char) ch3; + tokenEnd++; + token = PERCENTAGE; + } + else if (ch3 == -1 || (! (ch3 == '_' + || (ch3 >= 'a' && ch3 <= 'z') + || (ch3 >= 'A' && ch3 <= 'Z') + || ch3 == '\\' || ch3 > 177))) + { + lookahead[0] = ch3; + token = NUMBER; + } + else + { + lookahead[0] = ch3; + readIdent(); + token = DIMENSION; + } + break; + default: + // Handle IDENT that don't begin with '-'. + if (next == '_' || (next >= 'a' && next <= 'z') + || (next >= 'A' && next <= 'Z') || next == '\\' || next > 177) + { + lookahead[0] = next; + readIdent(); + int ch4 = read(); + if (ch4 == -1 || ch4 != '(') + { + lookahead[0] = ch4; + token = IDENT; + } + else + { + parseBuffer[tokenEnd] = (char) ch4; + tokenEnd++; + token = FUNCTION; + } + } + else + { + parseBuffer[0] = (char) next; + tokenEnd = 1; + token = DELIM; + } + break; + } + } + return token; + } + + String currentTokenString() + { + return new String(parseBuffer, 0, tokenEnd); + } + + /** + * Reads one character from the input stream or from the lookahead + * buffer, if it contains one character. + * + * @return the next character + * + * @throws IOException if problems occur on the input source + */ + private int read() + throws IOException + { + int ret; + if (lookahead[0] != -1) + { + ret = lookahead[0]; + lookahead[0] = -1; + } + else if (lookahead[1] != -1) + { + ret = lookahead[1]; + lookahead[1] = -1; + } + else + { + ret = in.read(); + } + return ret; + } + + /** + * Reads and identifier. + * + * @throws IOException if something goes wrong in the input source or if + * the lexical analyser fails to read an identifier + */ + private void readIdent() + throws IOException + { + int ch1 = read(); + // Read possibly leading '-'. + if (ch1 == '-') + { + parseBuffer[tokenEnd] = (char) ch1; + tokenEnd++; + ch1 = read(); + } + // What follows must be '_' or a-z or A-Z or nonascii (>177) or an + // escape. + if (ch1 == '_' || (ch1 >= 'a' && ch1 <= 'z') + || (ch1 >= 'A' && ch1 <= 'Z') || ch1 > 177) + { + parseBuffer[tokenEnd] = (char) ch1; + tokenEnd++; + } + else if (ch1 == '\\') + { + // Try to read an escape. + lookahead[0] = ch1; + readEscape(); + } + else + throw new CSSLexicalException("First character of identifier incorrect"); + + // Read any number of [_a-zA-Z0-9-] chars. + int ch = read(); + while (ch != -1 && (ch == '_' || ch == '-' || (ch >= 'a' && ch <= 'z') + || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'))) + { + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + ch = read(); + } + + // Push back last read character since it doesn't belong to the IDENT. + lookahead[0] = ch; + } + + /** + * Reads an escape. + * + * @throws IOException if something goes wrong in the input source or if + * the lexical analyser fails to read an escape + */ + private void readEscape() + throws IOException + { + int ch = read(); + if (ch != -1 && ch == '\\') + { + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + ch = read(); + if ((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f')) + { + // Read unicode escape. + // Zero to five 0-9a-f chars can follow. + int hexcount = 0; + ch = read(); + while (((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f')) + && hexcount < 5) + { + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + hexcount++; + ch = read(); + } + // Now we can have a \r\n or any whitespace character following. + if (ch == '\r') + { + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + ch = read(); + if (ch == '\n') + { + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + } + else + { + lookahead[0] = ch; + } + } + else if (ch == ' ' || ch == '\n' || ch == '\f' || ch == '\t') + { + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + } + else + { + lookahead[0] = ch; + } + } + else if (ch != '\n' && ch != '\r' && ch != '\f') + { + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + } + else + throw new CSSLexicalException("Can't read escape"); + } + else + throw new CSSLexicalException("Escape must start with '\\'"); + + } + + private void readName() + throws IOException + { + // Read first name character. + int ch = read(); + if (ch != -1 && (ch == '_' || ch == '-' || (ch >= 'a' && ch <= 'z') + || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'))) + { + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + } + else + throw new CSSLexicalException("Invalid name"); + + // Read any number (at least one) of [_a-zA-Z0-9-] chars. + ch = read(); + while (ch != -1 && (ch == '_' || ch == '-' || (ch >= 'a' && ch <= 'z') + || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9'))) + { + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + } + + // Push back last read character since it doesn't belong to the IDENT. + lookahead[0] = ch; + } + + /** + * Reads in a string. + * + * @throws IOException + */ + private void readString() + throws IOException + { + int ch1 = read(); + if (ch1 != -1 && (ch1 == '\'' || ch1 == '\"')) + { + parseBuffer[tokenEnd] = (char) ch1; + tokenEnd++; + + // Read any number of chars until we hit another chc1 char. + // Reject newlines, except if prefixed with \. + int ch = read(); + while (ch != -1 && ch != ch1) + { + // Every non-newline and non-\ char should be ok. + if (ch != '\n' && ch != '\r' && ch != '\f' && ch != '\\') + { + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + } + // Ok when followed by newline or as part of escape. + else if (ch == '\\') + { + int ch2 = read(); + if (ch2 == '\n' || ch2 == '\r') + { + parseBuffer[tokenEnd] = (char) ch; + parseBuffer[tokenEnd + 1] = (char) ch2; + tokenEnd += 2; + } + else + { + // Try to parse an escape. + lookahead[0] = ch; + lookahead[1] = ch2; + readEscape(); + } + } + else + throw new CSSLexicalException("Invalid string"); + + ch = read(); + } + if (ch != -1) + { + // Push the final char on the buffer. + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + } + else + throw new CSSLexicalException("Unterminated string"); + } + else + throw new CSSLexicalException("Invalid string"); + } + + /** + * Reads a chunk of whitespace. + * + * @throws IOException + */ + private void readWhitespace() + throws IOException + { + int ch = read(); + while (ch != -1 && (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n' + || ch == '\f')) + { + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + ch = read(); + } + // Push back last character read. + lookahead[0] = ch; + + } + + private void readURI() + throws IOException + { + // FIXME: Implement. + } + + /** + * Reads a comment block. + * + * @throws IOException + */ + private void readComment() + throws IOException + { + // First we need a / and a * + int ch = read(); + if (ch != -1 && ch == '/') + { + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + ch = read(); + if (ch != -1 && ch == '*') + { + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + ch = read(); + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + boolean finished = false; + int lastChar = ch; + ch = read(); + while (! finished && ch != -1) + { + if (lastChar == '*' && ch == '/') + finished = true; + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + lastChar = ch; + ch = read(); + } + } + } + if (ch == -1) + throw new CSSLexicalException("Unterminated comment"); + + // Push back last character read. + lookahead[0] = ch; + } + + /** + * Reads a number. + * + * @throws IOException + */ + private void readNum() + throws IOException + { + boolean hadDot = false; + // First char must be number or . + int ch = read(); + if (ch != -1 && ((ch >= '0' && ch <= '9') || ch == '.')) + { + if (ch == '.') + hadDot = true; + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + // Now read in any number of digits afterwards, and maybe one dot, + // if we hadn't one already. + ch = read(); + while (ch != -1 && ((ch >= '0' && ch <= '9') + || (ch == '.' && ! hadDot))) + { + if (ch == '.') + hadDot = true; + parseBuffer[tokenEnd] = (char) ch; + tokenEnd++; + ch = read(); + } + } + else + throw new CSSLexicalException("Invalid number"); + + // Check if we haven't accidentally finished with a dot. + if (parseBuffer[tokenEnd - 1] == '.') + throw new CSSLexicalException("Invalid number"); + + // Push back last character read. + lookahead[0] = ch; + } + + /** + * For testing, we read in the default.css in javax/swing/text/html + * + * @param args + */ + public static void main(String[] args) + { + try + { + String name = "/javax/swing/text/html/default.css"; + InputStream in = CSSScanner.class.getResourceAsStream(name); + BufferedInputStream bin = new BufferedInputStream(in); + InputStreamReader r = new InputStreamReader(bin); + CSSScanner s = new CSSScanner(r); + int token; + do + { + token = s.nextToken(); + System.out.println("token: " + token + ": " + + s.currentTokenString()); + } while (token != -1); + } + catch (IOException ex) + { + ex.printStackTrace(); + } + } +} diff --git a/gnu/javax/swing/text/html/css/FontSize.java b/gnu/javax/swing/text/html/css/FontSize.java new file mode 100644 index 000000000..19e1d701a --- /dev/null +++ b/gnu/javax/swing/text/html/css/FontSize.java @@ -0,0 +1,156 @@ +/* FontSize.java -- Converts CSS font size values into real values + 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 gnu.javax.swing.text.html.css; + +/** + * Converts CSS font-size values into real (point) values. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class FontSize +{ + + /** + * The CSS value. + */ + private String value; + + /** + * The default size for 'medium' absolute size. The other absolute sizes + * are calculated from this. + */ + public static final int DEFAULT_FONT_SIZE = 12; + + /** + * The scaling factors relative to the medium size. Medium is at index 2. + */ + private static final double[] SCALE = {0.8, 0.9, 1.0, 1.2, 1.4, 1.6, 1.8 }; + + /** + * Creates a new FontSize for the specified value. + * + * @param val the value to convert + */ + public FontSize(String val) + { + value = val; + } + + /** + * Returns the converted real value in point. + * + * @return the converted real value in point + */ + public int getValue() + { + int intVal; + if (value.contains("pt")) + intVal = mapPoints(); + else if (value.contains("px")) + intVal = mapPixels(); + else + intVal = mapAbsolute(); + // FIXME: Allow relative font values, ('larger' and 'smaller'). This + // requires knowledge about the parent element's font size. + return intVal; + } + + /** + * Maps point values ('XXXpt'). + * + * @return the real font size + */ + private int mapPoints() + { + int end = value.indexOf("pt"); + String number = value.substring(0, end); + int intVal = Integer.parseInt(number); + return intVal; + } + + /** + * Maps pixel values ('XXXpx'). + * + * @return the real font size + */ + private int mapPixels() + { + int end = value.indexOf("pt"); + String number = value.substring(0, end); + int intVal = Integer.parseInt(number); + return intVal; + } + + /** + * Maps absolute font-size values. + * + * @return the real value + */ + private int mapAbsolute() + { + int index; + if (value.equals("xx-small") || value.equals("x-small")) + index = 0; + else if (value.equals("small")) + index = 1; + else if (value.equals("medium")) + index = 2; + else if (value.equals("large")) + index = 3; + else if (value.equals("x-large")) + index = 4; + else if (value.equals("xx-large")) + index = 5; + else + index = 2; + double scale = SCALE[index]; + // FIXME: Scale the real medium size of the document, rather than the + // constant here. + int intVal = (int) (scale * DEFAULT_FONT_SIZE); + return intVal; + } + + /** + * Returns the string representation. + */ + public String toString() + { + return value; + } +} diff --git a/gnu/javax/swing/text/html/css/FontStyle.java b/gnu/javax/swing/text/html/css/FontStyle.java new file mode 100644 index 000000000..e52893193 --- /dev/null +++ b/gnu/javax/swing/text/html/css/FontStyle.java @@ -0,0 +1,80 @@ +/* FontStyle.java -- Converts font-size CSS values + 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 gnu.javax.swing.text.html.css; + +import java.awt.Font; + +/** + * Converts font-size CSS values to a form to be used by {@link java.awt.Font}. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class FontStyle +{ + + /** + * The real value. + */ + private String value; + + /** + * Creates a new instance. + * + * @param val the CSS value + */ + public FontStyle(String val) + { + value = val; + } + + /** + * Returns the converted value. + * + * @return the converted value + */ + public int getValue() + { + int intVal; + if (value.equals("italic") || value.equals("oblique")) + intVal = Font.ITALIC; + else + intVal = Font.PLAIN; + return intVal; + } +} diff --git a/gnu/javax/swing/text/html/css/FontWeight.java b/gnu/javax/swing/text/html/css/FontWeight.java new file mode 100644 index 000000000..d338c6f55 --- /dev/null +++ b/gnu/javax/swing/text/html/css/FontWeight.java @@ -0,0 +1,84 @@ +/* FontWeight.java -- Converts font-weight values + 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 gnu.javax.swing.text.html.css; + +import java.awt.Font; + +/** + * Converts font-weight CSS values to the constants defined for + * {@link java.awt.Font} + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class FontWeight +{ + + /** + * The value to convert. + */ + private String value; + + /** + * Creates a new instance. + * + * @param val the value to convert + */ + public FontWeight(String val) + { + value = val; + } + + /** + * Returns the converted value. + * + * @return the converted value + */ + public int getValue() + { + int intVal; + if (value.equals("normal")) + intVal = Font.PLAIN; + else if (value.equals("bold")) + intVal = Font.BOLD; + else + // FIXME: Implement finer-grained weights. + intVal = Font.PLAIN; + return intVal; + } +} diff --git a/gnu/javax/swing/text/html/css/Length.java b/gnu/javax/swing/text/html/css/Length.java new file mode 100644 index 000000000..7091fbae7 --- /dev/null +++ b/gnu/javax/swing/text/html/css/Length.java @@ -0,0 +1,90 @@ +/* Length.java -- Converts CSS length values + 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 gnu.javax.swing.text.html.css; + +/** + * Converts CSS length values to usable length values. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class Length +{ + + /** + * The original value. + */ + private String value; + + /** + * The converted value. + */ + private float floatValue; + + /** + * Creates a new length converter instance. + * + * @param val the CSS value + */ + public Length(String val) + { + value = val; + int i = value.indexOf("px"); + floatValue = 0.0F; + if (i != -1) + { + String sub = value.substring(0, i); + floatValue = Float.parseFloat(sub); + } + else + { + // TODO: Implement other length options. + floatValue = Float.parseFloat(value); + } + } + + /** + * Returns the value converted to pixels. + * + * @return the value converted to pixels + */ + public float getValue() + { + return floatValue; + } +} diff --git a/gnu/javax/swing/text/html/parser/HTML_401F.java b/gnu/javax/swing/text/html/parser/HTML_401F.java index c3c347e36..1894b6a1a 100644 --- a/gnu/javax/swing/text/html/parser/HTML_401F.java +++ b/gnu/javax/swing/text/html/parser/HTML_401F.java @@ -2445,8 +2445,10 @@ public class HTML_401F attr(VALUE, null, null, 0, IMPLIED) } ); + + // Headers in the paragraph are not allowed. defElement(P, 0, false, true, new ContentModel( 0, - new noTagModel(P), null), + new noTagModel(new String[] { P, H1, H2, H3, H4, H5, H6 }), null), NONE , new String[] { diff --git a/gnu/javax/swing/text/html/parser/support/textPreProcessor.java b/gnu/javax/swing/text/html/parser/support/textPreProcessor.java index cc1610585..b81275b1f 100644 --- a/gnu/javax/swing/text/html/parser/support/textPreProcessor.java +++ b/gnu/javax/swing/text/html/parser/support/textPreProcessor.java @@ -42,17 +42,17 @@ import gnu.javax.swing.text.html.parser.support.low.Constants; /** * Pre - processes text in text parts of the html document. - * Not thread - safe. + * * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) */ public class textPreProcessor { /** - * Pre - process non-preformatted text. - * \t, \r and \n mutate into spaces, then multiple spaces mutate - * into single one, all whitespace around tags is consumed. - * The content of the passed buffer is destroyed. - * @param text A text to pre-process. + * Pre - process non-preformatted text. \t, \r and \n mutate into spaces, then + * multiple spaces mutate into single one, all whitespace around tags is + * consumed. The content of the passed buffer is destroyed. + * + * @param a_text A text to pre-process. */ public char[] preprocess(StringBuffer a_text) { @@ -64,17 +64,22 @@ public class textPreProcessor int a = 0; int b = text.length - 1; + // Remove leading/trailing whitespace, leaving at most one character try { - while (Constants.bWHITESPACE.get(text [ a ])) + while (Constants.bWHITESPACE.get(text[a]) + && Constants.bWHITESPACE.get(text[a + 1])) a++; - while (Constants.bWHITESPACE.get(text [ b ])) + + while (b > a && Constants.bWHITESPACE.get(text[b]) + && Constants.bWHITESPACE.get(text[b - 1])) b--; } catch (ArrayIndexOutOfBoundsException sx) { - // A text fragment, consisting from line breaks only. - return null; + // A text fragment, consisting from spaces and line breaks only, + // mutates into single space. + return new char[] { ' ' }; } a_text.setLength(0); @@ -83,10 +88,9 @@ public class textPreProcessor boolean spaceNow; char c; - chars: - for (int i = a; i <= b; i++) + chars: for (int i = a; i <= b; i++) { - c = text [ i ]; + c = text[i]; spaceNow = Constants.bWHITESPACE.get(c); if (spacesWere && spaceNow) continue chars; diff --git a/include/gnu_java_awt_peer_gtk_ComponentGraphics.h b/include/gnu_java_awt_peer_gtk_ComponentGraphics.h index 0067119f6..fbd5f6a93 100644 --- a/include/gnu_java_awt_peer_gtk_ComponentGraphics.h +++ b/include/gnu_java_awt_peer_gtk_ComponentGraphics.h @@ -15,8 +15,8 @@ JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_ComponentGraphics_disposeSurfa JNIEXPORT jlong JNICALL Java_gnu_java_awt_peer_gtk_ComponentGraphics_initFromVolatile (JNIEnv *env, jobject, jlong, jint, jint); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_ComponentGraphics_start_1gdk_1drawing (JNIEnv *env, jobject); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_ComponentGraphics_end_1gdk_1drawing (JNIEnv *env, jobject); -JNIEXPORT jobject JNICALL Java_gnu_java_awt_peer_gtk_ComponentGraphics_nativeGrab(JNIEnv *env, jclass, jobject ); JNIEXPORT jboolean JNICALL Java_gnu_java_awt_peer_gtk_ComponentGraphics_hasXRender (JNIEnv *env, jclass); +JNIEXPORT jobject JNICALL Java_gnu_java_awt_peer_gtk_ComponentGraphics_nativeGrab (JNIEnv *env, jclass, jobject); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_ComponentGraphics_copyAreaNative (JNIEnv *env, jobject, jobject, jint, jint, jint, jint, jint, jint); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_ComponentGraphics_drawVolatile (JNIEnv *env, jobject, jobject, jlong, jint, jint, jint, jint, jint, jint, jint, jint); diff --git a/include/gnu_java_awt_peer_gtk_GtkChoicePeer.h b/include/gnu_java_awt_peer_gtk_GtkChoicePeer.h index 5450434d8..fc6a72a59 100644 --- a/include/gnu_java_awt_peer_gtk_GtkChoicePeer.h +++ b/include/gnu_java_awt_peer_gtk_GtkChoicePeer.h @@ -12,12 +12,12 @@ extern "C" JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkChoicePeer_create (JNIEnv *env, jobject); JNIEXPORT jint JNICALL Java_gnu_java_awt_peer_gtk_GtkChoicePeer_nativeGetSelected (JNIEnv *env, jobject); -JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkChoicePeer_add (JNIEnv *env, jobject, jstring, jint); -JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkChoicePeer_nativeRemove (JNIEnv *env, jobject, jint); -JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkChoicePeer_nativeRemoveAll (JNIEnv *env, jobject); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkChoicePeer_connectSignals (JNIEnv *env, jobject); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkChoicePeer_selectNative (JNIEnv *env, jobject, jint); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkChoicePeer_selectNativeUnlocked (JNIEnv *env, jobject, jint); +JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkChoicePeer_add (JNIEnv *env, jobject, jstring, jint); +JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkChoicePeer_nativeRemove (JNIEnv *env, jobject, jint); +JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkChoicePeer_nativeRemoveAll (JNIEnv *env, jobject); #ifdef __cplusplus } diff --git a/include/gnu_java_awt_peer_gtk_GtkToolkit.h b/include/gnu_java_awt_peer_gtk_GtkToolkit.h index f1f5326cd..b91d54cdb 100644 --- a/include/gnu_java_awt_peer_gtk_GtkToolkit.h +++ b/include/gnu_java_awt_peer_gtk_GtkToolkit.h @@ -11,13 +11,13 @@ extern "C" #endif JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkInit (JNIEnv *env, jclass, jint); +JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkMain (JNIEnv *env, jclass); +JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkQuit (JNIEnv *env, jclass); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_beep (JNIEnv *env, jobject); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_getScreenSizeDimensions (JNIEnv *env, jobject, jintArray); JNIEXPORT jint JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_getScreenResolution (JNIEnv *env, jobject); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_sync (JNIEnv *env, jobject); JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_loadSystemColors (JNIEnv *env, jobject, jintArray); -JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkMain (JNIEnv *env, jclass); -JNIEXPORT void JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_gtkQuit (JNIEnv *env, jclass); JNIEXPORT jint JNICALL Java_gnu_java_awt_peer_gtk_GtkToolkit_getMouseNumberOfButtons (JNIEnv *env, jobject); #ifdef __cplusplus diff --git a/include/jvmti.h b/include/jvmti.h index 23419f920..b26f7932e 100644 --- a/include/jvmti.h +++ b/include/jvmti.h @@ -45,6 +45,8 @@ exception statement from your version. */ #define _CLASSPATH_JVMTI_H #include <jni.h> +#include "jvmti_md.h" + /* The VM might define JVMTI base types */ #ifndef _CLASSPATH_VM_JVMTI_TYPES_DEFINED @@ -79,7 +81,7 @@ typedef struct _jvmtiAddrLocationMap jvmtiAddrLocationMap; #ifdef __cplusplus typedef struct _Jv_JVMTIEnv jvmtiEnv; #else -typedef const struct _Jv_jvmtiEnv jvmtiEnv; +typedef const struct _Jv_jvmtiEnv *jvmtiEnv; #endif /* @@ -140,7 +142,7 @@ typedef enum JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED = 70, JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED = 71, JVMTI_ERROR_MUST_POSSESS_CAPABILITY = 99, - JVMTI_ERROR_ILLEGAL_ARGUMENT = 103, + JVMTI_ERROR_ILLEGAL_ARGUMENT = 103 } jvmtiError; /* @@ -283,7 +285,7 @@ typedef enum JVMTI_EVENT_EXCEPTION_CATCH = 59, JVMTI_EVENT_SINGLE_STEP = 60, JVMTI_EVENT_FRAME_POP = 61, - JVMTI_EVENT_BERAKPOINT = 62, + JVMTI_EVENT_BREAKPOINT = 62, JVMTI_EVENT_FIELD_ACCESS = 63, JVMTI_EVENT_FIELD_MODIFICATION = 64, JVMTI_EVENT_METHOD_ENTRY = 65, @@ -658,7 +660,8 @@ struct _Jv_jvmtiEnv jthread thread); jvmtiError (JNICALL *StopThread) (jvmtiEnv *env, - jthread thread); + jthread thread, + jobject exception); jvmtiError (JNICALL *InterruptThread) (jvmtiEnv *env, jthread thread); @@ -790,7 +793,8 @@ struct _Jv_jvmtiEnv jrawMonitorID monitor); jvmtiError (JNICALL *RawMonitorWait) (jvmtiEnv *env, - jrawMonitorID monitor); + jrawMonitorID monitor, + jlong millis); jvmtiError (JNICALL *RawMonitorNotify) (jvmtiEnv *env, jrawMonitorID monitor); @@ -844,7 +848,7 @@ struct _Jv_jvmtiEnv jvmtiError (JNICALL *GetSourceFileName) (jvmtiEnv *env, jclass klass, - char *source_name_ptr); + char **source_name_ptr); jvmtiError (JNICALL *GetClassModifiers) (jvmtiEnv *env, jclass klass, @@ -1249,8 +1253,8 @@ class _Jv_JVMTIEnv jvmtiError ResumeThread (jthread thread) { return p->ResumeThread (this, thread); } - jvmtiError StopThread (jthread thread) - { return p->StopThread (this, thread); } + jvmtiError StopThread (jthread thread, jobject exception) + { return p->StopThread (this, thread, exception); } jvmtiError InterruptThread (jthread thread) { return p->InterruptThread (this, thread); } @@ -1360,8 +1364,8 @@ class _Jv_JVMTIEnv jvmtiError RawMonitorExit (jrawMonitorID monitor) { return p->RawMonitorExit (this, monitor); } - jvmtiError RawMonitorWait (jrawMonitorID monitor) - { return p->RawMonitorWait (this, monitor); } + jvmtiError RawMonitorWait (jrawMonitorID monitor, jlong millis) + { return p->RawMonitorWait (this, monitor, millis); } jvmtiError RawMonitorNotify (jrawMonitorID monitor) { return p->RawMonitorNotify (this, monitor); } @@ -1400,7 +1404,7 @@ class _Jv_JVMTIEnv jvmtiError GetClassStatus (jclass klass, jint *status_ptr) { return p->GetClassStatus (this, klass, status_ptr); } - jvmtiError GetSourceFileName (jclass klass, char *source_name_ptr) + jvmtiError GetSourceFileName (jclass klass, char **source_name_ptr) { return p->GetSourceFileName (this, klass, source_name_ptr); } jvmtiError GetClassModifiers (jclass klass, jint *modifiers_ptr) diff --git a/java/awt/Color.java b/java/awt/Color.java index b03129241..c3d04c049 100644 --- a/java/awt/Color.java +++ b/java/awt/Color.java @@ -534,14 +534,31 @@ public class Color implements Paint, Serializable { // Do not inline getRGB() to this.value, because of SystemColor. int value = getRGB(); - int red = (value & RED_MASK) >> 16; - int green = (value & GREEN_MASK) >> 8; - int blue = value & BLUE_MASK; - // We have to special case 0-2 because they won't scale by division. - red = red < 3 ? 3 : (int) Math.min(255, red / BRIGHT_SCALE); - green = green < 3 ? 3 : (int) Math.min(255, green / BRIGHT_SCALE); - blue = blue < 3 ? 3 : (int) Math.min(255, blue / BRIGHT_SCALE); - return new Color(red, green, blue, 255); + int[] hues = new int[3]; + hues[0] = (value & RED_MASK) >> 16; + hues[1] = (value & GREEN_MASK) >> 8; + hues[2] = value & BLUE_MASK; + + // (0,0,0) is a special case. + if (hues[0] == 0 && hues[1] == 0 && hues[2] ==0) + { + hues[0] = 3; + hues[1] = 3; + hues[2] = 3; + } + else + { + for (int index = 0; index < 3; index++) + { + + if (hues[index] > 2) + hues[index] = (int) Math.min(255, hues[index]/0.7f); + if (hues[index] == 1 || hues[index] == 2) + hues[index] = 4; + } + } + + return new Color(hues[0], hues[1], hues[2], 255); } /** diff --git a/java/awt/Component.java b/java/awt/Component.java index ba669f8ec..26ba605b5 100644 --- a/java/awt/Component.java +++ b/java/awt/Component.java @@ -579,7 +579,7 @@ public abstract class Component transient ComponentPeer peer; /** The preferred component orientation. */ - transient ComponentOrientation orientation = ComponentOrientation.UNKNOWN; + transient ComponentOrientation componentOrientation = ComponentOrientation.UNKNOWN; /** * The associated graphics configuration. @@ -5244,8 +5244,8 @@ p * <li>the set of backward traversal keys public void setComponentOrientation(ComponentOrientation o) { - ComponentOrientation oldOrientation = orientation; - orientation = o; + ComponentOrientation oldOrientation = componentOrientation; + componentOrientation = o; firePropertyChange("componentOrientation", oldOrientation, o); } @@ -5257,7 +5257,7 @@ p * <li>the set of backward traversal keys */ public ComponentOrientation getComponentOrientation() { - return orientation; + return componentOrientation; } /** diff --git a/java/awt/Container.java b/java/awt/Container.java index e6d8493d3..17ca3e0ad 100644 --- a/java/awt/Container.java +++ b/java/awt/Container.java @@ -87,8 +87,6 @@ public class Container extends Component Component[] component; LayoutManager layoutMgr; - Dimension maxSize; - /** * @since 1.4 */ @@ -653,45 +651,39 @@ public class Container extends Component return; ContainerPeer cPeer = null; - if (peer != null && ! (peer instanceof LightweightPeer)) + if (peer instanceof ContainerPeer) { cPeer = (ContainerPeer) peer; cPeer.beginValidate(); } - for (int i = 0; i < ncomponents; ++i) - { - Component comp = component[i]; - - if (comp.getPeer () == null) - comp.addNotify(); - } - doLayout (); for (int i = 0; i < ncomponents; ++i) { Component comp = component[i]; - if (! comp.isValid()) + if (comp instanceof Container && ! (comp instanceof Window) + && ! comp.valid) { - if (comp instanceof Container) - { - ((Container) comp).validateTree(); - } - else - { - component[i].validate(); - } + ((Container) comp).validateTree(); + } + else + { + comp.validate(); } } + if (peer instanceof ContainerPeer) + { + cPeer = (ContainerPeer) peer; + cPeer.endValidate(); + } + /* children will call invalidate() when they are layed out. It is therefore important that valid is not set to true until after the children have been layed out. */ valid = true; - if (cPeer != null) - cPeer.endValidate(); } public void setFont(Font f) diff --git a/java/awt/MenuShortcut.java b/java/awt/MenuShortcut.java index adfd1d318..259cbf1ae 100644 --- a/java/awt/MenuShortcut.java +++ b/java/awt/MenuShortcut.java @@ -38,6 +38,8 @@ exception statement from your version. */ package java.awt; +import java.awt.event.KeyEvent; + /** * This class implements a keyboard accelerator for a menu item. * @@ -70,6 +72,8 @@ private int key; */ private boolean usesShift; +private String keyName; + /*************************************************************************/ /** @@ -99,6 +103,7 @@ MenuShortcut(int key, boolean usesShift) { this.key = key; this.usesShift = usesShift; + setKeyName(key); } /*************************************************************************/ @@ -181,7 +186,11 @@ equals(Object obj) public String toString() { - return(getClass().getName() + "[" + paramString () + "]"); + String temp = "Ctrl+"; + if (usesShift) + temp = temp + "Shift+"; + temp = temp + keyName; + return temp; } public int @@ -204,4 +213,224 @@ paramString() return "key=" + key + ",usesShift=" + usesShift; } -} // class MenuShortcut +private void +setKeyName(int key) +{ + if (key == '\n') + keyName = "Enter"; + else if (key == '\b') + keyName = "Backspace"; + else if (key == '\t') + keyName = "Tab"; + else if (key == ' ') + keyName = "Space"; + else if (key == ',') + keyName = "Comma"; + else if (key == '.') + keyName = "Period"; + else if (key == '/') + keyName = "Slash"; + else if (key == '\\') + keyName = "Back Slash"; + else if (key == ';') + keyName = "Semicolon"; + else if (key == '=') + keyName = "Equals"; + else if (key == '[') + keyName = "Open Bracket"; + else if (key == ']') + keyName = "Close Bracket"; + else if (key == '0') + keyName = "0"; + else if (key == '1') + keyName = "1"; + else if (key == '2') + keyName = "2"; + else if (key == '3') + keyName = "3"; + else if (key == '4') + keyName = "4"; + else if (key == '5') + keyName = "5"; + else if (key == '6') + keyName = "6"; + else if (key == '7') + keyName = "7"; + else if (key == '8') + keyName = "8"; + else if (key == '9') + keyName = "9"; + else if (key == 'A') + keyName = "A"; + else if (key == 'B') + keyName = "B"; + else if (key == 'C') + keyName = "C"; + else if (key == 'D') + keyName = "D"; + else if (key == 'E') + keyName = "E"; + else if (key == 'F') + keyName = "F"; + else if (key == 'G') + keyName = "G"; + else if (key == 'H') + keyName = "H"; + else if (key == 'I') + keyName = "I"; + else if (key == 'J') + keyName = "J"; + else if (key == 'K') + keyName = "K"; + else if (key == 'L') + keyName = "L"; + else if (key == 'M') + keyName = "M"; + else if (key == 'N') + keyName = "N"; + else if (key == 'O') + keyName = "O"; + else if (key == 'P') + keyName = "P"; + else if (key == 'Q') + keyName = "Q"; + else if (key == 'R') + keyName = "R"; + else if (key == 'S') + keyName = "S"; + else if (key == 'T') + keyName = "T"; + else if (key == 'U') + keyName = "U"; + else if (key == 'V') + keyName = "V"; + else if (key == 'W') + keyName = "W"; + else if (key == 'X') + keyName = "X"; + else if (key == 'Y') + keyName = "Y"; + else if (key == 'Z') + keyName = "Z"; + else if (key == 3) + keyName = "Cancel"; + else if (key == 12) + keyName = "Clear"; + else if (key == 16) + keyName = "Shift"; + else if (key == 17) + keyName = "Ctrl"; + else if (key == 18) + keyName = "Alt"; + else if (key == 19) + keyName = "Pause"; + else if (key == 20) + keyName = "Caps Lock"; + else if (key == 21) + keyName = "Kana"; + else if (key == 24) + keyName = "Final"; + else if (key == 25) + keyName = "Kanji"; + else if (key == 27) + keyName = "Escape"; + else if (key == 28) + keyName = "Convert"; + else if (key == 29) + keyName = "No Convert"; + else if (key == 30) + keyName = "Accept"; + else if (key == 31) + keyName = "Mode Change"; + else if (key == 33) + keyName = "Page Up"; + else if (key == 34) + keyName = "Page Down"; + else if (key == 35) + keyName = "End"; + else if (key == 36) + keyName = "Home"; + else if (key == 37) + keyName = "Left"; + else if (key == 38) + keyName = "Up"; + else if (key == 39) + keyName = "Right"; + else if (key == 40) + keyName = "Down"; + else if (key == 96) + keyName = "NumPad-0"; + else if (key == 97) + keyName = "NumPad-1"; + else if (key == 98) + keyName = "NumPad-2"; + else if (key == 99) + keyName = "NumPad-3"; + else if (key == 100) + keyName = "NumPad-4"; + else if (key == 101) + keyName = "NumPad-5"; + else if (key == 102) + keyName = "NumPad-6"; + else if (key == 103) + keyName = "NumPad-7"; + else if (key == 104) + keyName = "NumPad-8"; + else if (key == 105) + keyName = "NumPad-9"; + else if (key == 106) + keyName = "NumPad *"; + else if (key == 107) + keyName = "NumPad +"; + else if (key == 108) + keyName = "NumPad ,"; + else if (key == 109) + keyName = "NumPad -"; + else if (key == 110) + keyName = "NumPad ."; + else if (key == 111) + keyName = "NumPad /"; + else if (key == 112) + keyName = "F1"; + else if (key == 113) + keyName = "F2"; + else if (key == 114) + keyName = "F3"; + else if (key == 115) + keyName = "F4"; + else if (key == 116) + keyName = "F5"; + else if (key == 117) + keyName = "F6"; + else if (key == 118) + keyName = "F7"; + else if (key == 119) + keyName = "F8"; + else if (key == 120) + keyName = "F9"; + else if (key == 121) + keyName = "F10"; + else if (key == 122) + keyName = "F11"; + else if (key == 123) + keyName = "F12"; + else if (key == 127) + keyName = "Delete"; + else if (key == 144) + keyName = "Num Lock"; + else if (key == 145) + keyName = "Scroll Lock"; + else if (key == 154) + keyName = "Print Screen"; + else if (key == 155) + keyName = "Insert"; + else if (key == 156) + keyName = "Help"; + else if (key == 157) + keyName = "Meta"; + else if (key == 192) + keyName = "Back Quote"; + else if (key == 222) + keyName = "Quote"; +} +} // class MenuShortcut diff --git a/java/awt/dnd/DropTargetDragEvent.java b/java/awt/dnd/DropTargetDragEvent.java index 838141ec3..58feb4387 100644 --- a/java/awt/dnd/DropTargetDragEvent.java +++ b/java/awt/dnd/DropTargetDragEvent.java @@ -147,7 +147,6 @@ public class DropTargetDragEvent extends DropTargetEvent */ public Transferable getTransferable() { - // FIXME: Not implemented - return null; + return context.getTransferable(); } } // class DropTargetDragEvent diff --git a/java/awt/image/AffineTransformOp.java b/java/awt/image/AffineTransformOp.java index bb4b79523..7820684d2 100644 --- a/java/awt/image/AffineTransformOp.java +++ b/java/awt/image/AffineTransformOp.java @@ -1,6 +1,6 @@ /* AffineTransformOp.java -- This class performs affine transformation between two images or rasters in 2 dimensions. - Copyright (C) 2004 Free Software Foundation + Copyright (C) 2004, 2006 Free Software Foundation This file is part of GNU Classpath. @@ -52,6 +52,7 @@ import java.util.Arrays; * rasters in 2 dimensions. * * @author Olga Rodimina (rodimina@redhat.com) + * @author Francis Kung (fkung@redhat.com) */ public class AffineTransformOp implements BufferedImageOp, RasterOp { @@ -74,6 +75,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp * * @param xform AffineTransform that will applied to the source image * @param interpolationType type of interpolation used + * @throws ImagingOpException if the transform matrix is noninvertible */ public AffineTransformOp (AffineTransform xform, int interpolationType) { @@ -102,6 +104,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp * * @param xform AffineTransform that will applied to the source image * @param hints rendering hints that will be used during transformation + * @throws ImagingOpException if the transform matrix is noninvertible */ public AffineTransformOp (AffineTransform xform, RenderingHints hints) { @@ -115,8 +118,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp * Creates empty BufferedImage with the size equal to that of the * transformed image and correct number of bands. The newly created * image is created with the specified ColorModel. - * If the ColorModel is equal to null, then image is created - * with the ColorModel of the source image. + * If the ColorModel is equal to null, an appropriate ColorModel is used. * * @param src source image * @param destCM color model for the destination image @@ -125,17 +127,21 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp public BufferedImage createCompatibleDestImage (BufferedImage src, ColorModel destCM) { + if (destCM != null) + return new BufferedImage(destCM, + createCompatibleDestRaster(src.getRaster()), + src.isAlphaPremultiplied(), null); + + // This behaviour was determined by Mauve testcases, and is compatible + // with the reference implementation + if (src.getType() == BufferedImage.TYPE_INT_ARGB_PRE + || src.getType() == BufferedImage.TYPE_4BYTE_ABGR + || src.getType() == BufferedImage.TYPE_4BYTE_ABGR_PRE) + return new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); - // if destCm is not specified, use color model of the source image - - if (destCM == null) - destCM = src.getColorModel (); - - return new BufferedImage (destCM, - createCompatibleDestRaster (src.getRaster ()), - src.isAlphaPremultiplied (), - null); - + else + return new BufferedImage(src.getWidth(), src.getHeight(), + BufferedImage.TYPE_INT_ARGB); } /** @@ -148,7 +154,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp */ public WritableRaster createCompatibleDestRaster (Raster src) { - Rectangle rect = (Rectangle) getBounds2D (src); + Rectangle2D rect = getBounds2D(src); // throw RasterFormatException if resulting width or height of the // transformed raster is 0 @@ -156,33 +162,31 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp if (rect.getWidth () == 0 || rect.getHeight () == 0) throw new RasterFormatException("width or height is 0"); - return src.createCompatibleWritableRaster ((int) rect.getWidth (), - (int) rect.getHeight ()); + return src.createCompatibleWritableRaster((int) rect.getWidth(), + (int) rect.getHeight()); } /** * Transforms source image using transform specified at the constructor. - * The resulting transformed image is stored in the destination image. + * The resulting transformed image is stored in the destination image if one + * is provided; otherwise a new BufferedImage is created and returned. * * @param src source image * @param dst destination image + * @throws IllegalArgumentException if the source and destination image are + * the same * @return transformed source image */ public final BufferedImage filter (BufferedImage src, BufferedImage dst) { if (dst == src) - throw new IllegalArgumentException ("src image cannot be the same as the dst image"); - - // If the destination image is null, then BufferedImage is - // created with ColorModel of the source image + throw new IllegalArgumentException ("src image cannot be the same as " + + "the dst image"); + // If the destination image is null, then use a compatible BufferedImage if (dst == null) - dst = createCompatibleDestImage(src, src.getColorModel ()); - - // FIXME: Must check if color models of src and dst images are the same. - // If it is not, then source image should be converted to color model - // of the destination image + dst = createCompatibleDestImage(src, null); Graphics2D gr = (Graphics2D) dst.createGraphics (); gr.setRenderingHints (hints); @@ -193,10 +197,13 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp /** * Transforms source raster using transform specified at the constructor. - * The resulting raster is stored in the destination raster. + * The resulting raster is stored in the destination raster if it is not + * null, otherwise a new raster is created and returned. * * @param src source raster * @param dst destination raster + * @throws IllegalArgumentException if the source and destination are not + * compatible * @return transformed raster */ public final WritableRaster filter (Raster src, WritableRaster dst) @@ -212,85 +219,47 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp throw new IllegalArgumentException("src and dst must have same number" + " of bands"); - double[] dpts = new double[dst.getWidth() * 2]; - double[] pts = new double[dst.getWidth() * 2]; + // Create arrays to hold all the points + double[] dstPts = new double[dst.getHeight() * dst.getWidth() * 2]; + double[] srcPts = new double[dst.getHeight() * dst.getWidth() * 2]; + + // Populate array with all points in the *destination* raster + int i = 0; for (int x = 0; x < dst.getWidth(); x++) - { - dpts[2 * x] = x + dst.getMinX(); - dpts[2 * x + 1] = x; - } - Rectangle srcbounds = src.getBounds(); - if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)) - { - for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++) - { - try { - transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2); - } catch (NoninvertibleTransformException e) { - // Can't happen since the constructor traps this - e.printStackTrace(); - } - - for (int x = 0; x < dst.getWidth(); x++) - { - if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1])) - continue; - dst.setDataElements(x + dst.getMinX(), y, - src.getDataElements((int)pts[2 * x], - (int)pts[2 * x + 1], - null)); - } - } - } - else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) - { - double[] tmp = new double[4 * src.getNumBands()]; - for (int y = dst.getMinY(); y < dst.getMinY() + dst.getHeight(); y++) { - try { - transform.inverseTransform(dpts, 0, pts, 0, dst.getWidth() * 2); - } catch (NoninvertibleTransformException e) { - // Can't happen since the constructor traps this - e.printStackTrace(); - } - - for (int x = 0; x < dst.getWidth(); x++) - { - if (!srcbounds.contains(pts[2 * x], pts[2 * x + 1])) - continue; - int xx = (int)pts[2 * x]; - int yy = (int)pts[2 * x + 1]; - double dx = (pts[2 * x] - xx); - double dy = (pts[2 * x + 1] - yy); - - // TODO write this more intelligently - if (xx == src.getMinX() + src.getWidth() - 1 || - yy == src.getMinY() + src.getHeight() - 1) + for (int y = 0; y < dst.getHeight(); y++) { - // bottom or right edge - Arrays.fill(tmp, 0); - src.getPixel(xx, yy, tmp); + dstPts[i++] = x; + dstPts[i++] = y; } - else - { - // Normal case - src.getPixels(xx, yy, 2, 2, tmp); - for (int b = 0; b < src.getNumBands(); b++) - tmp[b] = dx * dy * tmp[b] - + (1 - dx) * dy * tmp[b + src.getNumBands()] - + dx * (1 - dy) * tmp[b + 2 * src.getNumBands()] - + (1 - dx) * (1 - dy) * tmp[b + 3 * src.getNumBands()]; - } - dst.setPixel(x, y, tmp); - } } - } - else - { - // Bicubic - throw new UnsupportedOperationException("not implemented yet"); - } + Rectangle srcbounds = src.getBounds(); + + // Use an inverse transform to map each point in the destination to + // a point in the source. Note that, while all points in the destination + // matrix are integers, this is not necessarily true for points in the + // source (hence why interpolation is required) + try + { + AffineTransform inverseTx = transform.createInverse(); + inverseTx.transform(dstPts, 0, srcPts, 0, dstPts.length / 2); + } + catch (NoninvertibleTransformException e) + { + // Shouldn't happen since the constructor traps this + throw new ImagingOpException(e.getMessage()); + } + + // Different interpolation methods... + if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)) + filterNearest(src, dst, dstPts, srcPts); + else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) + filterBilinear(src, dst, dstPts, srcPts); + + else // bicubic + filterBicubic(src, dst, dstPts, srcPts); + return dst; } @@ -314,16 +283,7 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp */ public final Rectangle2D getBounds2D (Raster src) { - // determine new size for the transformed raster. - // Need to calculate transformed coordinates of the lower right - // corner of the raster. The upper left corner is always (0,0) - - double x2 = (double) src.getWidth () + src.getMinX (); - double y2 = (double) src.getHeight () + src.getMinY (); - Point2D p2 = getPoint2D (new Point2D.Double (x2,y2), null); - - Rectangle2D rect = new Rectangle (0, 0, (int) p2.getX (), (int) p2.getY ()); - return rect.getBounds (); + return transform.createTransformedShape(src.getBounds()).getBounds2D(); } /** @@ -333,8 +293,12 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp */ public final int getInterpolationType () { - if(hints.containsValue (RenderingHints.VALUE_INTERPOLATION_BILINEAR)) + if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BILINEAR)) return TYPE_BILINEAR; + + else if (hints.containsValue(RenderingHints.VALUE_INTERPOLATION_BICUBIC)) + return TYPE_BICUBIC; + else return TYPE_NEAREST_NEIGHBOR; } @@ -372,4 +336,191 @@ public class AffineTransformOp implements BufferedImageOp, RasterOp { return transform; } + + /** + * Perform nearest-neighbour filtering + * + * @param src the source raster + * @param dst the destination raster + * @param dpts array of points on the destination raster + * @param pts array of corresponding points on the source raster + */ + private void filterNearest(Raster src, WritableRaster dst, double[] dpts, + double[] pts) + { + Rectangle srcbounds = src.getBounds(); + + // For all points on the destination raster, copy the value from the + // corrosponding (rounded) source point + for (int i = 0; i < dpts.length; i += 2) + { + int srcX = (int) Math.round(pts[i]) + src.getMinX(); + int srcY = (int) Math.round(pts[i + 1]) + src.getMinY(); + + if (srcbounds.contains(srcX, srcY)) + dst.setDataElements((int) dpts[i] + dst.getMinX(), + (int) dpts[i + 1] + dst.getMinY(), + src.getDataElements(srcX, srcY, null)); + } + } + + /** + * Perform bilinear filtering + * + * @param src the source raster + * @param dst the destination raster + * @param dpts array of points on the destination raster + * @param pts array of corresponding points on the source raster + */ + private void filterBilinear(Raster src, WritableRaster dst, double[] dpts, + double[] pts) + { + Rectangle srcbounds = src.getBounds(); + + // For all points in the destination raster, use bilinear interpolation + // to find the value from the corrosponding source points + for (int i = 0; i < dpts.length; i += 2) + { + int srcX = (int) Math.round(pts[i]) + src.getMinX(); + int srcY = (int) Math.round(pts[i + 1]) + src.getMinY(); + + if (srcbounds.contains(srcX, srcY)) + { + // Corner case at the bottom or right edge; use nearest neighbour + if (pts[i] >= src.getWidth() - 1 + || pts[i + 1] >= src.getHeight() - 1) + dst.setDataElements((int) dpts[i] + dst.getMinX(), + (int) dpts[i + 1] + dst.getMinY(), + src.getDataElements(srcX, srcY, null)); + + // Standard case, apply the bilinear formula + else + { + int x = (int) Math.floor(pts[i] + src.getMinX()); + int y = (int) Math.floor(pts[i + 1] + src.getMinY()); + double xdiff = pts[i] + src.getMinX() - x; + double ydiff = pts[i + 1] + src.getMinY() - y; + + // Run the interpolation for each band + for (int j = 0; j < src.getNumBands(); j++) + { + double result = (src.getSampleDouble(x, y, j) * (1 - xdiff) + + src.getSampleDouble(x + 1, y, j) * xdiff) + * (1 - ydiff) + + (src.getSampleDouble(x, y + 1, j) + * (1 - xdiff) + + src.getSampleDouble(x + 1, y + 1, j) + * xdiff) + * ydiff; + dst.setSample((int) dpts[i] + dst.getMinX(), + (int) dpts[i + 1] + dst.getMinY(), + j, result); + } + } + } + } + } + + /** + * Perform bicubic filtering + * based on http://local.wasp.uwa.edu.au/~pbourke/colour/bicubic/ + * + * @param src the source raster + * @param dst the destination raster + * @param dpts array of points on the destination raster + * @param pts array of corresponding points on the source raster + */ + private void filterBicubic(Raster src, WritableRaster dst, double[] dpts, + double[] pts) + { + Rectangle srcbounds = src.getBounds(); + + // For all points on the destination raster, perform bicubic interpolation + // from corrosponding source points + double[] result = new double[src.getNumBands()]; + for (int i = 0; i < dpts.length; i += 2) + { + if (srcbounds.contains((int) Math.round(pts[i]) + src.getMinX(), + (int) Math.round(pts[i + 1]) + src.getMinY())) + { + int x = (int) Math.floor(pts[i] + src.getMinX()); + int y = (int) Math.floor(pts[i + 1] + src.getMinY()); + double dx = pts[i] + src.getMinX() - x; + double dy = pts[i + 1] + src.getMinY() - y; + Arrays.fill(result, 0); + + for (int m = - 1; m < 3; m++) + { + for (int n = - 1; n < 3; n++) + { + // R(x) = ( P(x+2)^3 - 4 P(x+1)^3 + 6 P(x)^3 - 4 P(x-1)^3 ) / 6 + double r1 = 0; + double r2 = 0; + + // Calculate R(m - dx) + double rx = m - dx + 2; + if (rx > 0) + r1 += rx * rx * rx; + + rx = m - dx + 1; + if (rx > 0) + r1 -= 4 * rx * rx * rx; + + rx = m - dx; + if (rx > 0) + r1 += 6 * rx * rx * rx; + + rx = m - dx - 1; + if (rx > 0) + r1 -= 4 * rx * rx * rx; + + r1 /= 6; + + // Calculate R(dy - n); + rx = dy - n + 2; + if (rx > 0) + r2 += rx * rx * rx; + + rx = dy - n + 1; + if (rx > 0) + r2 -= 4 * rx * rx * rx; + + rx = dy - n; + if (rx > 0) + r2 += 6 * rx * rx * rx; + + rx = dy - n - 1; + if (rx > 0) + r2 -= 4 * rx * rx * rx; + + r2 /= 6; + + // Calculate F(i+m, j+n) R(m - dx) R(dy - n) + // Check corner cases + int srcX = x + m; + if (srcX >= src.getMinX() + src.getWidth()) + srcX = src.getMinX() + src.getWidth() - 1; + else if (srcX < src.getMinX()) + srcX = src.getMinX(); + + int srcY = y + n; + if (srcY >= src.getMinY() + src.getHeight()) + srcY = src.getMinY() + src.getHeight() - 1; + else if (srcY < src.getMinY()) + srcY = src.getMinY(); + + // Calculate once for each band + for (int j = 0; j < result.length; j++) + result[j] += src.getSample(srcX, srcY, j) * r1 * r2; + } + } + + // Put it all together + for (int j = 0; j < result.length; j++) + dst.setSample((int) dpts[i] + dst.getMinX(), + (int) dpts[i + 1] + dst.getMinY(), + j, result[j]); + } + } + } } diff --git a/java/awt/image/BandCombineOp.java b/java/awt/image/BandCombineOp.java index 634125ed2..c4e2d5810 100644 --- a/java/awt/image/BandCombineOp.java +++ b/java/awt/image/BandCombineOp.java @@ -1,4 +1,5 @@ -/* Copyright (C) 2004 Free Software Foundation +/* BandCombineOp.java - perform a combination on the bands of a raster + Copyright (C) 2004, 2006 Free Software Foundation This file is part of GNU Classpath. @@ -36,7 +37,6 @@ exception statement from your version. */ package java.awt.image; -import java.awt.Point; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; @@ -65,52 +65,66 @@ public class BandCombineOp implements RasterOp * * @param matrix The matrix to filter pixels with. * @param hints Rendering hints to apply. Ignored. + * @throws ArrayIndexOutOfBoundsException if the matrix is invalid */ public BandCombineOp(float[][] matrix, RenderingHints hints) { - this.matrix = matrix; + this.matrix = new float[matrix.length][]; + int width = matrix[0].length; + for (int i = 0; i < matrix.length; i++) + { + this.matrix[i] = new float[width + 1]; + for (int j = 0; j < width; j++) + this.matrix[i][j] = matrix[i][j]; + + // The reference implementation pads the array with a trailing zero... + this.matrix[i][width] = 0; + } + this.hints = hints; } /** - * Filter Raster pixels through a matrix. - * - * Applies the Op matrix to source pixes to produce dest pixels. Each row - * of the matrix is multiplied by the src pixel components to produce the - * dest pixel. If matrix is one more than the number of bands in the src, - * the last element is implicitly multiplied by 1, i.e. added to the sum - * for that dest component. - * - * If dest is null, a suitable Raster is created. This implementation uses - * createCompatibleDestRaster. + * Filter Raster pixels through a matrix. Applies the Op matrix to source + * pixes to produce dest pixels. Each row of the matrix is multiplied by the + * src pixel components to produce the dest pixel. If matrix is one more than + * the number of bands in the src, the last element is implicitly multiplied + * by 1, i.e. added to the sum for that dest component. If dest is null, a + * suitable Raster is created. This implementation uses + * createCompatibleDestRaster. * * @param src The source Raster. - * @param dest The destination Raster, or null. - * @returns The destination Raster or an allocated Raster. + * @param dest The destination Raster, or null. + * @throws IllegalArgumentException if the destination raster is incompatible + * with the source raster. + * @return The filtered Raster. * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, - *java.awt.image.WritableRaster) + * java.awt.image.WritableRaster) */ public WritableRaster filter(Raster src, WritableRaster dest) { if (dest == null) dest = createCompatibleDestRaster(src); - + else if (dest.getNumBands() != src.getNumBands() + || dest.getTransferType() != src.getTransferType()) + throw new IllegalArgumentException("Destination raster is incompatible with source raster"); + // Filter the pixels - float[] spix = new float[matrix[0].length]; + float[] spix = new float[matrix[0].length - 1]; float[] dpix = new float[matrix.length]; for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) for (int x = src.getMinX(); x < src.getWidth() + src.getMinX(); x++) - { - // In case matrix rows have implicit translation - spix[spix.length - 1] = 1.0f; - src.getPixel(x, y, spix); - for (int i = 0; i < matrix.length; i++) { - dpix[i] = 0; - for (int j = 0; j < matrix[0].length; j++) - dpix[i] += spix[j] * matrix[i][j]; + // In case matrix rows have implicit translation + spix[spix.length - 1] = 1.0f; + src.getPixel(x, y, spix); + for (int i = 0; i < matrix.length; i++) + { + dpix[i] = 0; + for (int j = 0; j < matrix[0].length - 1; j++) + dpix[i] += spix[j] * matrix[i][j]; + } + dest.setPixel(x, y, dpix); } - dest.setPixel(x, y, dpix); - } return dest; } @@ -125,28 +139,48 @@ public class BandCombineOp implements RasterOp /** * Creates a new WritableRaster that can be used as the destination for this - * Op. This implementation creates a Banded Raster with data type FLOAT. - * @see - *java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + * Op. The number of bands in the source raster must equal the number of rows + * in the op matrix, which must also be equal to either the number of columns + * or (columns - 1) in the matrix. + * + * @param src The source raster. + * @return A compatible raster. + * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + * @throws IllegalArgumentException if the raster is incompatible with the + * matrix. */ public WritableRaster createCompatibleDestRaster(Raster src) { - return Raster.createBandedRaster(DataBuffer.TYPE_FLOAT, src.getWidth(), - src.getHeight(), matrix.length, - new Point(src.getMinX(), src.getMinY())); + // Destination raster must have same number of bands as source + if (src.getNumBands() != matrix.length) + throw new IllegalArgumentException("Number of rows in matrix specifies an " + + "incompatible number of bands"); + + // We use -1 and -2 because we previously padded the rows with a trailing 0 + if (src.getNumBands() != matrix[0].length - 1 + && src.getNumBands() != matrix[0].length - 2) + throw new IllegalArgumentException("Incompatible number of bands: " + + "the number of bands in the raster must equal the number of " + + "columns in the matrix, optionally minus one"); + + return src.createCompatibleWritableRaster(); } - /** Return corresponding destination point for source point. + /** + * Return corresponding destination point for source point. Because this is + * not a geometric operation, it simply returns a copy of the source. * - * LookupOp will return the value of src unchanged. * @param src The source point. * @param dst The destination point. + * @return dst The destination point. * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, *java.awt.geom.Point2D) */ public final Point2D getPoint2D(Point2D src, Point2D dst) { - if (dst == null) return (Point2D)src.clone(); + if (dst == null) + return (Point2D)src.clone(); + dst.setLocation(src); return dst; } @@ -159,7 +193,11 @@ public class BandCombineOp implements RasterOp return hints; } - /** Return the matrix for this Op. */ + /** + * Return the matrix used in this operation. + * + * @return The matrix used in this operation. + */ public final float[][] getMatrix() { return matrix; diff --git a/java/awt/image/ColorConvertOp.java b/java/awt/image/ColorConvertOp.java index 1f85a5ecd..e6c85412d 100644 --- a/java/awt/image/ColorConvertOp.java +++ b/java/awt/image/ColorConvertOp.java @@ -38,7 +38,10 @@ exception statement from your version. */ package java.awt.image; +import gnu.java.awt.Buffers; + import java.awt.Graphics2D; +import java.awt.Point; import java.awt.RenderingHints; import java.awt.color.ColorSpace; import java.awt.color.ICC_ColorSpace; @@ -47,9 +50,9 @@ import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; /** - * ColorConvertOp is a filter for converting an image from one colorspace to - * another colorspace. The filter can convert the image through a sequence - * of colorspaces or just from source to destination. + * ColorConvertOp is a filter for converting images or rasters between + * colorspaces, either through a sequence of colorspaces or just from source to + * destination. * * Color conversion is done on the color components without alpha. Thus * if a BufferedImage has alpha premultiplied, this is divided out before @@ -63,24 +66,22 @@ import java.awt.geom.Rectangle2D; */ public class ColorConvertOp implements BufferedImageOp, RasterOp { - private ColorSpace srccs; - private ColorSpace dstcs; private RenderingHints hints; - private ICC_Profile[] profiles; + private ICC_Profile[] profiles = null; private ColorSpace[] spaces; - private boolean rasterValid; /** - * Convert BufferedImage through a ColorSpace. + * Convert a BufferedImage through a ColorSpace. * - * This filter version is only valid for BufferedImages. The source image - * is converted to cspace. If the destination is not null, it is then - * converted to the destination colorspace. Normally this filter will only - * be used with a null destination. + * Objects created with this constructor can be used to convert + * BufferedImage's to a destination ColorSpace. Attempts to convert Rasters + * with this constructor will result in an IllegalArgumentException when the + * filter(Raster, WritableRaster) method is called. * * @param cspace The target color space. - * @param hints Rendering hints to use in conversion, or null. + * @param hints Rendering hints to use in conversion, if any (may be null) + * @throws NullPointerException if the ColorSpace is null. */ public ColorConvertOp(ColorSpace cspace, RenderingHints hints) { @@ -88,9 +89,27 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp throw new NullPointerException(); spaces = new ColorSpace[]{cspace}; this.hints = hints; - rasterValid = false; } + /** + * Convert from a source colorspace to a destination colorspace. + * + * This constructor takes two ColorSpace arguments as the source and + * destination color spaces. It is usually used with the + * filter(Raster, WritableRaster) method, in which case the source colorspace + * is assumed to correspond to the source Raster, and the destination + * colorspace with the destination Raster. + * + * If used with BufferedImages that do not match the source or destination + * colorspaces specified here, there is an implicit conversion from the + * source image to the source ColorSpace, or the destination ColorSpace to + * the destination image. + * + * @param srcCspace The source ColorSpace. + * @param dstCspace The destination ColorSpace. + * @param hints Rendering hints to use in conversion, if any (may be null). + * @throws NullPointerException if any ColorSpace is null. + */ public ColorConvertOp(ColorSpace srcCspace, ColorSpace dstCspace, RenderingHints hints) { @@ -101,61 +120,77 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp } /** - * Convert from a source image destination image color space. + * Convert from a source colorspace to a destinatino colorspace. * * This constructor builds a ColorConvertOp from an array of ICC_Profiles. - * The source image will be converted through the sequence of color spaces + * The source will be converted through the sequence of color spaces * defined by the profiles. If the sequence of profiles doesn't give a - * well-defined conversion, throws IllegalArgumentException. - * - * NOTE: Sun's docs don't clearly define what a well-defined conversion is - * - or perhaps someone smarter can come along and sort it out. + * well-defined conversion, an IllegalArgumentException is thrown. * - * For BufferedImages, when the first and last profiles match the - * requirements of the source and destination color space respectively, the - * corresponding conversion is unnecessary. TODO: code this up. I don't - * yet understand how you determine this. + * If used with BufferedImages that do not match the source or destination + * colorspaces specified here, there is an implicit conversion from the + * source image to the source ColorSpace, or the destination ColorSpace to + * the destination image. * * For Rasters, the first and last profiles must have the same number of * bands as the source and destination Rasters, respectively. If this is * not the case, or there fewer than 2 profiles, an IllegalArgumentException * will be thrown. * - * @param profiles - * @param hints + * @param profiles An array of ICC_Profile's to convert through. + * @param hints Rendering hints to use in conversion, if any (may be null). + * @throws NullPointerException if the profile array is null. + * @throws IllegalArgumentException if the array is not a well-defined + * conversion. */ public ColorConvertOp(ICC_Profile[] profiles, RenderingHints hints) { if (profiles == null) throw new NullPointerException(); + this.hints = hints; this.profiles = profiles; - // TODO: Determine if this is well-defined. + // Create colorspace array with space for src and dest colorspace + // Note that the ICC_ColorSpace constructor will throw an + // IllegalArgumentException if the profile is invalid; thus we check + // for a "well defined conversion" spaces = new ColorSpace[profiles.length]; for (int i = 0; i < profiles.length; i++) spaces[i] = new ICC_ColorSpace(profiles[i]); } - /** Convert from source image color space to destination image color space. + /** + * Convert from source color space to destination color space. * * Only valid for BufferedImage objects, this Op converts from the source - * color space to the destination color space. The destination can't be - * null for this operation. + * image's color space to the destination image's color space. * - * @param hints Rendering hints to use during conversion, or null. + * The destination in the filter(BufferedImage, BufferedImage) method cannot + * be null for this operation, and it also cannot be used with the + * filter(Raster, WritableRaster) method. + * + * @param hints Rendering hints to use in conversion, if any (may be null). */ public ColorConvertOp(RenderingHints hints) { - this.hints = hints; - srccs = null; - dstcs = null; - rasterValid = false; + this.hints = hints; + spaces = new ColorSpace[0]; } - /* (non-Javadoc) - * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, - java.awt.image.BufferedImage) + /** + * Converts the source image using the conversion path specified in the + * constructor. The resulting image is stored in the destination image if one + * is provided; otherwise a new BufferedImage is created and returned. + * + * The source and destination BufferedImage (if one is supplied) must have + * the same dimensions. + * + * @param src The source image. + * @param dst The destination image. + * @throws IllegalArgumentException if the rasters and/or color spaces are + * incompatible. + * @return The transformed image. */ public final BufferedImage filter(BufferedImage src, BufferedImage dst) { @@ -163,129 +198,241 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp // For now we just suck it up and create intermediate buffers. if (dst == null && spaces.length == 0) - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Not enough color space information " + + "to complete conversion."); + + if (dst != null + && (src.getHeight() != dst.getHeight() || src.getWidth() != dst.getWidth())) + throw new IllegalArgumentException("Source and destination images have " + + "different dimensions"); // Make sure input isn't premultiplied by alpha if (src.isAlphaPremultiplied()) - { - BufferedImage tmp = createCompatibleDestImage(src, src.getColorModel()); - copyimage(src, tmp); - tmp.coerceData(false); - src = tmp; - } + { + BufferedImage tmp = createCompatibleDestImage(src, src.getColorModel()); + copyimage(src, tmp); + tmp.coerceData(false); + src = tmp; + } - ColorModel scm = src.getColorModel(); + // Convert through defined intermediate conversions + BufferedImage tmp; for (int i = 0; i < spaces.length; i++) - { - BufferedImage tmp = createCompatibleDestImage(src, scm); - copyimage(src, tmp); - src = tmp; - } + { + if (src.getColorModel().getColorSpace().getType() != spaces[i].getType()) + { + tmp = createCompatibleDestImage(src, + createCompatibleColorModel(src, + spaces[i])); + copyimage(src, tmp); + src = tmp; + } + } - // Intermediate conversions leave result in src + // No implicit conversion to destination type needed; return result from the + // last intermediate conversions (which was left in src) if (dst == null) - return src; - - // Apply final conversion - copyimage(src, dst); - + dst = src; + + // Implicit conversion to destination image's color space + else + copyimage(src, dst); + return dst; } - /* (non-Javadoc) - * @see java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, java.awt.image.ColorModel) + /** + * Converts the source raster using the conversion path specified in the + * constructor. The resulting raster is stored in the destination raster if + * one is provided; otherwise a new WritableRaster is created and returned. + * + * This operation is not valid with every constructor of this class; see + * the constructors for details. Further, the source raster must have the + * same number of bands as the source ColorSpace, and the destination raster + * must have the same number of bands as the destination ColorSpace. + * + * The source and destination raster (if one is supplied) must also have the + * same dimensions. + * + * @param src The source raster. + * @param dest The destination raster. + * @throws IllegalArgumentException if the rasters and/or color spaces are + * incompatible. + * @return The transformed raster. + */ + public final WritableRaster filter(Raster src, WritableRaster dest) + { + // Various checks to ensure that the rasters and color spaces are compatible + if (spaces.length < 2) + throw new IllegalArgumentException("Not enough information about " + + "source and destination colorspaces."); + + if (spaces[0].getNumComponents() != src.getNumBands() + || (dest != null && spaces[spaces.length - 1].getNumComponents() != dest.getNumBands())) + throw new IllegalArgumentException("Source or destination raster " + + "contains the wrong number of bands."); + + if (dest != null + && (src.getHeight() != dest.getHeight() || src.getWidth() != dest.getWidth())) + throw new IllegalArgumentException("Source and destination rasters " + + "have different dimensions"); + + // Need to iterate through each color space. + // spaces[0] corresponds to the ColorSpace of the source raster, and + // spaces[spaces.length - 1] corresponds to the ColorSpace of the + // destination, with any number (or zero) of intermediate conversions. + + for (int i = 0; i < spaces.length - 2; i++) + { + WritableRaster tmp = createCompatibleDestRaster(src, spaces[i + 1], + false, + src.getTransferType()); + copyraster(src, spaces[i], tmp, spaces[i + 1]); + src = tmp; + } + + // The last conversion is done outside of the loop so that we can + // use the dest raster supplied, instead of creating our own temp raster + if (dest == null) + dest = createCompatibleDestRaster(src, spaces[spaces.length - 1], false, + DataBuffer.TYPE_BYTE); + copyraster(src, spaces[spaces.length - 2], dest, spaces[spaces.length - 1]); + + return dest; + } + + /** + * Creates an empty BufferedImage with the size equal to the source and the + * correct number of bands for the conversion defined in this Op. The newly + * created image is created with the specified ColorModel, or if no ColorModel + * is supplied, an appropriate one is chosen. + * + * @param src The source image. + * @param dstCM A color model for the destination image (may be null). + * @throws IllegalArgumentException if an appropriate colormodel cannot be + * chosen with the information given. + * @return The new compatible destination image. */ public BufferedImage createCompatibleDestImage(BufferedImage src, - ColorModel dstCM) + ColorModel dstCM) { - // FIXME: set properties to those in src + if (dstCM == null && spaces.length == 0) + throw new IllegalArgumentException("Don't know the destination " + + "colormodel"); + + if (dstCM == null) + { + dstCM = createCompatibleColorModel(src, spaces[spaces.length - 1]); + } + return new BufferedImage(dstCM, - src.getRaster().createCompatibleWritableRaster(), - src.isPremultiplied, - null); + createCompatibleDestRaster(src.getRaster(), + dstCM.getColorSpace(), + src.getColorModel().hasAlpha, + dstCM.getTransferType()), + src.isPremultiplied, null); } - public final ICC_Profile[] getICC_Profiles() + /** + * Creates a new WritableRaster with the size equal to the source and the + * correct number of bands. + * + * Note, the new Raster will always use a BYTE storage size, regardless of + * the color model or defined destination; this is for compatibility with + * the reference implementation. + * + * @param src The source Raster. + * @throws IllegalArgumentException if there isn't enough colorspace + * information to create a compatible Raster. + * @return The new compatible destination raster. + */ + public WritableRaster createCompatibleDestRaster(Raster src) { - return profiles; - } + if (spaces.length < 2) + throw new IllegalArgumentException("Not enough destination colorspace " + + "information"); - /** Return the rendering hints for this op. */ - public final RenderingHints getRenderingHints() - { - return hints; + // Create a new raster with the last ColorSpace in the conversion + // chain, and with no alpha (implied) + return createCompatibleDestRaster(src, spaces[spaces.length-1], false, + DataBuffer.TYPE_BYTE); } - /* (non-Javadoc) - * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, java.awt.image.WritableRaster) + /** + * Returns the array of ICC_Profiles used to create this Op, or null if the + * Op was created using ColorSpace arguments. + * + * @return The array of ICC_Profiles, or null. */ - public final WritableRaster filter(Raster src, WritableRaster dest) + public final ICC_Profile[] getICC_Profiles() { - if (!rasterValid) - throw new IllegalArgumentException(); - - // Need to iterate through each color space - there must be at least 2 - for (int i = 1; i < spaces.length - 1; i++) - { - // FIXME: this is wrong. tmp needs to have the same number of bands as - // spaces[i] has. - WritableRaster tmp = createCompatibleDestRaster(src); - copyraster(src, spaces[i - 1], tmp, spaces[i]); - src = tmp; - } - - // FIXME: this is wrong. dst needs to have the same number of bands as - // spaces[i] has. - if (dest == null) - dest = createCompatibleDestRaster(src); - copyraster(src, spaces[spaces.length - 2], - dest, spaces[spaces.length - 1]); - - return dest; + return profiles; } - /* (non-Javadoc) - * @see java.awt.image.RasterOp#createCompatibleDestRaster(java.awt.image.Raster) + /** + * Returns the rendering hints for this op. + * + * @return The rendering hints for this Op, or null. */ - public WritableRaster createCompatibleDestRaster(Raster src) + public final RenderingHints getRenderingHints() { - return src.createCompatibleWritableRaster(); + return hints; } - /** Return corresponding destination point for source point. + /** + * Returns the corresponding destination point for a source point. + * Because this is not a geometric operation, the destination and source + * points will be identical. * - * LookupOp will return the value of src unchanged. * @param src The source point. - * @param dst The destination point. - * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, java.awt.geom.Point2D) + * @param dst The transformed destination point. + * @return The transformed destination point. */ public final Point2D getPoint2D(Point2D src, Point2D dst) { - if (dst == null) return (Point2D)src.clone(); + if (dst == null) + return (Point2D)src.clone(); + dst.setLocation(src); return dst; } - /* (non-Javadoc) - * @see java.awt.image.BufferedImageOp#getBounds2D(java.awt.image.BufferedImage) + /** + * Returns the corresponding destination boundary of a source boundary. + * Because this is not a geometric operation, the destination and source + * boundaries will be identical. + * + * @param src The source boundary. + * @return The boundaries of the destination. */ public final Rectangle2D getBounds2D(BufferedImage src) { return src.getRaster().getBounds(); } - /* (non-Javadoc) - * @see java.awt.image.RasterOp#getBounds2D(java.awt.image.Raster) + /** + * Returns the corresponding destination boundary of a source boundary. + * Because this is not a geometric operation, the destination and source + * boundaries will be identical. + * + * @param src The source boundary. + * @return The boundaries of the destination. */ public final Rectangle2D getBounds2D(Raster src) { return src.getBounds(); } - - // According to Sven de Marothy, we need to copy the src into the dest - // using Graphics2D, in order to use the rendering hints. + + /** + * Copy a source image to a destination image, respecting their colorspaces + * and performing colorspace conversions if necessary. + * + * @param src The source image. + * @param dst The destination image. + */ private void copyimage(BufferedImage src, BufferedImage dst) { + // This is done using Graphics2D in order to respect the rendering hints. Graphics2D gg = dst.createGraphics(); // If no hints are set there is no need to call @@ -297,13 +444,23 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp gg.dispose(); } - private void copyraster(Raster src, ColorSpace scs, WritableRaster dst, - ColorSpace dcs) + /** + * Copy a source raster to a destination raster, performing a colorspace + * conversion between the two. The conversion will respect the + * KEY_COLOR_RENDERING rendering hint if one is present. + * + * @param src The source raster. + * @param scs The colorspace of the source raster. + * @dst The destination raster. + * @dcs The colorspace of the destination raster. + */ + private void copyraster(Raster src, ColorSpace scs, WritableRaster dst, ColorSpace dcs) { float[] sbuf = new float[src.getNumBands()]; - if (hints.get(RenderingHints.KEY_COLOR_RENDERING) == - RenderingHints.VALUE_COLOR_RENDER_QUALITY) + if (hints != null + && hints.get(RenderingHints.KEY_COLOR_RENDERING) == + RenderingHints.VALUE_COLOR_RENDER_QUALITY) { // use cie for accuracy for (int y = src.getMinY(); y < src.getHeight() + src.getMinY(); y++) @@ -321,4 +478,60 @@ public class ColorConvertOp implements BufferedImageOp, RasterOp } } + /** + * This method creates a color model with the same colorspace and alpha + * settings as the source image. The created color model will always be a + * ComponentColorModel and have a BYTE transfer type. + * + * @param img The source image. + * @param cs The ColorSpace to use. + * @return A color model compatible with the source image. + */ + private ColorModel createCompatibleColorModel(BufferedImage img, ColorSpace cs) + { + // The choice of ComponentColorModel and DataBuffer.TYPE_BYTE is based on + // Mauve testing of the reference implementation. + return new ComponentColorModel(cs, + img.getColorModel().hasAlpha(), + img.isAlphaPremultiplied(), + img.getColorModel().getTransparency(), + DataBuffer.TYPE_BYTE); + } + + /** + * This method creates a compatible Raster, given a source raster, colorspace, + * alpha value, and transfer type. + * + * @param src The source raster. + * @param cs The ColorSpace to use. + * @param hasAlpha Whether the raster should include a component for an alpha. + * @param transferType The size of a single data element. + * @return A compatible WritableRaster. + */ + private WritableRaster createCompatibleDestRaster(Raster src, ColorSpace cs, + boolean hasAlpha, + int transferType) + { + // The use of a PixelInterleavedSampleModel weas determined using mauve + // tests, based on the reference implementation + + int numComponents = cs.getNumComponents(); + if (hasAlpha) + numComponents++; + + int[] offsets = new int[numComponents]; + for (int i = 0; i < offsets.length; i++) + offsets[i] = i; + + DataBuffer db = Buffers.createBuffer(transferType, + src.getWidth() * src.getHeight() * numComponents, + 1); + return new WritableRaster(new PixelInterleavedSampleModel(transferType, + src.getWidth(), + src.getHeight(), + numComponents, + numComponents * src.getWidth(), + offsets), + db, new Point(src.getMinX(), src.getMinY())); + } } diff --git a/java/awt/image/ColorModel.java b/java/awt/image/ColorModel.java index 9e559db37..fc413d0b4 100644 --- a/java/awt/image/ColorModel.java +++ b/java/awt/image/ColorModel.java @@ -628,7 +628,7 @@ public abstract class ColorModel implements Transparency public ColorModel coerceData(WritableRaster raster, boolean isAlphaPremultiplied) { - if (this.isAlphaPremultiplied == isAlphaPremultiplied) + if (this.isAlphaPremultiplied == isAlphaPremultiplied || ! hasAlpha) return this; int w = raster.getWidth(); diff --git a/java/awt/image/ComponentColorModel.java b/java/awt/image/ComponentColorModel.java index f56688f93..f3f3e374b 100644 --- a/java/awt/image/ComponentColorModel.java +++ b/java/awt/image/ComponentColorModel.java @@ -42,9 +42,11 @@ import gnu.java.awt.Buffers; import java.awt.Point; import java.awt.color.ColorSpace; +import java.util.Arrays; public class ComponentColorModel extends ColorModel { + // Find sum of all elements of the array. private static int sum(int[] values) { int sum = 0; @@ -52,6 +54,22 @@ public class ComponentColorModel extends ColorModel sum += values[i]; return sum; } + + // Create an appropriate array of bits, given a colorspace (ie, number of + // bands), size of the storage data type, and presence of an alpha band. + private static int[] findBits(ColorSpace colorSpace, int transferType, + boolean hasAlpha) + { + int[] bits; + if (hasAlpha) + bits = new int[colorSpace.getNumComponents()+1]; + else + bits = new int[colorSpace.getNumComponents()]; + + Arrays.fill(bits, DataBuffer.getDataTypeSize(transferType)); + + return bits; + } public ComponentColorModel(ColorSpace colorSpace, int[] bits, boolean hasAlpha, @@ -84,8 +102,8 @@ public class ComponentColorModel extends ColorModel boolean isAlphaPremultiplied, int transparency, int transferType) { - this(colorSpace, null, hasAlpha, isAlphaPremultiplied, - transparency, transferType); + this(colorSpace, findBits(colorSpace, transferType, hasAlpha), hasAlpha, + isAlphaPremultiplied, transparency, transferType); } public int getRed(int pixel) diff --git a/java/awt/image/ConvolveOp.java b/java/awt/image/ConvolveOp.java index ffb834874..cd3b01131 100644 --- a/java/awt/image/ConvolveOp.java +++ b/java/awt/image/ConvolveOp.java @@ -38,7 +38,6 @@ exception statement from your version. */ package java.awt.image; -import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; @@ -51,11 +50,13 @@ import java.awt.geom.Rectangle2D; * with elements in the kernel to compute a new pixel. * * Each band in a Raster is convolved and copied to the destination Raster. + * For BufferedImages, convolution is applied to all components. Color + * conversion will be applied if needed. * - * For BufferedImages, convolution is applied to all components. If the - * source is not premultiplied, the data will be premultiplied before - * convolving. Premultiplication will be undone if the destination is not - * premultiplied. Color conversion will be applied if needed. + * Note that this filter ignores whether the source or destination is alpha + * premultiplied. The reference spec states that data will be premultiplied + * prior to convolving and divided back out afterwards (if needed), but testing + * has shown that this is not the case with their implementation. * * @author jlquinn@optonline.net */ @@ -104,59 +105,83 @@ public class ConvolveOp implements BufferedImageOp, RasterOp hints = null; } - - /* (non-Javadoc) - * @see java.awt.image.BufferedImageOp#filter(java.awt.image.BufferedImage, - * java.awt.image.BufferedImage) + /** + * Converts the source image using the kernel specified in the + * constructor. The resulting image is stored in the destination image if one + * is provided; otherwise a new BufferedImage is created and returned. + * + * The source and destination BufferedImage (if one is supplied) must have + * the same dimensions. + * + * @param src The source image. + * @param dst The destination image. + * @throws IllegalArgumentException if the rasters and/or color spaces are + * incompatible. + * @return The convolved image. */ public final BufferedImage filter(BufferedImage src, BufferedImage dst) { if (src == dst) - throw new IllegalArgumentException(); + throw new IllegalArgumentException("Source and destination images " + + "cannot be the same."); if (dst == null) dst = createCompatibleDestImage(src, src.getColorModel()); // Make sure source image is premultiplied BufferedImage src1 = src; - if (!src.isPremultiplied) + // The spec says we should do this, but mauve testing shows that Sun's + // implementation does not check this. + /* + if (!src.isAlphaPremultiplied()) { src1 = createCompatibleDestImage(src, src.getColorModel()); src.copyData(src1.getRaster()); src1.coerceData(true); } + */ BufferedImage dst1 = dst; - if (!src.getColorModel().equals(dst.getColorModel())) + if (src1.getColorModel().getColorSpace().getType() != dst.getColorModel().getColorSpace().getType()) dst1 = createCompatibleDestImage(src, src.getColorModel()); filter(src1.getRaster(), dst1.getRaster()); + // Since we don't coerceData above, we don't need to divide it back out. + // This is wrong (one mauve test specifically tests converting a non- + // premultiplied image to a premultiplied image, and it shows that Sun + // simply ignores the premultipled flag, contrary to the spec), but we + // mimic it for compatibility. + /* + if (! dst.isAlphaPremultiplied()) + dst1.coerceData(false); + */ + + // Convert between color models if needed if (dst1 != dst) - { - // Convert between color models. - // TODO Check that premultiplied alpha is handled correctly here. - Graphics2D gg = dst.createGraphics(); - gg.setRenderingHints(hints); - gg.drawImage(dst1, 0, 0, null); - gg.dispose(); - } - + new ColorConvertOp(hints).filter(dst1, dst); + return dst; } - /* (non-Javadoc) - * @see - * java.awt.image.BufferedImageOp#createCompatibleDestImage(java.awt.image.BufferedImage, - * java.awt.image.ColorModel) + /** + * Creates an empty BufferedImage with the size equal to the source and the + * correct number of bands. The new image is created with the specified + * ColorModel, or if no ColorModel is supplied, an appropriate one is chosen. + * + * @param src The source image. + * @param dstCM A color model for the destination image (may be null). + * @return The new compatible destination image. */ public BufferedImage createCompatibleDestImage(BufferedImage src, - ColorModel dstCM) + ColorModel dstCM) { - // FIXME: set properties to those in src - return new BufferedImage(dstCM, - src.getRaster().createCompatibleWritableRaster(), - src.isPremultiplied, null); + if (dstCM != null) + return new BufferedImage(dstCM, + src.getRaster().createCompatibleWritableRaster(), + src.isAlphaPremultiplied(), null); + + return new BufferedImage(src.getWidth(), src.getHeight(), src.getType()); } /* (non-Javadoc) @@ -168,6 +193,8 @@ public class ConvolveOp implements BufferedImageOp, RasterOp } /** + * Get the edge condition for this Op. + * * @return The edge condition. */ public int getEdgeCondition() @@ -185,9 +212,22 @@ public class ConvolveOp implements BufferedImageOp, RasterOp return (Kernel) kernel.clone(); } - /* (non-Javadoc) - * @see java.awt.image.RasterOp#filter(java.awt.image.Raster, - * java.awt.image.WritableRaster) + /** + * Converts the source raster using the kernel specified in the constructor. + * The resulting raster is stored in the destination raster if one is + * provided; otherwise a new WritableRaster is created and returned. + * + * If the convolved value for a sample is outside the range of [0-255], it + * will be clipped. + * + * The source and destination raster (if one is supplied) cannot be the same, + * and must also have the same dimensions. + * + * @param src The source raster. + * @param dest The destination raster. + * @throws IllegalArgumentException if the rasters identical. + * @throws ImagingOpException if the convolution is not possible. + * @return The transformed raster. */ public final WritableRaster filter(Raster src, WritableRaster dest) { @@ -228,7 +268,16 @@ public class ConvolveOp implements BufferedImageOp, RasterOp v += tmp[tmp.length - i - 1] * kvals[i]; // FIXME: in the above line, I've had to reverse the order of // the samples array to make the tests pass. I haven't worked - // out why this is necessary. + // out why this is necessary. + + // This clipping is pretty strange, and seems to be hard-coded + // at 255 (regardless of the raster's datatype or transfertype). + // But it's what the reference does. + if (v > 255) + v = 255; + if (v < 0) + v = 0; + dest.setSample(x + kernel.getXOrigin(), y + kernel.getYOrigin(), b, v); } @@ -310,13 +359,14 @@ public class ConvolveOp implements BufferedImageOp, RasterOp return src.getBounds(); } - /** Return corresponding destination point for source point. + /** + * Returns the corresponding destination point for a source point. Because + * this is not a geometric operation, the destination and source points will + * be identical. * - * ConvolveOp will return the value of src unchanged. * @param src The source point. - * @param dst The destination point. - * @see java.awt.image.RasterOp#getPoint2D(java.awt.geom.Point2D, - * java.awt.geom.Point2D) + * @param dst The transformed destination point. + * @return The transformed destination point. */ public final Point2D getPoint2D(Point2D src, Point2D dst) { diff --git a/java/io/File.java b/java/io/File.java index 7dbc6109e..4f5ab4e66 100644 --- a/java/io/File.java +++ b/java/io/File.java @@ -286,7 +286,8 @@ public class File implements Serializable, Comparable<File> // example, is a valid and minimal path). if (plen > 1 && p.charAt (plen - 1) == separatorChar) { - if (! (separatorChar == '\\' && plen == 3 && p.charAt (1) == ':')) + if (! (separatorChar == '\\' && ((plen == 3 && p.charAt(1) == ':') + || (plen == 2 && p.charAt(0) == separatorChar)))) return p.substring (0, plen - 1); } else @@ -303,7 +304,16 @@ public class File implements Serializable, Comparable<File> { dupIndex++; if (dupIndex == plen) - return newpath.toString(); + { + if ((separatorChar == '\\' + && newpath.length() == 2 + && newpath.charAt(1) == ':') + || (separatorChar != '\\' && newpath.length() == 0)) + { + newpath.append(separatorChar); + } + return newpath.toString(); + } } newpath.append(separatorChar); last = dupIndex; @@ -315,7 +325,9 @@ public class File implements Serializable, Comparable<File> int end; if (plen > 1 && p.charAt (plen - 1) == separatorChar) { - if (separatorChar == '\\' && plen == 3 && p.charAt (1) == ':') + if (separatorChar == '\\' + && ((plen == 3 && p.charAt(1) == ':') + || (plen == 2 && p.charAt(0) == separatorChar))) end = plen; else end = plen - 1; @@ -427,45 +439,8 @@ public class File implements Serializable, Comparable<File> { if (isAbsolute()) return path; - else if (separatorChar == '\\' - && path.length() > 0 && path.charAt (0) == '\\') - { - // On Windows, even if the path starts with a '\\' it is not - // really absolute until we prefix the drive specifier from - // the current working directory to it. - return System.getProperty ("user.dir").substring (0, 2) + path; - } - else if (separatorChar == '\\' - && path.length() > 1 && path.charAt (1) == ':' - && ((path.charAt (0) >= 'a' && path.charAt (0) <= 'z') - || (path.charAt (0) >= 'A' && path.charAt (0) <= 'Z'))) - { - // On Windows, a process has a current working directory for - // each drive and a path like "G:foo\bar" would mean the - // absolute path "G:\wombat\foo\bar" if "\wombat" is the - // working directory on the G drive. - String drvDir = null; - try - { - drvDir = new File (path.substring (0, 2)).getCanonicalPath(); - } - catch (IOException e) - { - drvDir = path.substring (0, 2) + "\\"; - } - - // Note: this would return "C:\\." for the path "C:.", if "\" - // is the working folder on the C drive, but this is - // consistent with what Sun's JRE 1.4.1.01 actually returns! - if (path.length() > 2) - return drvDir + '\\' + path.substring (2, path.length()); - else - return drvDir; - } - else if (path.equals("")) - return System.getProperty ("user.dir"); else - return System.getProperty ("user.dir") + separatorChar + path; + return VMFile.getAbsolutePath(path); } /** @@ -657,15 +632,7 @@ public class File implements Serializable, Comparable<File> */ public boolean isAbsolute() { - if (separatorChar == '\\') - return path.startsWith(dupSeparator) || - (path.length() > 2 && - ((path.charAt(0) >= 'a' && path.charAt(0) <= 'z') || - (path.charAt(0) >= 'A' && path.charAt(0) <= 'Z')) && - path.charAt(1) == ':' && - path.charAt(2) == '\\'); - else - return path.startsWith(separator); + return VMFile.isAbsolute(path); } /** @@ -998,14 +965,7 @@ public class File implements Serializable, Comparable<File> */ public URL toURL() throws MalformedURLException { - // On Win32, Sun's JDK returns URLs of the form "file:/c:/foo/bar.txt", - // while on UNIX, it returns URLs of the form "file:/foo/bar.txt". - if (separatorChar == '\\') - return new URL ("file:/" + getAbsolutePath().replace ('\\', '/') - + (isDirectory() ? "/" : "")); - else - return new URL ("file:" + getAbsolutePath() - + (isDirectory() ? "/" : "")); + return VMFile.toURL(this); } diff --git a/java/io/FileDescriptor.java b/java/io/FileDescriptor.java index d300c9cb6..dd3db1c74 100644 --- a/java/io/FileDescriptor.java +++ b/java/io/FileDescriptor.java @@ -133,7 +133,8 @@ public final class FileDescriptor * native file handle, <code>false</code> otherwise */ public boolean valid () - { - return channel != null && channel.isOpen(); + { + ByteChannel c = channel; + return (c != null) && (c.isOpen()); } } diff --git a/java/io/InputStreamReader.java b/java/io/InputStreamReader.java index 936a03c95..8d97799d5 100644 --- a/java/io/InputStreamReader.java +++ b/java/io/InputStreamReader.java @@ -135,6 +135,16 @@ public class InputStreamReader extends Reader private boolean hasSavedSurrogate = false; /** + * A byte array to be reused in read(byte[], int, int). + */ + private byte[] bytesCache; + + /** + * Locks the bytesCache above in read(byte[], int, int). + */ + private Object cacheLock = new Object(); + + /** * This method initializes a new instance of <code>InputStreamReader</code> * to read from the specified stream using the default encoding. * @@ -355,9 +365,19 @@ public class InputStreamReader extends Reader throw new IOException("Reader has been closed"); if (isDone) return -1; - if(decoder != null){ - int totalBytes = (int)((double)length * maxBytesPerChar); - byte[] bytes = new byte[totalBytes]; + if(decoder != null) + { + int totalBytes = (int)((double) length * maxBytesPerChar); + byte[] bytes; + // Fetch cached bytes array if available and big enough. + synchronized(cacheLock) + { + bytes = bytesCache; + if (bytes == null || bytes.length < totalBytes) + bytes = new byte[totalBytes]; + else + bytesCache = null; + } int remaining = 0; if(byteBuffer != null) @@ -410,12 +430,40 @@ public class InputStreamReader extends Reader byteBuffer = null; read = cb.position() - startPos; - return (read <= 0) ? -1 : read; - } else { - byte[] bytes = new byte[length]; + + // Put cached bytes array back if we are finished and the cache + // is null or smaller than the used bytes array. + synchronized (cacheLock) + { + if (byteBuffer == null + && (bytesCache == null || bytesCache.length < bytes.length)) + bytesCache = bytes; + } + return (read <= 0) ? -1 : read; + } + else + { + byte[] bytes; + // Fetch cached bytes array if available and big enough. + synchronized (cacheLock) + { + bytes = bytesCache; + if (bytes == null || length < bytes.length) + bytes = new byte[length]; + else + bytesCache = null; + } + int read = in.read(bytes); for(int i=0;i<read;i++) buf[offset+i] = (char)(bytes[i]&0xFF); + + // Put back byte array into cache if appropriate. + synchronized (cacheLock) + { + if (bytesCache == null || bytesCache.length < bytes.length) + bytesCache = bytes; + } return read; } } diff --git a/java/io/ObjectInputStream.java b/java/io/ObjectInputStream.java index 98632607c..d6c1406ea 100644 --- a/java/io/ObjectInputStream.java +++ b/java/io/ObjectInputStream.java @@ -39,7 +39,7 @@ exception statement from your version. */ package java.io; -import gnu.java.io.ObjectIdentityWrapper; +import gnu.classpath.VMStackWalker; import java.lang.reflect.Array; import java.lang.reflect.Constructor; @@ -104,7 +104,7 @@ public class ObjectInputStream extends InputStream this.blockDataInput = new DataInputStream(this); this.realInputStream = new DataInputStream(in); this.nextOID = baseWireHandle; - this.objectLookupTable = new Hashtable<Integer,ObjectIdentityWrapper>(); + this.objectLookupTable = new Vector<Object>(); this.classLookupTable = new Hashtable<Class,ObjectStreamClass>(); setBlockDataMode(true); readStreamHeader(); @@ -204,10 +204,9 @@ public class ObjectInputStream extends InputStream case TC_REFERENCE: { if(dump) dumpElement("REFERENCE "); - Integer oid = new Integer(this.realInputStream.readInt()); - if(dump) dumpElementln(Integer.toHexString(oid.intValue())); - ret_val = ((ObjectIdentityWrapper) - this.objectLookupTable.get(oid)).object; + int oid = realInputStream.readInt(); + if(dump) dumpElementln(Integer.toHexString(oid)); + ret_val = lookupHandle(oid); break; } @@ -360,8 +359,7 @@ public class ObjectInputStream extends InputStream this.currentObject = obj; this.currentObjectValidators = null; - ObjectStreamClass[] hierarchy = - inputGetObjectStreamClasses(clazz); + ObjectStreamClass[] hierarchy = hierarchy(clazz); for (int i = 0; i < hierarchy.length; i++) { @@ -825,7 +823,7 @@ public class ObjectInputStream extends InputStream */ private ClassLoader currentLoader() { - return VMObjectInputStream.currentClassLoader(); + return VMStackWalker.firstNonNullClassLoader(); } /** @@ -853,41 +851,20 @@ public class ObjectInputStream extends InputStream } /** - * Reconstruct class hierarchy the same way - * {@link java.io.ObjectStreamClass#getObjectStreamClasses(Class)} does - * but using lookupClass instead of ObjectStreamClass.lookup. This - * dup is necessary localize the lookup table. Hopefully some future - * rewritings will be able to prevent this. + * Reconstruct class hierarchy the same way {@link + * java.io.ObjectStreamClass#hierarchy} does but using lookupClass + * instead of ObjectStreamClass.lookup. * * @param clazz This is the class for which we want the hierarchy. * * @return An array of valid {@link java.io.ObjectStreamClass} instances which * represent the class hierarchy for clazz. */ - private ObjectStreamClass[] inputGetObjectStreamClasses(Class clazz) - { + private ObjectStreamClass[] hierarchy(Class clazz) + { ObjectStreamClass osc = lookupClass(clazz); - if (osc == null) - return new ObjectStreamClass[0]; - else - { - Vector<ObjectStreamClass> oscs = new Vector<ObjectStreamClass>(); - - while (osc != null) - { - oscs.addElement(osc); - osc = osc.getSuper(); - } - - int count = oscs.size(); - ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[count]; - - for (int i = count - 1; i >= 0; i--) - sorted_oscs[count - i - 1] = (ObjectStreamClass) oscs.elementAt(i); - - return sorted_oscs; - } + return osc == null ? new ObjectStreamClass[0] : osc.hierarchy(); } /** @@ -1571,9 +1548,47 @@ public class ObjectInputStream extends InputStream */ private int assignNewHandle(Object obj) { - this.objectLookupTable.put(new Integer(this.nextOID), - new ObjectIdentityWrapper(obj)); - return this.nextOID++; + int handle = this.nextOID; + this.nextOID = handle + 1; + rememberHandle(obj,handle); + return handle; + } + + /** + * Remember the object associated with the given handle. + * + * @param obj an object + * + * @param handle a handle, must be >= baseWireHandle + * + * @see #lookupHandle + */ + private void rememberHandle(Object obj, int handle) + { + Vector olt = this.objectLookupTable; + handle = handle - baseWireHandle; + + if (olt.size() <= handle) + olt.setSize(handle + 1); + + olt.set(handle, obj); + } + + /** + * Look up the object associated with a given handle. + * + * @param handle a handle, must be >= baseWireHandle + * + * @return the object remembered for handle or null if none. + * + * @see #rememberHandle + */ + private Object lookupHandle(int handle) + { + Vector olt = this.objectLookupTable; + handle = handle - baseWireHandle; + Object result = handle < olt.size() ? olt.get(handle) : null; + return result; } private Object processResolution(ObjectStreamClass osc, Object obj, int handle) @@ -1607,9 +1622,7 @@ public class ObjectInputStream extends InputStream if (this.resolveEnabled) obj = resolveObject(obj); - this.objectLookupTable.put(new Integer(handle), - new ObjectIdentityWrapper(obj)); - + rememberHandle(obj, handle); return obj; } @@ -1942,7 +1955,7 @@ public class ObjectInputStream extends InputStream private boolean useSubclassMethod; private int nextOID; private boolean resolveEnabled; - private Hashtable<Integer,ObjectIdentityWrapper> objectLookupTable; + private Vector<Object> objectLookupTable; private Object currentObject; private ObjectStreamClass currentObjectStreamClass; private TreeSet<ValidatorAndPriority> currentObjectValidators; diff --git a/java/io/ObjectOutputStream.java b/java/io/ObjectOutputStream.java index eb781acab..c3c3df9a3 100644 --- a/java/io/ObjectOutputStream.java +++ b/java/io/ObjectOutputStream.java @@ -39,7 +39,7 @@ exception statement from your version. */ package java.io; -import gnu.java.io.ObjectIdentityWrapper; +import gnu.java.io.ObjectIdentityMap2Int; import gnu.java.lang.reflect.TypeSignature; import gnu.java.security.action.SetAccessibleAction; @@ -47,8 +47,6 @@ import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.security.AccessController; -import java.util.Hashtable; /** @@ -146,7 +144,7 @@ public class ObjectOutputStream extends OutputStream replacementEnabled = false; isSerializing = false; nextOID = baseWireHandle; - OIDLookupTable = new Hashtable<ObjectIdentityWrapper,Integer>(); + OIDLookupTable = new ObjectIdentityMap2Int(); protocolVersion = defaultProtocolVersion; useSubclassMethod = false; writeStreamHeader(); @@ -213,11 +211,11 @@ public class ObjectOutputStream extends OutputStream break; } - Integer handle = findHandle(obj); - if (handle != null) + int handle = findHandle(obj); + if (handle >= 0) { realOutput.writeByte(TC_REFERENCE); - realOutput.writeInt(handle.intValue()); + realOutput.writeInt(handle); break; } @@ -231,7 +229,7 @@ public class ObjectOutputStream extends OutputStream writeObject (osc); } else - { + {System.err.println("1"); realOutput.writeByte(TC_PROXYCLASSDESC); Class[] intfs = cl.getInterfaces(); realOutput.writeInt(intfs.length); @@ -344,8 +342,7 @@ public class ObjectOutputStream extends OutputStream Object prevObject = this.currentObject; ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; currentObject = obj; - ObjectStreamClass[] hierarchy = - ObjectStreamClass.getObjectStreamClasses(clazz); + ObjectStreamClass[] hierarchy = osc.hierarchy(); for (int i = 0; i < hierarchy.length; i++) { @@ -1110,17 +1107,16 @@ public class ObjectOutputStream extends OutputStream // lookup the handle for OBJ, return null if OBJ doesn't have a // handle yet - private Integer findHandle(Object obj) + private int findHandle(Object obj) { - return (Integer)OIDLookupTable.get(new ObjectIdentityWrapper(obj)); + return OIDLookupTable.get(obj); } // assigns the next availible handle to OBJ private int assignNewHandle(Object obj) { - OIDLookupTable.put(new ObjectIdentityWrapper(obj), - new Integer(nextOID)); + OIDLookupTable.put(obj, nextOID); return nextOID++; } @@ -1222,39 +1218,70 @@ public class ObjectOutputStream extends OutputStream { ObjectStreamField[] fields = osc.fields; boolean oldmode = setBlockDataMode(false); - String field_name; - Class type; - for (int i = 0; i < fields.length; i++) + try { - field_name = fields[i].getName(); - type = fields[i].getType(); - - if (dump) - dumpElementln ("WRITE FIELD: " + field_name + " type=" + type); - - if (type == Boolean.TYPE) - realOutput.writeBoolean(getBooleanField(obj, osc.forClass(), field_name)); - else if (type == Byte.TYPE) - realOutput.writeByte(getByteField(obj, osc.forClass(), field_name)); - else if (type == Character.TYPE) - realOutput.writeChar(getCharField(obj, osc.forClass(), field_name)); - else if (type == Double.TYPE) - realOutput.writeDouble(getDoubleField(obj, osc.forClass(), field_name)); - else if (type == Float.TYPE) - realOutput.writeFloat(getFloatField(obj, osc.forClass(), field_name)); - else if (type == Integer.TYPE) - realOutput.writeInt(getIntField(obj, osc.forClass(), field_name)); - else if (type == Long.TYPE) - realOutput.writeLong(getLongField(obj, osc.forClass(), field_name)); - else if (type == Short.TYPE) - realOutput.writeShort(getShortField(obj, osc.forClass(), field_name)); - else - writeObject(getObjectField(obj, osc.forClass(), field_name, - fields[i].getTypeString ())); + writeFields(obj,fields); + } + catch (IllegalArgumentException _) + { + InvalidClassException e = new InvalidClassException + ("writing fields of class " + osc.forClass().getName()); + e.initCause(_); + throw e; + } + catch (IOException e) + { + throw e; } + catch (Exception _) + { + IOException e = new IOException("Unexpected exception " + _); + e.initCause(_); + throw(e); + } + setBlockDataMode(oldmode); } + + + /** + * Helper function for writeFields(Object,ObjectStreamClass): write + * fields from given fields array. Pass exception on. + * + * @param obj the object to be written + * + * @param fields the fields of obj to be written. + */ + private void writeFields(Object obj, ObjectStreamField[] fields) + throws + IllegalArgumentException, IllegalAccessException, IOException + { + for (int i = 0; i < fields.length; i++) + { + ObjectStreamField osf = fields[i]; + Field field = osf.field; + + if (DEBUG && dump) + dumpElementln ("WRITE FIELD: " + osf.getName() + " type=" + osf.getType()); + + switch (osf.getTypeCode()) + { + case 'Z': realOutput.writeBoolean(field.getBoolean(obj)); break; + case 'B': realOutput.writeByte (field.getByte (obj)); break; + case 'S': realOutput.writeShort (field.getShort (obj)); break; + case 'C': realOutput.writeChar (field.getChar (obj)); break; + case 'I': realOutput.writeInt (field.getInt (obj)); break; + case 'F': realOutput.writeFloat (field.getFloat (obj)); break; + case 'J': realOutput.writeLong (field.getLong (obj)); break; + case 'D': realOutput.writeDouble (field.getDouble (obj)); break; + case 'L': + case '[': writeObject (field.get (obj)); break; + default: + throw new IOException("Unexpected type code " + osf.getTypeCode()); + } + } + } // Toggles writing primitive data to block-data buffer. @@ -1313,248 +1340,6 @@ public class ObjectOutputStream extends OutputStream } } - private boolean getBooleanField(Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField(klass, field_name); - boolean b = f.getBoolean(obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private byte getByteField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - byte b = f.getByte (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private char getCharField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - char b = f.getChar (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private double getDoubleField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - double b = f.getDouble (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private float getFloatField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - float b = f.getFloat (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private int getIntField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - int b = f.getInt (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private long getLongField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - long b = f.getLong (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private short getShortField (Object obj, Class klass, String field_name) - throws IOException - { - try - { - Field f = getField (klass, field_name); - short b = f.getShort (obj); - return b; - } - catch (IllegalArgumentException _) - { - throw new InvalidClassException - ("invalid requested type for field " + field_name + " in class " + klass.getName()); - } - catch (IOException e) - { - throw e; - } - catch (Exception _) - { - throw new IOException("Unexpected exception " + _); - } - } - - private Object getObjectField (Object obj, Class klass, String field_name, - String type_code) throws IOException - { - try - { - Field f = getField (klass, field_name); - ObjectStreamField of = new ObjectStreamField(f.getName(), f.getType()); - - /* if of is primitive something went wrong - * in the check for primitive classes in writeFields. - */ - if (of.isPrimitive()) - throw new InvalidClassException - ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field is primitive"); - - if (!of.getTypeString().equals(type_code)) - throw new InvalidClassException - ("invalid type code for " + field_name + " in class " + klass.getName() + " : object stream field " + of + " has type string " + of.getTypeString() + " instead of " + type_code); - - Object o = f.get (obj); - // FIXME: We should check the type_code here - return o; - } - catch (IOException e) - { - throw e; - } - catch (Exception e) - { - throw new IOException (); - } - } - - private Field getField (Class klass, String name) - throws java.io.InvalidClassException - { - try - { - final Field f = klass.getDeclaredField(name); - setAccessible.setMember(f); - AccessController.doPrivileged(setAccessible); - return f; - } - catch (java.lang.NoSuchFieldException e) - { - throw new InvalidClassException - ("no field called " + name + " in class " + klass.getName()); - } - } - private void dumpElementln (String msg) { for (int i = 0; i < depth; i++) @@ -1582,7 +1367,7 @@ public class ObjectOutputStream extends OutputStream private boolean replacementEnabled; private boolean isSerializing; private int nextOID; - private Hashtable<ObjectIdentityWrapper,Integer> OIDLookupTable; + private ObjectIdentityMap2Int OIDLookupTable; private int protocolVersion; private boolean useSubclassMethod; private SetAccessibleAction setAccessible = new SetAccessibleAction(); diff --git a/java/io/ObjectStreamClass.java b/java/io/ObjectStreamClass.java index 1ca78ed9f..21a80c392 100644 --- a/java/io/ObjectStreamClass.java +++ b/java/io/ObjectStreamClass.java @@ -59,7 +59,6 @@ import java.security.Security; import java.util.Arrays; import java.util.Comparator; import java.util.Hashtable; -import java.util.Vector; /** * @author Tom Tromey (tromey@redhat.com) @@ -242,37 +241,45 @@ public class ObjectStreamClass implements Serializable return superClass; } - - // returns an array of ObjectStreamClasses that represent the super - // classes of CLAZZ and CLAZZ itself in order from most super to - // CLAZZ. ObjectStreamClass[0] is the highest superclass of CLAZZ - // that is serializable. - static ObjectStreamClass[] getObjectStreamClasses(Class<?> clazz) + /** + * returns an array of ObjectStreamClasses that represent the super + * classes of the class represented by this and the class + * represented by this itself in order from most super to this. + * ObjectStreamClass[0] is the highest superclass of this that is + * serializable. + * + * The result of consecutive calls this hierarchy() will be the same + * array instance. + * + * @return an array of ObjectStreamClass representing the + * super-class hierarchy of serializable classes. + */ + ObjectStreamClass[] hierarchy() { - ObjectStreamClass osc = ObjectStreamClass.lookup(clazz); - - if (osc == null) - return new ObjectStreamClass[0]; - else - { - Vector<ObjectStreamClass> oscs = new Vector<ObjectStreamClass>(); - - while (osc != null) - { - oscs.addElement (osc); - osc = osc.getSuper(); - } - - int count = oscs.size(); - ObjectStreamClass[] sorted_oscs = new ObjectStreamClass[ count ]; - - for (int i = count - 1; i >= 0; i--) - sorted_oscs[ count - i - 1 ] = (ObjectStreamClass) oscs.elementAt(i); - - return sorted_oscs; + ObjectStreamClass[] result = hierarchy; + if (result == null) + { + int d = 0; + + for(ObjectStreamClass osc = this; osc != null; osc = osc.getSuper()) + d++; + + result = new ObjectStreamClass[d]; + + for (ObjectStreamClass osc = this; osc != null; osc = osc.getSuper()) + { + result[--d] = osc; + } + + hierarchy = result; } + return result; } + /** + * Cache for hierarchy() result. + */ + private ObjectStreamClass[] hierarchy = null; // Returns an integer that consists of bit-flags that indicate // properties of the class represented by this ObjectStreamClass. @@ -305,7 +312,7 @@ public class ObjectStreamClass implements Serializable * already set UID is found. */ void setClass(Class cl, ObjectStreamClass superClass) throws InvalidClassException - { + {hierarchy = null; this.clazz = cl; cacheMethods(); @@ -432,6 +439,7 @@ public class ObjectStreamClass implements Serializable void setSuperclass (ObjectStreamClass osc) { superClass = osc; + hierarchy = null; } void calculateOffsets() @@ -554,21 +562,62 @@ outer: return null; } + /** + * Helper routine to check if a class was loaded by boot or + * application class loader. Classes for which this is not the case + * should not be cached since caching prevent class file garbage + * collection. + * + * @param cl a class + * + * @return true if cl was loaded by boot or application class loader, + * false if cl was loaded by a user class loader. + */ + private static boolean loadedByBootOrApplicationClassLoader(Class cl) + { + ClassLoader l = cl.getClassLoader(); + return + ( l == null /* boot loader */ ) + || (l == ClassLoader.getSystemClassLoader() /* application loader */); + } + + static Hashtable methodCache = new Hashtable(); + + static final Class[] readObjectSignature = { ObjectInputStream.class }; + static final Class[] writeObjectSignature = { ObjectOutputStream.class }; + private void cacheMethods() { - Method[] methods = forClass().getDeclaredMethods(); - - readObjectMethod = findMethod(methods, "readObject", - new Class[] { ObjectInputStream.class }, - Void.TYPE, true); - writeObjectMethod = findMethod(methods, "writeObject", - new Class[] { ObjectOutputStream.class }, - Void.TYPE, true); - - // readResolve and writeReplace can be in parent classes, as long as they - // are accessible from this class. - readResolveMethod = findAccessibleMethod("readResolve", forClass()); - writeReplaceMethod = findAccessibleMethod("writeReplace", forClass()); + Class cl = forClass(); + Method[] cached = (Method[]) methodCache.get(cl); + if (cached == null) + { + cached = new Method[4]; + Method[] methods = cl.getDeclaredMethods(); + + cached[0] = findMethod(methods, "readObject", + readObjectSignature, + Void.TYPE, true); + cached[1] = findMethod(methods, "writeObject", + writeObjectSignature, + Void.TYPE, true); + + // readResolve and writeReplace can be in parent classes, as long as they + // are accessible from this class. + cached[2] = findAccessibleMethod("readResolve", cl); + cached[3] = findAccessibleMethod("writeReplace", cl); + + /* put in cache if classes not loaded by user class loader. + * For a user class loader, the cache may otherwise grow + * without limit. + */ + if (loadedByBootOrApplicationClassLoader(cl)) + methodCache.put(cl,cached); + } + readObjectMethod = cached[0]; + writeObjectMethod = cached[1]; + readResolveMethod = cached[2]; + writeReplaceMethod = cached[3]; } private ObjectStreamClass(Class cl) @@ -720,152 +769,208 @@ outer: calculateOffsets(); } + static Hashtable uidCache = new Hashtable(); + // Returns the serial version UID defined by class, or if that // isn't present, calculates value of serial version UID. private long getClassUID(Class cl) { - try + long result = 0; + Long cache = (Long) uidCache.get(cl); + if (cache != null) + result = cache.longValue(); + else { - // Use getDeclaredField rather than getField, since serialVersionUID - // may not be public AND we only want the serialVersionUID of this - // class, not a superclass or interface. - final Field suid = cl.getDeclaredField("serialVersionUID"); - SetAccessibleAction setAccessible = new SetAccessibleAction(suid); - AccessController.doPrivileged(setAccessible); - int modifiers = suid.getModifiers(); - - if (Modifier.isStatic(modifiers) - && Modifier.isFinal(modifiers) - && suid.getType() == Long.TYPE) - return suid.getLong(null); + try + { + result = getClassUIDFromField(cl); + } + catch (NoSuchFieldException ignore) + { + try + { + result = calculateClassUID(cl); + } + catch (NoSuchAlgorithmException e) + { + throw new RuntimeException + ("The SHA algorithm was not found to use in computing the Serial Version UID for class " + + cl.getName(), e); + } + catch (IOException ioe) + { + throw new RuntimeException(ioe); + } + } + + if (loadedByBootOrApplicationClassLoader(cl)) + uidCache.put(cl,new Long(result)); } - catch (NoSuchFieldException ignore) + return result; + } + + /** + * Search for a serialVersionUID field in the given class and read + * its value. + * + * @return the contents of the serialVersionUID field + * + * @throws NoSuchFieldException if such a field does not exist or is + * not static, not final, not of type Long or not accessible. + */ + long getClassUIDFromField(Class cl) + throws NoSuchFieldException + { + long result; + + try { + // Use getDeclaredField rather than getField, since serialVersionUID + // may not be public AND we only want the serialVersionUID of this + // class, not a superclass or interface. + final Field suid = cl.getDeclaredField("serialVersionUID"); + SetAccessibleAction setAccessible = new SetAccessibleAction(suid); + AccessController.doPrivileged(setAccessible); + int modifiers = suid.getModifiers(); + + if (Modifier.isStatic(modifiers) + && Modifier.isFinal(modifiers) + && suid.getType() == Long.TYPE) + result = suid.getLong(null); + else + throw new NoSuchFieldException(); } catch (IllegalAccessException ignore) { + throw new NoSuchFieldException(); } - // cl didn't define serialVersionUID, so we have to compute it - try - { - MessageDigest md; - try - { - md = MessageDigest.getInstance("SHA"); - } - catch (NoSuchAlgorithmException e) - { - // If a provider already provides SHA, use it; otherwise, use this. - Gnu gnuProvider = new Gnu(); - Security.addProvider(gnuProvider); - md = MessageDigest.getInstance("SHA"); - } - - DigestOutputStream digest_out = - new DigestOutputStream(nullOutputStream, md); - DataOutputStream data_out = new DataOutputStream(digest_out); - - data_out.writeUTF(cl.getName()); - - int modifiers = cl.getModifiers(); - // just look at interesting bits - modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL - | Modifier.INTERFACE | Modifier.PUBLIC); - data_out.writeInt(modifiers); - - // Pretend that an array has no interfaces, because when array - // serialization was defined (JDK 1.1), arrays didn't have it. - if (! cl.isArray()) - { - Class[] interfaces = cl.getInterfaces(); - Arrays.sort(interfaces, interfaceComparator); - for (int i = 0; i < interfaces.length; i++) - data_out.writeUTF(interfaces[i].getName()); - } - - Field field; - Field[] fields = cl.getDeclaredFields(); - Arrays.sort(fields, memberComparator); - for (int i = 0; i < fields.length; i++) - { - field = fields[i]; - modifiers = field.getModifiers(); - if (Modifier.isPrivate(modifiers) - && (Modifier.isStatic(modifiers) - || Modifier.isTransient(modifiers))) - continue; - - data_out.writeUTF(field.getName()); - data_out.writeInt(modifiers); - data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType())); - } - - // write class initializer method if present - if (VMObjectStreamClass.hasClassInitializer(cl)) - { - data_out.writeUTF("<clinit>"); - data_out.writeInt(Modifier.STATIC); - data_out.writeUTF("()V"); - } - - Constructor constructor; - Constructor[] constructors = cl.getDeclaredConstructors(); - Arrays.sort (constructors, memberComparator); - for (int i = 0; i < constructors.length; i++) - { - constructor = constructors[i]; - modifiers = constructor.getModifiers(); - if (Modifier.isPrivate(modifiers)) - continue; - - data_out.writeUTF("<init>"); - data_out.writeInt(modifiers); - - // the replacement of '/' with '.' was needed to make computed - // SUID's agree with those computed by JDK - data_out.writeUTF - (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.')); - } - - Method method; - Method[] methods = cl.getDeclaredMethods(); - Arrays.sort(methods, memberComparator); - for (int i = 0; i < methods.length; i++) - { - method = methods[i]; - modifiers = method.getModifiers(); - if (Modifier.isPrivate(modifiers)) - continue; - - data_out.writeUTF(method.getName()); - data_out.writeInt(modifiers); - - // the replacement of '/' with '.' was needed to make computed - // SUID's agree with those computed by JDK - data_out.writeUTF - (TypeSignature.getEncodingOfMethod(method).replace('/', '.')); - } - - data_out.close(); - byte[] sha = md.digest(); - long result = 0; - int len = sha.length < 8 ? sha.length : 8; - for (int i = 0; i < len; i++) - result += (long) (sha[i] & 0xFF) << (8 * i); + return result; + } - return result; + /** + * Calculate class serial version UID for a class that does not + * define serialVersionUID: + * + * @param cl a class + * + * @return the calculated serial varsion UID. + * + * @throws NoSuchAlgorithmException if SHA algorithm not found + * + * @throws IOException if writing to the DigestOutputStream causes + * an IOException. + */ + long calculateClassUID(Class cl) + throws NoSuchAlgorithmException, IOException + { + long result; + MessageDigest md; + try + { + md = MessageDigest.getInstance("SHA"); } catch (NoSuchAlgorithmException e) { - throw new RuntimeException - ("The SHA algorithm was not found to use in computing the Serial Version UID for class " - + cl.getName(), e); + // If a provider already provides SHA, use it; otherwise, use this. + Gnu gnuProvider = new Gnu(); + Security.addProvider(gnuProvider); + md = MessageDigest.getInstance("SHA"); + } + + DigestOutputStream digest_out = + new DigestOutputStream(nullOutputStream, md); + DataOutputStream data_out = new DataOutputStream(digest_out); + + data_out.writeUTF(cl.getName()); + + int modifiers = cl.getModifiers(); + // just look at interesting bits + modifiers = modifiers & (Modifier.ABSTRACT | Modifier.FINAL + | Modifier.INTERFACE | Modifier.PUBLIC); + data_out.writeInt(modifiers); + + // Pretend that an array has no interfaces, because when array + // serialization was defined (JDK 1.1), arrays didn't have it. + if (! cl.isArray()) + { + Class[] interfaces = cl.getInterfaces(); + Arrays.sort(interfaces, interfaceComparator); + for (int i = 0; i < interfaces.length; i++) + data_out.writeUTF(interfaces[i].getName()); + } + + Field field; + Field[] fields = cl.getDeclaredFields(); + Arrays.sort(fields, memberComparator); + for (int i = 0; i < fields.length; i++) + { + field = fields[i]; + modifiers = field.getModifiers(); + if (Modifier.isPrivate(modifiers) + && (Modifier.isStatic(modifiers) + || Modifier.isTransient(modifiers))) + continue; + + data_out.writeUTF(field.getName()); + data_out.writeInt(modifiers); + data_out.writeUTF(TypeSignature.getEncodingOfClass (field.getType())); + } + + // write class initializer method if present + if (VMObjectStreamClass.hasClassInitializer(cl)) + { + data_out.writeUTF("<clinit>"); + data_out.writeInt(Modifier.STATIC); + data_out.writeUTF("()V"); } - catch (IOException ioe) + + Constructor constructor; + Constructor[] constructors = cl.getDeclaredConstructors(); + Arrays.sort (constructors, memberComparator); + for (int i = 0; i < constructors.length; i++) + { + constructor = constructors[i]; + modifiers = constructor.getModifiers(); + if (Modifier.isPrivate(modifiers)) + continue; + + data_out.writeUTF("<init>"); + data_out.writeInt(modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF + (TypeSignature.getEncodingOfConstructor(constructor).replace('/','.')); + } + + Method method; + Method[] methods = cl.getDeclaredMethods(); + Arrays.sort(methods, memberComparator); + for (int i = 0; i < methods.length; i++) { - throw new RuntimeException(ioe); + method = methods[i]; + modifiers = method.getModifiers(); + if (Modifier.isPrivate(modifiers)) + continue; + + data_out.writeUTF(method.getName()); + data_out.writeInt(modifiers); + + // the replacement of '/' with '.' was needed to make computed + // SUID's agree with those computed by JDK + data_out.writeUTF + (TypeSignature.getEncodingOfMethod(method).replace('/', '.')); } + + data_out.close(); + byte[] sha = md.digest(); + result = 0; + int len = sha.length < 8 ? sha.length : 8; + for (int i = 0; i < len; i++) + result += (long) (sha[i] & 0xFF) << (8 * i); + + return result; } /** diff --git a/java/io/ObjectStreamField.java b/java/io/ObjectStreamField.java index b0cac5015..91f557870 100644 --- a/java/io/ObjectStreamField.java +++ b/java/io/ObjectStreamField.java @@ -65,7 +65,7 @@ public class ObjectStreamField private boolean unshared; private boolean persistent = false; private boolean toset = true; - private Field field; + Field field; ObjectStreamField (Field field) { diff --git a/java/io/PrintStream.java b/java/io/PrintStream.java index 9726ccdba..2d747c8c8 100644 --- a/java/io/PrintStream.java +++ b/java/io/PrintStream.java @@ -71,7 +71,7 @@ public class PrintStream extends FilterOutputStream implements Appendable // Line separator string. private static final char[] line_separator - = SystemProperties.getProperty("line.separator").toCharArray(); + = SystemProperties.getProperty("line.separator", "\n").toCharArray(); /** * Encoding name diff --git a/java/lang/management/ManagementFactory.java b/java/lang/management/ManagementFactory.java index d6727ea10..d9a02ee23 100644 --- a/java/lang/management/ManagementFactory.java +++ b/java/lang/management/ManagementFactory.java @@ -67,6 +67,49 @@ import javax.management.NotCompliantMBeanException; * <li>Calling the appropriate static method of this factory. * </li> * </ol> + * <h2>Open Data Types</h2> + * <p> + * The data types used by the management beans are restricted + * to <emph>open</emph> data types to aid interoperability. This + * allows the beans to be accessed remotely, including from non-Java + * clients. Below is a table which lists the types used by the beans + * on the left, and the types they are converted to when returned via + * a bean server on the right. Type information is provided for each + * bean by obtaining its instance of {@link javax.management.MBeanInfo}. + * </p> + * <table> + * <th><td>Data Type Used</td><td>Data Type Returned</td></th> + * <tr> + * <td>Primitive types (<code>int</code>, <code>char</code>, etc.)</td> + * <td>Same</td> + * </tr><tr> + * <td>Wrapper classes ({@link{java.lang.Integer}, + * @link{java.lang.Character}, etc.)</td> + * <td>Same</td> + * </tr><tr> + * <td>An {@link java.lang.Enum}</td> + * <td>The <code>name</code> of the enumeration constant</td> + * </tr><tr> + * <td>An array of type <code>E</code></td> + * <td>An array of the same dimensions with this mapping applied + * to <code>E</code>.</td> + * </tr><tr> + * <td>A class with `getter' methods and a + * <code>from({@link javax.management.openmbean.CompositeData})</code> + * method.</td> + * <td>The equivalent {@link javax.management.openmbean.CompositeData} + * instance, specified by the <code>from</code> method.</td> + * </tr><tr> + * <td>A map with keys of type <code>K</code> and values of + * type <code>V</code>.</td> + * <td>A {@link javax.management.openmbean.TabularData} instance, + * with the row type containing two items, <code>"key"</code> and + * <code>"value"</code> with the types <code>K</code> and <code>V</code> + * respectively (with translation applied).</td> + * </tr><tr> + * <td>A list of type <code>E</code>.</td> + * <td>An array with this mapping applied to <code>E</code>.</td> + * </tr></table> * * @author Andrew John Hughes (gnu_andrew@member.fsf.org) * @since 1.5 diff --git a/java/lang/ref/Reference.java b/java/lang/ref/Reference.java index 8a7501863..ce224b891 100644 --- a/java/lang/ref/Reference.java +++ b/java/lang/ref/Reference.java @@ -1,5 +1,5 @@ /* java.lang.ref.Reference - Copyright (C) 1999, 2002, 2003, 2004 Free Software Foundation, Inc. + Copyright (C) 1999, 2002, 2003, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -82,7 +82,7 @@ public abstract class Reference<T> * The queue this reference is registered on. This is null, if this * wasn't registered to any queue or reference was already enqueued. */ - ReferenceQueue<? super T> queue; + volatile ReferenceQueue<? super T> queue; /** * Link to the next entry on the queue. If this is null, this @@ -91,7 +91,7 @@ public abstract class Reference<T> * (not to null, that value is used to mark a not enqueued * reference). */ - Reference nextOnQueue; + volatile Reference nextOnQueue; /** * This lock should be taken by the garbage collector, before @@ -166,11 +166,10 @@ public abstract class Reference<T> */ public boolean enqueue() { - if (queue != null && nextOnQueue == null) + ReferenceQueue q = queue; + if (q != null) { - queue.enqueue(this); - queue = null; - return true; + return q.enqueue(this); } return false; } diff --git a/java/lang/ref/ReferenceQueue.java b/java/lang/ref/ReferenceQueue.java index 189b81b17..281628779 100644 --- a/java/lang/ref/ReferenceQueue.java +++ b/java/lang/ref/ReferenceQueue.java @@ -1,5 +1,5 @@ /* java.lang.ref.ReferenceQueue - Copyright (C) 1999, 2004 Free Software Foundation, Inc. + Copyright (C) 1999, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -63,6 +63,12 @@ public class ReferenceQueue<T> private Reference<? extends T> first; /** + * This is the lock that protects our linked list and is used to signal + * a thread waiting in remove(). + */ + private final Object lock = new Object(); + + /** * Creates a new empty reference queue. */ public ReferenceQueue() @@ -76,7 +82,7 @@ public class ReferenceQueue<T> * @return a reference on the queue, if there is one, * <code>null</code> otherwise. */ - public synchronized Reference<? extends T> poll() + public Reference<? extends T> poll() { return dequeue(); } @@ -84,14 +90,23 @@ public class ReferenceQueue<T> /** * This is called by reference to enqueue itself on this queue. * @param ref the reference that should be enqueued. + * @return true if successful, false if not. */ - synchronized void enqueue(Reference<? extends T> ref) - { - /* last reference will point to itself */ - ref.nextOnQueue = first == null ? ref : first; - first = ref; - /* this wakes only one remove thread. */ - notify(); + final boolean enqueue(Reference<? extends T> ref) + { + synchronized (lock) + { + if (ref.queue != this) + return false; + + /* last reference will point to itself */ + ref.nextOnQueue = first == null ? ref : first; + ref.queue = null; + first = ref; + /* this wakes only one remove thread. */ + lock.notify(); + return true; + } } /** @@ -100,13 +115,16 @@ public class ReferenceQueue<T> */ private Reference<? extends T> dequeue() { - if (first == null) - return null; - - Reference<? extends T> result = first; - first = (first == first.nextOnQueue) ? null : first.nextOnQueue; - result.nextOnQueue = null; - return result; + synchronized (lock) + { + if (first == null) + return null; + + Reference<? extends T> result = first; + first = (first == first.nextOnQueue) ? null : first.nextOnQueue; + result.nextOnQueue = null; + return result; + } } /** @@ -118,12 +136,13 @@ public class ReferenceQueue<T> * <code>null</code> if timeout period expired. * @exception InterruptedException if the wait was interrupted. */ - public synchronized Reference<? extends T> remove(long timeout) + public Reference<? extends T> remove(long timeout) throws InterruptedException { - if (first == null) + synchronized (lock) { - wait(timeout); + if (first == null) + lock.wait(timeout); } return dequeue(); diff --git a/java/math/BigInteger.java b/java/math/BigInteger.java index 0a683f7b7..c897d8bf4 100644 --- a/java/math/BigInteger.java +++ b/java/math/BigInteger.java @@ -1,5 +1,5 @@ /* java.math.BigInteger -- Arbitary precision integers - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -198,8 +198,20 @@ public class BigInteger extends Number implements Comparable<BigInteger> private void init(int numBits, Random rnd) { int highbits = numBits & 31; + // minimum number of bytes to store the above number of bits + int highBitByteCount = (highbits + 7) / 8; + // number of bits to discard from the last byte + int discardedBitCount = highbits % 8; + if (discardedBitCount != 0) + discardedBitCount = 8 - discardedBitCount; + byte[] highBitBytes = new byte[highBitByteCount]; if (highbits > 0) - highbits = rnd.nextInt() >>> (32 - highbits); + { + rnd.nextBytes(highBitBytes); + highbits = (highBitBytes[highBitByteCount - 1] & 0xFF) >>> discardedBitCount; + for (int i = highBitByteCount - 2; i >= 0; i--) + highbits = (highbits << 8) | (highBitBytes[i] & 0xFF); + } int nwords = numBits / 32; while (highbits == 0 && nwords > 0) @@ -226,8 +238,13 @@ public class BigInteger extends Number implements Comparable<BigInteger> this(bitLength, rnd); // Keep going until we find a probable prime. + BigInteger result; while (true) { + // ...but first ensure that BI has bitLength bits + result = setBit(bitLength - 1); + this.ival = result.ival; + this.words = result.words; if (isProbablePrime(certainty)) return; @@ -1583,24 +1600,31 @@ public class BigInteger extends Number implements Comparable<BigInteger> // but slightly more expensive, for little practical gain. if (len <= 15 && radix <= 16) return valueOf(Long.parseLong(s, radix)); - + + int i, digit; + boolean negative; + byte[] bytes; + char ch = s.charAt(0); + if (ch == '-') + { + negative = true; + i = 1; + bytes = new byte[len - 1]; + } + else + { + negative = false; + i = 0; + bytes = new byte[len]; + } int byte_len = 0; - byte[] bytes = new byte[len]; - boolean negative = false; - for (int i = 0; i < len; i++) + for ( ; i < len; i++) { - char ch = s.charAt(i); - if (ch == '-') - negative = true; - else if (ch == '_' || (byte_len == 0 && (ch == ' ' || ch == '\t'))) - continue; - else - { - int digit = Character.digit(ch, radix); - if (digit < 0) - break; - bytes[byte_len++] = (byte) digit; - } + ch = s.charAt(i); + digit = Character.digit(ch, radix); + if (digit < 0) + throw new NumberFormatException(); + bytes[byte_len++] = (byte) digit; } return valueOf(bytes, byte_len, negative, radix); } diff --git a/java/net/SocketPermission.java b/java/net/SocketPermission.java index 723ccc7a5..97e93dcbb 100644 --- a/java/net/SocketPermission.java +++ b/java/net/SocketPermission.java @@ -164,24 +164,69 @@ public final class SocketPermission extends Permission implements Serializable */ public SocketPermission(String hostport, String actions) { - super(hostport); + super(processHostport(hostport)); - setHostPort(hostport); + setHostPort(getName()); setActions(actions); } /** + * There are two cases in which hostport needs rewriting before + * being passed to the superclass constructor. If hostport is an + * empty string then it is substituted with "localhost". And if + * the host part of hostport is a literal IPv6 address in the full + * uncompressed form not enclosed with "[" and "]" then we enclose + * it with them. + */ + private static String processHostport(String hostport) + { + if (hostport.length() == 0) + return "localhost"; + + if (hostport.charAt(0) == '[') + return hostport; + + int colons = 0, last_colon = 0; + for (int i = 0; i < hostport.length(); i++) + { + if (hostport.charAt(i) == ':') + { + if (i - last_colon == 1) + throw new IllegalArgumentException("Ambiguous hostport part"); + colons++; + last_colon = i; + } + } + + switch (colons) + { + case 0: + case 1: + // a hostname or IPv4 address + return hostport; + + case 7: + // an IPv6 address with no ports + return "[" + hostport + "]"; + + case 8: + // an IPv6 address with ports + return "[" + hostport.substring(0, last_colon) + "]" + + hostport.substring(last_colon); + + default: + throw new IllegalArgumentException("Ambiguous hostport part"); + } + } + + /** * Parse the hostport argument to the constructor. */ private void setHostPort(String hostport) { // Split into host and ports String ports; - if (hostport.length() == 0) - { - host = ports = ""; - } - else if (hostport.charAt(0) == '[') + if (hostport.charAt(0) == '[') { // host is a bracketed IPv6 address int end = hostport.indexOf("]"); @@ -211,8 +256,6 @@ public final class SocketPermission extends Permission implements Serializable ports = hostport.substring(sep + 1); } } - if (ports.indexOf(":") != -1) - throw new IllegalArgumentException("Unexpected ':'"); // Parse and validate the ports if (ports.length() == 0) diff --git a/java/security/AlgorithmParameterGenerator.java b/java/security/AlgorithmParameterGenerator.java index e33fbaf81..e2a17d4bf 100644 --- a/java/security/AlgorithmParameterGenerator.java +++ b/java/security/AlgorithmParameterGenerator.java @@ -40,6 +40,7 @@ package java.security; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.security.spec.AlgorithmParameterSpec; /** @@ -97,26 +98,29 @@ public class AlgorithmParameterGenerator * Returns a new <code>AlgorithmParameterGenerator</code> instance which * generates algorithm parameters for the specified algorithm. * - * @param algorithm - * the name of algorithm to use. + * @param algorithm the name of algorithm to use. * @return the new instance. - * @throws NoSuchAlgorithmException - * if <code>algorithm</code> is not implemented by any provider. + * @throws NoSuchAlgorithmException if <code>algorithm</code> is not + * implemented by any provider. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static AlgorithmParameterGenerator getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) try { return getInstance(algorithm, p[i]); } - catch (NoSuchAlgorithmException e) - { - // Ignore. - } - + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } @@ -124,27 +128,27 @@ public class AlgorithmParameterGenerator * Returns a new <code>AlgorithmParameterGenerator</code> instance which * generates algorithm parameters for the specified algorithm. * - * @param algorithm - * the name of algorithm to use. - * @param provider - * the name of the {@link Provider} to use. + * @param algorithm the name of algorithm to use. + * @param provider the name of the {@link Provider} to use. * @return the new instance. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the named provider. - * @throws NoSuchProviderException - * if the named provider was not found. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * named provider. + * @throws NoSuchProviderException if the named provider was not found. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static AlgorithmParameterGenerator getInstance(String algorithm, - String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } @@ -152,38 +156,50 @@ public class AlgorithmParameterGenerator * Returns a new <code>AlgorithmParameterGenerator</code> instance which * generates algorithm parameters for the specified algorithm. * - * @param algorithm - * the name of algorithm to use. - * @param provider - * the {@link Provider} to use. + * @param algorithm the name of algorithm to use. + * @param provider the {@link Provider} to use. * @return the new instance. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by {@link Provider}. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by + * {@link Provider}. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. * @since 1.4 * @see Provider */ public static AlgorithmParameterGenerator getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); - + StringBuilder sb = new StringBuilder() + .append("AlgorithmParameterGenerator for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new AlgorithmParameterGenerator( - (AlgorithmParameterGeneratorSpi) Engine.getInstance( - ALGORITHM_PARAMETER_GENERATOR, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(ALGORITHM_PARAMETER_GENERATOR, + algorithm, + provider); + return new AlgorithmParameterGenerator((AlgorithmParameterGeneratorSpi) spi, + provider, + algorithm); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } /** @return the {@link Provider} of this generator. */ diff --git a/java/security/AlgorithmParameters.java b/java/security/AlgorithmParameters.java index 911c566c8..f5e5063a1 100644 --- a/java/security/AlgorithmParameters.java +++ b/java/security/AlgorithmParameters.java @@ -41,6 +41,7 @@ package java.security; import gnu.java.security.Engine; import java.io.IOException; +import java.lang.reflect.InvocationTargetException; import java.security.spec.AlgorithmParameterSpec; import java.security.spec.InvalidParameterSpecException; @@ -91,106 +92,115 @@ public class AlgorithmParameters /** * Returns a new instance of <code>AlgorithmParameters</code> representing * the specified algorithm parameters. + * <p> + * The returned <code>AlgorithmParameters</code> must still be initialized + * with an <code>init()</code> method. * - * <p>The returned <code>AlgorithmParameters</code> must still be initialized - * with an <code>init()</code> method.</p> - * - * @param algorithm - * the algorithm to use. + * @param algorithm the algorithm to use. * @return the new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by any provider. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by any + * provider. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static AlgorithmParameters getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); - + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) try { return getInstance(algorithm, p[i]); } - catch (NoSuchAlgorithmException e) - { - // Ignore this. - } - + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } /** * Returns a new instance of <code>AlgorithmParameters</code> representing * the specified algorithm parameters from a named provider. + * <p> + * The returned <code>AlgorithmParameters</code> must still be intialized + * with an <code>init()</code> method. + * </p> * - * <p>The returned <code>AlgorithmParameters</code> must still be intialized - * with an <code>init()</code> method.</p> - * - * @param algorithm - * the algorithm to use. - * @param provider - * the name of the {@link Provider} to use. + * @param algorithm the algorithm to use. + * @param provider the name of the {@link Provider} to use. * @return the new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the named provider. - * @throws NoSuchProviderException - * if the named provider was not found. - * @throws IllegalArgumentException - * if <code>provider</code> is <code>null</code> or is an empty - * string. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * named provider. + * @throws NoSuchProviderException if the named provider was not found. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ - public static AlgorithmParameters getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + public static AlgorithmParameters getInstance(String algorithm, + String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } /** * Returns a new instance of <code>AlgorithmParameters</code> representing * the specified algorithm parameters from the specified {@link Provider}. + * <p> + * The returned <code>AlgorithmParameters</code> must still be intialized + * with an <code>init()</code> method. * - * <p>The returned <code>AlgorithmParameters</code> must still be intialized - * with an <code>init()</code> method.</p> - * - * @param algorithm - * the algorithm to use. - * @param provider - * the {@link Provider} to use. + * @param algorithm the algorithm to use. + * @param provider the {@link Provider} to use. * @return the new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the {@link Provider}. - * @throws IllegalArgumentException - * if <code>provider</code> is <code>null</code>. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * {@link Provider}. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. * @since 1.4 */ public static AlgorithmParameters getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); - + StringBuilder sb = new StringBuilder("AlgorithmParameters for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new AlgorithmParameters((AlgorithmParametersSpi) - Engine.getInstance(ALGORITHM_PARAMETERS, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(ALGORITHM_PARAMETERS, algorithm, provider); + return new AlgorithmParameters((AlgorithmParametersSpi) spi, + provider, + algorithm); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } /** @return the provider of this parameter object. */ diff --git a/java/security/KeyFactory.java b/java/security/KeyFactory.java index 1ecfd2f72..043dd59a1 100644 --- a/java/security/KeyFactory.java +++ b/java/security/KeyFactory.java @@ -40,6 +40,7 @@ package java.security; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.security.spec.InvalidKeySpecException; import java.security.spec.KeySpec; @@ -93,26 +94,29 @@ public class KeyFactory * Returns a new instance of <code>KeyFactory</code> representing the * specified key factory. * - * @param algorithm - * the name of algorithm to use. + * @param algorithm the name of algorithm to use. * @return a new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by any provider. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by any + * provider. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static KeyFactory getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) try { return getInstance(algorithm, p[i]); } - catch (NoSuchAlgorithmException e) - { - // Ignore. - } - + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } @@ -120,29 +124,26 @@ public class KeyFactory * Returns a new instance of <code>KeyFactory</code> representing the * specified key factory from the specified provider. * - * @param algorithm - * the name of algorithm to use. - * @param provider - * the name of the provider to use. + * @param algorithm the name of algorithm to use. + * @param provider the name of the provider to use. * @return a new instance repesenting the desired algorithm. - * @throws IllegalArgumentException - * if <code>provider</code> is <code>null</code> or is an empty - * string. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the named provider. - * @throws NoSuchProviderException - * if the named provider was not found. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * named provider. + * @throws NoSuchProviderException if the named provider was not found. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static KeyFactory getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } @@ -150,38 +151,44 @@ public class KeyFactory * Returns a new instance of <code>KeyFactory</code> representing the * specified key factory from the designated {@link Provider}. * - * @param algorithm - * the name of algorithm to use. - * @param provider - * the {@link Provider} to use. + * @param algorithm the name of algorithm to use. + * @param provider the {@link Provider} to use. * @return a new instance repesenting the desired algorithm. - * @throws IllegalArgumentException - * if <code>provider</code> is <code>null</code>. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by {@link Provider}. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by + * {@link Provider}. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. * @since 1.4 * @see Provider */ public static KeyFactory getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); - + StringBuilder sb = new StringBuilder("KeyFactory for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new KeyFactory((KeyFactorySpi) - Engine.getInstance(KEY_FACTORY, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(KEY_FACTORY, algorithm, provider); + return new KeyFactory((KeyFactorySpi) spi, provider, algorithm); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); - } + cause = x; + } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } /** diff --git a/java/security/KeyPairGenerator.java b/java/security/KeyPairGenerator.java index 357d7a75f..6974035fd 100644 --- a/java/security/KeyPairGenerator.java +++ b/java/security/KeyPairGenerator.java @@ -40,6 +40,7 @@ package java.security; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.security.spec.AlgorithmParameterSpec; /** @@ -90,28 +91,29 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi * Returns a new instance of <code>KeyPairGenerator</code> which generates * key-pairs for the specified algorithm. * - * @param algorithm - * the name of the algorithm to use. + * @param algorithm the name of the algorithm to use. * @return a new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by any provider. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by any + * provider. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static KeyPairGenerator getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(algorithm, p[i]); - } - catch (NoSuchAlgorithmException e) - { - // Ignored. - } - } - + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } @@ -119,23 +121,26 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi * Returns a new instance of <code>KeyPairGenerator</code> which generates * key-pairs for the specified algorithm from a named provider. * - * @param algorithm - * the name of the algorithm to use. - * @param provider - * the name of a {@link Provider} to use. + * @param algorithm the name of the algorithm to use. + * @param provider the name of a {@link Provider} to use. * @return a new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the named provider. - * @throws NoSuchProviderException - * if the named provider was not found. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * named provider. + * @throws NoSuchProviderException if the named provider was not found. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static KeyPairGenerator getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } @@ -148,10 +153,11 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi * @param provider * the {@link Provider} to use. * @return a new insatnce repesenting the desired algorithm. - * @throws IllegalArgumentException - * if <code>provider</code> is <code>null</code>. * @throws NoSuchAlgorithmException * if the algorithm is not implemented by the {@link Provider}. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. * @since 1.4 * @see Provider */ @@ -159,20 +165,27 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi Provider provider) throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); - - Object o = null; + StringBuilder sb = new StringBuilder("KeyPairGenerator for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] "); + Object o; try { o = Engine.getInstance(KEY_PAIR_GENERATOR, algorithm, provider); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + Throwable cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; + sb.append("could not be created"); + NoSuchAlgorithmException y = new NoSuchAlgorithmException(sb.toString()); + y.initCause(cause); + throw y; } - - KeyPairGenerator result = null; + KeyPairGenerator result; if (o instanceof KeyPairGenerator) { result = (KeyPairGenerator) o; @@ -180,7 +193,11 @@ public abstract class KeyPairGenerator extends KeyPairGeneratorSpi } else if (o instanceof KeyPairGeneratorSpi) result = new DummyKeyPairGenerator((KeyPairGeneratorSpi) o, algorithm); - + else + { + sb.append("is of an unexpected Type: ").append(o.getClass().getName()); + throw new NoSuchAlgorithmException(sb.toString()); + } result.provider = provider; return result; } diff --git a/java/security/KeyStore.java b/java/security/KeyStore.java index e31e8a6c9..1d036c31c 100644 --- a/java/security/KeyStore.java +++ b/java/security/KeyStore.java @@ -43,6 +43,7 @@ import gnu.java.security.Engine; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.lang.reflect.InvocationTargetException; import java.security.cert.CertificateException; import java.util.Date; import java.util.Enumeration; @@ -108,105 +109,100 @@ public class KeyStore this.type = type; } - // Class methods. - // ------------------------------------------------------------------------ - - /** - * Gets an instance of the KeyStore class representing - * the specified keystore. If the type is not - * found then, it throws KeyStoreException. - * - * @param type the type of keystore to choose - * @return a KeyStore repesenting the desired type - * @throws KeyStoreException if the type of keystore is not implemented - * by providers or the implementation cannot be instantiated. + /** + * Returns an instance of a <code>KeyStore</code> representing the specified + * type, from the first provider that implements it. + * + * @param type the type of keystore to create. + * @return a <code>KeyStore</code> repesenting the desired type. + * @throws KeyStoreException if the designated type of is not implemented by + * any provider, or the implementation could not be instantiated. + * @throws IllegalArgumentException if <code>type</code> is + * <code>null</code> or is an empty string. */ public static KeyStore getInstance(String type) throws KeyStoreException { Provider[] p = Security.getProviders(); - + KeyStoreException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(type, p[i]); - } - catch (KeyStoreException e) - { - // Ignore. - } - } - + try + { + return getInstance(type, p[i]); + } + catch (KeyStoreException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new KeyStoreException(type); } - /** - * Gets an instance of the KeyStore class representing - * the specified key store from the specified provider. - * If the type is not found then, it throws KeyStoreException. - * If the provider is not found, then it throws - * NoSuchProviderException. - * - * @param type the type of keystore to choose - * @param provider the provider name - * @return a KeyStore repesenting the desired type - * @throws KeyStoreException if the type of keystore is not - * implemented by the given provider - * @throws NoSuchProviderException if the provider is not found - * @throws IllegalArgumentException if the provider string is - * null or empty + /** + * Returns an instance of a <code>KeyStore</code> representing the specified + * type, from the named provider. + * + * @param type the type of keystore to create. + * @param provider the name of the provider to use. + * @return a <code>KeyStore</code> repesenting the desired type. + * @throws KeyStoreException if the designated type is not implemented by the + * given provider. + * @throws NoSuchProviderException if the provider is not found. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static KeyStore getInstance(String type, String provider) throws KeyStoreException, NoSuchProviderException { - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(type, p); } - /** - * Gets an instance of the KeyStore class representing - * the specified key store from the specified provider. - * If the type is not found then, it throws KeyStoreException. - * If the provider is not found, then it throws - * NoSuchProviderException. - * - * @param type the type of keystore to choose - * @param provider the keystore provider - * @return a KeyStore repesenting the desired type - * @throws KeyStoreException if the type of keystore is not - * implemented by the given provider - * @throws IllegalArgumentException if the provider object is null + /** + * Returns an instance of a <code>KeyStore</code> representing the specified + * type, from the specified provider. + * + * @param type the type of keystore to create. + * @param provider the provider to use. + * @return a <code>KeyStore</code> repesenting the desired type. + * @throws KeyStoreException if the designated type is not implemented by the + * given provider. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>type</code> is an empty string. * @since 1.4 */ public static KeyStore getInstance(String type, Provider provider) - throws KeyStoreException + throws KeyStoreException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); + Throwable cause; try { - return new KeyStore( - (KeyStoreSpi) Engine.getInstance(KEY_STORE, type, provider), - provider, type); + Object spi = Engine.getInstance(KEY_STORE, type, provider); + return new KeyStore((KeyStoreSpi) spi, provider, type); } - catch (NoSuchAlgorithmException nsae) + catch (NoSuchAlgorithmException x) { - throw new KeyStoreException(type); + cause = x; } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new KeyStoreException(type); + cause = x.getCause() != null ? x.getCause() : x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new KeyStoreException(type); + cause = x; } + KeyStoreException x = new KeyStoreException(type); + x.initCause(cause); + throw x; } /** diff --git a/java/security/MessageDigest.java b/java/security/MessageDigest.java index 8a5de92df..0f8e934e5 100644 --- a/java/security/MessageDigest.java +++ b/java/security/MessageDigest.java @@ -40,6 +40,8 @@ package java.security; import gnu.java.security.Engine; import java.nio.ByteBuffer; +import java.lang.reflect.InvocationTargetException; + /** * Message digests are secure one-way hash functions that take arbitrary-sized * data and output a fixed-length hash value. @@ -73,28 +75,29 @@ public abstract class MessageDigest extends MessageDigestSpi * Returns a new instance of <code>MessageDigest</code> representing the * specified algorithm. * - * @param algorithm - * the name of the digest algorithm to use. + * @param algorithm the name of the digest algorithm to use. * @return a new instance representing the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by any provider. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by any + * provider. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static MessageDigest getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(algorithm, p[i]); - } - catch (NoSuchAlgorithmException ignored) - { - // Ignore. - } - } - + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } @@ -102,29 +105,26 @@ public abstract class MessageDigest extends MessageDigestSpi * Returns a new instance of <code>MessageDigest</code> representing the * specified algorithm from a named provider. * - * @param algorithm - * the name of the digest algorithm to use. - * @param provider - * the name of the provider to use. + * @param algorithm the name of the digest algorithm to use. + * @param provider the name of the provider to use. * @return a new instance representing the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the named provider. - * @throws NoSuchProviderException - * if the named provider was not found. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * named provider. + * @throws NoSuchProviderException if the named provider was not found. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static MessageDigest getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { - if (provider != null) - provider = provider.trim(); - - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } @@ -132,39 +132,43 @@ public abstract class MessageDigest extends MessageDigestSpi * Returns a new instance of <code>MessageDigest</code> representing the * specified algorithm from a designated {@link Provider}. * - * @param algorithm - * the name of the digest algorithm to use. - * @param provider - * the {@link Provider} to use. + * @param algorithm the name of the digest algorithm to use. + * @param provider the {@link Provider} to use. * @return a new instance representing the desired algorithm. - * @throws IllegalArgumentException - * if <code>provider</code> is <code>null</code>. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by {@link Provider}. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by + * {@link Provider}. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. * @since 1.4 * @see Provider */ public static MessageDigest getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); - - MessageDigest result = null; - Object o = null; + StringBuilder sb = new StringBuilder("MessageDigest for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] "); + Object o; try { o = Engine.getInstance(MESSAGE_DIGEST, algorithm, provider); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + Throwable cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; + sb.append("could not be created"); + NoSuchAlgorithmException y = new NoSuchAlgorithmException(sb.toString()); + y.initCause(cause); + throw y; } - + MessageDigest result; if (o instanceof MessageDigestSpi) - { - result = new DummyMessageDigest((MessageDigestSpi) o, algorithm); - } + result = new DummyMessageDigest((MessageDigestSpi) o, algorithm); else if (o instanceof MessageDigest) { result = (MessageDigest) o; @@ -172,7 +176,8 @@ public abstract class MessageDigest extends MessageDigestSpi } else { - throw new NoSuchAlgorithmException(algorithm); + sb.append("is of an unexpected Type: ").append(o.getClass().getName()); + throw new NoSuchAlgorithmException(sb.toString()); } result.provider = provider; return result; diff --git a/java/security/SecureClassLoader.java b/java/security/SecureClassLoader.java index bcc3cab88..dfc1758b5 100644 --- a/java/security/SecureClassLoader.java +++ b/java/security/SecureClassLoader.java @@ -1,5 +1,5 @@ /* SecureClassLoader.java --- A Secure Class Loader - Copyright (C) 1999, 2004 Free Software Foundation, Inc. + Copyright (C) 1999, 2004, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -39,6 +39,9 @@ package java.security; import java.util.WeakHashMap; +import java.nio.ByteBuffer; +import java.util.HashMap; + /** * A Secure Class Loader for loading classes with additional * support for specifying code source and permissions when @@ -50,22 +53,16 @@ import java.util.WeakHashMap; */ public class SecureClassLoader extends ClassLoader { - WeakHashMap<CodeSource, ProtectionDomain> protectionDomainCache - = new WeakHashMap<CodeSource, ProtectionDomain>(); + private final HashMap<CodeSource,ProtectionDomain> protectionDomainCache + = new HashMap<CodeSource, ProtectionDomain>(); protected SecureClassLoader(ClassLoader parent) { super(parent); - SecurityManager sm = System.getSecurityManager(); - if(sm != null) - sm.checkCreateClassLoader(); } protected SecureClassLoader() { - SecurityManager sm = System.getSecurityManager(); - if(sm != null) - sm.checkCreateClassLoader(); } /** @@ -85,10 +82,35 @@ public class SecureClassLoader extends ClassLoader protected final Class<?> defineClass(String name, byte[] b, int off, int len, CodeSource cs) { + return super.defineClass(name, b, off, len, getProtectionDomain(cs)); + } + + /** + * Creates a class using an ByteBuffer and a + * CodeSource. + * + * @param name the name to give the class. null if unknown. + * @param b the data representing the classfile, in classfile format. + * @param cs the CodeSource for the class or null when unknown. + * + * @return the class that was defined and optional CodeSource. + * + * @exception ClassFormatError if the byte array is not in proper classfile format. + * + * @since 1.5 + */ + protected final Class defineClass(String name, ByteBuffer b, CodeSource cs) + { + return super.defineClass(name, b, getProtectionDomain(cs)); + } + + /* Lookup or create a protection domain for the CodeSource, + * if CodeSource is null it will return null. */ + private ProtectionDomain getProtectionDomain(CodeSource cs) + { + ProtectionDomain protectionDomain = null; if (cs != null) { - ProtectionDomain protectionDomain; - synchronized (protectionDomainCache) { protectionDomain = (ProtectionDomain)protectionDomainCache.get(cs); @@ -108,10 +130,8 @@ public class SecureClassLoader extends ClassLoader protectionDomain = domain; } } - return super.defineClass(name, b, off, len, protectionDomain); - } - else - return super.defineClass(name, b, off, len); + } + return protectionDomain; } /** @@ -120,7 +140,7 @@ public class SecureClassLoader extends ClassLoader * java.security.Policy.getPermissions. * * This method is called by defineClass that takes a CodeSource - * arguement to build a proper ProtectionDomain for the class + * argument to build a proper ProtectionDomain for the class * being defined. */ protected PermissionCollection getPermissions(CodeSource cs) diff --git a/java/security/SecureRandom.java b/java/security/SecureRandom.java index c66963e8f..005f4670e 100644 --- a/java/security/SecureRandom.java +++ b/java/security/SecureRandom.java @@ -45,6 +45,7 @@ import gnu.java.security.jce.prng.Sha160RandomSpi; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; @@ -187,101 +188,106 @@ public class SecureRandom extends Random this.algorithm = algorithm; } - // Class methods. - // ------------------------------------------------------------------------ - /** - * Returns an instance of a SecureRandom. It creates the class from - * the first provider that implements it. - * + * Returns an instance of a <code>SecureRandom</code> from the first provider + * that implements it. + * * @param algorithm The algorithm name. - * @return A new SecureRandom implementing the given algorithm. - * @throws NoSuchAlgorithmException If no installed provider implements - * the given algorithm. + * @return A new <code>SecureRandom</code> implementing the given algorithm. + * @throws NoSuchAlgorithmException If no installed provider implements the + * given algorithm. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static SecureRandom getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); - + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(algorithm, p[i]); - } - catch (NoSuchAlgorithmException e) - { - // Ignore. - } - } - - // None found. + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } /** - * Returns an instance of a SecureRandom. It creates the class - * for the specified algorithm from the named provider. - * + * Returns an instance of a <code>SecureRandom</code> for the specified + * algorithm from the named provider. + * * @param algorithm The algorithm name. - * @param provider The provider name. - * @return A new SecureRandom implementing the chosen algorithm. + * @param provider The provider name. + * @return A new <code>SecureRandom</code> implementing the chosen + * algorithm. * @throws NoSuchAlgorithmException If the named provider does not implement - * the algorithm, or if the implementation cannot be - * instantiated. - * @throws NoSuchProviderException If no provider named - * <code>provider</code> is currently installed. - * @throws IllegalArgumentException If <code>provider</code> is null - * or is empty. + * the algorithm, or if the implementation cannot be instantiated. + * @throws NoSuchProviderException If no provider named <code>provider</code> + * is currently installed. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static SecureRandom getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } /** - * Returns an instance of a SecureRandom. It creates the class for - * the specified algorithm from the given provider. - * - * @param algorithm The SecureRandom algorithm to create. - * @param provider The provider to get the instance from. - * @throws NoSuchAlgorithmException If the algorithm cannot be found, or - * if the class cannot be instantiated. - * @throws IllegalArgumentException If <code>provider</code> is null. + * Returns an instance of a <code>SecureRandom</code> for the specified + * algorithm from the given provider. + * + * @param algorithm The <code>SecureRandom</code> algorithm to create. + * @param provider The provider to use. + * @throws NoSuchAlgorithmException If the algorithm cannot be found, or if + * the class cannot be instantiated. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static SecureRandom getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); + StringBuilder sb = new StringBuilder("SecureRandom for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new SecureRandom((SecureRandomSpi) - Engine.getInstance(SECURE_RANDOM, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(SECURE_RANDOM, algorithm, provider); + return new SecureRandom((SecureRandomSpi) spi, provider, algorithm); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ------------------------------------------------------------------------ - /** Returns the provider being used by the current SecureRandom class. diff --git a/java/security/Signature.java b/java/security/Signature.java index 68ae99d42..1245707f7 100644 --- a/java/security/Signature.java +++ b/java/security/Signature.java @@ -40,6 +40,7 @@ package java.security; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.nio.ByteBuffer; import java.security.cert.Certificate; import java.security.cert.X509Certificate; @@ -128,28 +129,29 @@ public abstract class Signature extends SignatureSpi * Returns an instance of <code>Signature</code> representing the specified * signature. * - * @param algorithm - * the algorithm to use. + * @param algorithm the algorithm to use. * @return a new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by any provider. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by any + * provider. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static Signature getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(algorithm, p[i]); - } - catch (NoSuchAlgorithmException e) - { - // Ignored. - } - } - + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } @@ -157,28 +159,26 @@ public abstract class Signature extends SignatureSpi * Returns an instance of <code>Signature</code> representing the specified * signature from the named provider. * - * @param algorithm - * the algorithm to use. - * @param provider - * the name of the provider to use. + * @param algorithm the algorithm to use. + * @param provider the name of the provider to use. * @return a new instance repesenting the desired algorithm. - * @throws IllegalArgumentException if <code>provider</code> is - * <code>null</code> or is an empty string. - * @throws NoSuchProviderException - * if the named provider was not found. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the named provider. + * @throws NoSuchProviderException if the named provider was not found. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * named provider. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code> or empty. */ public static Signature getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { - if (provider == null || provider.length() == 0) - throw new IllegalArgumentException("Illegal provider"); - + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); + provider = provider.trim(); + if (provider.length() == 0) + throw new IllegalArgumentException("provider MUST NOT be empty"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } @@ -186,35 +186,41 @@ public abstract class Signature extends SignatureSpi * Returns an instance of <code>Signature</code> representing the specified * signature from the specified {@link Provider}. * - * @param algorithm - * the algorithm to use. - * @param provider - * the {@link Provider} to use. + * @param algorithm the algorithm to use. + * @param provider the {@link Provider} to use. * @return a new instance repesenting the desired algorithm. - * @throws NoSuchAlgorithmException - * if the algorithm is not implemented by the {@link Provider}. + * @throws NoSuchAlgorithmException if the algorithm is not implemented by the + * {@link Provider}. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static Signature getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("Illegal provider"); - - Signature result = null; - Object o = null; + StringBuilder sb = new StringBuilder("Signature algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] "); + Object o; try { o = Engine.getInstance(SIGNATURE, algorithm, provider); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + Throwable cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; + sb.append("could not be created"); + NoSuchAlgorithmException y = new NoSuchAlgorithmException(sb.toString()); + y.initCause(cause); + throw y; } - + Signature result; if (o instanceof SignatureSpi) - { - result = new DummySignature((SignatureSpi) o, algorithm); - } + result = new DummySignature((SignatureSpi) o, algorithm); else if (o instanceof Signature) { result = (Signature) o; @@ -222,7 +228,8 @@ public abstract class Signature extends SignatureSpi } else { - throw new NoSuchAlgorithmException(algorithm); + sb.append("is of an unexpected Type: ").append(o.getClass().getName()); + throw new NoSuchAlgorithmException(sb.toString()); } result.provider = provider; return result; diff --git a/java/security/cert/CertPathBuilder.java b/java/security/cert/CertPathBuilder.java index f6965205f..519ed2b6c 100644 --- a/java/security/cert/CertPathBuilder.java +++ b/java/security/cert/CertPathBuilder.java @@ -40,6 +40,7 @@ package java.security.cert; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; @@ -111,50 +112,54 @@ public class CertPathBuilder } /** - * Get an instance of a named CertPathBuilder, from the first provider - * that implements it. - * - * @param algorithm The name of the CertPathBuilder to create. + * Returns an instance of a named <code>CertPathBuilder</code> from the + * first provider that implements it. + * + * @param algorithm The name of the <code>CertPathBuilder</code> to create. * @return The new instance. - * @throws NoSuchAlgorithmException If no installed provider - * implements the named algorithm. + * @throws NoSuchAlgorithmException If no installed provider implements the + * named algorithm. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static CertPathBuilder getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); - + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(algorithm, p[i]); - } - catch (NoSuchAlgorithmException e) - { - // Ignored. - } - } - + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } /** - * Get an instance of a named CertPathBuilder from the named + * Returns an instance of a named <code>CertPathBuilder</code> from a named * provider. - * - * @param algorithm The name of the CertPathBuilder to create. - * @param provider The name of the provider from which to get the - * implementation. + * + * @param algorithm The name of the <code>CertPathBuilder</code> to create. + * @param provider The name of the provider to use. * @return The new instance. - * @throws NoSuchAlgorithmException If no installed provider - * implements the named algorithm. - * @throws NoSuchProviderException If the named provider does not - * exist. + * @throws NoSuchAlgorithmException If no installed provider implements the + * named algorithm. + * @throws NoSuchProviderException If the named provider does not exist. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static CertPathBuilder getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); @@ -162,41 +167,47 @@ public class CertPathBuilder } /** - * Get an instance of a named CertPathBuilder from the specified - * provider. - * - * @param algorithm The name of the CertPathBuilder to create. - * @param provider The provider from which to get the implementation. + * Returns an instance of a named <code>CertPathBuilder</code> from the + * specified provider. + * + * @param algorithm The name of the <code>CertPathBuilder</code> to create. + * @param provider The provider to use. * @return The new instance. - * @throws NoSuchAlgorithmException If no installed provider - * implements the named algorithm. - * @throws IllegalArgumentException If <i>provider</i> in - * <tt>null</tt>. + * @throws NoSuchAlgorithmException If no installed provider implements the + * named algorithm. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static CertPathBuilder getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("null provider"); + StringBuilder sb = new StringBuilder("CertPathBuilder for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new CertPathBuilder((CertPathBuilderSpi) - Engine.getInstance(CERT_PATH_BUILDER, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(CERT_PATH_BUILDER, algorithm, provider); + return new CertPathBuilder((CertPathBuilderSpi) spi, provider, algorithm); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ------------------------------------------------------------------------ - /** * Return the name of this CertPathBuilder algorithm. * diff --git a/java/security/cert/CertPathValidator.java b/java/security/cert/CertPathValidator.java index 5fed19e9a..bf7c9746e 100644 --- a/java/security/cert/CertPathValidator.java +++ b/java/security/cert/CertPathValidator.java @@ -40,6 +40,7 @@ package java.security.cert; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.security.AccessController; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; @@ -124,91 +125,103 @@ public class CertPathValidator { } /** - * Get an instance of the given validator from the first provider that + * Returns an instance of the given validator from the first provider that * implements it. - * + * * @param algorithm The name of the algorithm to get. * @return The new instance. - * @throws NoSuchAlgorithmException If no installed provider - * implements the requested algorithm. + * @throws NoSuchAlgorithmException If no installed provider implements the + * requested algorithm. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static CertPathValidator getInstance(String algorithm) throws NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(algorithm, p[i]); - } - catch (NoSuchAlgorithmException e) - { - // Ignored. - } - } + try + { + return getInstance(algorithm, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(algorithm); } /** - * Get an instance of the given validator from the named provider. - * + * Returns an instance of the given validator from the named provider. + * * @param algorithm The name of the algorithm to get. - * @param provider The name of the provider from which to get the - * implementation. + * @param provider The name of the provider from which to get the + * implementation. * @return The new instance. - * @throws NoSuchAlgorithmException If the named provider does not - * implement the algorithm. - * @throws NoSuchProviderException If no provider named - * <i>provider</i> is installed. + * @throws NoSuchAlgorithmException If the named provider does not implement + * the algorithm. + * @throws NoSuchProviderException If no provider named <i>provider</i> is + * installed. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ - public static CertPathValidator getInstance(String algorithm, - String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + public static CertPathValidator getInstance(String algorithm, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); - return getInstance(algorithm, p); } /** - * Get an instance of the given validator from the given provider. - * + * Returns an instance of the given validator from the given provider. + * * @param algorithm The name of the algorithm to get. - * @param provider The provider from which to get the implementation. + * @param provider The provider from which to get the implementation. * @return The new instance. - * @throws NoSuchAlgorithmException If the provider does not implement - * the algorithm. - * @throws IllegalArgumentException If <i>provider</i> is null. + * @throws NoSuchAlgorithmException If the provider does not implement the + * algorithm. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static CertPathValidator getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("null provider"); - + StringBuilder sb = new StringBuilder("CertPathValidator for algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new CertPathValidator((CertPathValidatorSpi) - Engine.getInstance(CERT_PATH_VALIDATOR, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(CERT_PATH_VALIDATOR, algorithm, provider); + return new CertPathValidator((CertPathValidatorSpi) spi, provider, algorithm); } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ------------------------------------------------------------------------ - /** * Return the name of this validator. * diff --git a/java/security/cert/CertStore.java b/java/security/cert/CertStore.java index 2d5d944db..a27086562 100644 --- a/java/security/cert/CertStore.java +++ b/java/security/cert/CertStore.java @@ -40,6 +40,7 @@ package java.security.cert; import gnu.java.security.Engine; +import java.lang.reflect.InvocationTargetException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; @@ -123,59 +124,63 @@ public class CertStore } /** - * Get an instance of the given certificate store from the first + * Returns an instance of the given certificate store type from the first * installed provider. - * - * @param type The type of CertStore to create. - * @param params The parameters to initialize this cert store with. + * + * @param type The type of <code>CertStore</code> to create. + * @param params The parameters to initialize this cert store with. * @return The new instance. - * @throws InvalidAlgorithmParameterException If the instance rejects - * the specified parameters. - * @throws NoSuchAlgorithmException If no installed provider - * implements the specified CertStore. - * @throws IllegalArgumentException If <i>provider</i> is null. + * @throws InvalidAlgorithmParameterException If the instance rejects the + * specified parameters. + * @throws NoSuchAlgorithmException If no installed provider implements the + * specified CertStore. + * @throws IllegalArgumentException if <code>type</code> is + * <code>null</code> or is an empty string. */ public static CertStore getInstance(String type, CertStoreParameters params) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(type, params, p[i]); - } - catch (NoSuchAlgorithmException e) - { - // Ignored. - } - } - + try + { + return getInstance(type, params, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(type); } /** - * Get an instance of the given certificate store from the named + * Returns an instance of the given certificate store type from a named * provider. - * - * @param type The type of CertStore to create. - * @param params The parameters to initialize this cert store with. - * @param provider The name of the provider from which to get the - * implementation. + * + * @param type The type of <code>CertStore</code> to create. + * @param params The parameters to initialize this cert store with. + * @param provider The name of the provider to use. * @return The new instance. - * @throws InvalidAlgorithmParameterException If the instance rejects - * the specified parameters. + * @throws InvalidAlgorithmParameterException If the instance rejects the + * specified parameters. * @throws NoSuchAlgorithmException If the specified provider does not - * implement the specified CertStore. - * @throws NoSuchProviderException If no provider named - * <i>provider</i> is installed. - * @throws IllegalArgumentException If <i>provider</i> is null. + * implement the specified CertStore. + * @throws NoSuchProviderException If no provider named <i>provider</i> is + * installed. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>type</code> is an empty string. */ public static CertStore getInstance(String type, CertStoreParameters params, String provider) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); @@ -183,48 +188,52 @@ public class CertStore } /** - * Get an instance of the given certificate store from the given + * Returns an instance of the given certificate store type from a given * provider. * - * @param type The type of CertStore to create. + * @param type The type of <code>CertStore</code> to create. * @param params The parameters to initialize this cert store with. - * @param provider The provider from which to get the implementation. + * @param provider The provider to use. * @return The new instance. * @throws InvalidAlgorithmParameterException If the instance rejects * the specified parameters. * @throws NoSuchAlgorithmException If the specified provider does not * implement the specified CertStore. - * @throws IllegalArgumentException If <i>provider</i> is null. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>type</code> is an empty string. */ public static CertStore getInstance(String type, CertStoreParameters params, Provider provider) - throws InvalidAlgorithmParameterException, NoSuchAlgorithmException + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("null provider"); - + StringBuilder sb = new StringBuilder("CertStore of type [") + .append(type).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new CertStore((CertStoreSpi) Engine.getInstance(CERT_STORE, - type, provider, new Object[] { params }), provider, type, params); + Object[] args = new Object[] { params }; + Object spi = Engine.getInstance(CERT_STORE, type, provider, args); + return new CertStore((CertStoreSpi) spi, provider, type, params); } - catch (ClassCastException cce) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(type); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (java.lang.reflect.InvocationTargetException ite) + catch (ClassCastException x) { - Throwable cause = ite.getCause(); - if (cause instanceof InvalidAlgorithmParameterException) - throw (InvalidAlgorithmParameterException) cause; - else - throw new NoSuchAlgorithmException(type); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } -// Instance methods. - // ------------------------------------------------------------------------ - /** * Return the type of certificate store this instance represents. * diff --git a/java/security/cert/CertificateFactory.java b/java/security/cert/CertificateFactory.java index 363c750cc..8139c6ec5 100644 --- a/java/security/cert/CertificateFactory.java +++ b/java/security/cert/CertificateFactory.java @@ -41,6 +41,8 @@ package java.security.cert; import gnu.java.security.Engine; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Provider; @@ -84,106 +86,102 @@ public class CertificateFactory this.type = type; } -// Class methods. - // ------------------------------------------------------------------------ - - /** - * Gets an instance of the CertificateFactory class representing - * the specified certificate factory. If the type is not - * found then, it throws CertificateException. - * - * @param type The type of certificate factory to create. - * @return a CertificateFactory repesenting the desired type - * @throws CertificateException If the type of certificate is not - * implemented by any installed provider. + /** + * Returns an instance of a <code>CertificateFactory</code> representing the + * specified certificate factory type. + * + * @param type The type of certificate factory to create. + * @return A <code>CertificateFactory</code> of the desired type. + * @throws CertificateException If the type of certificate factory is not + * implemented by any installed provider. + * @throws IllegalArgumentException if <code>type</code> is + * <code>null</code> or is an empty string. */ public static final CertificateFactory getInstance(String type) - throws CertificateException + throws CertificateException { Provider[] p = Security.getProviders(); - + CertificateException lastException = null; for (int i = 0; i < p.length; i++) - { - try - { - return getInstance(type, p[i]); - } - catch (CertificateException e) - { - // Ignored. - } - } - + try + { + return getInstance(type, p[i]); + } + catch (CertificateException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new CertificateException(type); } - /** - * Gets an instance of the CertificateFactory class representing - * the specified certificate factory from the specified provider. - * If the type is not found then, it throws {@link CertificateException}. - * If the provider is not found, then it throws - * {@link java.security.NoSuchProviderException}. - * - * @param type The type of certificate factory to create. - * @param provider The name of the provider from which to get the - * implementation. - * @return A CertificateFactory for the desired type. - * @throws CertificateException If the type of certificate is not - * implemented by the named provider. + /** + * Returns an instance of a <code>CertificateFactory</code> representing the + * specified certificate factory type from the named provider. + * + * @param type The type of certificate factory to create. + * @param provider The name of the provider to use. + * @return A <code>CertificateFactory</code> for the desired type. + * @throws CertificateException If the type of certificate is not implemented + * by the named provider. * @throws NoSuchProviderException If the named provider is not installed. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>type</code> is an empty string. */ public static final CertificateFactory getInstance(String type, String provider) throws CertificateException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); - if( p == null) + if (p == null) throw new NoSuchProviderException(provider); - return getInstance(type, p); } /** - * Get a certificate factory for the given certificate type from the - * given provider. - * - * @param type The type of certificate factory to create. + * Returns an instance of a <code>CertificateFactory</code> representing the + * specified certificate factory type from the designated provider. + * + * @param type The type of certificate factory to create. * @param provider The provider from which to get the implementation. - * @return A CertificateFactory for the desired type. - * @throws CertificateException If the type of certificate is not - * implemented by the provider. - * @throws IllegalArgumentException If the provider is null. + * @return A <code>CertificateFactory</code> for the desired type. + * @throws CertificateException If the type of certificate is not implemented + * by the provider. + * @throws IllegalArgumentException if either <code>type</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>type</code> is an empty string. */ public static final CertificateFactory getInstance(String type, Provider provider) - throws CertificateException + throws CertificateException { - if (provider == null) - throw new IllegalArgumentException("null provider"); - + Throwable cause; try { - return new CertificateFactory((CertificateFactorySpi) - Engine.getInstance(CERTIFICATE_FACTORY, type, provider), - provider, type); + Object spi = Engine.getInstance(CERTIFICATE_FACTORY, type, provider); + return new CertificateFactory((CertificateFactorySpi) spi, provider, type); } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new CertificateException(type); + cause = x; } - catch (java.lang.reflect.InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new CertificateException(type); + cause = x.getCause() != null ? x.getCause() : x; } - catch (NoSuchAlgorithmException nsae) + catch (NoSuchAlgorithmException x) { - throw new CertificateException(nsae.getMessage()); + cause = x; } + CertificateException x = new CertificateException(type); + x.initCause(cause); + throw x; } -// Instance methods. - // ------------------------------------------------------------------------ - /** * Gets the provider of this implementation. * diff --git a/java/util/Calendar.java b/java/util/Calendar.java index 6865230c8..8c46c0193 100644 --- a/java/util/Calendar.java +++ b/java/util/Calendar.java @@ -1,5 +1,6 @@ /* Calendar.java -- - Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005 Free Software Foundation, Inc. + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006, + Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -484,6 +485,8 @@ public abstract class Calendar /** * Creates a calendar representing the actual time, using the default * time zone and locale. + * + * @return The new calendar. */ public static synchronized Calendar getInstance() { @@ -493,7 +496,12 @@ public abstract class Calendar /** * Creates a calendar representing the actual time, using the given * time zone and the default locale. - * @param zone a time zone. + * + * @param zone a time zone (<code>null</code> not permitted). + * + * @return The new calendar. + * + * @throws NullPointerException if <code>zone</code> is <code>null</code>. */ public static synchronized Calendar getInstance(TimeZone zone) { @@ -503,7 +511,12 @@ public abstract class Calendar /** * Creates a calendar representing the actual time, using the default * time zone and the given locale. - * @param locale a locale. + * + * @param locale a locale (<code>null</code> not permitted). + * + * @return The new calendar. + * + * @throws NullPointerException if <code>locale</code> is <code>null</code>. */ public static synchronized Calendar getInstance(Locale locale) { @@ -525,8 +538,14 @@ public abstract class Calendar /** * Creates a calendar representing the actual time, using the given * time zone and locale. - * @param zone a time zone. - * @param locale a locale. + * + * @param zone a time zone (<code>null</code> not permitted). + * @param locale a locale (<code>null</code> not permitted). + * + * @return The new calendar. + * + * @throws NullPointerException if <code>zone</code> or <code>locale</code> + * is <code>null</code>. */ public static synchronized Calendar getInstance(TimeZone zone, Locale locale) { @@ -618,6 +637,10 @@ public abstract class Calendar /** * Sets this Calendar's time to the given Date. All time fields * are invalidated by this method. + * + * @param date the date (<code>null</code> not permitted). + * + * @throws NullPointerException if <code>date</code> is <code>null</code>. */ public final void setTime(Date date) { diff --git a/java/util/Locale.java b/java/util/Locale.java index d2aead43c..001c7afde 100644 --- a/java/util/Locale.java +++ b/java/util/Locale.java @@ -188,11 +188,17 @@ public final class Locale implements Serializable, Cloneable private String variant; /** - * This is the cached hashcode. When writing to stream, we write -1. + * This is where the JDK caches its hashcode. This is is only here + * for serialization purposes. The actual cache is hashcodeCache * * @serial should be -1 in serial streams */ - private transient int hashcode; + private int hashcode = -1; + + /** + * This is the cached hashcode. + */ + private transient int hashcodeCache; /** * Array storing all available locales. @@ -324,7 +330,7 @@ public final class Locale implements Serializable, Cloneable this.language = language; this.country = country; this.variant = variant; - hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); + hashcodeCache = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); } /** @@ -899,7 +905,7 @@ public final class Locale implements Serializable, Cloneable */ public int hashCode() { - return hashcode; + return hashcodeCache; } /** @@ -917,32 +923,12 @@ public final class Locale implements Serializable, Cloneable return false; Locale l = (Locale) obj; - return (language == l.language - && country == l.country + return (language == l.language + && country == l.country && variant == l.variant); } /** - * Write the locale to an object stream. - * - * @param s the stream to write to - * @throws IOException if the write fails - * @serialData The first three fields are Strings representing language, - * country, and variant. The fourth field is a placeholder for - * the cached hashcode, but this is always written as -1, and - * recomputed when reading it back. - */ - private void writeObject(ObjectOutputStream s) - throws IOException - { - s.writeObject(language); - s.writeObject(country); - s.writeObject(variant); - // Hashcode field is always written as -1. - s.writeInt(-1); - } - - /** * Reads a locale from the input stream. * * @param s the stream to read from @@ -953,10 +939,10 @@ public final class Locale implements Serializable, Cloneable private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException { - language = ((String) s.readObject()).intern(); - country = ((String) s.readObject()).intern(); - variant = ((String) s.readObject()).intern(); - // Recompute hashcode. - hashcode = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); + s.defaultReadObject(); + language = language.intern(); + country = country.intern(); + variant = variant.intern(); + hashcodeCache = language.hashCode() ^ country.hashCode() ^ variant.hashCode(); } } // class Locale diff --git a/java/util/ResourceBundle.java b/java/util/ResourceBundle.java index 751d380b2..9b82bc801 100644 --- a/java/util/ResourceBundle.java +++ b/java/util/ResourceBundle.java @@ -91,6 +91,14 @@ baseName</pre> public abstract class ResourceBundle { /** + * Maximum size of our cache of <code>ResourceBundle</code>s keyed by + * {@link BundleKey} instances. + * + * @see BundleKey + */ + private static final int CACHE_SIZE = 100; + + /** * The parent bundle. This is consulted when you call getObject and there * is no such resource in the current bundle. This field may be null. */ @@ -104,21 +112,22 @@ public abstract class ResourceBundle private Locale locale; /** - * The resource bundle cache. - */ - private static Map bundleCache; - - /** - * The last default Locale we saw. If this ever changes then we have to - * reset our caches. - */ - private static Locale lastDefaultLocale; - - /** - * The `empty' locale is created once in order to optimize - * tryBundle(). + * A VM-wide cache of resource bundles already fetched. + * <p> + * This {@link Map} is a Least Recently Used (LRU) cache, of the last + * {@link #CACHE_SIZE} accessed <code>ResourceBundle</code>s keyed by the + * tuple: default locale, resource-bundle name, resource-bundle locale, and + * classloader. + * + * @see BundleKey */ - private static final Locale emptyLocale = new Locale(""); + private static Map bundleCache = new LinkedHashMap(CACHE_SIZE + 1, 0.75F, true) + { + public boolean removeEldestEntry(Map.Entry entry) + { + return size() > CACHE_SIZE; + } + }; /** * The constructor. It does nothing special. @@ -246,6 +255,7 @@ public abstract class ResourceBundle by the combination of bundle name, locale, and class loader. */ private static class BundleKey { + Locale defaultLocale; String baseName; Locale locale; ClassLoader classLoader; @@ -253,18 +263,19 @@ public abstract class ResourceBundle BundleKey() {} - BundleKey(String s, Locale l, ClassLoader cl) + BundleKey(Locale dl, String s, Locale l, ClassLoader cl) { - set(s, l, cl); + set(dl, s, l, cl); } - void set(String s, Locale l, ClassLoader cl) + void set(Locale dl, String s, Locale l, ClassLoader cl) { + defaultLocale = dl; baseName = s; locale = l; classLoader = cl; - hashcode = baseName.hashCode() ^ locale.hashCode() ^ - classLoader.hashCode(); + hashcode = defaultLocale.hashCode() ^ baseName.hashCode() + ^ locale.hashCode() ^ classLoader.hashCode(); } public int hashCode() @@ -277,10 +288,11 @@ public abstract class ResourceBundle if (! (o instanceof BundleKey)) return false; BundleKey key = (BundleKey) o; - return hashcode == key.hashcode && - baseName.equals(key.baseName) && - locale.equals(key.locale) && - classLoader.equals(key.classLoader); + return hashcode == key.hashcode + && defaultLocale.equals(key.defaultLocale) + && baseName.equals(key.baseName) + && locale.equals(key.locale) + && classLoader.equals(key.classLoader); } } @@ -370,61 +382,39 @@ public abstract class ResourceBundle public static synchronized ResourceBundle getBundle (String baseName, Locale locale, ClassLoader classLoader) { - // If the default locale changed since the last time we were called, - // all cache entries are invalidated. Locale defaultLocale = Locale.getDefault(); - if (defaultLocale != lastDefaultLocale) - { - bundleCache = new HashMap(); - lastDefaultLocale = defaultLocale; - } - // This will throw NullPointerException if any arguments are null. - lookupKey.set(baseName, locale, classLoader); - + lookupKey.set(defaultLocale, baseName, locale, classLoader); Object obj = bundleCache.get(lookupKey); - ResourceBundle rb = null; - if (obj instanceof ResourceBundle) + return (ResourceBundle) obj; + + if (obj == nullEntry) + throw new MissingResourceException("Bundle " + baseName + + " not found for locale " + locale + + " by classloader " + classLoader, + baseName, ""); + // First, look for a bundle for the specified locale. We don't want + // the base bundle this time. + boolean wantBase = locale.equals(defaultLocale); + ResourceBundle bundle = tryBundle(baseName, locale, classLoader, wantBase); + // Try the default locale if neccessary. + if (bundle == null && ! wantBase) + bundle = tryBundle(baseName, defaultLocale, classLoader, true); + + BundleKey key = new BundleKey(defaultLocale, baseName, locale, classLoader); + if (bundle == null) { - return (ResourceBundle) obj; - } - else if (obj == nullEntry) - { - // Lookup has failed previously. Fall through. + // Cache the fact that this lookup has previously failed. + bundleCache.put(key, nullEntry); + throw new MissingResourceException("Bundle " + baseName + + " not found for locale " + locale + + " by classloader " + classLoader, + baseName, ""); } - else - { - // First, look for a bundle for the specified locale. We don't want - // the base bundle this time. - boolean wantBase = locale.equals(defaultLocale); - ResourceBundle bundle = tryBundle(baseName, locale, classLoader, - wantBase); - - // Try the default locale if neccessary. - if (bundle == null && !locale.equals(defaultLocale)) - bundle = tryBundle(baseName, defaultLocale, classLoader, true); - - BundleKey key = new BundleKey(baseName, locale, classLoader); - if (bundle == null) - { - // Cache the fact that this lookup has previously failed. - bundleCache.put(key, nullEntry); - } - else - { - // Cache the result and return it. - bundleCache.put(key, bundle); - return bundle; - } - } - - throw new MissingResourceException("Bundle " + baseName - + " not found for locale " - + locale - + " by classloader " - + classLoader, - baseName, ""); + // Cache the result and return it. + bundleCache.put(key, bundle); + return bundle; } /** diff --git a/java/util/Vector.java b/java/util/Vector.java index 02eb1539a..ea29ce093 100644 --- a/java/util/Vector.java +++ b/java/util/Vector.java @@ -720,8 +720,10 @@ public class Vector<T> extends AbstractList<T> */ public synchronized boolean removeAll(Collection<?> c) { - if (c == null) - throw new NullPointerException(); + // The NullPointerException is thrown implicitly when the Vector + // is not empty and c is null. The RI allows null arguments when + // the vector is empty. See Mauve test: + // gnu/testlet/java/util/Vector/removeAll.java int i; int j; @@ -749,8 +751,10 @@ public class Vector<T> extends AbstractList<T> */ public synchronized boolean retainAll(Collection<?> c) { - if (c == null) - throw new NullPointerException(); + // The NullPointerException is thrown implicitly when the Vector + // is not empty and c is null. The RI allows null arguments when + // the vector is empty. See Mauve test: + // gnu/testlet/java/util/Vector/retainAll.java int i; int j; diff --git a/java/util/regex/Matcher.java b/java/util/regex/Matcher.java index 25e73810e..3ddd42547 100644 --- a/java/util/regex/Matcher.java +++ b/java/util/regex/Matcher.java @@ -218,7 +218,7 @@ public final class Matcher implements MatchResult public boolean lookingAt () { - match = pattern.getRE().getMatch(inputCharIndexed, 0); + match = pattern.getRE().getMatch(inputCharIndexed, 0, RE.REG_FIX_STARTING_POSITION, null); if (match != null) { if (match.getStartIndex() == 0) @@ -243,7 +243,7 @@ public final class Matcher implements MatchResult */ public boolean matches () { - match = pattern.getRE().getMatch(inputCharIndexed, 0, RE.REG_TRY_ENTIRE_MATCH); + match = pattern.getRE().getMatch(inputCharIndexed, 0, RE.REG_TRY_ENTIRE_MATCH|RE.REG_FIX_STARTING_POSITION, null); if (match != null) { if (match.getStartIndex() == 0) @@ -309,6 +309,28 @@ public final class Matcher implements MatchResult return match.getStartIndex(group); } + /** + * @return True if and only if the matcher hit the end of input. + */ + public boolean hitEnd() + { + return inputCharIndexed.hitEnd(); + } + + /** + * @return A string expression of this matcher. + */ + public String toString() + { + StringBuilder sb = new StringBuilder(); + sb.append(this.getClass().getName()) + .append("[pattern=").append(pattern.pattern()) + .append(" region=").append("0").append(",").append(input.length()) + .append(" lastmatch=").append(match == null ? "" : match.toString()) + .append("]"); + return sb.toString(); + } + private void assertMatchOp() { if (match == null) throw new IllegalStateException(); diff --git a/java/util/zip/ZipFile.java b/java/util/zip/ZipFile.java index 00ba19b15..3b34bd1f5 100644 --- a/java/util/zip/ZipFile.java +++ b/java/util/zip/ZipFile.java @@ -48,6 +48,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedHashMap; @@ -516,6 +519,16 @@ public class ZipFile implements ZipConstants private static final class PartialInputStream extends InputStream { + /** + * The UTF-8 charset use for decoding the filenames. + */ + private static final Charset UTF8CHARSET = Charset.forName("UTF-8"); + + /** + * The actual UTF-8 decoder. Created on demand. + */ + private CharsetDecoder utf8Decoder; + private final RandomAccessFile raf; private final byte[] buffer; private long bufferOffset; @@ -652,23 +665,86 @@ public class ZipFile implements ZipConstants int readLeShort() throws IOException { - int b0 = read(); - int b1 = read(); - if (b1 == -1) - throw new EOFException(); - return (b0 & 0xff) | (b1 & 0xff) << 8; + int result; + if(pos + 1 < buffer.length) + { + result = ((buffer[pos + 0] & 0xff) | (buffer[pos + 1] & 0xff) << 8); + pos += 2; + } + else + { + int b0 = read(); + int b1 = read(); + if (b1 == -1) + throw new EOFException(); + result = (b0 & 0xff) | (b1 & 0xff) << 8; + } + return result; } int readLeInt() throws IOException { - int b0 = read(); - int b1 = read(); - int b2 = read(); - int b3 = read(); - if (b3 == -1) - throw new EOFException(); - return ((b0 & 0xff) | (b1 & 0xff) << 8) - | ((b2 & 0xff) | (b3 & 0xff) << 8) << 16; + int result; + if(pos + 3 < buffer.length) + { + result = (((buffer[pos + 0] & 0xff) | (buffer[pos + 1] & 0xff) << 8) + | ((buffer[pos + 2] & 0xff) + | (buffer[pos + 3] & 0xff) << 8) << 16); + pos += 4; + } + else + { + int b0 = read(); + int b1 = read(); + int b2 = read(); + int b3 = read(); + if (b3 == -1) + throw new EOFException(); + result = (((b0 & 0xff) | (b1 & 0xff) << 8) | ((b2 & 0xff) + | (b3 & 0xff) << 8) << 16); + } + return result; + } + + /** + * Decode chars from byte buffer using UTF8 encoding. This + * operation is performance-critical since a jar file contains a + * large number of strings for the name of each file in the + * archive. This routine therefore avoids using the expensive + * utf8Decoder when decoding is straightforward. + * + * @param buffer the buffer that contains the encoded character + * data + * @param pos the index in buffer of the first byte of the encoded + * data + * @param length the length of the encoded data in number of + * bytes. + * + * @return a String that contains the decoded characters. + */ + private String decodeChars(byte[] buffer, int pos, int length) + throws IOException + { + String result; + int i=length - 1; + while ((i >= 0) && (buffer[i] <= 0x7f)) + { + i--; + } + if (i < 0) + { + result = new String(buffer, 0, pos, length); + } + else + { + ByteBuffer bufferBuffer = ByteBuffer.wrap(buffer, pos, length); + if (utf8Decoder == null) + utf8Decoder = UTF8CHARSET.newDecoder(); + utf8Decoder.reset(); + char [] characters = utf8Decoder.decode(bufferBuffer).array(); + result = String.valueOf(characters); + } + return result; } String readString(int length) throws IOException @@ -676,25 +752,26 @@ public class ZipFile implements ZipConstants if (length > end - (bufferOffset + pos)) throw new EOFException(); + String result = null; try { if (buffer.length - pos >= length) { - String s = new String(buffer, pos, length, "UTF-8"); + result = decodeChars(buffer, pos, length); pos += length; - return s; } else { byte[] b = new byte[length]; readFully(b); - return new String(b, 0, length, "UTF-8"); + result = decodeChars(b, 0, length); } } catch (UnsupportedEncodingException uee) { throw new AssertionError(uee); } + return result; } public void addDummyByte() diff --git a/javax/crypto/Cipher.java b/javax/crypto/Cipher.java index 1b56a07f4..7c18e6f6a 100644 --- a/javax/crypto/Cipher.java +++ b/javax/crypto/Cipher.java @@ -157,161 +157,159 @@ public class Cipher /** Our current state (encrypting, wrapping, etc.) */ private int state; - - // Class methods. - // ------------------------------------------------------------------------ - /** - * <p>Creates a new cipher instance for the given transformation.</p> - * - * <p>The installed providers are tried in order for an - * implementation, and the first appropriate instance is returned. If - * no installed provider can provide the implementation, an - * appropriate exception is thrown.</p> - * + * Creates a new cipher instance for the given transformation. + * <p> + * The installed providers are tried in order for an implementation, and the + * first appropriate instance is returned. If no installed provider can + * provide the implementation, an appropriate exception is thrown. + * * @param transformation The transformation to create. * @return An appropriate cipher for this transformation. - * @throws java.security.NoSuchAlgorithmException If no installed - * provider can supply the appropriate cipher or mode. - * @throws javax.crypto.NoSuchPaddingException If no installed - * provider can supply the appropriate padding. + * @throws NoSuchAlgorithmException If no installed provider can supply the + * appropriate cipher or mode. + * @throws NoSuchPaddingException If no installed provider can supply the + * appropriate padding. */ public static final Cipher getInstance(String transformation) - throws NoSuchAlgorithmException, NoSuchPaddingException + throws NoSuchAlgorithmException, NoSuchPaddingException { - Provider[] providers = Security.getProviders(); - NoSuchPaddingException ex = null; - String msg = ""; - for (int i = 0; i < providers.length; i++) - { - try - { - return getInstance(transformation, providers[i]); - } - catch (NoSuchAlgorithmException nsae) - { - msg = nsae.getMessage(); - ex = null; - } - catch (NoSuchPaddingException nspe) - { - ex = nspe; - } - } - if (ex != null) - { - throw ex; - } - throw new NoSuchAlgorithmException(msg); + Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; + NoSuchPaddingException lastPaddingException = null; + for (int i = 0; i < p.length; i++) + try + { + return getInstance(transformation, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + lastPaddingException = null; + } + catch (NoSuchPaddingException x) + { + lastPaddingException = x; + } + if (lastPaddingException != null) + throw lastPaddingException; + if (lastException != null) + throw lastException; + throw new NoSuchAlgorithmException(transformation); } /** - * <p>Creates a new cipher instance for the given transformation and - * the named provider.</p> - * + * Creates a new cipher instance for the given transformation and the named + * provider. + * * @param transformation The transformation to create. - * @param provider The name of the provider to use. + * @param provider The name of the provider to use. * @return An appropriate cipher for this transformation. - * @throws java.security.NoSuchAlgorithmException If the provider cannot - * supply the appropriate cipher or mode. - * @throws java.security.NoSuchProviderException If the named provider - * is not installed. - * @throws javax.crypto.NoSuchPaddingException If the provider cannot - * supply the appropriate padding. + * @throws NoSuchAlgorithmException If the provider cannot supply the + * appropriate cipher or mode. + * @throws NoSuchProviderException If the named provider is not installed. + * @throws NoSuchPaddingException If the provider cannot supply the + * appropriate padding. + * @throws IllegalArgumentException if either <code>transformation</code> or + * <code>provider</code> is <code>null</code>. */ public static final Cipher getInstance(String transformation, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException, - NoSuchPaddingException + throws NoSuchAlgorithmException, NoSuchProviderException, + NoSuchPaddingException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) - { - throw new NoSuchProviderException(provider); - } + throw new NoSuchProviderException(provider); return getInstance(transformation, p); } /** - * Creates a new cipher instance for the given transform and the given + * Creates a new cipher instance for a given transformation from a given * provider. - * + * * @param transformation The transformation to create. - * @param provider The provider to use. + * @param provider The provider to use. * @return An appropriate cipher for this transformation. - * @throws java.security.NoSuchAlgorithmException If the given - * provider cannot supply the appropriate cipher or mode. - * @throws javax.crypto.NoSuchPaddingException If the given - * provider cannot supply the appropriate padding scheme. + * @throws NoSuchAlgorithmException If the given provider cannot supply the + * appropriate cipher or mode. + * @throws NoSuchPaddingException If the given provider cannot supply the + * appropriate padding scheme. */ - public static final Cipher getInstance(String transformation, Provider provider) - throws NoSuchAlgorithmException, NoSuchPaddingException + public static final Cipher getInstance(String transformation, + Provider provider) + throws NoSuchAlgorithmException, NoSuchPaddingException { - CipherSpi result = null; - String key = null; - String alg = null, mode = null, pad = null; - String msg = ""; + StringBuilder sb = new StringBuilder().append("Cipher transformation [") + .append(transformation).append("] from provider [") + .append(provider).append("] "); + Throwable cause; + Object spi; + CipherSpi result; if (transformation.indexOf('/') < 0) { try { - result = (CipherSpi) Engine.getInstance(SERVICE, transformation, - provider); - return new Cipher(result, provider, transformation); + spi = Engine.getInstance(SERVICE, transformation, provider); + return new Cipher((CipherSpi) spi, provider, transformation); } catch (Exception e) { - msg = e.getMessage(); + if (e instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) e; + cause = e; } } else { StringTokenizer tok = new StringTokenizer(transformation, "/"); if (tok.countTokens() != 3) - { - throw new NoSuchAlgorithmException("badly formed transformation"); - } - alg = tok.nextToken(); - mode = tok.nextToken(); - pad = tok.nextToken(); + throw new NoSuchAlgorithmException(sb.append("is malformed").toString()); + + String alg = tok.nextToken(); + String mode = tok.nextToken(); + String pad = tok.nextToken(); try { - result = (CipherSpi) Engine.getInstance(SERVICE, transformation, - provider); - return new Cipher(result, provider, transformation); + spi = Engine.getInstance(SERVICE, transformation, provider); + return new Cipher((CipherSpi) spi, provider, transformation); } catch (Exception e) { - msg = e.getMessage(); + cause = e; } + try { - result = (CipherSpi) Engine.getInstance(SERVICE, alg + '/' + mode, - provider); + spi = Engine.getInstance(SERVICE, alg + '/' + mode, provider); + result = (CipherSpi) spi; result.engineSetPadding(pad); return new Cipher(result, provider, transformation); } catch (Exception e) { if (e instanceof NoSuchPaddingException) - { - throw (NoSuchPaddingException) e; - } - msg = e.getMessage(); + throw (NoSuchPaddingException) e; + cause = e; } + try { - result = (CipherSpi) Engine.getInstance(SERVICE, alg + "//" + pad, - provider); + spi = Engine.getInstance(SERVICE, alg + "//" + pad, provider); + result = (CipherSpi) spi; result.engineSetMode(mode); return new Cipher(result, provider, transformation); } catch (Exception e) { - msg = e.getMessage(); + cause = e; } + try { - result = (CipherSpi) Engine.getInstance(SERVICE, alg, provider); + spi = Engine.getInstance(SERVICE, alg, provider); + result = (CipherSpi) spi; result.engineSetMode(mode); result.engineSetPadding(pad); return new Cipher(result, provider, transformation); @@ -319,18 +317,16 @@ public class Cipher catch (Exception e) { if (e instanceof NoSuchPaddingException) - { - throw (NoSuchPaddingException) e; - } - msg = e.getMessage(); + throw (NoSuchPaddingException) e; + cause = e; } } - throw new NoSuchAlgorithmException(transformation + ": " + msg); + sb.append("could not be created"); + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } -// Constructor. - // ------------------------------------------------------------------------ - /** * Create a cipher. * @@ -347,9 +343,6 @@ public class Cipher state = INITIAL_STATE; } -// Public instance methods. - // ------------------------------------------------------------------------ - /** * Get the name that this cipher instance was created with; this is * equivalent to the "transformation" argument given to any of the diff --git a/javax/crypto/ExemptionMechanism.java b/javax/crypto/ExemptionMechanism.java index b6cb02c63..baf6bad99 100644 --- a/javax/crypto/ExemptionMechanism.java +++ b/javax/crypto/ExemptionMechanism.java @@ -87,66 +87,111 @@ public class ExemptionMechanism virgin = true; } - // Class methods. - // ------------------------------------------------------------------------ - + /** + * Create an instance of <code>ExemptionMechanism</code> for a designated + * <code>mechanism</code> from the first Security Provider offering it. + * + * @param mechanism the name of the exemption mechanism to create. + * @return a newly created instance of <code>ExemptionMechanism</code>. + * @throws IllegalArgumentException if the provider is null. + * @throws NoSuchAlgorithmException if no such exemption mechanism is + * available from any known Security Provider. + * @throws IllegalArgumentException if <code>mechanism</code> is + * <code>null</code> or is an empty string. + */ public static final ExemptionMechanism getInstance(String mechanism) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - Provider[] provs = Security.getProviders(); - String msg = ""; - for (int i = 0; i < provs.length; i++) - { - try - { - return getInstance(mechanism, provs[i]); - } - catch (NoSuchAlgorithmException nsae) - { - msg = nsae.getMessage(); - } - } - throw new NoSuchAlgorithmException(msg); + Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; + for (int i = 0; i < p.length; i++) + try + { + return getInstance(mechanism, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; + throw new NoSuchAlgorithmException(mechanism); } + /** + * Create an instance of <code>ExemptionMechanism</code> for a designated + * <code>mechanism</code> from a named <code>provider</code>. + * + * @param mechanism the name of the exemption mechanism to create. + * @param provider the security provider to provide the exemption + * <code>mechanism</code>. + * @return a newly created instance of <code>ExemptionMechanism</code>. + * @throws NoSuchAlgorithmException if no such exemption mechanism is + * available from the named <code>provider</code>. + * @throws NoSuchProviderException if no Security Provider with the designated + * name is known to the underlying JVM. + * @throws IllegalArgumentException if either <code>mechanism</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>mechanism</code> is an empty string. + */ public static final ExemptionMechanism getInstance(String mechanism, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) - { - throw new NoSuchProviderException(provider); - } + throw new NoSuchProviderException(provider); return getInstance(mechanism, p); } + /** + * Create an instance of <code>ExemptionMechanism</code> for a designated + * <code>mechanism</code> from a designated <code>provider</code>. + * + * @param mechanism the name of the exemption mechanism to create. + * @param provider the security provider to provide the exemption + * <code>mechanism</code>. + * @return a newly created instance of <code>ExemptionMechanism</code>. + * @throws NoSuchAlgorithmException if an exemption mechanism could not be + * created. + * @throws IllegalArgumentException if either <code>mechanism</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>mechanism</code> is an empty string. + */ public static final ExemptionMechanism getInstance(String mechanism, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { + StringBuilder sb = new StringBuilder("ExemptionMechanism [") + .append(mechanism).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new ExemptionMechanism((ExemptionMechanismSpi) - Engine.getInstance(SERVICE, mechanism, provider), - provider, mechanism); + Object spi = Engine.getInstance(SERVICE, mechanism, provider); + return new ExemptionMechanism((ExemptionMechanismSpi) spi, + provider, + mechanism); } - catch (InvocationTargetException ite) + catch (InvocationTargetException x) { - if (ite.getCause() instanceof NoSuchAlgorithmException) - throw (NoSuchAlgorithmException) ite.getCause(); - else - throw new NoSuchAlgorithmException(mechanism); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(mechanism); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ------------------------------------------------------------------------ - public final byte[] genExemptionBlob() throws IllegalStateException, ExemptionMechanismException { diff --git a/javax/crypto/KeyAgreement.java b/javax/crypto/KeyAgreement.java index d71743e3e..490031091 100644 --- a/javax/crypto/KeyAgreement.java +++ b/javax/crypto/KeyAgreement.java @@ -101,97 +101,103 @@ public class KeyAgreement virgin = true; } - // Class methods. - // ------------------------------------------------------------------------ - /** * Get an implementation of an algorithm from the first provider that * implements it. - * + * * @param algorithm The name of the algorithm to get. * @return The proper KeyAgreement instacne, if found. - * @throws java.security.NoSuchAlgorithmException If the specified - * algorithm is not implemented by any installed provider. + * @throws NoSuchAlgorithmException If the specified algorithm is not + * implemented by any installed provider. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static final KeyAgreement getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - Provider[] provs = Security.getProviders(); - String msg = algorithm; - for (int i = 0; i < provs.length; i++) - { - try - { - return getInstance(algorithm, provs[i]); - } - catch (NoSuchAlgorithmException nsae) - { - msg = nsae.getMessage(); - } - } - throw new NoSuchAlgorithmException(msg); + 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); } /** - * Get an implementation of an algorithm from a 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 an implementation of an algorithm from a named provider. + * + * @param algorithm The name of the algorithm to create. + * @param provider The name of the provider from which to get the + * implementation. * @return The proper KeyAgreement instance, if found. - * @throws java.security.NoSuchAlgorithmException If the named provider - * does not implement the algorithm. - * @throws java.security.NoSuchProviderException If the named provider - * does not exist. + * @throws NoSuchAlgorithmException If the named provider does not implement + * the algorithm. + * @throws NoSuchProviderException If the named provider does not exist. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ - public static final KeyAgreement getInstance(String algorithm, - String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + public static final KeyAgreement 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); - } + throw new NoSuchProviderException(provider); return getInstance(algorithm, p); } /** - * Get an implementation of an algorithm from a specific provider. - * + * Return an implementation of an algorithm from a specific provider. + * * @param algorithm The name of the algorithm to get. - * @param provider The provider from which to get the implementation. + * @param provider The provider from which to get the implementation. * @return The proper KeyAgreement instance, if found. - * @throws java.security.NoSuchAlgorithmException If this provider - * does not implement the algorithm. + * @throws NoSuchAlgorithmException If this provider does not implement the + * algorithm. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static final KeyAgreement getInstance(String algorithm, Provider provider) throws NoSuchAlgorithmException { + StringBuilder sb = new StringBuilder("KeyAgreement algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new KeyAgreement((KeyAgreementSpi) - Engine.getInstance(SERVICE, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(SERVICE, algorithm, provider); + return new KeyAgreement((KeyAgreementSpi) spi, provider, algorithm); } - catch (InvocationTargetException ite) + catch (InvocationTargetException x) { - if (ite.getCause() == null) - throw new NoSuchAlgorithmException(algorithm); - if (ite.getCause() instanceof NoSuchAlgorithmException) - throw (NoSuchAlgorithmException) ite.getCause(); - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ------------------------------------------------------------------------ - /** * Do a phase in the key agreement. The number of times this method is * called depends upon the algorithm and the number of parties diff --git a/javax/crypto/KeyGenerator.java b/javax/crypto/KeyGenerator.java index e824c6452..79334e9e0 100644 --- a/javax/crypto/KeyGenerator.java +++ b/javax/crypto/KeyGenerator.java @@ -94,95 +94,103 @@ public class KeyGenerator this.algorithm = algorithm; } - // Class methods. - // ------------------------------------------------------------------------ - /** - * Create a new key generator, returning the first available - * implementation. - * + * Create a new key generator, returning the first available implementation. + * * @param algorithm The generator algorithm name. - * @throws java.security.NoSuchAlgorithmException If the specified - * algorithm does not exist. + * @throws NoSuchAlgorithmException If the specified algorithm does not exist. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static final KeyGenerator getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - Provider[] provs = Security.getProviders(); - String msg = algorithm; - for (int i = 0; i < provs.length; i++) - { - try - { - return getInstance(algorithm, provs[i]); - } - catch (NoSuchAlgorithmException nsae) - { - msg = nsae.getMessage(); - } - } - throw new NoSuchAlgorithmException(msg); + 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); } /** * Create a new key generator from the named provider. - * + * * @param algorithm The generator algorithm name. - * @param provider The name of the provider to use. + * @param provider The name of the provider to use. * @return An appropriate key generator, if found. - * @throws java.security.NoSuchAlgorithmException If the specified - * algorithm is not implemented by the named provider. - * @throws java.security.NoSuchProviderException If the named provider - * does not exist. + * @throws NoSuchAlgorithmException If the specified algorithm is not + * implemented by the named provider. + * @throws NoSuchProviderException If the named provider does not exist. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static final KeyGenerator getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) - { - throw new NoSuchProviderException(provider); - } + throw new NoSuchProviderException(provider); return getInstance(algorithm, p); } /** * Create a new key generator from the supplied provider. - * + * * @param algorithm The generator algorithm name. - * @param provider The provider to use. + * @param provider The provider to use. * @return An appropriate key generator, if found. - * @throws java.security.NoSuchAlgorithmException If the specified - * algorithm is not implemented by the provider. + * @throws NoSuchAlgorithmException If the specified algorithm is not + * implemented by the provider. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ - public static final KeyGenerator getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + public static final KeyGenerator getInstance(String algorithm, + Provider provider) + throws NoSuchAlgorithmException { + StringBuilder sb = new StringBuilder("KeyGenerator algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - KeyGenerator instance = new KeyGenerator((KeyGeneratorSpi) - Engine.getInstance(SERVICE, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(SERVICE, algorithm, provider); + KeyGenerator instance = new KeyGenerator((KeyGeneratorSpi) spi, + provider, + algorithm); instance.init(new SecureRandom()); return instance; } - catch (InvocationTargetException ite) + catch (InvocationTargetException x) { - if (ite.getCause() == null) - throw new NoSuchAlgorithmException(algorithm); - if (ite.getCause() instanceof NoSuchAlgorithmException) - throw (NoSuchAlgorithmException) ite.getCause(); - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ------------------------------------------------------------------------ - /** * Generate a key. * diff --git a/javax/crypto/Mac.java b/javax/crypto/Mac.java index 055b20a4f..2a269ab80 100644 --- a/javax/crypto/Mac.java +++ b/javax/crypto/Mac.java @@ -108,96 +108,104 @@ public class Mac implements Cloneable virgin = true; } - // Class methods. - // ------------------------------------------------------------------------ - /** - * Get an instance of the named algorithm from the first provider with - * an appropriate implementation. - * + * Create an instance of the named algorithm from the first provider with an + * appropriate implementation. + * * @param algorithm The name of the algorithm. - * @return An appropriate Mac instance, if the specified algorithm - * is implemented by a provider. - * @throws java.security.NoSuchAlgorithmException If no implementation - * of the named algorithm is installed. + * @return An appropriate Mac instance, if the specified algorithm is + * implemented by a provider. + * @throws NoSuchAlgorithmException If no implementation of the named + * algorithm is installed. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static final Mac getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - Provider[] provs = Security.getProviders(); - String msg = ""; - for (int i = 0; i < provs.length; i++) - { - try - { - return getInstance(algorithm, provs[i]); - } - catch (NoSuchAlgorithmException nsae) - { - msg = nsae.getMessage(); - } - } - throw new NoSuchAlgorithmException(msg); + 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); } /** - * Get an instance of the named algorithm from the named provider. - * + * Create an instance of the named algorithm from the named provider. + * * @param algorithm The name of the algorithm. - * @param provider The name of the provider. + * @param provider The name of the provider. * @return An appropriate Mac instance, if the specified algorithm is * implemented by the named provider. - * @throws java.security.NoSuchAlgorithmException If the named provider - * has no implementation of the algorithm. - * @throws java.security.NoSuchProviderException If the named provider - * does not exist. + * @throws NoSuchAlgorithmException If the named provider has no + * implementation of the algorithm. + * @throws NoSuchProviderException If the named provider does not exist. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static final Mac getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) - { - throw new NoSuchProviderException(provider); - } + throw new NoSuchProviderException(provider); return getInstance(algorithm, p); } /** - * Get an instance of the named algorithm from a provider. - * + * Create an instance of the named algorithm from a provider. + * * @param algorithm The name of the algorithm. - * @param provider The provider. + * @param provider The provider. * @return An appropriate Mac instance, if the specified algorithm is * implemented by the provider. - * @throws java.security.NoSuchAlgorithmException If the provider - * has no implementation of the algorithm. + * @throws NoSuchAlgorithmException If the provider has no implementation of + * the algorithm. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static final Mac getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { + StringBuilder sb = new StringBuilder("Mac algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new Mac((MacSpi) Engine.getInstance(SERVICE, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(SERVICE, algorithm, provider); + return new Mac((MacSpi) spi, provider, algorithm); } - catch (InvocationTargetException ite) + catch (InvocationTargetException x) { - if (ite.getCause() == null) - throw new NoSuchAlgorithmException(algorithm); - if (ite.getCause() instanceof NoSuchAlgorithmException) - throw (NoSuchAlgorithmException) ite.getCause(); - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ------------------------------------------------------------------------ - /** * Finishes the computation of a MAC and returns the digest. * diff --git a/javax/crypto/SecretKeyFactory.java b/javax/crypto/SecretKeyFactory.java index 0a63ef067..1c857d274 100644 --- a/javax/crypto/SecretKeyFactory.java +++ b/javax/crypto/SecretKeyFactory.java @@ -94,94 +94,102 @@ public class SecretKeyFactory this.algorithm = algorithm; } - // Class methods. - // ------------------------------------------------------------------------ - /** - * Create a new secret key factory from the first appropriate - * instance. - * + * Create a new secret key factory from the first appropriate instance. + * * @param algorithm The algorithm name. * @return The appropriate key factory, if found. - * @throws java.security.NoSuchAlgorithmException If no provider - * implements the specified algorithm. + * @throws NoSuchAlgorithmException If no provider implements the specified + * algorithm. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static final SecretKeyFactory getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - Provider[] provs = Security.getProviders(); - for (int i = 0; i < provs.length; i++) - { - try - { - return getInstance(algorithm, provs[i]); - } - catch (NoSuchAlgorithmException nsae) - { - } - } - throw new NoSuchAlgorithmException(algorithm); + 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); } /** * Create a new secret key factory from the named provider. - * + * * @param algorithm The algorithm name. - * @param provider The provider name. + * @param provider The provider name. * @return The appropriate key factory, if found. - * @throws java.security.NoSuchAlgorithmException If the named - * provider does not implement the algorithm. - * @throws java.security.NoSuchProviderException If the named provider - * does not exist. + * @throws NoSuchAlgorithmException If the named provider does not implement + * the algorithm. + * @throws NoSuchProviderException If the named provider does not exist. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static final SecretKeyFactory getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + throws NoSuchAlgorithmException, NoSuchProviderException { + if (provider == null) + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) - { - throw new NoSuchProviderException(provider); - } + throw new NoSuchProviderException(provider); return getInstance(algorithm, p); } /** * Create a new secret key factory from the specified provider. - * + * * @param algorithm The algorithm name. - * @param provider The provider. + * @param provider The provider. * @return The appropriate key factory, if found. - * @throws java.security.NoSuchAlgorithmException If the provider - * does not implement the algorithm. + * @throws NoSuchAlgorithmException If the provider does not implement the + * algorithm. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static final SecretKeyFactory getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { + StringBuilder sb = new StringBuilder("SecretKeyFactory algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new SecretKeyFactory((SecretKeyFactorySpi) - Engine.getInstance(SERVICE, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(SERVICE, algorithm, provider); + return new SecretKeyFactory((SecretKeyFactorySpi) spi, provider, algorithm); } - catch (InvocationTargetException ite) + catch (InvocationTargetException x) { - if (ite.getCause() == null) - throw new NoSuchAlgorithmException(algorithm); - if (ite.getCause() instanceof NoSuchAlgorithmException) - throw (NoSuchAlgorithmException) ite.getCause(); - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ------------------------------------------------------------------------ - /** * Generate a secret key from a key specification, if possible. * diff --git a/javax/net/ssl/KeyManagerFactory.java b/javax/net/ssl/KeyManagerFactory.java index ab8abd626..33f2fda9c 100644 --- a/javax/net/ssl/KeyManagerFactory.java +++ b/javax/net/ssl/KeyManagerFactory.java @@ -132,49 +132,55 @@ public class KeyManagerFactory } /** - * Get an instance of the named key manager factory, from the first + * Create an instance of the named key manager factory, from the first * provider that implements it. - * + * * @param algorithm The type of key manager factory to get. * @return An appropriate implementation of that algoritm. - * @throws NoSuchAlgorithmException If no provider implements the - * requested algorithm. + * @throws NoSuchAlgorithmException If no provider implements the requested + * algorithm. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static final KeyManagerFactory getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - Provider[] provs = Security.getProviders(); - for (int i = 0; i < provs.length; i++) - { - try - { - return getInstance(algorithm, provs[i]); - } - catch (NoSuchAlgorithmException ignore) - { - } - } + 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); } /** - * Get an instance of the named key manager factory, from the named + * Create an instance of the named key manager factory, from the named * provider. - * + * * @param algorithm The type of key manager factory to get. - * @param provider The name of the provider to get the - * implementation from. + * @param provider The name of the provider to get the implementation from. * @return An appropriate implementation of that algorithm. - * @throws NoSuchAlgorithmException If the provider does not - * implement the requested algorithm. - * @throws NoSuchProviderException If the named provider does not - * exist. + * @throws NoSuchAlgorithmException If the provider does not implement the + * requested algorithm. + * @throws NoSuchProviderException If the named provider does not exist. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ - public static final KeyManagerFactory getInstance(String algorithm, String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + public static final KeyManagerFactory getInstance(String algorithm, + String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { if (provider == null) - throw new IllegalArgumentException("provider is null"); + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) throw new NoSuchProviderException(provider); @@ -182,40 +188,48 @@ public class KeyManagerFactory } /** - * Get an instance of the named key manager factory, from the given + * Create an instance of the named key manager factory, from the given * provider. - * + * * @param algorithm The type of key manager factory to get. * @param provider The provider to get the implementation from. * @return An appropriate implementation of that algorithm. - * @throws NoSuchAlgorithmException If the provider does not - * implement the requested algorithm. - * @throws IllegalArgumentException If <i>provider</i> is null. + * @throws NoSuchAlgorithmException If the provider does not implement the + * requested algorithm. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ - public static final KeyManagerFactory getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + public static final KeyManagerFactory getInstance(String algorithm, + Provider provider) + throws NoSuchAlgorithmException { - if (provider == null) - throw new IllegalArgumentException("provider is null"); + StringBuilder sb = new StringBuilder("KeyManagerFactory algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new KeyManagerFactory((KeyManagerFactorySpi) - Engine.getInstance(KEY_MANAGER_FACTORY, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(KEY_MANAGER_FACTORY, algorithm, provider); + return new KeyManagerFactory((KeyManagerFactorySpi) spi, provider, algorithm); } - catch (InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ------------------------------------------------------------------- - /** * Returns the name of this key manager factory algorithm. * diff --git a/javax/net/ssl/SSLContext.java b/javax/net/ssl/SSLContext.java index 3f284ab1a..dcc850809 100644 --- a/javax/net/ssl/SSLContext.java +++ b/javax/net/ssl/SSLContext.java @@ -91,102 +91,103 @@ public class SSLContext this.protocol = protocol; } - // Class methods. - // ------------------------------------------------------------------ - /** - * Get an instance of a context for the specified protocol from the - * first provider that implements it. - * + * Get an instance of a context for the specified protocol from the first + * provider that implements it. + * * @param protocol The name of the protocol to get a context for. * @return The new context. - * @throws NoSuchAlgorithm If no provider implements the given - * protocol. + * @throws NoSuchAlgorithmException If no provider implements the given + * protocol. + * @throws IllegalArgumentException if <code>protocol</code> is + * <code>null</code> or is an empty string. */ public static final SSLContext getInstance(String protocol) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - Provider[] provs = Security.getProviders(); - for (int i = 0; i < provs.length; i++) - { - try - { - return getInstance(protocol, provs[i]); - } - catch (NoSuchAlgorithmException ignore) - { - } - } + Provider[] p = Security.getProviders(); + NoSuchAlgorithmException lastException = null; + for (int i = 0; i < p.length; i++) + try + { + return getInstance(protocol, p[i]); + } + catch (NoSuchAlgorithmException x) + { + lastException = x; + } + if (lastException != null) + throw lastException; throw new NoSuchAlgorithmException(protocol); } /** - * Get an instance of a context for the specified protocol from the - * named provider. - * + * Get an instance of a context for the specified protocol from the named + * provider. + * * @param protocol The name of the protocol to get a context for. - * @param provider The name of the provider to get the - * implementation from. + * @param provider The name of the provider to get the implementation from. * @return The new context. - * @throws NoSuchAlgorithmException If the provider does not - * implement the given protocol. - * @throws NoSuchProviderException If the named provider does not - * exist. - * @throws IllegalArgumentException If <i>provider</i> is null. + * @throws NoSuchAlgorithmException If the provider does not implement the + * given protocol. + * @throws NoSuchProviderException If the named provider does not exist. + * @throws IllegalArgumentException if either <code>protocol</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>protocol</code> is an empty string. */ - public static final SSLContext getInstance(String protocol, - String provider) - throws NoSuchAlgorithmException, NoSuchProviderException + public static final SSLContext getInstance(String protocol, String provider) + throws NoSuchAlgorithmException, NoSuchProviderException { if (provider == null) - { - throw new IllegalArgumentException("null provider"); - } + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) - { - throw new NoSuchProviderException(provider); - } + throw new NoSuchProviderException(provider); return getInstance(protocol, p); } /** - * Get an instance of a context for the specified protocol from the - * specified provider. - * + * Get an instance of a context for the specified protocol from the specified + * provider. + * * @param protocol The name of the protocol to get a context for. - * @param provider The name of the provider to get the - * implementation from. + * @param provider The name of the provider to get the implementation from. * @return The new context. - * @throws NoSuchAlgorithmException If the provider does not - * implement the given protocol. - * @throws IllegalArgumentException If <i>provider</i> is null. + * @throws NoSuchAlgorithmException If the provider does not implement the + * given protocol. + * @throws IllegalArgumentException if either <code>protocol</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>protocol</code> is an empty string. */ - public static final SSLContext getInstance(String protocol, - Provider provider) - throws NoSuchAlgorithmException + public static final SSLContext getInstance(String protocol, Provider provider) + throws NoSuchAlgorithmException { + StringBuilder sb = new StringBuilder("SSLContext for protocol [") + .append(protocol).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new SSLContext((SSLContextSpi) - Engine.getInstance(SSL_CONTEXT, protocol, provider), - provider, protocol); + Object spi = Engine.getInstance(SSL_CONTEXT, protocol, provider); + return new SSLContext((SSLContextSpi) spi, provider, protocol); } - catch (InvocationTargetException ite) + catch (InvocationTargetException x) { - NoSuchAlgorithmException nsae = new NoSuchAlgorithmException(protocol); - throw (NoSuchAlgorithmException) nsae.initCause(ite); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - NoSuchAlgorithmException nsae = new NoSuchAlgorithmException(protocol); - throw (NoSuchAlgorithmException) nsae.initCause(cce); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } - // Instance methods. - // ----------------------------------------------------------------- - /** * Creates a new {@link SSLEngine} for this context. * diff --git a/javax/net/ssl/TrustManagerFactory.java b/javax/net/ssl/TrustManagerFactory.java index 62ab1c2df..f868ae75b 100644 --- a/javax/net/ssl/TrustManagerFactory.java +++ b/javax/net/ssl/TrustManagerFactory.java @@ -93,96 +93,105 @@ public class TrustManagerFactory this.algorithm = algorithm; } - // Class methods. - // ------------------------------------------------------------------------- - /** - * Returns an instance of a trust manager factory for the given algorithm - * from the first provider that implements it. - * + * Returns an instance of a trust manager factory for the given algorithm from + * the first provider that implements it. + * * @param algorithm The name of the algorithm to get. * @return The instance of the trust manager factory. * @throws NoSuchAlgorithmException If no provider implements the given - * algorithm. + * algorithm. + * @throws IllegalArgumentException if <code>algorithm</code> is + * <code>null</code> or is an empty string. */ public static final TrustManagerFactory getInstance(String algorithm) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - Provider[] provs = Security.getProviders(); - for (int i = 0; i < provs.length; i++) - { - try - { - return getInstance(algorithm, provs[i]); - } - catch (NoSuchAlgorithmException ignore) - { - } - } + 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 trust manager factory for the given algorithm - * from the named provider. - * + * Returns an instance of a trust manager factory for the given algorithm from + * the named provider. + * * @param algorithm The name of the algorithm to get. * @param provider The name of the provider to get the instance from. * @return The instance of the trust manager factory. * @throws NoSuchAlgorithmException If the provider does not implement the - * given algorithm. + * given algorithm. * @throws NoSuchProviderException If there is no such named provider. - * @throws IllegalArgumentException If the provider argument is null. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static final TrustManagerFactory getInstance(String algorithm, String provider) throws NoSuchAlgorithmException, NoSuchProviderException { if (provider == null) - { - throw new IllegalArgumentException(); - } + throw new IllegalArgumentException("provider MUST NOT be null"); Provider p = Security.getProvider(provider); if (p == null) - { - throw new NoSuchProviderException(provider); - } + throw new NoSuchProviderException(provider); return getInstance(algorithm, p); } /** - * Returns an instance of a trust manager factory for the given algorithm - * from the specified provider. - * + * Returns an instance of a trust manager factory for the given algorithm from + * the specified provider. + * * @param algorithm The name of the algorithm to get. * @param provider The provider to get the instance from. * @return The instance of the trust manager factory. * @throws NoSuchAlgorithmException If the provider does not implement the - * given algorithm. - * @throws IllegalArgumentException If the provider argument is null. + * given algorithm. + * @throws IllegalArgumentException if either <code>algorithm</code> or + * <code>provider</code> is <code>null</code>, or if + * <code>algorithm</code> is an empty string. */ public static final TrustManagerFactory getInstance(String algorithm, Provider provider) - throws NoSuchAlgorithmException + throws NoSuchAlgorithmException { - if (provider == null) - { - throw new IllegalArgumentException(); - } + StringBuilder sb = new StringBuilder("TrustManagerFactory algorithm [") + .append(algorithm).append("] from provider[") + .append(provider).append("] could not be created"); + Throwable cause; try { - return new TrustManagerFactory((TrustManagerFactorySpi) - Engine.getInstance(TRUST_MANAGER_FACTORY, algorithm, provider), - provider, algorithm); + Object spi = Engine.getInstance(TRUST_MANAGER_FACTORY, algorithm, provider); + return new TrustManagerFactory((TrustManagerFactorySpi) spi, + provider, + algorithm); } - catch (InvocationTargetException ite) + catch (InvocationTargetException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x.getCause(); + if (cause instanceof NoSuchAlgorithmException) + throw (NoSuchAlgorithmException) cause; + if (cause == null) + cause = x; } - catch (ClassCastException cce) + catch (ClassCastException x) { - throw new NoSuchAlgorithmException(algorithm); + cause = x; } + NoSuchAlgorithmException x = new NoSuchAlgorithmException(sb.toString()); + x.initCause(cause); + throw x; } /** diff --git a/javax/swing/AbstractButton.java b/javax/swing/AbstractButton.java index 63f827a1a..ed8daca27 100644 --- a/javax/swing/AbstractButton.java +++ b/javax/swing/AbstractButton.java @@ -187,9 +187,32 @@ public abstract class AbstractButton extends JComponent */ public void stateChanged(ChangeEvent ev) { - AbstractButton.this.fireStateChanged(); + getEventHandler().stateChanged(ev); + } + } + + /** + * The combined event handler for ActionEvent, ChangeEvent and + * ItemEvent. This combines ButtonChangeListener, ActionListener + */ + private class EventHandler + implements ActionListener, ChangeListener, ItemListener + { + public void actionPerformed(ActionEvent ev) + { + fireActionPerformed(ev); + } + + public void stateChanged(ChangeEvent ev) + { + fireStateChanged(); repaint(); } + + public void itemStateChanged(ItemEvent ev) + { + fireItemStateChanged(ev); + } } /** The icon displayed by default. */ @@ -264,16 +287,29 @@ public abstract class AbstractButton extends JComponent */ int mnemonicIndex; - /** Listener the button uses to receive ActionEvents from its model. */ + /** + * Listener the button uses to receive ActionEvents from its model. + */ protected ActionListener actionListener; - /** Listener the button uses to receive ItemEvents from its model. */ + /** + * Listener the button uses to receive ItemEvents from its model. + */ protected ItemListener itemListener; - /** Listener the button uses to receive ChangeEvents from its model. */ + /** + * Listener the button uses to receive ChangeEvents from its model. + */ protected ChangeListener changeListener; /** + * The event handler for ActionEvent, ItemEvent and ChangeEvent. + * This replaces the above three handlers and combines them + * into one for efficiency. + */ + private EventHandler eventHandler; + + /** * The time in milliseconds in which clicks get coalesced into a single * <code>ActionEvent</code>. */ @@ -855,10 +891,6 @@ public abstract class AbstractButton extends JComponent */ public AbstractButton() { - actionListener = createActionListener(); - changeListener = createChangeListener(); - itemListener = createItemListener(); - horizontalAlignment = CENTER; horizontalTextPosition = TRAILING; verticalAlignment = CENTER; @@ -900,15 +932,21 @@ public abstract class AbstractButton extends JComponent if (model != null) { model.removeActionListener(actionListener); + actionListener = null; model.removeChangeListener(changeListener); + changeListener = null; model.removeItemListener(itemListener); + itemListener = null; } ButtonModel old = model; model = newModel; if (model != null) { + actionListener = createActionListener(); model.addActionListener(actionListener); + changeListener = createChangeListener(); model.addChangeListener(changeListener); + itemListener = createItemListener(); model.addItemListener(itemListener); } firePropertyChange(MODEL_CHANGED_PROPERTY, old, model); @@ -1923,13 +1961,7 @@ public abstract class AbstractButton extends JComponent */ protected ActionListener createActionListener() { - return new ActionListener() - { - public void actionPerformed(ActionEvent e) - { - AbstractButton.this.fireActionPerformed(e); - } - }; + return getEventHandler(); } /** @@ -1995,7 +2027,7 @@ public abstract class AbstractButton extends JComponent */ protected ChangeListener createChangeListener() { - return new ButtonChangeListener(); + return getEventHandler(); } /** @@ -2021,13 +2053,7 @@ public abstract class AbstractButton extends JComponent */ protected ItemListener createItemListener() { - return new ItemListener() - { - public void itemStateChanged(ItemEvent e) - { - AbstractButton.this.fireItemStateChanged(e); - } - }; + return getEventHandler(); } /** @@ -2490,4 +2516,17 @@ public abstract class AbstractButton extends JComponent super.setUIProperty(propertyName, value); } } + + /** + * Returns the combined event handler. The instance is created if + * necessary. + * + * @return the combined event handler + */ + EventHandler getEventHandler() + { + if (eventHandler == null) + eventHandler = new EventHandler(); + return eventHandler; + } } diff --git a/javax/swing/DefaultButtonModel.java b/javax/swing/DefaultButtonModel.java index 51a241760..c0eaea239 100644 --- a/javax/swing/DefaultButtonModel.java +++ b/javax/swing/DefaultButtonModel.java @@ -425,7 +425,7 @@ public class DefaultButtonModel implements ButtonModel, Serializable public void setRollover(boolean r) { // if this call does not represent a CHANGE in state, then return - if ((r && isRollover()) || (!r && !isRollover())) + if (r == isRollover()) return; // cannot set ROLLOVER property unless button is enabled diff --git a/javax/swing/JComponent.java b/javax/swing/JComponent.java index 25fc9b424..0b4631f6c 100644 --- a/javax/swing/JComponent.java +++ b/javax/swing/JComponent.java @@ -69,6 +69,7 @@ import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; import java.beans.VetoableChangeSupport; import java.io.Serializable; +import java.util.ArrayList; import java.util.EventListener; import java.util.Hashtable; import java.util.Locale; @@ -503,27 +504,6 @@ public abstract class JComponent extends Container implements Serializable } } - /** - * An explicit value for the component's preferred size; if not set by a - * user, this is calculated on the fly by delegating to the {@link - * ComponentUI#getPreferredSize} method on the {@link #ui} property. - */ - Dimension preferredSize; - - /** - * An explicit value for the component's minimum size; if not set by a - * user, this is calculated on the fly by delegating to the {@link - * ComponentUI#getMinimumSize} method on the {@link #ui} property. - */ - Dimension minimumSize; - - /** - * An explicit value for the component's maximum size; if not set by a - * user, this is calculated on the fly by delegating to the {@link - * ComponentUI#getMaximumSize} method on the {@link #ui} property. - */ - Dimension maximumSize; - /** * A value between 0.0 and 1.0 indicating the preferred horizontal * alignment of the component, relative to its siblings. The values @@ -687,7 +667,7 @@ public abstract class JComponent extends Container implements Serializable * Indicates whether we are calling paintDoubleBuffered() from * paintImmadiately (RepaintManager) or from paint() (AWT refresh). */ - static private boolean isRepainting = false; + static boolean isRepainting = false; /** * Listeners for events other than {@link PropertyChangeEvent} are @@ -783,6 +763,13 @@ public abstract class JComponent extends Container implements Serializable */ public static final int WHEN_IN_FOCUSED_WINDOW = 2; + + /** + * Used to optimize painting. This is set in paintImmediately2() to specify + * the exact component path to be painted by paintChildren. + */ + Component paintChild; + /** * Indicates if the opaque property has been set by a client program or by * the UI. @@ -1270,37 +1257,38 @@ public abstract class JComponent extends Container implements Serializable } /** - * Get the component's maximum size. If the {@link #maximumSize} property - * has been explicitly set, it is returned. If the {@link #maximumSize} + * Get the component's maximum size. If the <code>maximumSize</code> property + * has been explicitly set, it is returned. If the <code>maximumSize</code> * property has not been set but the {@link #ui} property has been, the * result of {@link ComponentUI#getMaximumSize} is returned. If neither * property has been set, the result of {@link Container#getMaximumSize} * is returned. * - * @return The maximum size of the component + * @return the maximum size of the component * - * @see #maximumSize - * @see #setMaximumSize + * @see Component#setMaximumSize + * @see Component#getMaximumSize() + * @see Component#isMaximumSizeSet() + * @see ComponentUI#getMaximumSize(JComponent) */ public Dimension getMaximumSize() { - if (maximumSize != null) - return maximumSize; - - if (ui != null) + Dimension size = null; + if (isMaximumSizeSet()) + size = super.getMaximumSize(); + else { - Dimension s = ui.getMaximumSize(this); - if (s != null) - return s; + if (ui != null) + size = ui.getMaximumSize(this); + if (size == null) + size = super.getMaximumSize(); } - - Dimension p = super.getMaximumSize(); - return p; + return size; } /** - * Get the component's minimum size. If the {@link #minimumSize} property - * has been explicitly set, it is returned. If the {@link #minimumSize} + * Get the component's minimum size. If the <code>minimumSize</code> property + * has been explicitly set, it is returned. If the <code>minimumSize</code> * property has not been set but the {@link #ui} property has been, the * result of {@link ComponentUI#getMinimumSize} is returned. If neither * property has been set, the result of {@link Container#getMinimumSize} @@ -1308,97 +1296,57 @@ public abstract class JComponent extends Container implements Serializable * * @return The minimum size of the component * - * @see #minimumSize - * @see #setMinimumSize + * @see Component#setMinimumSize + * @see Component#getMinimumSize() + * @see Component#isMinimumSizeSet() + * @see ComponentUI#getMinimumSize(JComponent) */ public Dimension getMinimumSize() { - if (minimumSize != null) - return minimumSize; - - if (ui != null) + Dimension size = null; + if (isMinimumSizeSet()) + size = super.getMinimumSize(); + else { - Dimension s = ui.getMinimumSize(this); - if (s != null) - return s; + if (ui != null) + size = ui.getMinimumSize(this); + if (size == null) + size = super.getMinimumSize(); } - - Dimension p = super.getMinimumSize(); - return p; + return size; } /** - * Get the component's preferred size. If the {@link #preferredSize} - * property has been explicitly set, it is returned. If the {@link - * #preferredSize} property has not been set but the {@link #ui} property - * has been, the result of {@link ComponentUI#getPreferredSize} is + * Get the component's preferred size. If the <code>preferredSize</code> + * property has been explicitly set, it is returned. If the + * <code>preferredSize</code> property has not been set but the {@link #ui} + * property has been, the result of {@link ComponentUI#getPreferredSize} is * returned. If neither property has been set, the result of {@link * Container#getPreferredSize} is returned. * * @return The preferred size of the component * - * @see #preferredSize - * @see #setPreferredSize + * @see Component#setPreferredSize + * @see Component#getPreferredSize() + * @see Component#isPreferredSizeSet() + * @see ComponentUI#getPreferredSize(JComponent) */ public Dimension getPreferredSize() { - Dimension prefSize = null; - if (preferredSize != null) - prefSize = new Dimension(preferredSize); - - else if (ui != null) + Dimension size = null; + if (isPreferredSizeSet()) + size = super.getPreferredSize(); + else { - Dimension s = ui.getPreferredSize(this); - if (s != null) - prefSize = s; + if (ui != null) + size = ui.getPreferredSize(this); + if (size == null) + size = super.getPreferredSize(); } - - if (prefSize == null) - prefSize = super.getPreferredSize(); - - return prefSize; + return size; } /** - * Checks if a maximum size was explicitely set on the component. - * - * @return <code>true</code> if a maximum size was set, - * <code>false</code> otherwise - * - * @since 1.3 - */ - public boolean isMaximumSizeSet() - { - return maximumSize != null; - } - - /** - * Checks if a minimum size was explicitely set on the component. - * - * @return <code>true</code> if a minimum size was set, - * <code>false</code> otherwise - * - * @since 1.3 - */ - public boolean isMinimumSizeSet() - { - return minimumSize != null; - } - - /** - * Checks if a preferred size was explicitely set on the component. - * - * @return <code>true</code> if a preferred size was set, - * <code>false</code> otherwise - * - * @since 1.3 - */ - public boolean isPreferredSizeSet() - { - return preferredSize != null; - } - - /** * Return the value of the <code>nextFocusableComponent</code> property. * * @return The current value of the property, or <code>null</code> @@ -1850,7 +1798,7 @@ public abstract class JComponent extends Container implements Serializable && rm.isDoubleBufferingEnabled()) { Rectangle clip = g.getClipBounds(); - paintDoubleBuffered(clip); + paintDoubleBuffered(clip.x, clip.y, clip.width, clip.height); } else { @@ -1865,8 +1813,22 @@ public abstract class JComponent extends Container implements Serializable dragBuffer = null; } - if (g.getClip() == null) - g.setClip(0, 0, getWidth(), getHeight()); + Rectangle clip = g.getClipBounds(); + int clipX, clipY, clipW, clipH; + if (clip == null) + { + clipX = 0; + clipY = 0; + clipW = getWidth(); + clipH = getHeight(); + } + else + { + clipX = clip.x; + clipY = clip.y; + clipW = clip.width; + clipH = clip.height; + } if (dragBuffer != null && dragBufferInitialized) { g.drawImage(dragBuffer, 0, 0, this); @@ -1874,14 +1836,51 @@ public abstract class JComponent extends Container implements Serializable else { Graphics g2 = getComponentGraphics(g); - paintComponent(g2); - paintBorder(g2); + if (! isOccupiedByChild(clipX, clipY, clipW, clipH)) + { + paintComponent(g2); + paintBorder(g2); + } paintChildren(g2); } } } /** + * Determines if a region of this component is completely occupied by + * an opaque child component, in which case we don't need to bother + * painting this component at all. + * + * @param x the area, x coordinate + * @param y the area, y coordinate + * @param w the area, width + * @param h the area, height + * + * @return <code>true</code> if the specified area is completely covered + * by a child component, <code>false</code> otherwise + */ + private boolean isOccupiedByChild(int x, int y, int w, int h) + { + boolean occupied = false; + int count = getComponentCount(); + for (int i = 0; i < count; i++) + { + Component child = getComponent(i); + int cx = child.getX(); + int cy = child.getY(); + int cw = child.getWidth(); + int ch = child.getHeight(); + if (child.isVisible() && x >= cx && x + w <= cx + cw && y >= cy + && y + h <= cy + ch) + { + occupied = child.isOpaque(); + break; + } + } + return occupied; + } + + /** * Initializes the drag buffer by creating a new image and painting this * component into it. */ @@ -1940,7 +1939,14 @@ public abstract class JComponent extends Container implements Serializable // Need to lock the tree to avoid problems with AWT and concurrency. synchronized (getTreeLock()) { - for (int i = getComponentCount() - 1; i >= 0; i--) + // Fast forward to the child to paint, if set by + // paintImmediately2() + int i = getComponentCount() - 1; + if (paintChild != null && paintChild.isOpaque()) + { + for (; i >= 0 && getComponent(i) != paintChild; i--); + } + for (; i >= 0; i--) { Component child = getComponent(i); if (child != null && child.isLightweight() @@ -1958,7 +1964,8 @@ public abstract class JComponent extends Container implements Serializable Rectangle clip = g.getClipBounds(); // A copy. SwingUtilities.computeIntersection(cx, cy, cw, ch, clip); - if (isCompletelyObscured(i, clip)) + if (isCompletelyObscured(i, clip.x, clip.y, + clip.width, clip.height)) continue; // Continues the for-loop. } Graphics cg = g.create(cx, cy, cw, ch); @@ -1984,12 +1991,15 @@ public abstract class JComponent extends Container implements Serializable * of its siblings. * * @param index the index of the child component - * @param rect the region to check + * @param x the region to check, x coordinate + * @param y the region to check, y coordinate + * @param w the region to check, width + * @param h the region to check, height * * @return <code>true</code> if the region is completely obscured by a * sibling, <code>false</code> otherwise */ - private boolean isCompletelyObscured(int index, Rectangle rect) + private boolean isCompletelyObscured(int index, int x, int y, int w, int h) { boolean obscured = false; for (int i = index - 1; i >= 0 && obscured == false; i--) @@ -1998,10 +2008,10 @@ public abstract class JComponent extends Container implements Serializable if (sib.isVisible()) { Rectangle sibRect = sib.getBounds(rectCache); - if (sib.isOpaque() && rect.x >= sibRect.x - && (rect.x + rect.width) <= (sibRect.x + sibRect.width) - && rect.y >= sibRect.y - && (rect.y + rect.height) <= (sibRect.y + sibRect.height)) + if (sib.isOpaque() && x >= sibRect.x + && (x + w) <= (sibRect.x + sibRect.width) + && y >= sibRect.y + && (y + h) <= (sibRect.y + sibRect.height)) { obscured = true; } @@ -2011,6 +2021,39 @@ public abstract class JComponent extends Container implements Serializable } /** + * Checks if a component/rectangle is partially obscured by one of its + * siblings. + * Note that this doesn't check for completely obscured, this is + * done by isCompletelyObscured() and should probably also be checked. + * + * @param i the component index from which to start searching + * @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 <code>true</code> if the rectangle is partially obscured + */ + private boolean isPartiallyObscured(int i, int x, int y, int w, int h) + { + boolean obscured = false; + for (int j = i - 1; j >= 0 && ! obscured; j--) + { + Component sibl = getComponent(j); + if (sibl.isVisible()) + { + Rectangle rect = sibl.getBounds(rectCache); + if (!(x + w <= rect.x) + || (y + h <= rect.y) + || (x >= rect.x + rect.width) + || (y >= rect.y + rect.height)) + obscured = true; + } + } + return obscured; + } + + /** * Paint the component's body. This usually means calling {@link * ComponentUI#update} on the {@link #ui} property of the component, if * it is non-<code>null</code>. You may override this if you wish to @@ -2050,7 +2093,26 @@ public abstract class JComponent extends Container implements Serializable */ public void paintImmediately(int x, int y, int w, int h) { - paintImmediately(new Rectangle(x, y, w, h)); + // Find opaque parent and call paintImmediately2() on it. + if (isShowing()) + { + Component c = this; + Component p; + while (c != null && ! c.isOpaque()) + { + p = c.getParent(); + if (p != null) + { + x += c.getX(); + y += c.getY(); + c = p; + } + } + if (c instanceof JComponent) + ((JComponent) c).paintImmediately2(x, y, w, h); + else + c.repaint(x, y, w, h); + } } /** @@ -2073,60 +2135,177 @@ public abstract class JComponent extends Container implements Serializable */ public void paintImmediately(Rectangle r) { - // Try to find a root pane for this component. - //Component root = findPaintRoot(r); - Component root = findPaintRoot(r); - // If no paint root is found, then this component is completely overlapped - // by another component and we don't need repainting. - if (root == null|| !root.isShowing()) - return; - SwingUtilities.convertRectangleToAncestor(this, r, root); - if (root instanceof JComponent) - ((JComponent) root).paintImmediately2(r); - else - root.repaint(r.x, r.y, r.width, r.height); + paintImmediately(r.x, r.y, r.width, r.height); } /** * Performs the actual work of paintImmediatly on the repaint root. * - * @param r the area to be repainted + * @param x the area to be repainted, X coordinate + * @param y the area to be repainted, Y coordinate */ - void paintImmediately2(Rectangle r) + void paintImmediately2(int x, int y, int w, int h) { - isRepainting = true; + // Optimization for components that are always painted on top. + boolean onTop = onTop() && isOpaque(); + + // Fetch the RepaintManager. RepaintManager rm = RepaintManager.currentManager(this); - if (rm.isDoubleBufferingEnabled() && isPaintingDoubleBuffered()) - paintDoubleBuffered(r); - else - paintSimple(r); - isRepainting = false; + + // The painting clip; + int paintX = x; + int paintY = y; + int paintW = w; + int paintH = h; + + // If we should paint buffered or not. + boolean haveBuffer = false; + + // The component that is finally triggered for painting. + JComponent paintRoot = this; + + // Stores the component and all its parents. This will be used to limit + // the actually painted components in paintChildren by setting + // the field paintChild. + int pIndex = -1; + int pCount = 0; + ArrayList components = new ArrayList(); + + // Offset to subtract from the paintRoot rectangle when painting. + int offsX = 0; + int offsY = 0; + + // The current component and its child. + Component child; + Container c; + + // Find appropriate paint root. + for (c = this, child = null; + c != null && ! (c instanceof Window) && ! (c instanceof Applet); + child = c, c = c.getParent()) + { + JComponent jc = c instanceof JComponent ? (JComponent) c : null; + components.add(c); + if (! onTop && jc != null && ! jc.isOptimizedDrawingEnabled()) + { + // Check obscured state of the child. + // Generally, we have 3 cases here: + // 1. Not obscured. No need to paint from the parent. + // 2. Partially obscured. Paint from the parent. + // 3. Completely obscured. No need to paint anything. + if (c != this) + { + int count = c.getComponentCount(); + int i = 0; + for (; i < count && c.getComponent(i) != child; i++); + + if (jc.isCompletelyObscured(i, paintX, paintY, paintW, paintH)) + return; // No need to paint anything. + else if (jc.isPartiallyObscured(i, paintX, paintY, paintW, + paintH)) + { + // Paint from parent. + paintRoot = jc; + pIndex = pCount; + offsX = 0; + offsY = 0; + haveBuffer = false; + } + } + } + pCount++; + // Check if component is double buffered. + if (rm.isDoubleBufferingEnabled() && jc != null + && jc.isDoubleBuffered()) + { + haveBuffer = true; + } + + // Clip the paint region with the parent. + if (! onTop) + { + paintX = Math.max(0, paintX); + paintY = Math.max(0, paintY); + paintW = Math.min(c.getWidth(), paintW + paintX) - paintX; + paintH = Math.min(c.getHeight(), paintH + paintY) - paintY; + int dx = c.getX(); + int dy = c.getY(); + paintX += dx; + paintY += dy; + offsX += dx; + offsY += dy; + } + } + if (c != null && c.getPeer() != null && paintW > 0 && paintH > 0) + { + isRepainting = true; + paintX -= offsX; + paintY -= offsY; + + // Set the painting path so that paintChildren paints only what we + // want. + if (paintRoot != this) + { + for (int i = pIndex; i > 0; i--) + { + Component paintParent = (Component) components.get(i); + if (paintParent instanceof JComponent) + ((JComponent) paintParent).paintChild = + (Component) components.get(i - 1); + } + } + + // Actually trigger painting. + if (haveBuffer) + paintRoot.paintDoubleBuffered(paintX, paintY, paintW, + paintH); + else + { + Graphics g = paintRoot.getGraphics(); + try + { + g.setClip(paintX, paintY, paintW, paintH); + paintRoot.paint(g); + } + finally + { + g.dispose(); + } + } + + // Reset the painting path. + if (paintRoot != this) + { + for (int i = pIndex; i > 0; i--) + { + Component paintParent = (Component) components.get(i); + if (paintParent instanceof JComponent) + ((JComponent) paintParent).paintChild = null; + } + } + + isRepainting = false; + } } /** - * Returns true if we must paint double buffered, that is, when this - * component or any of it's ancestors are double buffered. + * Returns <code>true</code> if the component is guaranteed to be painted + * on top of others. This returns false by default and is overridden by + * components like JMenuItem, JPopupMenu and JToolTip to return true for + * added efficiency. * - * @return true if we must paint double buffered, that is, when this - * component or any of it's ancestors are double buffered + * @return <code>true</code> if the component is guaranteed to be painted + * on top of others */ - private boolean isPaintingDoubleBuffered() + boolean onTop() { - boolean doubleBuffered = isDoubleBuffered(); - Component parent = getParent(); - while (! doubleBuffered && parent != null) - { - doubleBuffered = parent instanceof JComponent - && ((JComponent) parent).isDoubleBuffered(); - parent = parent.getParent(); - } - return doubleBuffered; + return false; } /** * Performs double buffered repainting. */ - private void paintDoubleBuffered(Rectangle r) + private void paintDoubleBuffered(int x, int y, int w, int h) { RepaintManager rm = RepaintManager.currentManager(this); @@ -2143,7 +2322,7 @@ public abstract class JComponent extends Container implements Serializable //Rectangle targetClip = SwingUtilities.convertRectangle(this, r, root); Graphics g2 = buffer.getGraphics(); clipAndTranslateGraphics(root, this, g2); - g2.clipRect(r.x, r.y, r.width, r.height); + g2.clipRect(x, y, w, h); g2 = getComponentGraphics(g2); paintingDoubleBuffered = true; try @@ -2164,7 +2343,7 @@ public abstract class JComponent extends Container implements Serializable } // Paint the buffer contents on screen. - rm.commitBuffer(this, r); + rm.commitBuffer(this, x, y, w, h); } /** @@ -2577,7 +2756,7 @@ public abstract class JComponent extends Container implements Serializable KeyEvent e, int condition, boolean pressed) - { + { if (isEnabled()) { Action act = null; @@ -2591,7 +2770,7 @@ public abstract class JComponent extends Container implements Serializable if (cmd instanceof ActionListenerProxy) act = (Action) cmd; else - act = (Action) getActionMap().get(cmd); + act = getActionMap().get(cmd); } } if (act != null && act.isEnabled()) @@ -2741,9 +2920,25 @@ public abstract class JComponent extends Container implements Serializable */ public void scrollRectToVisible(Rectangle r) { - Component p = getParent(); - if (p instanceof JComponent) - ((JComponent) p).scrollRectToVisible(r); + // Search nearest JComponent. + int xOffs = getX(); + int yOffs = getY(); + Component p; + for (p = getParent(); p != null && ! (p instanceof JComponent); + p = p.getParent()) + { + xOffs += p.getX(); + yOffs += p.getY(); + } + if (p != null) + { + r.x += xOffs; + r.y += yOffs; + JComponent jParent = (JComponent) p; + jParent.scrollRectToVisible(r); + r.x -= xOffs; + r.y -= yOffs; + } } /** @@ -2862,57 +3057,6 @@ public abstract class JComponent extends Container implements Serializable } /** - * Set the value of the {@link #maximumSize} property. The passed value is - * copied, the later direct changes on the argument have no effect on the - * property value. - * - * @param max The new value of the property - */ - public void setMaximumSize(Dimension max) - { - Dimension oldMaximumSize = maximumSize; - if (max != null) - maximumSize = new Dimension(max); - else - maximumSize = null; - firePropertyChange("maximumSize", oldMaximumSize, maximumSize); - } - - /** - * Set the value of the {@link #minimumSize} property. The passed value is - * copied, the later direct changes on the argument have no effect on the - * property value. - * - * @param min The new value of the property - */ - public void setMinimumSize(Dimension min) - { - Dimension oldMinimumSize = minimumSize; - if (min != null) - minimumSize = new Dimension(min); - else - minimumSize = null; - firePropertyChange("minimumSize", oldMinimumSize, minimumSize); - } - - /** - * Set the value of the {@link #preferredSize} property. The passed value is - * copied, the later direct changes on the argument have no effect on the - * property value. - * - * @param pref The new value of the property - */ - public void setPreferredSize(Dimension pref) - { - Dimension oldPreferredSize = preferredSize; - if (pref != null) - preferredSize = new Dimension(pref); - else - preferredSize = null; - firePropertyChange("preferredSize", oldPreferredSize, preferredSize); - } - - /** * Set the specified component to be the next component in the * focus cycle, overriding the {@link FocusTraversalPolicy} for * this component. @@ -3571,130 +3715,6 @@ public abstract class JComponent extends Container implements Serializable jc.fireAncestorEvent(ancestor, id); } } - - /** - * Finds a suitable paint root for painting this component. This method first - * checks if this component is overlapped using - * {@link #findOverlapFreeParent(Rectangle)}. The returned paint root is then - * feeded to {@link #findOpaqueParent(Component)} to find the nearest opaque - * component for this paint root. If no paint is necessary, then we return - * <code>null</code>. - * - * @param c the clip of this component - * - * @return the paint root or <code>null</code> if no painting is necessary - */ - private Component findPaintRoot(Rectangle c) - { - Component p = findOverlapFreeParent(c); - if (p == null) - return null; - Component root = findOpaqueParent(p); - return root; - } - - /** - * Scans the containment hierarchy upwards for components that overlap the - * this component in the specified clip. This method returns - * <code>this</code>, if no component overlaps this component. It returns - * <code>null</code> if another component completely covers this component - * in the specified clip (no repaint necessary). If another component partly - * overlaps this component in the specified clip, then the parent of this - * component is returned (this is the component that must be used as repaint - * root). For efficient lookup, the method - * {@link #isOptimizedDrawingEnabled()} is used. - * - * @param clip the clip of this component - * - * @return the paint root, or <code>null</code> if no paint is necessary - */ - private Component findOverlapFreeParent(Rectangle clip) - { - Rectangle currentClip = clip; - Component found = this; - Container parent = this; - - while (parent != null && !(parent instanceof Window)) - { - Container newParent = parent.getParent(); - if (newParent == null || newParent instanceof Window) - break; - // If the parent is optimizedDrawingEnabled, then its children are - // tiled and cannot have an overlapping child. Go directly to next - // parent. - if ((newParent instanceof JComponent - && ((JComponent) newParent).isOptimizedDrawingEnabled())) - - { - parent = newParent; - continue; - } - - // If the parent is not optimizedDrawingEnabled, we must check if the - // parent or some neighbor overlaps the current clip. - - // This is the current clip converted to the parent's coordinate - // system. TODO: We can do this more efficiently by succesively - // cumulating the parent-child translations. - Rectangle target = SwingUtilities.convertRectangle(found, - currentClip, - newParent); - - // We have an overlap if either: - // - The new parent itself doesn't completely cover the clip - // (this can be the case with viewports). - // - If some higher-level (than the current) children of the new parent - // intersect the target rectangle. - Rectangle parentRect = SwingUtilities.getLocalBounds(newParent); - boolean haveOverlap = - ! SwingUtilities.isRectangleContainingRectangle(parentRect, target); - if (! haveOverlap) - { - Component child; - for (int i = 0; (child = newParent.getComponent(i)) != parent && !haveOverlap; i++) - { - Rectangle childRect = child.getBounds(); - haveOverlap = target.intersects(childRect); - } - } - if (haveOverlap) - { - found = newParent; - currentClip = target; - } - parent = newParent; - } - //System.err.println("overlapfree parent: " + found); - return found; - } - - /** - * Finds the nearest component to <code>c</code> (upwards in the containment - * hierarchy), that is opaque. If <code>c</code> itself is opaque, - * this returns <code>c</code> itself. - * - * @param c the start component for the search - * @return the nearest component to <code>c</code> (upwards in the containment - * hierarchy), that is opaque; If <code>c</code> itself is opaque, - * this returns <code>c</code> itself - */ - private Component findOpaqueParent(Component c) - { - Component found = c; - while (true) - { - if ((found instanceof JComponent) && ((JComponent) found).isOpaque()) - break; - else if (!(found instanceof JComponent) && !found.isLightweight()) - break; - Container p = found.getParent(); - if (p == null) - break; - else - found = p; - } - return found; - } /** * This is the method that gets called when the WHEN_IN_FOCUSED_WINDOW map diff --git a/javax/swing/JEditorPane.java b/javax/swing/JEditorPane.java index 4ae3c5a1c..a5efa07df 100644 --- a/javax/swing/JEditorPane.java +++ b/javax/swing/JEditorPane.java @@ -56,6 +56,7 @@ import javax.accessibility.AccessibleStateSet; import javax.accessibility.AccessibleText; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; +import javax.swing.plaf.TextUI; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultEditorKit; import javax.swing.text.Document; @@ -695,10 +696,28 @@ public class JEditorPane extends JTextComponent public Dimension getPreferredSize() { Dimension pref = super.getPreferredSize(); - if (getScrollableTracksViewportWidth()) - pref.width = getUI().getMinimumSize(this).width; - if (getScrollableTracksViewportHeight()) - pref.height = getUI().getMinimumSize(this).height; + Container parent = getParent(); + if (parent instanceof JViewport) + { + JViewport vp = (JViewport) getParent(); + TextUI ui = getUI(); + Dimension min = null; + if (! getScrollableTracksViewportWidth()) + { + min = ui.getMinimumSize(this); + int vpWidth = vp.getWidth(); + if (vpWidth != 0 && vpWidth < min.width) + pref.width = min.width; + } + if (! getScrollableTracksViewportHeight()) + { + if (min == null) + min = ui.getMinimumSize(this); + int vpHeight = vp.getHeight(); + if (vpHeight != 0 && vpHeight < min.height) + pref.height = min.height; + } + } return pref; } @@ -716,8 +735,11 @@ public class JEditorPane extends JTextComponent // Tests show that this returns true when the parent is a JViewport // and has a height > minimum UI height. Container parent = getParent(); + int height = parent.getHeight(); + TextUI ui = getUI(); return parent instanceof JViewport - && parent.getHeight() > getUI().getMinimumSize(this).height; + && height >= ui.getMinimumSize(this).height + && height <= ui.getMaximumSize(this).height; } /** diff --git a/javax/swing/JList.java b/javax/swing/JList.java index 1cff9553a..ff1b23921 100644 --- a/javax/swing/JList.java +++ b/javax/swing/JList.java @@ -1946,72 +1946,74 @@ public class JList extends JComponent implements Accessible, Scrollable public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { - ListUI lui = this.getUI(); + int unit = -1; if (orientation == SwingConstants.VERTICAL) { - if (direction > 0) + int row = getFirstVisibleIndex(); + if (row == -1) + unit = 0; + else if (direction > 0) { - // Scrolling down - Point bottomLeft = new Point(visibleRect.x, - visibleRect.y + visibleRect.height); - int curIdx = lui.locationToIndex(this, bottomLeft); - Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx); - if (curBounds.y + curBounds.height == bottomLeft.y) + // Scrolling down. + Rectangle bounds = getCellBounds(row, row); + if (bounds != null) + unit = bounds.height - (visibleRect.y - bounds.y); + else + unit = 0; + } + else + { + // Scrolling up. + Rectangle bounds = getCellBounds(row, row); + // First row. + if (row == 0 && bounds.y == visibleRect.y) + unit = 0; // No need to scroll. + else if (bounds.y == visibleRect.y) { - // we are at the exact bottom of the current cell, so we - // are being asked to scroll to the end of the next one - if (curIdx + 1 < model.getSize()) - { - // there *is* a next item in the list - Rectangle nxtBounds = lui.getCellBounds(this, curIdx + 1, curIdx + 1); - return nxtBounds.height; - } + // Scroll to previous row. + Point loc = bounds.getLocation(); + loc.y--; + int prev = locationToIndex(loc); + Rectangle prevR = getCellBounds(prev, prev); + if (prevR == null || prevR.y >= bounds.y) + unit = 0; // For multicolumn lists. else - { - // no next item, no advance possible - return 0; - } + unit = prevR.height; } else - { - // we are part way through an existing cell, so we are being - // asked to scroll to the bottom of it - return (curBounds.y + curBounds.height) - bottomLeft.y; - } + unit = visibleRect.y - bounds.y; } - else + } + else if (orientation == SwingConstants.HORIZONTAL && getLayoutOrientation() != VERTICAL) + { + // Horizontal scrolling. + int i = locationToIndex(visibleRect.getLocation()); + if (i != -1) { - // scrolling up - Point topLeft = new Point(visibleRect.x, visibleRect.y); - int curIdx = lui.locationToIndex(this, topLeft); - Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx); - if (curBounds.y == topLeft.y) + Rectangle b = getCellBounds(i, i); + if (b != null) { - // we are at the exact top of the current cell, so we - // are being asked to scroll to the top of the previous one - if (curIdx > 0) + if (b.x != visibleRect.x) { - // there *is* a previous item in the list - Rectangle nxtBounds = lui.getCellBounds(this, curIdx - 1, curIdx - 1); - return -nxtBounds.height; + if (direction < 0) + unit = Math.abs(b.x - visibleRect.x); + else + unit = b.width + b.x - visibleRect.x; } else - { - // no previous item, no advance possible - return 0; - } + unit = b.width; } - else - { - // we are part way through an existing cell, so we are being - // asked to scroll to the top of it - return curBounds.y - topLeft.y; - } } } - // FIXME: handle horizontal scrolling (also wrapping?) - return 1; + if (unit == -1) + { + // This fallback seems to be used by the RI for the degenerate cases + // not covered above. + Font f = getFont(); + unit = f != null ? f.getSize() : 1; + } + return unit; } /** @@ -2040,10 +2042,120 @@ public class JList extends JComponent implements Accessible, Scrollable public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { - if (orientation == VERTICAL) - return visibleRect.height * direction; - else - return visibleRect.width * direction; + int block = -1; + if (orientation == SwingConstants.VERTICAL) + { + // Default block scroll. Special cases are handled below for + // better usability. + block = visibleRect.height; + if (direction > 0) + { + // Scroll down. + // Scroll so that after scrolling the last line aligns with + // the lower boundary of the visible area. + Point p = new Point(visibleRect.x, + visibleRect.y + visibleRect.height - 1); + int last = locationToIndex(p); + if (last != -1) + { + Rectangle lastR = getCellBounds(last, last); + if (lastR != null) + { + block = lastR.y - visibleRect.y; + if (block == 0&& last < getModel().getSize() - 1) + block = lastR.height; + } + } + } + else + { + // Scroll up. + // Scroll so that after scrolling the first line aligns with + // the upper boundary of the visible area. + Point p = new Point(visibleRect.x, + visibleRect.y - visibleRect.height); + int newFirst = locationToIndex(p); + if (newFirst != -1) + { + int first = getFirstVisibleIndex(); + if (first == -1) + first = locationToIndex(visibleRect.getLocation()); + Rectangle newFirstR = getCellBounds(newFirst, newFirst); + Rectangle firstR = getCellBounds(first, first); + if (newFirstR != null && firstR != null) + { + // Search first item that would left the current first + // item visible when scrolled to. + while (newFirstR.y + visibleRect.height + < firstR.y + firstR.height + && newFirstR.y < firstR.y) + { + newFirst++; + newFirstR = getCellBounds(newFirst, newFirst); + } + block = visibleRect.y - newFirstR.y; + if (block <= 0 && newFirstR.y > 0) + { + newFirst--; + newFirstR = getCellBounds(newFirst, newFirst); + if (newFirstR != null) + block = visibleRect.y - newFirstR.y; + } + } + } + } + } + else if (orientation == SwingConstants.HORIZONTAL + && getLayoutOrientation() != VERTICAL) + { + // Default block increment. Special cases are handled below for + // better usability. + block = visibleRect.width; + if (direction > 0) + { + // Scroll right. + Point p = new Point(visibleRect.x + visibleRect.width + 1, + visibleRect.y); + int last = locationToIndex(p); + if (last != -1) + { + Rectangle lastR = getCellBounds(last, last); + if (lastR != null) + { + block = lastR.x - visibleRect.x; + if (block < 0) + block += lastR.width; + else if (block == 0 && last < getModel().getSize() - 1) + block = lastR.width; + } + } + } + else + { + // Scroll left. + Point p = new Point(visibleRect.x - visibleRect.width, + visibleRect.y); + int first = locationToIndex(p); + if (first != -1) + { + Rectangle firstR = getCellBounds(first, first); + if (firstR != null) + { + if (firstR.x < visibleRect.x - visibleRect.width) + { + if (firstR.x + firstR.width > visibleRect.x) + block = visibleRect.x - firstR.x; + else + block = visibleRect.x - firstR.x - firstR.width; + } + else + block = visibleRect.x - firstR.x; + } + } + } + } + + return block; } /** diff --git a/javax/swing/JMenu.java b/javax/swing/JMenu.java index 68f31e335..7e627f118 100644 --- a/javax/swing/JMenu.java +++ b/javax/swing/JMenu.java @@ -39,7 +39,14 @@ exception statement from your version. */ package javax.swing; import java.awt.Component; +import java.awt.Dimension; +import java.awt.GraphicsConfiguration; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Insets; import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Toolkit; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; @@ -53,6 +60,8 @@ import javax.accessibility.Accessible; import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleRole; import javax.accessibility.AccessibleSelection; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.event.MenuEvent; import javax.swing.event.MenuListener; import javax.swing.plaf.MenuItemUI; @@ -71,6 +80,36 @@ import javax.swing.plaf.MenuItemUI; */ public class JMenu extends JMenuItem implements Accessible, MenuElement { + /** + * Receives notifications when the JMenu's ButtonModel is changed and + * fires menuSelected or menuDeselected events when appropriate. + */ + private class MenuChangeListener + implements ChangeListener + { + /** + * Indicates the last selected state. + */ + private boolean selected; + + /** + * Receives notification when the JMenu's ButtonModel changes. + */ + public void stateChanged(ChangeEvent ev) + { + ButtonModel m = (ButtonModel) ev.getSource(); + boolean s = m.isSelected(); + if (s != selected) + { + if (s) + fireMenuSelected(); + else + fireMenuDeselected(); + selected = s; + } + } + } + private static final long serialVersionUID = 4227225638931828014L; /** A Popup menu associated with this menu, which pops up when menu is selected */ @@ -87,11 +126,20 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement /* PopupListener */ protected WinListener popupListener; - /** Location at which popup menu associated with this menu will be - displayed */ + /** + * Location at which popup menu associated with this menu will be + * displayed + */ private Point menuLocation; /** + * The ChangeListener for the ButtonModel. + * + * @see MenuChangeListener + */ + private ChangeListener menuChangeListener; + + /** * Creates a new JMenu object. */ public JMenu() @@ -188,7 +236,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public JMenuItem add(String text) { - return getPopupMenu().add(text); + return add(new JMenuItem(text)); } /** @@ -200,7 +248,10 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public JMenuItem add(Action action) { - return getPopupMenu().add(action); + JMenuItem i = createActionComponent(action); + i.setAction(action); + add(i); + return i; } /** @@ -323,7 +374,18 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement */ public void setModel(ButtonModel model) { + ButtonModel oldModel = getModel(); + if (oldModel != null && menuChangeListener != null) + oldModel.removeChangeListener(menuChangeListener); + super.setModel(model); + + if (model != null) + { + if (menuChangeListener == null) + menuChangeListener = new MenuChangeListener(); + model.addChangeListener(menuChangeListener); + } } /** @@ -372,7 +434,8 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement if (popup && isShowing()) { // Set location as determined by getPopupLocation(). - Point loc = getPopupMenuOrigin(); + Point loc = menuLocation == null ? getPopupMenuOrigin() + : menuLocation; getPopupMenu().show(this, loc.x, loc.y); } else @@ -381,26 +444,109 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement } /** - * Returns origin point of the popup menu + * Returns origin point of the popup menu. This takes the screen bounds + * into account and places the popup where it fits best. * - * @return Point containing + * @return the origin of the popup menu */ protected Point getPopupMenuOrigin() { - Point point; - - // if menu in the menu bar + // The menu's screen location and size. + Point screenLoc = getLocationOnScreen(); + Dimension size = getSize(); + + // Determine the popup's size. + JPopupMenu popup = getPopupMenu(); + Dimension popupSize = popup.getSize(); + if (popupSize.width == 0 || popupSize.height == 0) + popupSize = popup.getPreferredSize(); + + // Determine screen bounds. + Toolkit tk = Toolkit.getDefaultToolkit(); + Rectangle screenBounds = new Rectangle(tk.getScreenSize()); + GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment(); + GraphicsDevice gd = ge.getDefaultScreenDevice(); + GraphicsConfiguration gc = gd.getDefaultConfiguration(); + Insets screenInsets = tk.getScreenInsets(gc); + screenBounds.x -= screenInsets.left; + screenBounds.width -= screenInsets.left + screenInsets.right; + screenBounds.y -= screenInsets.top; + screenBounds.height -= screenInsets.top + screenInsets.bottom; + screenLoc.x -= screenInsets.left; + screenLoc.y -= screenInsets.top; + + Point point = new Point(); if (isTopLevelMenu()) - point = new Point(0, this.getHeight()); - - // if submenu + { + // If menu in the menu bar. + int xOffset = UIManager.getInt("Menu.menuPopupOffsetX"); + int yOffset = UIManager.getInt("Menu.menuPopupOffsetY"); + // Determine X location. + if (getComponentOrientation().isLeftToRight()) + { + // Prefer popup to the right. + point.x = xOffset; + // Check if it fits, otherwise place popup wherever it fits. + if (screenLoc.x + point.x + popupSize.width + > screenBounds.width + screenBounds.width + && screenBounds.width - size.width + < 2 * (screenLoc.x - screenBounds.x)) + // Popup to the right if there's not enough room. + point.x = size.width - xOffset - popupSize.width; + } + else + { + // Prefer popup to the left. + point.x = size.width - xOffset - popupSize.width; + if (screenLoc.x + point.x < screenBounds.x + && screenBounds.width - size.width + > 2 * (screenLoc.x - screenBounds.x)) + // Popup to the left if there's not enough room. + point.x = xOffset; + } + // Determine Y location. Prefer popping down. + point.y = size.height + yOffset; + if (screenLoc.y + point.y + popupSize.height >= screenBounds.height + && screenBounds.height - size.height + < 2 * (screenLoc.y - screenBounds.y)) + // Position above if there's not enough room below. + point.y = - yOffset - popupSize.height; + } else { + // If submenu. int xOffset = UIManager.getInt("Menu.submenuPopupOffsetX"); int yOffset = UIManager.getInt("Menu.submenuPopupOffsetY"); - int x = getWidth() + xOffset; - int y = yOffset; - point = new Point(x, y); + // Determine X location. + if (getComponentOrientation().isLeftToRight()) + { + // Prefer popup to the right. + point.x = size.width + xOffset; + if (screenLoc.x + point.x + popupSize.width + >= screenBounds.x + screenBounds.width + && screenBounds.width - size.width + < 2 * (screenLoc.x - screenBounds.x)) + // Position to the left if there's not enough room on the right. + point.x = - xOffset - popupSize.width; + } + else + { + // Prefer popup on the left side. + point.x = - xOffset - popupSize.width; + if (screenLoc.x + point.x < screenBounds.x + && screenBounds.width - size.width + > 2 * (screenLoc.x - screenBounds.x)) + // Popup to the right if there's not enough room. + point.x = size.width + xOffset; + } + // Determine Y location. Prefer popping down. + point.y = yOffset; + if (screenLoc.y + point.y + popupSize.height + >= screenBounds.y + screenBounds.height + && screenBounds.height - size.height + < 2 * (screenLoc.y - screenBounds.y)) + // Pop up if there's not enough room below. + point.y = size.height - yOffset - popupSize.height; } return point; } @@ -442,6 +588,8 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement public void setMenuLocation(int x, int y) { menuLocation = new Point(x, y); + if (popupMenu != null) + popupMenu.setLocation(x, y); } /** @@ -559,7 +707,7 @@ public class JMenu extends JMenuItem implements Accessible, MenuElement if (getPopupMenu() == null || getMenuComponentCount() == 0) return null; - return (Component) popupMenu.getComponentAtIndex(index); + return popupMenu.getComponentAtIndex(index); } /** diff --git a/javax/swing/JMenuItem.java b/javax/swing/JMenuItem.java index f7f93bf00..324d61cd4 100644 --- a/javax/swing/JMenuItem.java +++ b/javax/swing/JMenuItem.java @@ -833,4 +833,20 @@ public class JMenuItem extends AbstractButton implements Accessible, return AccessibleRole.MENU_ITEM; } } + + /** + * Returns <code>true</code> if the component is guaranteed to be painted + * on top of others. This returns false by default and is overridden by + * components like JMenuItem, JPopupMenu and JToolTip to return true for + * added efficiency. + * + * @return <code>true</code> if the component is guaranteed to be painted + * on top of others + */ + boolean onTop() + { + return SwingUtilities.getAncestorOfClass(JInternalFrame.class, this) + == null; + } + } diff --git a/javax/swing/JPopupMenu.java b/javax/swing/JPopupMenu.java index 2e59d4767..1ae8adad0 100644 --- a/javax/swing/JPopupMenu.java +++ b/javax/swing/JPopupMenu.java @@ -902,6 +902,20 @@ public class JPopupMenu extends JComponent implements Accessible, MenuElement } } + /** + * Returns <code>true</code> if the component is guaranteed to be painted + * on top of others. This returns false by default and is overridden by + * components like JMenuItem, JPopupMenu and JToolTip to return true for + * added efficiency. + * + * @return <code>true</code> if the component is guaranteed to be painted + * on top of others + */ + boolean onTop() + { + return true; + } + protected class AccessibleJPopupMenu extends AccessibleJComponent { private static final long serialVersionUID = 7423261328879849768L; diff --git a/javax/swing/JTabbedPane.java b/javax/swing/JTabbedPane.java index 5c8d04748..a500b9264 100644 --- a/javax/swing/JTabbedPane.java +++ b/javax/swing/JTabbedPane.java @@ -760,11 +760,7 @@ public class JTabbedPane extends JComponent implements Serializable, this.tabPlacement = tabPlacement; layoutPolicy = tabLayoutPolicy; - changeEvent = new ChangeEvent(this); - changeListener = createChangeListener(); - - model = new DefaultSingleSelectionModel(); - model.addChangeListener(changeListener); + setModel(new DefaultSingleSelectionModel()); updateUI(); } @@ -877,16 +873,24 @@ public class JTabbedPane extends JComponent implements Serializable, /** * This method changes the model property of the JTabbedPane. * - * @param model The new model to use with the JTabbedPane. + * @param m The new model to use with the JTabbedPane. */ - public void setModel(SingleSelectionModel model) + public void setModel(SingleSelectionModel m) { - if (model != this.model) + if (m != model) { SingleSelectionModel oldModel = this.model; - this.model.removeChangeListener(changeListener); - this.model = model; - this.model.addChangeListener(changeListener); + if (oldModel != null && changeListener != null) + oldModel.removeChangeListener(changeListener); + + model = m; + + if (model != null) + { + if (changeListener != null) + changeListener = createChangeListener(); + model.addChangeListener(changeListener); + } firePropertyChange("model", oldModel, this.model); } } diff --git a/javax/swing/JTable.java b/javax/swing/JTable.java index efa1a80bc..28cc6728d 100644 --- a/javax/swing/JTable.java +++ b/javax/swing/JTable.java @@ -3303,10 +3303,21 @@ public class JTable public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { - if (orientation == SwingConstants.VERTICAL) - return visibleRect.height * direction; + int block; + if (orientation == SwingConstants.HORIZONTAL) + { + block = visibleRect.width; + } else - return visibleRect.width * direction; + { + int rowHeight = getRowHeight(); + if (rowHeight > 0) + block = Math.max(rowHeight, // Little hack for useful rounding. + (visibleRect.height / rowHeight) * rowHeight); + else + block = visibleRect.height; + } + return block; } /** @@ -3345,24 +3356,40 @@ public class JTable * The values greater than one means that more mouse wheel or similar * events were generated, and hence it is better to scroll the longer * distance. - * @author Audrius Meskauskas (audriusa@bioinformatics.org) + * + * @author Roman Kennke (kennke@aicas.com) */ public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { - int h = (rowHeight + rowMargin); - int delta = h * direction; - - // Round so that the top would start from the row boundary - if (orientation == SwingConstants.VERTICAL) + int unit; + if (orientation == SwingConstants.HORIZONTAL) + unit = 100; + else { - // Completely expose the top row - int near = ((visibleRect.y + delta + h / 2) / h) * h; - int diff = visibleRect.y + delta - near; - delta -= diff; + unit = getRowHeight(); + // The following adjustment doesn't work for variable height rows. + // It fully exposes partially visible rows in the scrolling direction. + if (rowHeights == null) + { + if (direction > 0) + { + // Scroll down. + // How much pixles are exposed from the last item? + int exposed = (visibleRect.y + visibleRect.height) % unit; + if (exposed > 0 && exposed < unit - 1) + unit = unit - exposed - 1; + } + else + { + // Scroll up. + int exposed = visibleRect.y % unit; + if (exposed > 0 && exposed < unit) + unit = exposed; + } + } } - return delta; - // TODO when scrollng horizontally, scroll into the column boundary. + return unit; } /** diff --git a/javax/swing/JTextPane.java b/javax/swing/JTextPane.java index c0a5f80cf..05968fc8c 100644 --- a/javax/swing/JTextPane.java +++ b/javax/swing/JTextPane.java @@ -214,20 +214,11 @@ public class JTextPane */ public void insertIcon(Icon icon) { - SimpleAttributeSet atts = new SimpleAttributeSet(); - atts.addAttribute(StyleConstants.IconAttribute, icon); - atts.addAttribute(StyleConstants.NameAttribute, - StyleConstants.IconElementName); - try - { - getDocument().insertString(getCaret().getDot(), " ", atts); - } - catch (BadLocationException ex) - { - AssertionError err = new AssertionError("Unexpected bad location"); - err.initCause(ex); - throw err; - } + MutableAttributeSet inputAtts = getInputAttributes(); + inputAtts.removeAttributes(inputAtts); + StyleConstants.setIcon(inputAtts, icon); + replaceSelection(" "); + inputAtts.removeAttributes(inputAtts); } /** diff --git a/javax/swing/JToolTip.java b/javax/swing/JToolTip.java index 836c122c6..3153894da 100644 --- a/javax/swing/JToolTip.java +++ b/javax/swing/JToolTip.java @@ -225,4 +225,18 @@ public class JToolTip extends JComponent implements Accessible { setUI((ToolTipUI) UIManager.getUI(this)); } + + /** + * Returns <code>true</code> if the component is guaranteed to be painted + * on top of others. This returns false by default and is overridden by + * components like JMenuItem, JPopupMenu and JToolTip to return true for + * added efficiency. + * + * @return <code>true</code> if the component is guaranteed to be painted + * on top of others + */ + boolean onTop() + { + return true; + } } diff --git a/javax/swing/JTree.java b/javax/swing/JTree.java index 32acca643..c76a9b783 100644 --- a/javax/swing/JTree.java +++ b/javax/swing/JTree.java @@ -1684,29 +1684,52 @@ public class JTree extends JComponent implements Scrollable, Accessible public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { - int delta; + int delta = 0; // Round so that the top would start from the row boundary if (orientation == SwingConstants.VERTICAL) { - // One pixel down, otherwise picks another row too high. - int row = getClosestRowForLocation(visibleRect.x, visibleRect.y + 1); - row = row + direction; - if (row < 0) - row = 0; - - Rectangle newTop = getRowBounds(row); - delta = newTop.y - visibleRect.y; + int row = getClosestRowForLocation(0, visibleRect.y); + if (row != -1) + { + Rectangle b = getRowBounds(row); + if (b.y != visibleRect.y) + { + if (direction < 0) + delta = Math.max(0, visibleRect.y - b.y); + else + delta = b.y + b.height - visibleRect.height; + } + else + { + if (direction < 0) + { + if (row != 0) + { + b = getRowBounds(row - 1); + delta = b.height; + } + } + else + delta = b.height; + } + } } else - delta = direction * rowHeight == 0 ? 20 : rowHeight; + // The RI always returns 4 for HORIZONTAL scrolling. + delta = 4; return delta; } public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { - return getScrollableUnitIncrement(visibleRect, orientation, direction); + int block; + if (orientation == SwingConstants.VERTICAL) + block = visibleRect.height; + else + block = visibleRect.width; + return block; } public boolean getScrollableTracksViewportHeight() diff --git a/javax/swing/JViewport.java b/javax/swing/JViewport.java index 7cf393996..babffb7ce 100644 --- a/javax/swing/JViewport.java +++ b/javax/swing/JViewport.java @@ -942,10 +942,10 @@ public class JViewport extends JComponent implements Accessible * * @param r the rectangle to paint */ - void paintImmediately2(Rectangle r) + void paintImmediately2(int x, int y, int w, int h) { isPaintRoot = true; - super.paintImmediately2(r); + super.paintImmediately2(x, y, w, h); isPaintRoot = false; } } diff --git a/javax/swing/RepaintManager.java b/javax/swing/RepaintManager.java index 29bf98fbe..2d29e9076 100644 --- a/javax/swing/RepaintManager.java +++ b/javax/swing/RepaintManager.java @@ -38,6 +38,7 @@ exception statement from your version. */ package javax.swing; +import java.applet.Applet; import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; @@ -49,7 +50,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; -import java.util.Map; import java.util.Set; import java.util.WeakHashMap; @@ -197,19 +197,6 @@ public class RepaintManager private WeakHashMap offscreenBuffers; /** - * Indicates if the RepaintManager is currently repainting an area. - */ - private boolean repaintUnderway; - - /** - * This holds buffer commit requests when the RepaintManager is working. - * This maps Component objects (the top level components) to Rectangle - * objects (the area of the corresponding buffer that must be blitted on - * the component). - */ - private HashMap commitRequests; - - /** * The maximum width and height to allocate as a double buffer. Requests * beyond this size are ignored. * @@ -232,8 +219,6 @@ public class RepaintManager doubleBufferMaximumSize = new Dimension(2000,2000); doubleBufferingEnabled = true; offscreenBuffers = new WeakHashMap(); - repaintUnderway = false; - commitRequests = new HashMap(); } /** @@ -398,8 +383,6 @@ public class RepaintManager if (w <= 0 || h <= 0 || !component.isShowing()) return; - Component parent = component.getParent(); - component.computeVisibleRect(rectCache); SwingUtilities.computeIntersection(x, y, w, h, rectCache); @@ -557,7 +540,6 @@ public class RepaintManager compileRepaintRoots(dirtyComponentsWork, dirty, repaintRoots); } - repaintUnderway = true; for (Iterator i = repaintRoots.iterator(); i.hasNext();) { JComponent comp = (JComponent) i.next(); @@ -567,13 +549,11 @@ public class RepaintManager comp.paintImmediately(damaged); } dirtyComponentsWork.clear(); - repaintUnderway = false; - commitRemainingBuffers(); } - + /** * Compiles a list of components that really get repainted. This is called - * once for each component in the dirtyComponents HashMap, each time with + * once for each component in the dirtyRegions HashMap, each time with * another <code>dirty</code> parameter. This searches up the component * hierarchy of <code>dirty</code> to find the highest parent that is also * marked dirty and merges the dirty regions. @@ -588,6 +568,29 @@ public class RepaintManager Component current = dirty; Component root = dirty; + // This will contain the dirty region in the root coordinate system, + // possibly clipped by ancestor's bounds. + Rectangle originalDirtyRect = (Rectangle) dirtyRegions.get(dirty); + rectCache.setBounds(originalDirtyRect); + + // The bounds of the current component. + int x = dirty.getX(); + int y = dirty.getY(); + int w = dirty.getWidth(); + int h = dirty.getHeight(); + + // Do nothing if dirty region is clipped away by the component's bounds. + rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache); + if (rectCache.isEmpty()) + return; + + // The cumulated offsets. + int dx = 0; + int dy = 0; + // The actual offset for the found root. + int rootDx = 0; + int rootDy = 0; + // Search the highest component that is also marked dirty. Component parent; while (true) @@ -597,10 +600,29 @@ public class RepaintManager break; current = parent; + // Update the offset. + dx += x; + dy += y; + rectCache.x += x; + rectCache.y += y; + + x = current.getX(); + y = current.getY(); + w = current.getWidth(); + h = current.getHeight(); + rectCache = SwingUtilities.computeIntersection(0, 0, w, h, rectCache); + + // Don't paint if the dirty regions is clipped away by any of + // its ancestors. + if (rectCache.isEmpty()) + return; + // We can skip to the next up when this parent is not dirty. if (dirtyRegions.containsKey(parent)) { root = current; + rootDx = dx; + rootDy = dy; } } @@ -608,15 +630,16 @@ public class RepaintManager // the are different. if (root != dirty) { - Rectangle dirtyRect = (Rectangle) dirtyRegions.get(dirty); - dirtyRect = SwingUtilities.convertRectangle(dirty, dirtyRect, root); - Rectangle rootRect = (Rectangle) dirtyRegions.get(root); - SwingUtilities.computeUnion(dirtyRect.x, dirtyRect.y, dirtyRect.width, - dirtyRect.height, rootRect); + rectCache.x += rootDx - dx; + rectCache.y += rootDy - dy; + Rectangle dirtyRect = (Rectangle) dirtyRegions.get(root); + SwingUtilities.computeUnion(rectCache.x, rectCache.y, rectCache.width, + rectCache.height, dirtyRect); } // Adds the root to the roots set. - roots.add(root); + if (! roots.contains(root)) + roots.add(root); } /** @@ -643,130 +666,40 @@ public class RepaintManager width = Math.min(doubleBufferMaximumSize.width, width); int height = Math.max(proposedHeight, root.getHeight()); height = Math.min(doubleBufferMaximumSize.height, height); - buffer = root.createImage(width, height); + buffer = component.createImage(width, height); offscreenBuffers.put(root, buffer); } return buffer; } /** - * Blits the back buffer of the specified root component to the screen. If - * the RepaintManager is currently working on a paint request, the commit - * requests are queued up and committed at once when the paint request is - * done (by {@link #commitRemainingBuffers}). This is package private because - * it must get called by JComponent. + * Blits the back buffer of the specified root component to the screen. + * This is package private because it must get called by JComponent. * * @param comp the component to be painted - * @param area the area to paint on screen, in comp coordinates - */ - void commitBuffer(Component comp, Rectangle area) - { - // Determine the component that we finally paint the buffer upon. - // We need to paint on the nearest heavyweight component, so that Swing - // hierarchies inside (non-window) heavyweights get painted correctly. - // Otherwise we would end up blitting the backbuffer behind the heavyweight - // which is wrong. - Component root = getHeavyweightParent(comp); - // FIXME: Optimize this. - Rectangle rootRect = SwingUtilities.convertRectangle(comp, area, root); - - // We synchronize on dirtyComponents here because that is what - // paintDirtyRegions also synchronizes on while painting. - synchronized (dirtyComponents) - { - // If the RepaintManager is not currently painting, then directly - // blit the requested buffer on the screen. - if (true || ! repaintUnderway) - { - blitBuffer(root, rootRect); - } - - // Otherwise queue this request up, until all the RepaintManager work - // is done. - else - { - if (commitRequests.containsKey(root)) - SwingUtilities.computeUnion(rootRect.x, rootRect.y, - rootRect.width, rootRect.height, - (Rectangle) commitRequests.get(root)); - else - commitRequests.put(root, rootRect); - } - } - } - - /** - * Copies the buffer to the screen. Note that the root component here is - * not necessarily the component with which the offscreen buffer is - * associated. The offscreen buffers are always allocated for the toplevel - * windows. However, painted is performed on lower-level heavyweight - * components too, if they contain Swing components. - * - * @param root the heavyweight component to blit upon - * @param rootRect the rectangle in the root component's coordinate space + * @param x the area to paint on screen, in comp coordinates + * @param y the area to paint on screen, in comp coordinates + * @param w the area to paint on screen, in comp coordinates + * @param h the area to paint on screen, in comp coordinates */ - private void blitBuffer(Component root, Rectangle rootRect) + void commitBuffer(Component comp, int x, int y, int w, int h) { - if (! root.isShowing()) - return; - - // Find the Window from which we use the backbuffer. - Component bufferRoot = root; - Rectangle bufferRect = rootRect.getBounds(); - if (!(bufferRoot instanceof Window)) + Component root = comp; + while (root != null + && ! (root instanceof Window || root instanceof Applet)) { - bufferRoot = SwingUtilities.getWindowAncestor(bufferRoot); - SwingUtilities.convertRectangleToAncestor(root, rootRect, bufferRoot); + x += root.getX(); + y += root.getY(); + root = root.getParent(); } Graphics g = root.getGraphics(); - Image buffer = (Image) offscreenBuffers.get(bufferRoot); + Image buffer = (Image) offscreenBuffers.get(root); // Make sure we have a sane clip at this point. - g.clipRect(rootRect.x, rootRect.y, rootRect.width, rootRect.height); - g.drawImage(buffer, rootRect.x - bufferRect.x, rootRect.y - bufferRect.y, - root); + g.clipRect(x, y, w, h); + g.drawImage(buffer, 0, 0, root); g.dispose(); - - } - - /** - * Finds and returns the nearest heavyweight parent for the specified - * component. If the component isn't contained inside a heavyweight parent, - * this returns null. - * - * @param comp the component - * - * @return the nearest heavyweight parent for the specified component or - * null if the component has no heavyweight ancestor - */ - private Component getHeavyweightParent(Component comp) - { - while (comp != null && comp.isLightweight()) - comp = comp.getParent(); - return comp; - } - - /** - * Commits the queued up back buffers to screen all at once. - */ - private void commitRemainingBuffers() - { - // We synchronize on dirtyComponents here because that is what - // paintDirtyRegions also synchronizes on while painting. - synchronized (dirtyComponents) - { - Set entrySet = commitRequests.entrySet(); - Iterator i = entrySet.iterator(); - while (i.hasNext()) - { - Map.Entry entry = (Map.Entry) i.next(); - Component root = (Component) entry.getKey(); - Rectangle area = (Rectangle) entry.getValue(); - blitBuffer(root, area); - i.remove(); - } - } } /** diff --git a/javax/swing/ScrollPaneLayout.java b/javax/swing/ScrollPaneLayout.java index 8ce8fd86f..2a16f26ea 100644 --- a/javax/swing/ScrollPaneLayout.java +++ b/javax/swing/ScrollPaneLayout.java @@ -46,6 +46,8 @@ import java.awt.LayoutManager; import java.awt.Rectangle; import java.io.Serializable; +import javax.swing.border.Border; + /** * ScrollPaneLayout * @author Andrew Selkirk @@ -277,6 +279,16 @@ public class ScrollPaneLayout width += rowHead.getPreferredSize().width; if (colHead != null && colHead.isVisible()) height += colHead.getPreferredSize().height; + + // Add insets of viewportBorder if present. + Border vpBorder = sc.getViewportBorder(); + if (vpBorder != null) + { + Insets i = vpBorder.getBorderInsets(sc); + width += i.left + i.right; + height += i.top + i.bottom; + } + Insets i = sc.getInsets(); return new Dimension(width + i.left + i.right, height + i.left + i.right); @@ -300,6 +312,15 @@ public class ScrollPaneLayout != JScrollPane.HORIZONTAL_SCROLLBAR_NEVER) height += sc.getHorizontalScrollBar().getMinimumSize().height; + // Add insets of viewportBorder if present. + Border vpBorder = sc.getViewportBorder(); + if (vpBorder != null) + { + i = vpBorder.getBorderInsets(sc); + width += i.left + i.right; + height += i.top + i.bottom; + } + return new Dimension(width, height); } @@ -342,6 +363,15 @@ public class ScrollPaneLayout int y1 = 0, y2 = 0, y3 = 0, y4 = 0; Rectangle scrollPaneBounds = SwingUtilities.calculateInnerArea(sc, null); + // If there is a viewportBorder, remove its insets from the available + // space. + Border vpBorder = sc.getViewportBorder(); + Insets vpi; + if (vpBorder != null) + vpi = vpBorder.getBorderInsets(sc); + else + vpi = new Insets(0, 0, 0, 0); + x1 = scrollPaneBounds.x; y1 = scrollPaneBounds.y; x4 = scrollPaneBounds.x + scrollPaneBounds.width; @@ -404,7 +434,9 @@ public class ScrollPaneLayout // now set the layout if (viewport != null) - viewport.setBounds(new Rectangle(x2, y2, x3 - x2, y3 - y2)); + viewport.setBounds(new Rectangle(x2 + vpi.left, y2 + vpi.top, + x3 - x2 - vpi.left - vpi.right, + y3 - y2 - vpi.top - vpi.bottom)); if (colHead != null) colHead.setBounds(new Rectangle(x2, y1, x3 - x2, y2 - y1)); @@ -415,7 +447,7 @@ public class ScrollPaneLayout if (showVsb) { vsb.setVisible(true); - vsb.setBounds(new Rectangle(x3, y2, x4 - x3, y3 - y2)); + vsb.setBounds(new Rectangle(x3, y2, x4 - x3, y3 - y2 )); } else if (vsb != null) vsb.setVisible(false); @@ -423,7 +455,7 @@ public class ScrollPaneLayout if (showHsb) { hsb.setVisible(true); - hsb.setBounds(new Rectangle(x2, y3, x3 - x2, y4 - y3)); + hsb.setBounds(new Rectangle(x2 , y3, x3 - x2, y4 - y3)); } else if (hsb != null) hsb.setVisible(false); diff --git a/javax/swing/SwingUtilities.java b/javax/swing/SwingUtilities.java index 2823367ce..6ff0b3346 100644 --- a/javax/swing/SwingUtilities.java +++ b/javax/swing/SwingUtilities.java @@ -40,7 +40,6 @@ package javax.swing; import java.applet.Applet; import java.awt.Component; -import java.awt.ComponentOrientation; import java.awt.Container; import java.awt.FontMetrics; import java.awt.Frame; @@ -721,38 +720,35 @@ public class SwingUtilities // Fix up the orientation-based horizontal positions. - if (horizontalTextPosition == LEADING) - { - if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT) - horizontalTextPosition = RIGHT; - else - horizontalTextPosition = LEFT; - } - else if (horizontalTextPosition == TRAILING) + boolean ltr = true; + if (c != null && ! c.getComponentOrientation().isLeftToRight()) + ltr = false; + + switch (horizontalTextPosition) { - if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT) - horizontalTextPosition = LEFT; - else - horizontalTextPosition = RIGHT; + case LEADING: + horizontalTextPosition = ltr ? LEFT : RIGHT; + break; + case TRAILING: + horizontalTextPosition = ltr ? RIGHT : LEFT; + break; + default: + // Nothing to do in the other cases. } // Fix up the orientation-based alignments. - - if (horizontalAlignment == LEADING) - { - if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT) - horizontalAlignment = RIGHT; - else - horizontalAlignment = LEFT; - } - else if (horizontalAlignment == TRAILING) + switch (horizontalAlignment) { - if (c.getComponentOrientation() == ComponentOrientation.RIGHT_TO_LEFT) - horizontalAlignment = LEFT; - else - horizontalAlignment = RIGHT; + case LEADING: + horizontalAlignment = ltr ? LEFT : RIGHT; + break; + case TRAILING: + horizontalAlignment = ltr ? RIGHT : LEFT; + break; + default: + // Nothing to do in the other cases. } - + return layoutCompoundLabelImpl(c, fm, text, icon, verticalAlignment, horizontalAlignment, @@ -921,104 +917,108 @@ public class SwingUtilities iconR.width = icon.getIconWidth(); iconR.height = icon.getIconHeight(); } + if (text == null || text.equals("")) { textIconGap = 0; textR.width = 0; textR.height = 0; + text = ""; } else { + int availableWidth = viewR.width; + if (horizontalTextPosition != CENTER) + availableWidth -= iconR.width + textIconGap; View html = c == null ? null : (View) c.getClientProperty(BasicHTML.propertyKey); if (html != null) { textR.width = (int) html.getPreferredSpan(View.X_AXIS); + textR.width = Math.min(availableWidth, textR.width); textR.height = (int) html.getPreferredSpan(View.Y_AXIS); } else { int fromIndex = 0; textR.width = fm.stringWidth(text); - textR.height = fm.getHeight(); - while (text.indexOf('\n', fromIndex) != -1) + textR.height = fm.getHeight(); + if (textR.width > availableWidth) { - textR.height += fm.getHeight(); - fromIndex = text.indexOf('\n', fromIndex) + 1; + text = clipString(c, fm, text, availableWidth); + textR.width = fm.stringWidth(text); } } } - // Work out the position of text and icon, assuming the top-left coord + // Work out the position of text, assuming the top-left coord // starts at (0,0). We will fix that up momentarily, after these // "position" decisions are made and we look at alignment. - switch (horizontalTextPosition) + switch (verticalTextPosition) { - case LEFT: - textR.x = 0; - iconR.x = textR.width + textIconGap; + case TOP: + textR.y = horizontalTextPosition == CENTER ? + - textR.height - textIconGap : 0; break; - case RIGHT: - iconR.x = 0; - textR.x = iconR.width + textIconGap; + case BOTTOM: + textR.y = horizontalTextPosition == CENTER ? + iconR.height + textIconGap : iconR.height - textR.height; break; case CENTER: - int centerLine = Math.max(textR.width, iconR.width) / 2; - textR.x = centerLine - textR.width/2; - iconR.x = centerLine - iconR.width/2; + textR.y = iconR.height / 2 - textR.height / 2; break; } - switch (verticalTextPosition) + switch (horizontalTextPosition) { - case TOP: - textR.y = 0; - iconR.y = (horizontalTextPosition == CENTER - ? textR.height + textIconGap : 0); + case LEFT: + textR.x = -(textR.width + textIconGap); break; - case BOTTOM: - iconR.y = 0; - textR.y = (horizontalTextPosition == CENTER - ? iconR.height + textIconGap - : Math.max(iconR.height - textR.height, 0)); + case RIGHT: + textR.x = iconR.width + textIconGap; break; case CENTER: - int centerLine = Math.max(textR.height, iconR.height) / 2; - textR.y = centerLine - textR.height/2; - iconR.y = centerLine - iconR.height/2; + textR.x = iconR.width / 2 - textR.width / 2; break; } + // The two rectangles are laid out correctly now, but only assuming // that their upper left corner is at (0,0). If we have any alignment other // than TOP and LEFT, we need to adjust them. - Rectangle u = textR.union(iconR); - int horizontalAdjustment = viewR.x; - int verticalAdjustment = viewR.y; + // These coordinates specify the rectangle that contains both the + // icon and text. Move it so that it fullfills the alignment properties. + int lx = Math.min(iconR.x, textR.x); + int lw = Math.max(iconR.x + iconR.width, textR.x + textR.width) - lx; + int ly = Math.min(iconR.y, textR.y); + int lh = Math.max(iconR.y + iconR.height, textR.y + textR.height) - ly; + int horizontalAdjustment = 0; + int verticalAdjustment = 0; switch (verticalAlignment) { case TOP: + verticalAdjustment = viewR.y - ly; break; case BOTTOM: - verticalAdjustment += (viewR.height - u.height); + verticalAdjustment = viewR.y + viewR.height - ly - lh; break; case CENTER: - verticalAdjustment += ((viewR.height/2) - (u.height/2)); + verticalAdjustment = viewR.y + viewR.height / 2 - ly - lh / 2; break; } switch (horizontalAlignment) { case LEFT: + horizontalAdjustment = viewR.x - lx; break; case RIGHT: - horizontalAdjustment += (viewR.width - u.width); + horizontalAdjustment = viewR.x + viewR.width - lx - lw; break; case CENTER: - horizontalAdjustment += ((viewR.width/2) - (u.width/2)); + horizontalAdjustment = (viewR.x + (viewR.width / 2)) - (lx + (lw / 2)); break; } - iconR.x += horizontalAdjustment; iconR.y += verticalAdjustment; @@ -1028,6 +1028,48 @@ public class SwingUtilities return text; } + /** + * The method clips the specified string so that it fits into the + * available width. It is only called when the text really doesn't fit, + * so we don't need to check that again. + * + * @param c the component + * @param fm the font metrics + * @param text the text + * @param availableWidth the available width + * + * @return the clipped string + */ + private static String clipString(JComponent c, FontMetrics fm, String text, + int availableWidth) + { + String dots = "..."; + int dotsWidth = fm.stringWidth(dots); + char[] string = text.toCharArray(); + int endIndex = string.length; + while (fm.charsWidth(string, 0, endIndex) + dotsWidth > availableWidth + && endIndex > 0) + endIndex--; + String clipped; + if (string.length >= endIndex + 3) + { + string[endIndex] = '.'; + string[endIndex + 1] = '.'; + string[endIndex + 2] = '.'; + clipped = new String(string, 0, endIndex + 3); + } + else + { + char[] clippedChars = new char[string.length + 3]; + System.arraycopy(string, 0, clippedChars, 0, string.length); + clippedChars[endIndex] = '.'; + clippedChars[endIndex + 1] = '.'; + clippedChars[endIndex + 2] = '.'; + clipped = new String(clippedChars, 0, endIndex + 3); + } + return clipped; + } + /** * Calls {@link java.awt.EventQueue#invokeLater} with the * specified {@link Runnable}. diff --git a/javax/swing/event/EventListenerList.java b/javax/swing/event/EventListenerList.java index f76dfa3fe..1568039f0 100644 --- a/javax/swing/event/EventListenerList.java +++ b/javax/swing/event/EventListenerList.java @@ -37,6 +37,9 @@ exception statement from your version. */ package javax.swing.event; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.lang.reflect.Array; import java.util.EventListener; @@ -304,4 +307,51 @@ public class EventListenerList } return buf.toString(); } + + /** + * Serializes an instance to an ObjectOutputStream. + * + * @param out the stream to serialize to + * + * @throws IOException if something goes wrong + */ + private void writeObject(ObjectOutputStream out) + throws IOException + { + out.defaultWriteObject(); + for (int i = 0; i < listenerList.length; i += 2) + { + Class cl = (Class) listenerList[i]; + EventListener l = (EventListener) listenerList[i + 1]; + if (l != null && l instanceof Serializable) + { + out.writeObject(cl.getName()); + out.writeObject(l); + } + } + // Write end marker. + out.writeObject(null); + } + + /** + * Deserializes an instance from an ObjectInputStream. + * + * @param in the input stream + * + * @throws ClassNotFoundException if a serialized class can't be found + * @throws IOException if something goes wrong + */ + private <T extends EventListener> void readObject(ObjectInputStream in) + throws ClassNotFoundException, IOException + { + listenerList = NO_LISTENERS; + in.defaultReadObject(); + Object type; + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + while ((type = in.readObject()) != null) + { + EventListener l = (EventListener) in.readObject(); + add(((Class<T>) Class.forName((String) type, true, cl)), (T) l); + } + } } diff --git a/javax/swing/filechooser/FileSystemView.java b/javax/swing/filechooser/FileSystemView.java index 84b80dd40..26ca4860c 100644 --- a/javax/swing/filechooser/FileSystemView.java +++ b/javax/swing/filechooser/FileSystemView.java @@ -37,6 +37,8 @@ exception statement from your version. */ package javax.swing.filechooser; +import gnu.classpath.NotImplementedException; + import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -169,16 +171,12 @@ public abstract class FileSystemView * @return A default {@link FileSystemView} appropriate for the platform. */ public static FileSystemView getFileSystemView() + throws NotImplementedException { if (defaultFileSystemView == null) { - if (File.separator.equals("/")) - defaultFileSystemView = new UnixFileSystemView(); - // FIXME: need to implement additional views - // else if (File.Separator.equals("\")) - // return new Win32FileSystemView(); - // else - // return new GenericFileSystemView(); + // FIXME: We need to support other file systems too. + defaultFileSystemView = new UnixFileSystemView(); } return defaultFileSystemView; } diff --git a/javax/swing/plaf/basic/BasicButtonListener.java b/javax/swing/plaf/basic/BasicButtonListener.java index 042192b62..c99de2c70 100644 --- a/javax/swing/plaf/basic/BasicButtonListener.java +++ b/javax/swing/plaf/basic/BasicButtonListener.java @@ -54,15 +54,79 @@ import java.beans.PropertyChangeListener; import javax.swing.AbstractAction; import javax.swing.AbstractButton; +import javax.swing.Action; +import javax.swing.ActionMap; import javax.swing.ButtonModel; +import javax.swing.InputMap; import javax.swing.JComponent; import javax.swing.SwingUtilities; +import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; +import javax.swing.plaf.ActionMapUIResource; +import javax.swing.plaf.ButtonUI; -public class BasicButtonListener implements MouseListener, MouseMotionListener, - FocusListener, ChangeListener, PropertyChangeListener +public class BasicButtonListener + implements MouseListener, MouseMotionListener, FocusListener, ChangeListener, + PropertyChangeListener { + /** + * Implements the keyboard action for Swing buttons. + */ + private class ButtonAction + extends AbstractAction + { + /** + * The key for pressed action. + */ + static final String PRESSED = "pressed"; + + /** + * The key for released action. + */ + static final String RELEASED = "released"; + + /** + * Performs the action. + */ + public void actionPerformed(ActionEvent event) + { + Object cmd = getValue("__command__"); + AbstractButton b = (AbstractButton) event.getSource(); + ButtonModel m = b.getModel(); + if (PRESSED.equals(cmd)) + { + m.setArmed(true); + m.setPressed(true); + if (! b.isFocusOwner()) + b.requestFocus(); + } + else if (RELEASED.equals(cmd)) + { + m.setPressed(false); + m.setArmed(false); + } + } + + /** + * Indicates if this action is enabled. + * + * @param source the source of the action + * + * @return <code>true</code> when enabled, <code>false</code> otherwise + */ + public boolean isEnabled(Object source) + { + boolean enabled = true; + if (source instanceof AbstractButton) + { + AbstractButton b = (AbstractButton) source; + enabled = b.isEnabled(); + } + return enabled; + } + } + public BasicButtonListener(AbstractButton b) { // Do nothing here. @@ -86,16 +150,25 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener, false, false); TextLayout layout = new TextLayout(text, b.getFont(), frc); b.putClientProperty(BasicGraphicsUtils.CACHED_TEXT_LAYOUT, layout); + + // Update HTML renderer. + BasicHTML.updateRenderer(b, b.getText()); } - if (property.equals(AbstractButton.TEXT_CHANGED_PROPERTY)) + else if (property.equals(AbstractButton.CONTENT_AREA_FILLED_CHANGED_PROPERTY)) { - BasicHTML.updateRenderer(b, b.getText()); + checkOpacity(b); } } - + + /** + * Checks the <code>contentAreaFilled</code> property and updates the + * opaque property of the button. + * + * @param b the button to check + */ protected void checkOpacity(AbstractButton b) { - // TODO: What should be done here? + b.setOpaque(b.isContentAreaFilled()); } public void focusGained(FocusEvent e) @@ -120,6 +193,26 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener, public void installKeyboardActions(JComponent c) { + ButtonUI ui = ((AbstractButton) c).getUI(); + if (ui instanceof BasicButtonUI) + { + // Install InputMap. + BasicButtonUI basicUI = (BasicButtonUI) ui; + String prefix = basicUI.getPropertyPrefix(); + InputMap focusInputMap = + (InputMap) UIManager.get(prefix + "focusInputMap"); + SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, + focusInputMap); + + ActionMap am = (ActionMap) UIManager.get(prefix + "actionMap"); + if (am == null) + { + am = createDefaultActionMap(); + UIManager.put(prefix + "actionMap", am); + } + SwingUtilities.replaceUIActionMap(c, am); + } + c.getActionMap().put("pressed", new AbstractAction() { @@ -146,31 +239,46 @@ public class BasicButtonListener implements MouseListener, MouseMotionListener, } }); } - + + /** + * Creates and returns the default action map for Swing buttons. + * + * @return the default action map for Swing buttons + */ + private ActionMap createDefaultActionMap() + { + Action action = new ButtonAction(); + ActionMapUIResource am = new ActionMapUIResource(); + am.put(ButtonAction.PRESSED, action); + am.put(ButtonAction.RELEASED, action); + return am; + } + public void uninstallKeyboardActions(JComponent c) { - c.getActionMap().put("pressed", null); - c.getActionMap().put("released", null); + SwingUtilities.replaceUIActionMap(c, null); + SwingUtilities.replaceUIInputMap(c, JComponent.WHEN_FOCUSED, null); } public void stateChanged(ChangeEvent e) { - // TODO: What should be done here, if anything? + // Need to repaint when the button state changes. + ((AbstractButton) e.getSource()).repaint(); } public void mouseMoved(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } public void mouseDragged(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } public void mouseClicked(MouseEvent e) { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** diff --git a/javax/swing/plaf/basic/BasicButtonUI.java b/javax/swing/plaf/basic/BasicButtonUI.java index cdaec2543..e2493d156 100644 --- a/javax/swing/plaf/basic/BasicButtonUI.java +++ b/javax/swing/plaf/basic/BasicButtonUI.java @@ -42,12 +42,13 @@ import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; +import java.awt.Insets; import java.awt.Rectangle; +import java.beans.PropertyChangeListener; import javax.swing.AbstractButton; import javax.swing.ButtonModel; import javax.swing.Icon; -import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.LookAndFeel; @@ -64,6 +65,34 @@ import javax.swing.text.View; public class BasicButtonUI extends ButtonUI { /** + * Cached rectangle for layouting the label. Used in paint() and + * BasicGraphicsUtils.getPreferredButtonSize(). + */ + static Rectangle viewR = new Rectangle(); + + /** + * Cached rectangle for layouting the label. Used in paint() and + * BasicGraphicsUtils.getPreferredButtonSize(). + */ + static Rectangle iconR = new Rectangle(); + + /** + * Cached rectangle for layouting the label. Used in paint() and + * BasicGraphicsUtils.getPreferredButtonSize(). + */ + static Rectangle textR = new Rectangle(); + + /** + * The shared button UI. + */ + private static BasicButtonUI sharedUI; + + /** + * The shared BasicButtonListener. + */ + private static BasicButtonListener sharedListener; + + /** * A constant used to pad out elements in the button's layout and * preferred size calculations. */ @@ -87,7 +116,9 @@ public class BasicButtonUI extends ButtonUI */ public static ComponentUI createUI(final JComponent c) { - return new BasicButtonUI(); + if (sharedUI == null) + sharedUI = new BasicButtonUI(); + return sharedUI; } /** @@ -154,14 +185,29 @@ public class BasicButtonUI extends ButtonUI protected void installDefaults(AbstractButton b) { String prefix = getPropertyPrefix(); + // Install colors and font. LookAndFeel.installColorsAndFont(b, prefix + "background", prefix + "foreground", prefix + "font"); + // Install border. LookAndFeel.installBorder(b, prefix + "border"); + + // Install margin property. if (b.getMargin() == null || b.getMargin() instanceof UIResource) b.setMargin(UIManager.getInsets(prefix + "margin")); - b.setIconTextGap(UIManager.getInt(prefix + "textIconGap")); - b.setInputMap(JComponent.WHEN_FOCUSED, - (InputMap) UIManager.get(prefix + "focusInputMap")); + + // Install rollover property. + Object rollover = UIManager.get(prefix + "rollover"); + if (rollover != null) + LookAndFeel.installProperty(b, "rolloverEnabled", rollover); + + // Fetch default textShiftOffset. + defaultTextShiftOffset = UIManager.getInt(prefix + "textShiftOffset"); + + // Make button opaque if needed. + if (b.isContentAreaFilled()) + LookAndFeel.installProperty(b, "opaque", Boolean.TRUE); + else + LookAndFeel.installProperty(b, "opaque", Boolean.FALSE); } /** @@ -171,21 +217,10 @@ public class BasicButtonUI extends ButtonUI */ protected void uninstallDefaults(AbstractButton b) { - if (b.getFont() instanceof UIResource) - b.setFont(null); - if (b.getForeground() instanceof UIResource) - b.setForeground(null); - if (b.getBackground() instanceof UIResource) - b.setBackground(null); - if (b.getBorder() instanceof UIResource) - b.setBorder(null); - b.setIconTextGap(defaultTextIconGap); - if (b.getMargin() instanceof UIResource) - b.setMargin(null); + // The other properties aren't uninstallable. + LookAndFeel.uninstallBorder(b); } - protected BasicButtonListener listener; - /** * Creates and returns a new instance of {@link BasicButtonListener}. This * method provides a hook to make it easy for subclasses to install a @@ -197,7 +232,13 @@ public class BasicButtonUI extends ButtonUI */ protected BasicButtonListener createButtonListener(AbstractButton b) { - return new BasicButtonListener(b); + // Note: The RI always returns a new instance here. However, + // the BasicButtonListener class is perfectly suitable to be shared + // between multiple buttons, so we return a shared instance here + // for efficiency. + if (sharedListener == null) + sharedListener = new BasicButtonListener(b); + return sharedListener; } /** @@ -207,12 +248,15 @@ public class BasicButtonUI extends ButtonUI */ protected void installListeners(AbstractButton b) { - listener = createButtonListener(b); - b.addChangeListener(listener); - b.addPropertyChangeListener(listener); - b.addFocusListener(listener); - b.addMouseListener(listener); - b.addMouseMotionListener(listener); + BasicButtonListener listener = createButtonListener(b); + if (listener != null) + { + b.addChangeListener(listener); + b.addPropertyChangeListener(listener); + b.addFocusListener(listener); + b.addMouseListener(listener); + b.addMouseMotionListener(listener); + } } /** @@ -222,21 +266,29 @@ public class BasicButtonUI extends ButtonUI */ protected void uninstallListeners(AbstractButton b) { - b.removeChangeListener(listener); - b.removePropertyChangeListener(listener); - b.removeFocusListener(listener); - b.removeMouseListener(listener); - b.removeMouseMotionListener(listener); + BasicButtonListener listener = getButtonListener(b); + if (listener != null) + { + b.removeChangeListener(listener); + b.removePropertyChangeListener(listener); + b.removeFocusListener(listener); + b.removeMouseListener(listener); + b.removeMouseMotionListener(listener); + } } protected void installKeyboardActions(AbstractButton b) { - listener.installKeyboardActions(b); + BasicButtonListener listener = getButtonListener(b); + if (listener != null) + listener.installKeyboardActions(b); } protected void uninstallKeyboardActions(AbstractButton b) { - listener.uninstallKeyboardActions(b); + BasicButtonListener listener = getButtonListener(b); + if (listener != null) + listener.uninstallKeyboardActions(b); } /** @@ -254,6 +306,9 @@ public class BasicButtonUI extends ButtonUI { AbstractButton b = (AbstractButton) c; installDefaults(b); + // It is important to install the listeners before installing + // the keyboard actions, because the keyboard actions + // are actually installed on the listener instance. installListeners(b); installKeyboardActions(b); BasicHTML.updateRenderer(b, b.getText()); @@ -378,44 +433,50 @@ public class BasicButtonUI extends ButtonUI { AbstractButton b = (AbstractButton) c; - Rectangle tr = new Rectangle(); - Rectangle ir = new Rectangle(); - Rectangle vr = new Rectangle(); + Insets i = c.getInsets(); + viewR.x = i.left; + viewR.y = i.top; + viewR.width = c.getWidth() - i.left - i.right; + viewR.height = c.getHeight() - i.top - i.bottom; + textR.x = 0; + textR.y = 0; + textR.width = 0; + textR.height = 0; + iconR.x = 0; + iconR.y = 0; + iconR.width = 0; + iconR.height = 0; Font f = c.getFont(); - g.setFont(f); + Icon icon = b.getIcon(); + String text = b.getText(); + text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f), + text, icon, + b.getVerticalAlignment(), + b.getHorizontalAlignment(), + b.getVerticalTextPosition(), + b.getHorizontalTextPosition(), + viewR, iconR, textR, + text == null ? 0 + : b.getIconTextGap()); - if (b.isBorderPainted()) - SwingUtilities.calculateInnerArea(b, vr); - else - vr = SwingUtilities.getLocalBounds(b); - String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f), - b.getText(), - currentIcon(b), - b.getVerticalAlignment(), - b.getHorizontalAlignment(), - b.getVerticalTextPosition(), - b.getHorizontalTextPosition(), - vr, ir, tr, - b.getIconTextGap() - + defaultTextShiftOffset); - - if ((b.getModel().isArmed() && b.getModel().isPressed()) - || b.isSelected()) + ButtonModel model = b.getModel(); + if (model.isArmed() && model.isPressed()) paintButtonPressed(g, b); - - paintIcon(g, c, ir); + + if (icon != null) + paintIcon(g, c, iconR); if (text != null) { View html = (View) b.getClientProperty(BasicHTML.propertyKey); if (html != null) - html.paint(g, tr); + html.paint(g, textR); else - paintText(g, b, tr, text); + paintText(g, b, textR, text); } if (b.isFocusOwner() && b.isFocusPainted()) - paintFocus(g, b, vr, tr, ir); + paintFocus(g, b, viewR, textR, iconR); } /** @@ -455,7 +516,16 @@ public class BasicButtonUI extends ButtonUI Icon i = currentIcon(b); if (i != null) - i.paintIcon(c, g, iconRect.x, iconRect.y); + { + ButtonModel m = b.getModel(); + if (m.isPressed() && m.isArmed()) + { + int offs = getTextShiftOffset(); + i.paintIcon(c, g, iconRect.x + offs, iconRect.y + offs); + } + else + i.paintIcon(c, g, iconRect.x, iconRect.y); + } } /** @@ -524,4 +594,31 @@ public class BasicButtonUI extends ButtonUI textRect.y + fm.getAscent()); } } + + /** + * A helper method that finds the BasicButtonListener for the specified + * button. This is there because this UI class is stateless and + * shared for all buttons, and thus can't store the listener + * as instance field. (We store our shared instance in sharedListener, + * however, subclasses may override createButtonListener() and we would + * be lost in this case). + * + * @param b the button + * + * @return the UI event listener + */ + private BasicButtonListener getButtonListener(AbstractButton b) + { + // The listener gets installed as PropertyChangeListener, + // so look for it in the list of property change listeners. + PropertyChangeListener[] listeners = b.getPropertyChangeListeners(); + BasicButtonListener l = null; + for (int i = 0; listeners != null && l == null && i < listeners.length; + i++) + { + if (listeners[i] instanceof BasicButtonListener) + l = (BasicButtonListener) listeners[i]; + } + return l; + } } diff --git a/javax/swing/plaf/basic/BasicFileChooserUI.java b/javax/swing/plaf/basic/BasicFileChooserUI.java index e641a1c10..e1f8e4b28 100644 --- a/javax/swing/plaf/basic/BasicFileChooserUI.java +++ b/javax/swing/plaf/basic/BasicFileChooserUI.java @@ -444,10 +444,10 @@ public class BasicFileChooserUI extends FileChooserUI setDirectory(null); } lastSelected = path; - parentPath = path.substring(0, path.lastIndexOf("/") + 1); + parentPath = f.getParent(); if (f.isFile()) - setFileName(path.substring(path.lastIndexOf("/") + 1)); + setFileName(f.getName()); else if (filechooser.getFileSelectionMode() != JFileChooser.FILES_ONLY) setFileName(path); @@ -827,9 +827,9 @@ public class BasicFileChooserUI extends FileChooserUI installComponents(fc); installListeners(fc); - Object path = filechooser.getCurrentDirectory(); + File path = filechooser.getCurrentDirectory(); if (path != null) - parentPath = path.toString().substring(path.toString().lastIndexOf("/")); + parentPath = path.getParent(); } } diff --git a/javax/swing/plaf/basic/BasicGraphicsUtils.java b/javax/swing/plaf/basic/BasicGraphicsUtils.java index 1e84be932..4c270682d 100644 --- a/javax/swing/plaf/basic/BasicGraphicsUtils.java +++ b/javax/swing/plaf/basic/BasicGraphicsUtils.java @@ -748,7 +748,6 @@ public class BasicGraphicsUtils } } - /** * Determines the preferred width and height of an AbstractButton, * given the gap between the button’s text and icon. @@ -769,24 +768,31 @@ public class BasicGraphicsUtils public static Dimension getPreferredButtonSize(AbstractButton b, int textIconGap) { - Rectangle contentRect; - Rectangle viewRect; - Rectangle iconRect = new Rectangle(); - Rectangle textRect = new Rectangle(); - Insets insets = b.getInsets(); - - viewRect = new Rectangle(); - - /* java.awt.Toolkit.getFontMetrics is deprecated. However, it - * seems not obvious how to get to the correct FontMetrics object - * otherwise. The real problem probably is that the method - * javax.swing.SwingUtilities.layoutCompundLabel should take a - * LineMetrics, not a FontMetrics argument. But fixing this that - * would change the public API. - */ + // These cached rectangles are use here and in BasicButtonUI.paint(), + // so these two methods must never be executed concurrently. Maybe + // we must use other Rectangle instances here. OTOH, Swing is + // designed to be not thread safe, and every layout and paint operation + // should be performed from the EventDispatchThread, so it _should_ be + // OK to do this optimization. + Rectangle viewRect = BasicButtonUI.viewR; + viewRect.x = 0; + viewRect.y = 0; + viewRect.width = Short.MAX_VALUE; + viewRect.height = Short.MAX_VALUE; + Rectangle iconRect = BasicButtonUI.iconR; + iconRect.x = 0; + iconRect.y = 0; + iconRect.width = 0; + iconRect.height = 0; + Rectangle textRect = BasicButtonUI.textR; + textRect.x = 0; + textRect.y = 0; + textRect.width = 0; + textRect.height = 0; + SwingUtilities.layoutCompoundLabel( b, // for the component orientation - b.getToolkit().getFontMetrics(b.getFont()), // see comment above + b.getFontMetrics(b.getFont()), // see comment above b.getText(), b.getIcon(), b.getVerticalAlignment(), @@ -804,13 +810,12 @@ public class BasicGraphicsUtils * +------------------------+ +------------------------+ */ - contentRect = textRect.union(iconRect); - - return new Dimension(insets.left - + contentRect.width - + insets.right + b.getHorizontalAlignment(), - insets.top - + contentRect.height - + insets.bottom); + Rectangle contentRect = + SwingUtilities.computeUnion(textRect.x, textRect.y, textRect.width, + textRect.height, iconRect); + + Insets insets = b.getInsets(); + return new Dimension(insets.left + contentRect.width + insets.right, + insets.top + contentRect.height + insets.bottom); } } diff --git a/javax/swing/plaf/basic/BasicHTML.java b/javax/swing/plaf/basic/BasicHTML.java index 98c9cb277..6e26d5355 100644 --- a/javax/swing/plaf/basic/BasicHTML.java +++ b/javax/swing/plaf/basic/BasicHTML.java @@ -48,6 +48,7 @@ import java.io.StringReader; import javax.swing.JComponent; import javax.swing.SwingConstants; import javax.swing.event.DocumentEvent; +import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.EditorKit; @@ -107,6 +108,7 @@ public class BasicHTML editorKit = kit; document = doc; setView(view); + setSize(view.getPreferredSpan(X_AXIS), view.getPreferredSpan(Y_AXIS)); } /** @@ -151,6 +153,14 @@ public class BasicHTML } /** + * Overridden to forward to real view. + */ + public void setSize(float w, float h) + { + view.setSize(w, h); + } + + /** * Returns the real root view, regardless of the index. * * @param index not used here @@ -346,6 +356,22 @@ public class BasicHTML { return document; } + + /** + * Overridden to return null, as a RootView has no attributes on its own. + */ + public AttributeSet getAttributes() + { + return null; + } + + /** + * Overridden to provide an element for the view. + */ + public Element getElement() + { + return view.getElement(); + } } /** diff --git a/javax/swing/plaf/basic/BasicLabelUI.java b/javax/swing/plaf/basic/BasicLabelUI.java index 304e13ad7..1ec020b1c 100644 --- a/javax/swing/plaf/basic/BasicLabelUI.java +++ b/javax/swing/plaf/basic/BasicLabelUI.java @@ -119,13 +119,37 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener { JLabel lab = (JLabel) c; Insets insets = lab.getInsets(); - FontMetrics fm = lab.getFontMetrics(lab.getFont()); - layoutCL(lab, fm, lab.getText(), lab.getIcon(), vr, ir, tr); - Rectangle cr = SwingUtilities.computeUnion(tr.x, tr.y, tr.width, tr.height, - ir); - return new Dimension(insets.left + cr.width + insets.right, insets.top - + cr.height + insets.bottom); - + int insetsX = insets.left + insets.right; + int insetsY = insets.top + insets.bottom; + Icon icon = lab.getIcon(); + String text = lab.getText(); + Dimension ret; + if (icon == null && text == null) + ret = new Dimension(insetsX, insetsY); + else if (icon != null && text == null) + ret = new Dimension(icon.getIconWidth() + insetsX, + icon.getIconHeight() + insetsY); + else + { + FontMetrics fm = lab.getFontMetrics(lab.getFont()); + ir.x = 0; + ir.y = 0; + ir.width = 0; + ir.height = 0; + tr.x = 0; + tr.y = 0; + tr.width = 0; + tr.height = 0; + vr.x = 0; + vr.y = 0; + vr.width = Short.MAX_VALUE; + vr.height = Short.MAX_VALUE; + layoutCL(lab, fm, text, icon, vr, ir, tr); + Rectangle cr = SwingUtilities.computeUnion(tr.x, tr.y, tr.width, + tr.height, ir); + ret = new Dimension(cr.width + insetsX, cr.height + insetsY); + } + return ret; } /** @@ -166,13 +190,20 @@ public class BasicLabelUI extends LabelUI implements PropertyChangeListener { JLabel b = (JLabel) c; FontMetrics fm = g.getFontMetrics(); - vr = SwingUtilities.calculateInnerArea(c, vr); - - if (vr.width < 0) - vr.width = 0; - if (vr.height < 0) - vr.height = 0; + Insets i = c.getInsets(); + vr.x = i.left; + vr.y = i.right; + vr.width = c.getWidth() - i.left + i.right; + vr.height = c.getHeight() - i.top + i.bottom; + ir.x = 0; + ir.y = 0; + ir.width = 0; + ir.height = 0; + tr.x = 0; + tr.y = 0; + tr.width = 0; + tr.height = 0; Icon icon = (b.isEnabled()) ? b.getIcon() : b.getDisabledIcon(); String text = layoutCL(b, fm, b.getText(), icon, vr, ir, tr); diff --git a/javax/swing/plaf/basic/BasicLookAndFeel.java b/javax/swing/plaf/basic/BasicLookAndFeel.java index c056a2403..76d67b002 100644 --- a/javax/swing/plaf/basic/BasicLookAndFeel.java +++ b/javax/swing/plaf/basic/BasicLookAndFeel.java @@ -1218,10 +1218,10 @@ public abstract class BasicLookAndFeel extends LookAndFeel "ctrl UP", "requestFocus", "ctrl KP_UP", "requestFocus" }), - "TabbedPane.background", new ColorUIResource(light), + "TabbedPane.background", new ColorUIResource(192, 192, 192), "TabbedPane.contentBorderInsets", new InsetsUIResource(2, 2, 3, 3), - "TabbedPane.darkShadow", new ColorUIResource(shadow), - "TabbedPane.focus", new ColorUIResource(darkShadow), + "TabbedPane.darkShadow", new ColorUIResource(Color.black), + "TabbedPane.focus", new ColorUIResource(Color.black), "TabbedPane.focusInputMap", new UIDefaults.LazyInputMap(new Object[] { KeyStroke.getKeyStroke("ctrl DOWN"), "requestFocusForVisibleComponent", KeyStroke.getKeyStroke("KP_UP"), "navigateUp", @@ -1235,17 +1235,16 @@ public abstract class BasicLookAndFeel extends LookAndFeel KeyStroke.getKeyStroke("DOWN"), "navigateDown" }), "TabbedPane.font", new FontUIResource("Dialog", Font.PLAIN, 12), - "TabbedPane.foreground", new ColorUIResource(darkShadow), - "TabbedPane.highlight", new ColorUIResource(highLight), - "TabbedPane.light", new ColorUIResource(highLight), + "TabbedPane.foreground", new ColorUIResource(Color.black), + "TabbedPane.highlight", new ColorUIResource(Color.white), + "TabbedPane.light", new ColorUIResource(192, 192, 192), "TabbedPane.selectedTabPadInsets", new InsetsUIResource(2, 2, 2, 1), - "TabbedPane.shadow", new ColorUIResource(shadow), - "TabbedPane.tabbedPaneContentBorderInsets", new InsetsUIResource(3, 2, 1, 2), - "TabbedPane.tabbedPaneTabPadInsets", new InsetsUIResource(1, 1, 1, 1), + "TabbedPane.shadow", new ColorUIResource(128, 128, 128), "TabbedPane.tabsOpaque", Boolean.TRUE, "TabbedPane.tabAreaInsets", new InsetsUIResource(3, 2, 0, 2), "TabbedPane.tabInsets", new InsetsUIResource(0, 4, 1, 4), "TabbedPane.tabRunOverlay", new Integer(2), + "TabbedPane.tabsOverlapBorder", Boolean.FALSE, "TabbedPane.textIconGap", new Integer(4), "Table.ancestorInputMap", new UIDefaults.LazyInputMap(new Object[] { "ctrl DOWN", "selectNextRowChangeLead", diff --git a/javax/swing/plaf/basic/BasicMenuItemUI.java b/javax/swing/plaf/basic/BasicMenuItemUI.java index 6110aca66..85eefb9ee 100644 --- a/javax/swing/plaf/basic/BasicMenuItemUI.java +++ b/javax/swing/plaf/basic/BasicMenuItemUI.java @@ -449,6 +449,7 @@ public class BasicMenuItemUI extends MenuItemUI // Layout the menu item. The result gets stored in the rectangle // fields of this class. + resetRectangles(null); layoutMenuItem(m, accelText); // The union of the text and icon areas is the label area. @@ -700,6 +701,8 @@ public class BasicMenuItemUI extends MenuItemUI // Layout menu item. The result gets stored in the rectangle fields // of this class. + resetRectangles(m); + layoutMenuItem(m, accelText); // Paint the background. @@ -1271,6 +1274,33 @@ public class BasicMenuItemUI extends MenuItemUI } /** + * Resets the cached layout rectangles. If <code>i</code> is not null, then + * the view rectangle is set to the inner area of the component, otherwise + * it is set to (0, 0, Short.MAX_VALUE, Short.MAX_VALUE), this is needed + * for layouting. + * + * @param i the component for which to initialize the rectangles + */ + private void resetRectangles(JMenuItem i) + { + // Reset rectangles. + iconRect.setBounds(0, 0, 0, 0); + textRect.setBounds(0, 0, 0, 0); + accelRect.setBounds(0, 0, 0, 0); + checkIconRect.setBounds(0, 0, 0, 0); + arrowIconRect.setBounds(0, 0, 0, 0); + if (i == null) + viewRect.setBounds(0, 0, Short.MAX_VALUE, Short.MAX_VALUE); + else + { + Insets insets = i.getInsets(); + viewRect.setBounds(insets.left, insets.top, + i.getWidth() - insets.left - insets.right, + i.getHeight() - insets.top - insets.bottom); + } + } + + /** * A helper method that lays out the menu item. The layout is stored * in the fields of this class. * @@ -1282,21 +1312,6 @@ public class BasicMenuItemUI extends MenuItemUI int width = m.getWidth(); int height = m.getHeight(); - // Reset rectangles. - iconRect.setBounds(0, 0, 0, 0); - textRect.setBounds(0, 0, 0, 0); - accelRect.setBounds(0, 0, 0, 0); - checkIconRect.setBounds(0, 0, 0, 0); - arrowIconRect.setBounds(0, 0, 0, 0); - viewRect.setBounds(0, 0, width, height); - - // Substract insets to the view rect. - Insets insets = m.getInsets(); - viewRect.x += insets.left; - viewRect.y += insets.top; - viewRect.width -= insets.left + insets.right; - viewRect.height -= insets.top + insets.bottom; - // Fetch the fonts. Font font = m.getFont(); FontMetrics fm = m.getFontMetrics(font); diff --git a/javax/swing/plaf/basic/BasicRadioButtonUI.java b/javax/swing/plaf/basic/BasicRadioButtonUI.java index aed4d69d6..802b25339 100644 --- a/javax/swing/plaf/basic/BasicRadioButtonUI.java +++ b/javax/swing/plaf/basic/BasicRadioButtonUI.java @@ -129,9 +129,22 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI { AbstractButton b = (AbstractButton) c; - Rectangle tr = new Rectangle(); - Rectangle ir = new Rectangle(); - Rectangle vr = new Rectangle(); + Insets i = b.getInsets(); + Rectangle tr = textR; + textR.x = 0; + textR.y = 0; + textR.width = 0; + textR.height = 0; + Rectangle ir = iconR; + iconR.x = 0; + iconR.y = 0; + iconR.width = 0; + iconR.height = 0; + Rectangle vr = viewR; + viewR.x = i.left; + viewR.y = i.right; + viewR.width = b.getWidth() - i.left - i.right; + viewR.height = b.getHeight() - i.top - i.bottom; Font f = c.getFont(); @@ -149,13 +162,12 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI currentIcon = getDefaultIcon(); } - SwingUtilities.calculateInnerArea(b, vr); String text = SwingUtilities.layoutCompoundLabel(c, g.getFontMetrics(f), b.getText(), currentIcon, b.getVerticalAlignment(), b.getHorizontalAlignment(), b.getVerticalTextPosition(), b.getHorizontalTextPosition(), vr, ir, tr, b.getIconTextGap() + defaultTextShiftOffset); - + currentIcon.paintIcon(c, g, ir.x, ir.y); if (text != null) @@ -174,17 +186,25 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI // The other icon properties are ignored. AbstractButton b = (AbstractButton) c; - Rectangle contentRect; - Rectangle viewRect; - Rectangle iconRect = new Rectangle(); - Rectangle textRect = new Rectangle(); Insets insets = b.getInsets(); - + + String text = b.getText(); Icon i = b.getIcon(); if (i == null) i = getDefaultIcon(); - viewRect = new Rectangle(); + textR.x = 0; + textR.y = 0; + textR.width = 0; + textR.height = 0; + iconR.x = 0; + iconR.y = 0; + iconR.width = 0; + iconR.height = 0; + viewR.x = 0; + viewR.y = 0; + viewR.width = Short.MAX_VALUE; + viewR.height = Short.MAX_VALUE; SwingUtilities.layoutCompoundLabel( b, // for the component orientation @@ -195,17 +215,14 @@ public class BasicRadioButtonUI extends BasicToggleButtonUI b.getHorizontalAlignment(), b.getVerticalTextPosition(), b.getHorizontalTextPosition(), - viewRect, iconRect, textRect, + viewR, iconR, textR, defaultTextIconGap + defaultTextShiftOffset); - contentRect = textRect.union(iconRect); - - return new Dimension(insets.left - + contentRect.width - + insets.right + b.getHorizontalAlignment(), - insets.top - + contentRect.height - + insets.bottom); + Rectangle r = SwingUtilities.computeUnion(textR.x, textR.y, textR.width, + textR.height, iconR); + + return new Dimension(insets.left + r.width + insets.right, + insets.top + r.height + insets.bottom); } /** diff --git a/javax/swing/plaf/basic/BasicScrollBarUI.java b/javax/swing/plaf/basic/BasicScrollBarUI.java index 78e5168fc..ee246cbba 100644 --- a/javax/swing/plaf/basic/BasicScrollBarUI.java +++ b/javax/swing/plaf/basic/BasicScrollBarUI.java @@ -1228,8 +1228,12 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected void scrollByBlock(int direction) { + if (direction > 0) scrollbar.setValue(scrollbar.getValue() + scrollbar.getBlockIncrement(direction)); + else + scrollbar.setValue(scrollbar.getValue() + - scrollbar.getBlockIncrement(direction)); } /** @@ -1239,8 +1243,12 @@ public class BasicScrollBarUI extends ScrollBarUI implements LayoutManager, */ protected void scrollByUnit(int direction) { - scrollbar.setValue(scrollbar.getValue() - + scrollbar.getUnitIncrement(direction)); + if (direction > 0) + scrollbar.setValue(scrollbar.getValue() + + scrollbar.getUnitIncrement(direction)); + else + scrollbar.setValue(scrollbar.getValue() + - scrollbar.getUnitIncrement(direction)); } /** diff --git a/javax/swing/plaf/basic/BasicScrollPaneUI.java b/javax/swing/plaf/basic/BasicScrollPaneUI.java index a0616a8c1..69e352711 100644 --- a/javax/swing/plaf/basic/BasicScrollPaneUI.java +++ b/javax/swing/plaf/basic/BasicScrollPaneUI.java @@ -38,8 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Component; import java.awt.Dimension; import java.awt.Graphics; @@ -54,7 +52,6 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.AbstractAction; -import javax.swing.Action; import javax.swing.ActionMap; import javax.swing.InputMap; import javax.swing.JComponent; @@ -69,11 +66,13 @@ import javax.swing.Scrollable; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; +import javax.swing.border.Border; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ScrollPaneUI; +import javax.swing.plaf.UIResource; /** * A UI delegate for the {@link JScrollPane} component. @@ -281,7 +280,7 @@ public class BasicScrollPaneUI extends ScrollPaneUI // Scroll non scrollables. delta = wheel * SCROLL_NON_SCROLLABLES; } - scroll(bar, delta); + scroll(bar, wheel > 0 ? delta : -delta); } // If not, try to scroll horizontally else @@ -436,16 +435,24 @@ public class BasicScrollPaneUI extends ScrollPaneUI "ScrollPane.foreground", "ScrollPane.font"); LookAndFeel.installBorder(p, "ScrollPane.border"); + + // Install Viewport border. + Border vpBorder = p.getViewportBorder(); + if (vpBorder == null || vpBorder instanceof UIResource) + { + vpBorder = UIManager.getBorder("ScrollPane.viewportBorder"); + p.setViewportBorder(vpBorder); + } + p.setOpaque(true); } protected void uninstallDefaults(JScrollPane p) { - p.setForeground(null); - p.setBackground(null); - p.setFont(null); - p.setBorder(null); - scrollpane = null; + LookAndFeel.uninstallBorder(p); + Border vpBorder = p.getViewportBorder(); + if (vpBorder != null && vpBorder instanceof UIResource) + p.setViewportBorder(null); } public void installUI(final JComponent c) @@ -809,8 +816,12 @@ public class BasicScrollPaneUI extends ScrollPaneUI public void paint(Graphics g, JComponent c) { - // do nothing; the normal painting-of-children algorithm, along with - // ScrollPaneLayout, does all the relevant work. + Border vpBorder = scrollpane.getViewportBorder(); + if (vpBorder != null) + { + Rectangle r = scrollpane.getViewportBorderBounds(); + vpBorder.paintBorder(scrollpane, g, r.x, r.y, r.width, r.height); + } } /** diff --git a/javax/swing/plaf/basic/BasicSplitPaneDivider.java b/javax/swing/plaf/basic/BasicSplitPaneDivider.java index 06d32984e..95468caa9 100644 --- a/javax/swing/plaf/basic/BasicSplitPaneDivider.java +++ b/javax/swing/plaf/basic/BasicSplitPaneDivider.java @@ -38,12 +38,15 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Insets; import java.awt.LayoutManager; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; @@ -52,7 +55,7 @@ import java.beans.PropertyChangeListener; import javax.swing.JButton; import javax.swing.JSplitPane; -import javax.swing.SwingConstants; +import javax.swing.UIManager; import javax.swing.border.Border; /** @@ -72,6 +75,207 @@ public class BasicSplitPaneDivider extends Container implements PropertyChangeListener { /** + * The buttons used as one touch buttons. + */ + private class BasicOneTouchButton + extends JButton + { + /** + * Denotes a left button. + */ + static final int LEFT = 0; + + /** + * Denotes a right button. + */ + static final int RIGHT = 1; + + /** + * The x points for the arrow. + */ + private int[] xpoints; + + /** + * The y points for the arrow. + */ + private int[] ypoints; + + /** + * Either LEFT or RIGHT. + */ + private int direction; + + /** + * Creates a new instance. + * + * @param dir either LEFT or RIGHT + */ + BasicOneTouchButton(int dir) + { + direction = dir; + xpoints = new int[3]; + ypoints = new int[3]; + } + + /** + * Never allow borders. + */ + public void setBorder(Border b) + { + } + + /** + * Never allow focus traversal. + */ + public boolean isFocusTraversable() + { + return false; + } + + /** + * Paints the one touch button. + */ + public void paint(Graphics g) + { + if (splitPane != null) + { + // Fill background. + g.setColor(splitPane.getBackground()); + g.fillRect(0, 0, getWidth(), getHeight()); + + // Draw arrow. + int size; + if (direction == LEFT) + { + if (orientation == JSplitPane.VERTICAL_SPLIT) + { + size = Math.min(getHeight(), ONE_TOUCH_SIZE); + xpoints[0] = 0; + xpoints[1] = size / 2; + xpoints[2] = size; + ypoints[0] = size; + ypoints[1] = 0; + ypoints[2] = size; + } + else + { + size = Math.min(getWidth(), ONE_TOUCH_SIZE); + xpoints[0] = size; + xpoints[1] = 0; + xpoints[2] = size; + ypoints[0] = 0; + ypoints[1] = size / 2; + ypoints[2] = size; + } + } + else + { + if (orientation == JSplitPane.VERTICAL_SPLIT) + { + size = Math.min(getHeight(), ONE_TOUCH_SIZE); + xpoints[0] = 0; + xpoints[1] = size / 2; + xpoints[2] = size; + ypoints[0] = 0; + ypoints[1] = size; + ypoints[2] = 0; + } + else + { + size = Math.min(getWidth(), ONE_TOUCH_SIZE); + xpoints[0] = 0; + xpoints[1] = size; + xpoints[2] = 0; + ypoints[0] = 0; + ypoints[1] = size / 2; + ypoints[2] = size; + } + } + g.setColor(Color.BLACK); + g.fillPolygon(xpoints, ypoints, 3); + } + } + } + + /** + * Listens for actions on the one touch buttons. + */ + private class OneTouchAction + implements ActionListener + { + + public void actionPerformed(ActionEvent ev) + { + Insets insets = splitPane.getInsets(); + int lastLoc = splitPane.getLastDividerLocation(); + int currentLoc = splitPaneUI.getDividerLocation(splitPane); + int newLoc; + + if (ev.getSource() == leftButton) + { + if (orientation == JSplitPane.VERTICAL_SPLIT) + { + if (currentLoc + >= splitPane.getHeight() - insets.bottom - getHeight()) + { + newLoc = Math.min(splitPane.getMaximumDividerLocation(), + lastLoc); + } + else + { + newLoc = insets.top; + } + } + else + { + if (currentLoc + >= splitPane.getWidth() - insets.right - getWidth()) + { + newLoc = Math.min(splitPane.getMaximumDividerLocation(), + lastLoc); + } + else + { + newLoc = insets.left; + } + } + } + else + { + if (orientation == JSplitPane.VERTICAL_SPLIT) + { + if (currentLoc == insets.top) + { + newLoc = Math.min(splitPane.getMaximumDividerLocation(), + lastLoc); + } + else + { + newLoc = splitPane.getHeight() - insets.top - getHeight(); + } + } + else + { + if (currentLoc == insets.left) + { + newLoc = Math.min(splitPane.getMaximumDividerLocation(), + lastLoc); + } + else + { + newLoc = splitPane.getWidth() - insets.left - getWidth(); + } + } + } + if (currentLoc != newLoc) + { + splitPane.setDividerLocation(newLoc); + splitPane.setLastDividerLocation(currentLoc); + } + } + } + + /** * Determined using the <code>serialver</code> tool of Apple/Sun JDK 1.3.1 * on MacOS X 10.1.5. */ @@ -161,6 +365,14 @@ public class BasicSplitPaneDivider extends Container transient int currentDividerLocation = 1; /** + * Indicates if the ont touch buttons are laid out centered or at the + * top/left. + * + * Package private to avoid accessor method. + */ + boolean centerOneTouchButtons; + + /** * Constructs a new divider. * * @param ui the UI delegate of the enclosing <code>JSplitPane</code>. @@ -170,6 +382,8 @@ public class BasicSplitPaneDivider extends Container setLayout(new DividerLayout()); setBasicSplitPaneUI(ui); setDividerSize(splitPane.getDividerSize()); + centerOneTouchButtons = + UIManager.getBoolean("SplitPane.centerOneTouchButtons"); } /** @@ -202,7 +416,8 @@ public class BasicSplitPaneDivider extends Container addMouseMotionListener(mouseHandler); hiddenDivider = splitPaneUI.getNonContinuousLayoutDivider(); orientation = splitPane.getOrientation(); - oneTouchExpandableChanged(); + if (splitPane.isOneTouchExpandable()) + oneTouchExpandableChanged(); } } @@ -293,7 +508,12 @@ public class BasicSplitPaneDivider extends Container */ public Dimension getPreferredSize() { - return getLayout().preferredLayoutSize(this); + Dimension d; + if (orientation == JSplitPane.HORIZONTAL_SPLIT) + d = new Dimension(getDividerSize(), 1); + else + d = new Dimension(1, getDividerSize()); + return d; } /** @@ -320,11 +540,9 @@ public class BasicSplitPaneDivider extends Container else if (e.getPropertyName().equals(JSplitPane.ORIENTATION_PROPERTY)) { orientation = splitPane.getOrientation(); - if (splitPane.isOneTouchExpandable()) - { - layout(); - repaint(); - } + invalidate(); + if (splitPane != null) + splitPane.revalidate(); } else if (e.getPropertyName().equals(JSplitPane.DIVIDER_SIZE_PROPERTY)) dividerSize = splitPane.getDividerSize(); @@ -345,11 +563,6 @@ public class BasicSplitPaneDivider extends Container dividerSize = getSize(); border.paintBorder(this, g, 0, 0, dividerSize.width, dividerSize.height); } - if (splitPane.isOneTouchExpandable()) - { - ((BasicArrowButton) rightButton).paint(g); - ((BasicArrowButton) leftButton).paint(g); - } } /** @@ -361,31 +574,23 @@ public class BasicSplitPaneDivider extends Container if (splitPane.isOneTouchExpandable()) { leftButton = createLeftOneTouchButton(); - rightButton = createRightOneTouchButton(); - add(leftButton); - add(rightButton); + if (leftButton != null) + leftButton.addActionListener(new OneTouchAction()); - leftButton.addMouseListener(mouseHandler); - rightButton.addMouseListener(mouseHandler); + rightButton = createRightOneTouchButton(); + if (rightButton != null) + rightButton.addActionListener(new OneTouchAction()); - // Set it to 1. - currentDividerLocation = 1; - } - else - { + // Only add them when both are non-null. if (leftButton != null && rightButton != null) - { - leftButton.removeMouseListener(mouseHandler); - rightButton.removeMouseListener(mouseHandler); - - remove(leftButton); - remove(rightButton); - leftButton = null; - rightButton = null; + { + add(leftButton); + add(rightButton); } } - layout(); - repaint(); + invalidate(); + if (splitPane != null) + splitPane.revalidate(); } /** @@ -396,12 +601,9 @@ public class BasicSplitPaneDivider extends Container */ protected JButton createLeftOneTouchButton() { - int dir = SwingConstants.WEST; - if (orientation == JSplitPane.VERTICAL_SPLIT) - dir = SwingConstants.NORTH; - JButton button = new BasicArrowButton(dir); - button.setBorder(null); - + JButton button = new BasicOneTouchButton(BasicOneTouchButton.LEFT); + button.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE)); + button.setRequestFocusEnabled(false); return button; } @@ -413,11 +615,9 @@ public class BasicSplitPaneDivider extends Container */ protected JButton createRightOneTouchButton() { - int dir = SwingConstants.EAST; - if (orientation == JSplitPane.VERTICAL_SPLIT) - dir = SwingConstants.SOUTH; - JButton button = new BasicArrowButton(dir); - button.setBorder(null); + JButton button = new BasicOneTouchButton(BasicOneTouchButton.RIGHT); + button.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE)); + button.setRequestFocusEnabled(false); return button; } @@ -521,25 +721,6 @@ public class BasicSplitPaneDivider extends Container */ public void mousePressed(MouseEvent e) { - if (splitPane.isOneTouchExpandable()) - { - if (e.getSource() == leftButton) - { - currentDividerLocation--; - if (currentDividerLocation < 0) - currentDividerLocation = 0; - moveDividerTo(currentDividerLocation); - return; - } - else if (e.getSource() == rightButton) - { - currentDividerLocation++; - if (currentDividerLocation > 2) - currentDividerLocation = 2; - moveDividerTo(currentDividerLocation); - return; - } - } isDragging = true; currentDividerLocation = 1; if (orientation == JSplitPane.HORIZONTAL_SPLIT) @@ -797,10 +978,64 @@ public class BasicSplitPaneDivider extends Container */ public void layoutContainer(Container c) { - if (splitPane.isOneTouchExpandable()) + if (leftButton != null && rightButton != null + && c == BasicSplitPaneDivider.this) { - changeButtonOrientation(); - positionButtons(); + if (splitPane.isOneTouchExpandable()) + { + Insets insets = getInsets(); + if (orientation == JSplitPane.HORIZONTAL_SPLIT) + { + int size = getWidth() - insets.left - insets.right; + size = Math.max(size, 0); + size = Math.min(size, ONE_TOUCH_SIZE); + int x, y; + if (centerOneTouchButtons) + { + y = insets.top; + x = (getWidth() - size) / 2; + } + else + { + x = insets.left; + y = 0; + } + + leftButton.setBounds(x, y + ONE_TOUCH_OFFSET, size, + size * 2); + rightButton.setBounds(x, y + ONE_TOUCH_OFFSET + + ONE_TOUCH_SIZE * 2, size, size * 2); + } + else + { + int size = getHeight() - insets.top - insets.bottom; + size = Math.max(size, 0); + size = Math.min(size, ONE_TOUCH_SIZE); + int x, y; + if (centerOneTouchButtons) + { + x = insets.left; + y = (getHeight() - size) / 2; + } + else + { + x = 0; + y = insets.top; + } + leftButton.setBounds(x + ONE_TOUCH_OFFSET, y, size * 2, + size); + rightButton.setBounds(x + ONE_TOUCH_OFFSET + + ONE_TOUCH_SIZE * 2, y, size * 2, + size); + } + } + else + { + // The JDK sets this bounds for disabled one touch buttons, so + // do we. + leftButton.setBounds(-5, -5, 1, 1); + rightButton.setBounds(-5, -5, 1, 1); + } } } @@ -838,50 +1073,5 @@ public class BasicSplitPaneDivider extends Container // Do nothing. } - /** - * This method changes the button orientation when the orientation of the - * SplitPane changes. - */ - private void changeButtonOrientation() - { - if (orientation == JSplitPane.HORIZONTAL_SPLIT) - { - ((BasicArrowButton) rightButton).setDirection(SwingConstants.EAST); - ((BasicArrowButton) leftButton).setDirection(SwingConstants.WEST); - } - else - { - ((BasicArrowButton) rightButton).setDirection(SwingConstants.SOUTH); - ((BasicArrowButton) leftButton).setDirection(SwingConstants.NORTH); - } - } - - /** - * This method sizes and positions the buttons. - */ - private void positionButtons() - { - int w = 0; - int h = 0; - if (orientation == JSplitPane.HORIZONTAL_SPLIT) - { - rightButton.setLocation(ONE_TOUCH_OFFSET, ONE_TOUCH_OFFSET); - leftButton.setLocation(ONE_TOUCH_OFFSET, - ONE_TOUCH_OFFSET + 2 * ONE_TOUCH_SIZE); - w = dividerSize - 2 * ONE_TOUCH_OFFSET; - h = 2 * ONE_TOUCH_SIZE; - } - else - { - leftButton.setLocation(ONE_TOUCH_OFFSET, ONE_TOUCH_OFFSET); - rightButton.setLocation(ONE_TOUCH_OFFSET + 2 * ONE_TOUCH_SIZE, - ONE_TOUCH_OFFSET); - h = dividerSize - 2 * ONE_TOUCH_OFFSET; - w = 2 * ONE_TOUCH_SIZE; - } - Dimension dims = new Dimension(w, h); - leftButton.setSize(dims); - rightButton.setSize(dims); - } } } diff --git a/javax/swing/plaf/basic/BasicSplitPaneUI.java b/javax/swing/plaf/basic/BasicSplitPaneUI.java index 2d5955974..6ef4c08ce 100644 --- a/javax/swing/plaf/basic/BasicSplitPaneUI.java +++ b/javax/swing/plaf/basic/BasicSplitPaneUI.java @@ -63,6 +63,7 @@ import javax.swing.JSlider; import javax.swing.JSplitPane; import javax.swing.KeyStroke; import javax.swing.LookAndFeel; +import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.plaf.ActionMapUIResource; @@ -107,13 +108,34 @@ public class BasicSplitPaneUI extends SplitPaneUI protected int[] sizes = new int[3]; /** + * This is used to determine if we are vertical or horizontal layout. + * In the JDK, the BasicVerticalLayoutManager seems to have no more + * methods implemented (as of JDK5), so we keep this state here. + */ + private int axis; + + /** * Creates a new instance. This is package private because the reference * implementation has no public constructor either. Still, we need to * call it from BasicVerticalLayoutManager. */ BasicHorizontalLayoutManager() { - // Nothing to do here. + this(SwingConstants.HORIZONTAL); + } + + /** + * Creates a new instance for a specified axis. This is provided for + * compatibility, since the BasicVerticalLayoutManager seems to have + * no more implementation in the RI, according to the specs. So + * we handle all the axis specific stuff here. + * + * @param a the axis, either SwingConstants#HORIZONTAL, + * or SwingConstants#VERTICAL + */ + BasicHorizontalLayoutManager(int a) + { + axis = a; } /** @@ -167,7 +189,12 @@ public class BasicSplitPaneUI extends SplitPaneUI */ protected int getAvailableSize(Dimension containerSize, Insets insets) { - return containerSize.width - insets.left - insets.right; + int size; + if (axis == SwingConstants.HORIZONTAL) + size = containerSize.width - insets.left - insets.right; + else + size = containerSize.height - insets.top - insets.bottom; + return size; } /** @@ -180,9 +207,15 @@ public class BasicSplitPaneUI extends SplitPaneUI */ protected int getInitialLocation(Insets insets) { + int loc = 0; if (insets != null) - return insets.left; - return 0; + { + if (axis == SwingConstants.HORIZONTAL) + loc = insets.left; + else + loc = insets.top; + } + return loc; } /** @@ -195,7 +228,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public float getLayoutAlignmentX(Container target) { - return target.getAlignmentX(); + return 0.0f; } /** @@ -208,7 +241,7 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public float getLayoutAlignmentY(Container target) { - return target.getAlignmentY(); + return 0.0f; } /** @@ -220,10 +253,19 @@ public class BasicSplitPaneUI extends SplitPaneUI */ protected int getPreferredSizeOfComponent(Component c) { + int size = 0; Dimension dims = c.getPreferredSize(); - if (dims != null) - return dims.width; - return 0; + if (axis == SwingConstants.HORIZONTAL) + { + if (dims != null) + size = dims.width; + } + else + { + if (dims != null) + size = dims.height; + } + return size; } /** @@ -235,7 +277,12 @@ public class BasicSplitPaneUI extends SplitPaneUI */ protected int getSizeOfComponent(Component c) { - return c.getWidth(); + int size; + if (axis == SwingConstants.HORIZONTAL) + size = c.getHeight(); + else + size = c.getWidth(); + return size; } /** @@ -313,27 +360,31 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public Dimension minimumLayoutSize(Container target) { + Dimension dim = new Dimension(); if (target instanceof JSplitPane) { JSplitPane split = (JSplitPane) target; - Insets insets = target.getInsets(); - - int height = 0; - int width = 0; + int primary = 0; + int secondary = 0; for (int i = 0; i < components.length; i++) { - if (components[i] == null) - continue; - Dimension dims = components[i].getMinimumSize(); - if (dims != null) + if (components[i] != null) { - width += dims.width; - height = Math.max(height, dims.height); + Dimension dims = components[i].getMinimumSize(); + primary += axis == SwingConstants.HORIZONTAL ? dims.width + : dims.height; + int sec = axis == SwingConstants.HORIZONTAL ? dims.height + : dims.width; + secondary = Math.max(sec, secondary); } } - return new Dimension(width, height); + int width = axis == SwingConstants.HORIZONTAL ? primary : secondary; + int height = axis == SwingConstants.VERTICAL ? secondary : primary; + + Insets i = splitPane.getInsets(); + dim.setSize(width + i.left + i.right, height + i.top + i.bottom); } - return null; + return dim; } /** @@ -347,28 +398,31 @@ public class BasicSplitPaneUI extends SplitPaneUI */ public Dimension preferredLayoutSize(Container target) { + Dimension dim = new Dimension(); if (target instanceof JSplitPane) { JSplitPane split = (JSplitPane) target; - Insets insets = target.getInsets(); - - int height = 0; - int width = 0; + int primary = 0; + int secondary = 0; for (int i = 0; i < components.length; i++) { - if (components[i] == null) - continue; - Dimension dims = components[i].getPreferredSize(); - if (dims != null) + if (components[i] != null) { - width += dims.width; - if (!(components[i] instanceof BasicSplitPaneDivider)) - height = Math.max(height, dims.height); + Dimension dims = components[i].getPreferredSize(); + primary += axis == SwingConstants.HORIZONTAL ? dims.width + : dims.height; + int sec = axis == SwingConstants.HORIZONTAL ? dims.height + : dims.width; + secondary = Math.max(sec, secondary); } } - return new Dimension(width, height); + int width = axis == SwingConstants.HORIZONTAL ? primary : secondary; + int height = axis == SwingConstants.VERTICAL ? secondary : primary; + + Insets i = splitPane.getInsets(); + dim.setSize(width + i.left + i.right, height + i.top + i.bottom); } - return null; + return dim; } /** @@ -425,11 +479,23 @@ public class BasicSplitPaneUI extends SplitPaneUI protected void setComponentToSize(Component c, int size, int location, Insets insets, Dimension containerSize) { - int w = size; - int h = containerSize.height - insets.top - insets.bottom; - int x = location; - int y = insets.top; - c.setBounds(x, y, w, h); + if (insets != null) + { + if (axis == SwingConstants.HORIZONTAL) + c.setBounds(location, insets.top, size, + containerSize.height - insets.top - insets.bottom); + else + c.setBounds(insets.left, location, + containerSize.width - insets.left - insets.right, + size); + } + else + { + if (axis == SwingConstants.HORIZONTAL) + c.setBounds(location, 0, size, containerSize.height); + else + c.setBounds(0, location, containerSize.width, size); + } } /** @@ -462,7 +528,6 @@ public class BasicSplitPaneUI extends SplitPaneUI resetSizeAt(1); } components[2] = divider; - resetSizeAt(2); } /** @@ -485,10 +550,13 @@ public class BasicSplitPaneUI extends SplitPaneUI int minimumSizeOfComponent(int index) { Dimension dims = components[index].getMinimumSize(); + int size = 0; if (dims != null) - return dims.width; - else - return 0; + if (axis == SwingConstants.HORIZONTAL) + size = dims.width; + else + size = dims.height; + return size; } } //end BasicHorizontalLayoutManager @@ -504,163 +572,11 @@ public class BasicSplitPaneUI extends SplitPaneUI extends BasicHorizontalLayoutManager { /** - * This method returns the height of the container minus the top and - * bottom inset. - * - * @param containerSize The size of the container. - * @param insets The insets of the container. - * - * @return The height minus top and bottom inset. - */ - protected int getAvailableSize(Dimension containerSize, Insets insets) - { - return containerSize.height - insets.top - insets.bottom; - } - - /** - * This method returns the top inset. - * - * @param insets The Insets to use. - * - * @return The top inset. - */ - protected int getInitialLocation(Insets insets) - { - return insets.top; - } - - /** - * This method returns the preferred height of the component. - * - * @param c The component to measure. - * - * @return The preferred height of the component. + * Creates a new instance. */ - protected int getPreferredSizeOfComponent(Component c) + public BasicVerticalLayoutManager() { - Dimension dims = c.getPreferredSize(); - if (dims != null) - return dims.height; - return 0; - } - - /** - * This method returns the current height of the component. - * - * @param c The component to measure. - * - * @return The current height of the component. - */ - protected int getSizeOfComponent(Component c) - { - return c.getHeight(); - } - - /** - * This method returns the minimum layout size. The minimum height is the - * sum of all the components' minimum heights. The minimum width is the - * maximum of all the components' minimum widths. - * - * @param container The container to measure. - * - * @return The minimum size. - */ - public Dimension minimumLayoutSize(Container container) - { - if (container instanceof JSplitPane) - { - JSplitPane split = (JSplitPane) container; - Insets insets = container.getInsets(); - - int height = 0; - int width = 0; - for (int i = 0; i < components.length; i++) - { - if (components[i] == null) - continue; - Dimension dims = components[i].getMinimumSize(); - if (dims != null) - { - height += dims.height; - width = Math.max(width, dims.width); - } - } - return new Dimension(width, height); - } - return null; - } - - /** - * This method returns the preferred layout size. The preferred height is - * the sum of all the components' preferred heights. The preferred width - * is the maximum of all the components' preferred widths. - * - * @param container The container to measure. - * - * @return The preferred size. - */ - public Dimension preferredLayoutSize(Container container) - { - if (container instanceof JSplitPane) - { - JSplitPane split = (JSplitPane) container; - Insets insets = container.getInsets(); - - int height = 0; - int width = 0; - for (int i = 0; i < components.length; i++) - { - if (components[i] == null) - continue; - Dimension dims = components[i].getPreferredSize(); - if (dims != null) - { - height += dims.height; - width = Math.max(width, dims.width); - } - } - return new Dimension(width, height); - } - return null; - } - - /** - * This method sets the bounds of the given component. The y coordinate is - * the location given. The x coordinate is the left inset. The height is - * the size given. The width is the container size minus the left and - * right inset. - * - * @param c The component to set bounds for. - * @param size The height. - * @param location The y coordinate. - * @param insets The insets to use. - * @param containerSize The container's size. - */ - protected void setComponentToSize(Component c, int size, int location, - Insets insets, Dimension containerSize) - { - int y = location; - int x = insets.left; - int h = size; - int w = containerSize.width - insets.left - insets.right; - c.setBounds(x, y, w, h); - } - - /** - * This method returns the minimum height of the component at the given - * index. - * - * @param index The index of the component to check. - * - * @return The minimum height of the given component. - */ - int minimumSizeOfComponent(int index) - { - Dimension dims = components[index].getMinimumSize(); - if (dims != null) - return dims.height; - else - return 0; + super(SwingConstants.VERTICAL); } } @@ -1007,8 +923,10 @@ public class BasicSplitPaneUI extends SplitPaneUI nonContinuousLayoutDivider = createDefaultNonContinuousLayoutDivider(); splitPane.add(divider, JSplitPane.DIVIDER); - // There is no need to add the nonContinuousLayoutDivider - splitPane.setDividerSize(UIManager.getInt("SplitPane.dividerSize")); + // There is no need to add the nonContinuousLayoutDivider. + dividerSize = UIManager.getInt("SplitPane.dividerSize"); + splitPane.setDividerSize(dividerSize); + divider.setDividerSize(dividerSize); splitPane.setOpaque(true); } diff --git a/javax/swing/plaf/basic/BasicTabbedPaneUI.java b/javax/swing/plaf/basic/BasicTabbedPaneUI.java index 11f25167d..21dcf0d29 100644 --- a/javax/swing/plaf/basic/BasicTabbedPaneUI.java +++ b/javax/swing/plaf/basic/BasicTabbedPaneUI.java @@ -76,7 +76,6 @@ import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.ActionMapUIResource; import javax.swing.plaf.ComponentUI; -import javax.swing.plaf.PanelUI; import javax.swing.plaf.TabbedPaneUI; import javax.swing.plaf.UIResource; import javax.swing.text.View; @@ -252,7 +251,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { public void mouseReleased(MouseEvent e) { - // Nothing to do here. + Object s = e.getSource(); + + // Event may originate from the viewport in + // SCROLL_TAB_LAYOUT mode. It is redisptached + // through the tabbed pane then. + if (tabPane != e.getSource()) + { + redispatchEvent(e); + e.setSource(s); + } } /** @@ -264,6 +272,16 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants public void mousePressed(MouseEvent e) { Object s = e.getSource(); + + // Event may originate from the viewport in + // SCROLL_TAB_LAYOUT mode. It is redisptached + // through the tabbed pane then. + if (tabPane != e.getSource()) + { + redispatchEvent(e); + e.setSource(s); + } + int placement = tabPane.getTabPlacement(); if (s == incrButton) @@ -298,47 +316,61 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants if(!decrButton.isEnabled()) return; - // The scroll location may be zero but the offset - // greater than zero because of an adjustement to - // make a partially visible tab completely visible. - if (currentScrollLocation > 0) - currentScrollLocation--; + // The scroll location may be zero but the offset + // greater than zero because of an adjustement to + // make a partially visible tab completely visible. + if (currentScrollLocation > 0) + currentScrollLocation--; - // Set the offset back to 0 and recompute it. - currentScrollOffset = 0; - - switch (placement) - { - case JTabbedPane.TOP: - case JTabbedPane.BOTTOM: - // Take the tab area inset into account. - if (currentScrollLocation > 0) - currentScrollOffset = getTabAreaInsets(placement).left; - // Recompute scroll offset. - for (int i = 0; i < currentScrollLocation; i++) - currentScrollOffset += rects[i].width; - break; - default: - // Take the tab area inset into account. - if (currentScrollLocation > 0) - currentScrollOffset = getTabAreaInsets(placement).top; + // Set the offset back to 0 and recompute it. + currentScrollOffset = 0; + + switch (placement) + { + case JTabbedPane.TOP: + case JTabbedPane.BOTTOM: + // Take the tab area inset into account. + if (currentScrollLocation > 0) + currentScrollOffset = getTabAreaInsets(placement).left; + // Recompute scroll offset. + for (int i = 0; i < currentScrollLocation; i++) + currentScrollOffset += rects[i].width; + break; + default: + // Take the tab area inset into account. + if (currentScrollLocation > 0) + currentScrollOffset = getTabAreaInsets(placement).top; + + for (int i = 0; i < currentScrollLocation; i++) + currentScrollOffset += rects[i].height; + } - for (int i = 0; i < currentScrollLocation; i++) - currentScrollOffset += rects[i].height; - } + updateViewPosition(); + updateButtons(); - updateViewPosition(); - updateButtons(); - - tabPane.repaint(); - } else if (tabPane.isEnabled()) + tabPane.repaint(); + } + else if (tabPane.isEnabled()) { int index = tabForCoordinate(tabPane, e.getX(), e.getY()); + if (!tabPane.isEnabledAt(index)) + return; + if (tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT && s == panel) + { scrollTab(index, placement); + + tabPane.setSelectedIndex(index); + tabPane.repaint(); + } + else + { + tabPane.setSelectedIndex(index); + tabPane.revalidate(); + tabPane.repaint(); + } - tabPane.setSelectedIndex(index); } } @@ -347,11 +379,22 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants * Receives notification when the mouse pointer has entered the tabbed * pane. * - * @param ev the mouse event + * @param e the mouse event */ - public void mouseEntered(MouseEvent ev) + public void mouseEntered(MouseEvent e) { - int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY()); + Object s = e.getSource(); + + // Event may originate from the viewport in + // SCROLL_TAB_LAYOUT mode. It is redisptached + // through the tabbed pane then. + if (tabPane != e.getSource()) + { + redispatchEvent(e); + e.setSource(s); + } + + int tabIndex = tabForCoordinate(tabPane, e.getX(), e.getY()); setRolloverTab(tabIndex); } @@ -359,10 +402,21 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants * Receives notification when the mouse pointer has exited the tabbed * pane. * - * @param ev the mouse event + * @param e the mouse event */ - public void mouseExited(MouseEvent ev) + public void mouseExited(MouseEvent e) { + Object s = e.getSource(); + + // Event may originate from the viewport in + // SCROLL_TAB_LAYOUT mode. It is redisptached + // through the tabbed pane then. + if (tabPane != e.getSource()) + { + redispatchEvent(e); + e.setSource(s); + } + setRolloverTab(-1); } @@ -374,9 +428,37 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void mouseMoved(MouseEvent ev) { + Object s = ev.getSource(); + + if (tabPane != ev.getSource()) + { + ev.setSource(tabPane); + tabPane.dispatchEvent(ev); + + ev.setSource(s); + } + int tabIndex = tabForCoordinate(tabPane, ev.getX(), ev.getY()); setRolloverTab(tabIndex); } + + /** Modifies the mouse event to originate from + * the tabbed pane and redispatches it. + * + * @param me + */ + void redispatchEvent(MouseEvent me) + { + me.setSource(tabPane); + Point viewPos = viewport.getViewPosition(); + viewPos.x -= viewport.getX(); + viewPos.y -= viewport.getY(); + me.translatePoint(-viewPos.x, -viewPos.y); + tabPane.dispatchEvent(me); + + me.translatePoint(viewPos.x, viewPos.y); + } + } /** @@ -396,20 +478,56 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void propertyChange(PropertyChangeEvent e) { - if (e.getPropertyName().equals("tabLayoutPolicy")) - { - currentScrollLocation = currentScrollOffset = 0; - - layoutManager = createLayoutManager(); - - tabPane.setLayout(layoutManager); - } - else if (e.getPropertyName().equals("tabPlacement") - && tabPane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + out: { - incrButton = createIncreaseButton(); - decrButton = createDecreaseButton(); + if (e.getPropertyName().equals("tabLayoutPolicy")) + { + currentScrollLocation = currentScrollOffset = 0; + + layoutManager = createLayoutManager(); + + tabPane.setLayout(layoutManager); + } + else if (e.getPropertyName().equals("tabPlacement") + && tabPane.getTabLayoutPolicy() + == JTabbedPane.SCROLL_TAB_LAYOUT) + { + incrButton = createIncreaseButton(); + decrButton = createDecreaseButton(); + + // If the tab placement value was changed of a tabbed pane + // in SCROLL_TAB_LAYOUT mode we investigate the change to + // implement the following behavior which was observed in + // the RI: + // The scrolling offset will be reset if we change to + // a direction which is orthogonal to the current + // direction and stays the same if it is parallel. + + int oldPlacement = ((Integer) e.getOldValue()).intValue(); + int newPlacement = ((Integer) e.getNewValue()).intValue(); + switch (newPlacement) + { + case JTabbedPane.TOP: + case JTabbedPane.BOTTOM: + if (oldPlacement == JTabbedPane.TOP + || oldPlacement == JTabbedPane.BOTTOM) + break out; + + currentScrollOffset = getTabAreaInsets(newPlacement).left; + break; + default: + if (oldPlacement == JTabbedPane.LEFT + || oldPlacement == JTabbedPane.RIGHT) + break out; + + currentScrollOffset = getTabAreaInsets(newPlacement).top; + } + + updateViewPosition(); + updateButtons(); + } } + tabPane.revalidate(); tabPane.repaint(); } @@ -784,6 +902,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants default: tabAreaHeight = calculateTabAreaHeight(tabPlacement, runCount, maxTabHeight); + compX = insets.left + contentBorderInsets.left; compY = tabAreaHeight + insets.top + contentBorderInsets.top; } @@ -838,7 +957,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants protected void normalizeTabRuns(int tabPlacement, int tabCount, int start, int max) { - Insets tabAreaInsets = getTabAreaInsets(tabPlacement); if (tabPlacement == SwingUtilities.TOP || tabPlacement == SwingUtilities.BOTTOM) { @@ -1325,7 +1443,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { super.layoutContainer(pane); int tabCount = tabPane.getTabCount(); - Point p = null; if (tabCount == 0) return; int tabPlacement = tabPane.getTabPlacement(); @@ -1512,7 +1629,7 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants */ public void updateUI() { - setUI((PanelUI) new ScrollingPanelUI()); + setUI(new ScrollingPanelUI()); } } @@ -1892,15 +2009,19 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants final void updateViewPosition() { Point p = viewport.getViewPosition(); - + + // The unneeded coordinate must be set to zero + // in order to correctly handle placement changes. switch (tabPane.getTabPlacement()) { case JTabbedPane.LEFT: case JTabbedPane.RIGHT: + p.x = 0; p.y = currentScrollOffset; break; default: p.x = currentScrollOffset; + p.y = 0; } viewport.setViewPosition(p); @@ -2331,7 +2452,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants paintTabBorder(g, tabPlacement, tabIndex, rect.x, rect.y, rect.width, rect.height, isSelected); - // Layout label. FontMetrics fm = getFontMetrics(); Icon icon = getIconForTab(tabIndex); @@ -2369,7 +2489,17 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Rectangle tabRect, Rectangle iconRect, Rectangle textRect, boolean isSelected) { - SwingUtilities.layoutCompoundLabel(metrics, title, icon, + // Reset the icon and text rectangles, as the result is not specified + // when the locations are not (0,0). + textRect.x = 0; + textRect.y = 0; + textRect.width = 0; + textRect.height = 0; + iconRect.x = 0; + iconRect.y = 0; + iconRect.width = 0; + iconRect.height = 0; + SwingUtilities.layoutCompoundLabel(tabPane, metrics, title, icon, SwingConstants.CENTER, SwingConstants.CENTER, SwingConstants.CENTER, @@ -2764,7 +2894,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int width = tabPane.getWidth(); int height = tabPane.getHeight(); Insets insets = tabPane.getInsets(); - Insets tabAreaInsets = getTabAreaInsets(tabPlacement); // Calculate coordinates of content area. int x = insets.left; @@ -2869,8 +2998,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int endgap = rects[selectedIndex].y + rects[selectedIndex].height - currentScrollOffset; - int diff = 0; - if (tabPlacement == SwingConstants.LEFT && startgap >= 0) { g.drawLine(x, y, x, startgap); @@ -2957,8 +3084,6 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants int endgap = rects[selectedIndex].y + rects[selectedIndex].height - currentScrollOffset; - int diff = 0; - if (tabPlacement == SwingConstants.RIGHT && startgap >= 0) { g.setColor(shadow); @@ -2988,8 +3113,13 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants } /** - * This method returns the tab bounds for the given index. - * + * <p>This method returns the bounds of a tab for the given index + * and shifts it by the current scrolling offset if the tabbed + * pane is in scrolling tab layout mode.</p> + * + * <p>Subclassses should retrievs a tab's bounds by this method + * if they want to find out whether the tab is currently visible.</p> + * * @param pane The JTabbedPane. * @param i The index to look for. * @@ -3000,6 +3130,26 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants // Need to re-layout container if tab does not exist. if (i >= rects.length) layoutManager.layoutContainer(pane); + + // Properly shift coordinates if scrolling has taken + // place. + if (pane.getTabLayoutPolicy() == JTabbedPane.SCROLL_TAB_LAYOUT) + { + Rectangle r = new Rectangle(rects[i]); + + switch(pane.getTabPlacement()) + { + case SwingConstants.TOP: + case SwingConstants.BOTTOM: + r.x -= currentScrollOffset; + break; + default: + r.y -= currentScrollOffset; + } + + return r; + } + return rects[i]; } @@ -3048,7 +3198,10 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants } /** - * This method returns the tab bounds in the given rectangle. + * <p>This method returns the tab bounds in the given rectangle.</p> + * + * <p>The returned rectangle will be shifted by the current scroll + * offset if the tabbed pane is in scrolling tab layout mode.</p>. * * @param tabIndex The index to get bounds for. * @param dest The rectangle to store bounds in. @@ -3324,21 +3477,20 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants Icon icon = getIconForTab(tabIndex); Insets insets = getTabInsets(tabPlacement, tabIndex); - int width = 0; + int width = insets.bottom + insets.right + 3; if (icon != null) { - Rectangle vr = new Rectangle(); - Rectangle ir = new Rectangle(); - Rectangle tr = new Rectangle(); - layoutLabel(tabPlacement, getFontMetrics(), tabIndex, - tabPane.getTitleAt(tabIndex), icon, vr, ir, tr, - tabIndex == tabPane.getSelectedIndex()); - width = tr.union(ir).width; + width += icon.getIconWidth() + textIconGap; } - else - width = metrics.stringWidth(tabPane.getTitleAt(tabIndex)); - width += insets.left + insets.right; + View v = getTextViewForTab(tabIndex); + if (v != null) + width += v.getPreferredSpan(View.X_AXIS); + else + { + String label = tabPane.getTitleAt(tabIndex); + width += metrics.stringWidth(label); + } return width; } @@ -3377,7 +3529,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { Insets insets = getTabAreaInsets(tabPlacement); int tabAreaHeight = horizRunCount * maxTabHeight - - (horizRunCount - 1) * tabRunOverlay; + - (horizRunCount - 1) + * getTabRunOverlay(tabPlacement); tabAreaHeight += insets.top + insets.bottom; @@ -3399,7 +3552,8 @@ public class BasicTabbedPaneUI extends TabbedPaneUI implements SwingConstants { Insets insets = getTabAreaInsets(tabPlacement); int tabAreaWidth = vertRunCount * maxTabWidth - - (vertRunCount - 1) * tabRunOverlay; + - (vertRunCount - 1) + * getTabRunOverlay(tabPlacement); tabAreaWidth += insets.left + insets.right; diff --git a/javax/swing/plaf/basic/BasicTableHeaderUI.java b/javax/swing/plaf/basic/BasicTableHeaderUI.java index abe7cab43..8a8eeb837 100644 --- a/javax/swing/plaf/basic/BasicTableHeaderUI.java +++ b/javax/swing/plaf/basic/BasicTableHeaderUI.java @@ -38,8 +38,6 @@ exception statement from your version. */ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; - import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; @@ -415,9 +413,8 @@ public class BasicTableHeaderUI extends TableHeaderUI } protected void installKeyboardActions() - throws NotImplementedException { - // TODO: Implement this properly. + // AFAICS, the RI does nothing here. } /** @@ -448,9 +445,8 @@ public class BasicTableHeaderUI extends TableHeaderUI } protected void uninstallKeyboardActions() - throws NotImplementedException { - // TODO: Implement this properly. + // AFAICS, the RI does nothing here. } /** diff --git a/javax/swing/plaf/basic/BasicTextUI.java b/javax/swing/plaf/basic/BasicTextUI.java index 8e9c8c949..dc30347f5 100644 --- a/javax/swing/plaf/basic/BasicTextUI.java +++ b/javax/swing/plaf/basic/BasicTextUI.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.basic; +import gnu.classpath.SystemProperties; + import java.awt.Color; import java.awt.Container; import java.awt.Dimension; @@ -71,6 +73,7 @@ import javax.swing.plaf.InputMapUIResource; import javax.swing.plaf.TextUI; import javax.swing.plaf.UIResource; import javax.swing.text.AbstractDocument; +import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.Caret; import javax.swing.text.DefaultCaret; @@ -120,6 +123,140 @@ public abstract class BasicTextUI extends TextUI } } + private static class FocusHandler + implements FocusListener + { + public void focusGained(FocusEvent e) + { + // Nothing to do here. + } + public void focusLost(FocusEvent e) + { + JTextComponent textComponent = (JTextComponent) e.getComponent(); + // Integrates Swing text components with the system clipboard: + // The idea is that if one wants to copy text around X11-style + // (select text and middle-click in the target component) the focus + // will move to the new component which gives the old focus owner the + // possibility to paste its selection into the clipboard. + if (!e.isTemporary() + && textComponent.getSelectionStart() + != textComponent.getSelectionEnd()) + { + SecurityManager sm = System.getSecurityManager(); + try + { + if (sm != null) + sm.checkSystemClipboardAccess(); + + Clipboard cb = Toolkit.getDefaultToolkit().getSystemSelection(); + if (cb != null) + { + StringSelection selection = new StringSelection( + textComponent.getSelectedText()); + cb.setContents(selection, selection); + } + } + catch (SecurityException se) + { + // Not allowed to access the clipboard: Ignore and + // do not access it. + } + catch (HeadlessException he) + { + // There is no AWT: Ignore and do not access the + // clipboard. + } + catch (IllegalStateException ise) + { + // Clipboard is currently unavaible. + } + } + } + } + + /** + * This FocusListener triggers repaints on focus shift. + */ + private static FocusListener focusListener; + + /** + * Receives notifications when properties of the text component change. + */ + private class Handler + implements PropertyChangeListener, DocumentListener + { + /** + * Notifies when a property of the text component changes. + * + * @param event the PropertyChangeEvent describing the change + */ + public void propertyChange(PropertyChangeEvent event) + { + if (event.getPropertyName().equals("document")) + { + // Document changed. + Object oldValue = event.getOldValue(); + if (oldValue != null) + { + Document oldDoc = (Document) oldValue; + oldDoc.removeDocumentListener(handler); + } + Object newValue = event.getNewValue(); + if (newValue != null) + { + Document newDoc = (Document) newValue; + newDoc.addDocumentListener(handler); + } + modelChanged(); + } + + BasicTextUI.this.propertyChange(event); + } + + /** + * Notification about a document change event. + * + * @param ev the DocumentEvent describing the change + */ + public void changedUpdate(DocumentEvent ev) + { + // Updates are forwarded to the View even if 'getVisibleEditorRect' + // method returns null. This means the View classes have to be + // aware of that possibility. + rootView.changedUpdate(ev, getVisibleEditorRect(), + rootView.getViewFactory()); + } + + /** + * Notification about a document insert event. + * + * @param ev the DocumentEvent describing the insertion + */ + public void insertUpdate(DocumentEvent ev) + { + // Updates are forwarded to the View even if 'getVisibleEditorRect' + // method returns null. This means the View classes have to be + // aware of that possibility. + rootView.insertUpdate(ev, getVisibleEditorRect(), + rootView.getViewFactory()); + } + + /** + * Notification about a document removal event. + * + * @param ev the DocumentEvent describing the removal + */ + public void removeUpdate(DocumentEvent ev) + { + // Updates are forwarded to the View even if 'getVisibleEditorRect' + // method returns null. This means the View classes have to be + // aware of that possibility. + rootView.removeUpdate(ev, getVisibleEditorRect(), + rootView.getViewFactory()); + } + + } + /** * This view forms the root of the View hierarchy. However, it delegates * most calls to another View which is the real root of the hierarchy. @@ -225,20 +362,10 @@ public abstract class BasicTextUI extends TextUI return textComponent; } - /** - * Returns the preferred span along the specified <code>axis</code>. - * This is delegated to the real root view. - * - * @param axis the axis for which the preferred span is queried - * - * @return the preferred span along the axis - */ - public float getPreferredSpan(int axis) + public void setSize(float w, float h) { if (view != null) - return view.getPreferredSpan(axis); - - return Integer.MAX_VALUE; + view.setSize(w, h); } /** @@ -312,7 +439,8 @@ public abstract class BasicTextUI extends TextUI */ public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) { - view.insertUpdate(ev, shape, vf); + if (view != null) + view.insertUpdate(ev, shape, vf); } /** @@ -325,7 +453,8 @@ public abstract class BasicTextUI extends TextUI */ public void removeUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) { - view.removeUpdate(ev, shape, vf); + if (view != null) + view.removeUpdate(ev, shape, vf); } /** @@ -338,7 +467,8 @@ public abstract class BasicTextUI extends TextUI */ public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) { - view.changedUpdate(ev, shape, vf); + if (view != null) + view.changedUpdate(ev, shape, vf); } /** @@ -400,116 +530,73 @@ public abstract class BasicTextUI extends TextUI { return textComponent.getDocument(); } - } - /** - * Receives notifications when properties of the text component change. - */ - private class PropertyChangeHandler implements PropertyChangeListener - { /** - * Notifies when a property of the text component changes. - * - * @param event the PropertyChangeEvent describing the change + * Returns the attributes, which is null for the RootView. */ - public void propertyChange(PropertyChangeEvent event) + public AttributeSet getAttributes() { - if (event.getPropertyName().equals("document")) - { - // Document changed. - Object oldValue = event.getOldValue(); - if (oldValue != null) - { - Document oldDoc = (Document) oldValue; - oldDoc.removeDocumentListener(documentHandler); - } - Object newValue = event.getNewValue(); - if (newValue != null) - { - Document newDoc = (Document) newValue; - newDoc.addDocumentListener(documentHandler); - } - modelChanged(); - } - - BasicTextUI.this.propertyChange(event); + return null; } - } - /** - * Listens for changes on the underlying model and forwards notifications - * to the View. This also updates the caret position of the text component. - * - * TODO: Maybe this should somehow be handled through EditorKits - */ - class DocumentHandler implements DocumentListener - { /** - * Notification about a document change event. - * - * @param ev the DocumentEvent describing the change + * Overridden to forward to the view. */ - public void changedUpdate(DocumentEvent ev) + public float getPreferredSpan(int axis) { - // Updates are forwarded to the View even if 'getVisibleEditorRect' - // method returns null. This means the View classes have to be - // aware of that possibility. - rootView.changedUpdate(ev, getVisibleEditorRect(), - rootView.getViewFactory()); + // The RI returns 10 in the degenerate case. + float span = 10; + if (view != null) + span = view.getPreferredSpan(axis); + return span; } /** - * Notification about a document insert event. - * - * @param ev the DocumentEvent describing the insertion + * Overridden to forward to the real view. */ - public void insertUpdate(DocumentEvent ev) + public float getMinimumSpan(int axis) { - // Updates are forwarded to the View even if 'getVisibleEditorRect' - // method returns null. This means the View classes have to be - // aware of that possibility. - rootView.insertUpdate(ev, getVisibleEditorRect(), - rootView.getViewFactory()); + // The RI returns 10 in the degenerate case. + float span = 10; + if (view != null) + span = view.getMinimumSpan(axis); + return span; } /** - * Notification about a document removal event. - * - * @param ev the DocumentEvent describing the removal + * Overridden to return Integer.MAX_VALUE. */ - public void removeUpdate(DocumentEvent ev) + public float getMaximumSpan(int axis) { - // Updates are forwarded to the View even if 'getVisibleEditorRect' - // method returns null. This means the View classes have to be - // aware of that possibility. - rootView.removeUpdate(ev, getVisibleEditorRect(), - rootView.getViewFactory()); + // The RI returns Integer.MAX_VALUE here, regardless of the real view's + // maximum size. + return Integer.MAX_VALUE; } } /** * The EditorKit used by this TextUI. */ - // FIXME: should probably be non-static. - static EditorKit kit = new DefaultEditorKit(); + private static EditorKit kit; /** - * The root view. + * The combined event handler for text components. + * + * This is package private to avoid accessor methods. */ - RootView rootView = new RootView(); + Handler handler; /** - * The text component that we handle. + * The root view. + * + * This is package private to avoid accessor methods. */ - JTextComponent textComponent; + RootView rootView; /** - * Receives notification when the model changes. + * The text component that we handle. */ - private PropertyChangeHandler updateHandler = new PropertyChangeHandler(); - - /** The DocumentEvent handler. */ - DocumentHandler documentHandler = new DocumentHandler(); + JTextComponent textComponent; /** * Creates a new <code>BasicTextUI</code> instance. @@ -558,17 +645,31 @@ public abstract class BasicTextUI extends TextUI public void installUI(final JComponent c) { textComponent = (JTextComponent) c; + + if (rootView == null) + rootView = new RootView(); + installDefaults(); - textComponent.addPropertyChangeListener(updateHandler); + installFixedDefaults(); + + // These listeners must be installed outside of installListeners(), + // because overriding installListeners() doesn't prevent installing + // these in the RI, but overriding isntallUI() does. + if (handler == null) + handler = new Handler(); + textComponent.addPropertyChangeListener(handler); Document doc = textComponent.getDocument(); if (doc == null) { + // The Handler takes care of installing the necessary listeners + // on the document here. doc = getEditorKit(textComponent).createDefaultDocument(); textComponent.setDocument(doc); } else { - doc.addDocumentListener(documentHandler); + // Must install the document listener. + doc.addDocumentListener(handler); modelChanged(); } @@ -586,7 +687,6 @@ public abstract class BasicTextUI extends TextUI LookAndFeel.installColorsAndFont(textComponent, prefix + ".background", prefix + ".foreground", prefix + ".font"); LookAndFeel.installBorder(textComponent, prefix + ".border"); - textComponent.setMargin(UIManager.getInsets(prefix + ".margin")); // Some additional text component only properties. Color color = textComponent.getCaretColor(); @@ -600,7 +700,7 @@ public abstract class BasicTextUI extends TextUI color = textComponent.getDisabledTextColor(); if (color == null || color instanceof UIResource) { - color = UIManager.getColor(prefix + ".inactiveBackground"); + color = UIManager.getColor(prefix + ".inactiveForeground"); textComponent.setDisabledTextColor(color); } color = textComponent.getSelectedTextColor(); @@ -623,6 +723,15 @@ public abstract class BasicTextUI extends TextUI textComponent.setMargin(margin); } + } + + /** + * Installs defaults that can't be overridden by overriding + * installDefaults(). + */ + private void installFixedDefaults() + { + String prefix = getPropertyPrefix(); Caret caret = textComponent.getCaret(); if (caret == null || caret instanceof UIResource) { @@ -638,64 +747,18 @@ public abstract class BasicTextUI extends TextUI } /** - * This FocusListener triggers repaints on focus shift. - */ - private FocusListener focuslistener = new FocusListener() { - public void focusGained(FocusEvent e) - { - textComponent.repaint(); - } - public void focusLost(FocusEvent e) - { - textComponent.repaint(); - - // Integrates Swing text components with the system clipboard: - // The idea is that if one wants to copy text around X11-style - // (select text and middle-click in the target component) the focus - // will move to the new component which gives the old focus owner the - // possibility to paste its selection into the clipboard. - if (!e.isTemporary() - && textComponent.getSelectionStart() - != textComponent.getSelectionEnd()) - { - SecurityManager sm = System.getSecurityManager(); - try - { - if (sm != null) - sm.checkSystemClipboardAccess(); - - Clipboard cb = Toolkit.getDefaultToolkit().getSystemSelection(); - if (cb != null) - { - StringSelection selection = new StringSelection( - textComponent.getSelectedText()); - cb.setContents(selection, selection); - } - } - catch (SecurityException se) - { - // Not allowed to access the clipboard: Ignore and - // do not access it. - } - catch (HeadlessException he) - { - // There is no AWT: Ignore and do not access the - // clipboard. - } - catch (IllegalStateException ise) - { - // Clipboard is currently unavaible. - } - } - } - }; - - /** * Install all listeners on the text component. */ protected void installListeners() { - textComponent.addFocusListener(focuslistener); + // + if (SystemProperties.getProperty("gnu.swing.text.no-xlike-clipboard") + == null) + { + if (focusListener == null) + focusListener = new FocusHandler(); + textComponent.addFocusListener(focusListener); + } } /** @@ -834,10 +897,12 @@ public abstract class BasicTextUI extends TextUI */ public void uninstallUI(final JComponent component) { - super.uninstallUI(component); + textComponent.removePropertyChangeListener(handler); + textComponent.getDocument().removeDocumentListener(handler); rootView.setView(null); uninstallDefaults(); + uninstallFixedDefaults(); uninstallListeners(); uninstallKeyboardActions(); @@ -850,7 +915,29 @@ public abstract class BasicTextUI extends TextUI */ protected void uninstallDefaults() { - // Do nothing here. + if (textComponent.getCaretColor() instanceof UIResource) + textComponent.setCaretColor(null); + if (textComponent.getSelectionColor() instanceof UIResource) + textComponent.setSelectionColor(null); + if (textComponent.getDisabledTextColor() instanceof UIResource) + textComponent.setDisabledTextColor(null); + if (textComponent.getSelectedTextColor() instanceof UIResource) + textComponent.setSelectedTextColor(null); + LookAndFeel.uninstallBorder(textComponent); + if (textComponent.getMargin() instanceof UIResource) + textComponent.setMargin(null); + } + + /** + * Uninstalls additional fixed defaults that were installed + * by installFixedDefaults(). + */ + private void uninstallFixedDefaults() + { + if (textComponent.getCaret() instanceof UIResource) + textComponent.setCaret(null); + if (textComponent.getHighlighter() instanceof UIResource) + textComponent.setHighlighter(null); } /** @@ -859,7 +946,10 @@ public abstract class BasicTextUI extends TextUI */ protected void uninstallListeners() { - textComponent.removeFocusListener(focuslistener); + // Don't nullify the focusListener field, as it is static and shared + // between components. + if (focusListener != null) + textComponent.removeFocusListener(focusListener); } /** @@ -891,14 +981,33 @@ public abstract class BasicTextUI extends TextUI */ public Dimension getPreferredSize(JComponent c) { - View v = getRootView(textComponent); - - float w = v.getPreferredSpan(View.X_AXIS); - float h = v.getPreferredSpan(View.Y_AXIS); - + Dimension d = c.getSize(); Insets i = c.getInsets(); - return new Dimension((int) w + i.left + i.right, + // We need to lock here, since we require the view hierarchy to _not_ + // change in between. + float w; + float h; + Document doc = textComponent.getDocument(); + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readLock(); + try + { + if (d.width > (i.left + i.right) && d.height > (i.top + i.bottom)) + { + rootView.setSize(d.width - i.left - i.right, + d.height - i.top - i.bottom); + } + w = rootView.getPreferredSpan(View.X_AXIS); + h = rootView.getPreferredSpan(View.Y_AXIS); + } + finally + { + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readUnlock(); + } + Dimension size = new Dimension((int) w + i.left + i.right, (int) h + i.top + i.bottom); + return size; } /** @@ -912,8 +1021,27 @@ public abstract class BasicTextUI extends TextUI */ public Dimension getMaximumSize(JComponent c) { - // Sun's implementation returns Integer.MAX_VALUE here, so do we. - return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + Dimension d = new Dimension(); + Insets i = c.getInsets(); + Document doc = textComponent.getDocument(); + // We need to lock here, since we require the view hierarchy to _not_ + // change in between. + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readLock(); + try + { + // Check for overflow here. + d.width = (int) Math.min((long) rootView.getMaximumSpan(View.X_AXIS) + + i.left + i.right, Integer.MAX_VALUE); + d.height = (int) Math.min((long) rootView.getMaximumSpan(View.Y_AXIS) + + i.top + i.bottom, Integer.MAX_VALUE); + } + finally + { + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readUnlock(); + } + return d; } /** @@ -924,8 +1052,26 @@ public abstract class BasicTextUI extends TextUI */ public Dimension getMinimumSize(JComponent c) { + Dimension d = new Dimension(); + Document doc = textComponent.getDocument(); + // We need to lock here, since we require the view hierarchy to _not_ + // change in between. + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readLock(); + try + { + d.width = (int) rootView.getMinimumSpan(View.X_AXIS); + d.height = (int) rootView.getMinimumSpan(View.Y_AXIS); + } + finally + { + if (doc instanceof AbstractDocument) + ((AbstractDocument) doc).readUnlock(); + } Insets i = c.getInsets(); - return new Dimension(i.left + i.right, i.top + i.bottom); + d.width += i.left + i.right; + d.height += i.top + i.bottom; + return d; } /** @@ -996,7 +1142,6 @@ public abstract class BasicTextUI extends TextUI g.setColor(oldColor); } - rootView.paint(g, getVisibleEditorRect()); if (caret != null && textComponent.hasFocus()) @@ -1104,6 +1249,8 @@ public abstract class BasicTextUI extends TextUI */ public EditorKit getEditorKit(JTextComponent t) { + if (kit == null) + kit = new DefaultEditorKit(); return kit; } @@ -1224,7 +1371,7 @@ public abstract class BasicTextUI extends TextUI */ public int viewToModel(JTextComponent t, Point pt) { - return viewToModel(t, pt, null); + return viewToModel(t, pt, new Position.Bias[1]); } /** diff --git a/javax/swing/plaf/basic/BasicToolTipUI.java b/javax/swing/plaf/basic/BasicToolTipUI.java index 5cec2e333..94e7bc322 100644 --- a/javax/swing/plaf/basic/BasicToolTipUI.java +++ b/javax/swing/plaf/basic/BasicToolTipUI.java @@ -40,19 +40,20 @@ package javax.swing.plaf.basic; import java.awt.Color; import java.awt.Dimension; +import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; -import java.awt.Toolkit; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JToolTip; import javax.swing.LookAndFeel; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.ToolTipUI; +import javax.swing.text.View; /** * This is the Basic Look and Feel UI class for JToolTip. @@ -60,6 +61,28 @@ import javax.swing.plaf.ToolTipUI; public class BasicToolTipUI extends ToolTipUI { + /** + * Receives notification when a property of the JToolTip changes. + * This updates the HTML renderer if appropriate. + */ + private class PropertyChangeHandler + implements PropertyChangeListener + { + + public void propertyChange(PropertyChangeEvent e) + { + String prop = e.getPropertyName(); + if (prop.equals("tiptext") || prop.equals("font") + || prop.equals("foreground")) + { + JToolTip tip = (JToolTip) e.getSource(); + String text = tip.getTipText(); + BasicHTML.updateRenderer(tip, text); + } + } + + } + /** The shared instance of BasicToolTipUI used for all ToolTips. */ private static BasicToolTipUI shared; @@ -67,6 +90,11 @@ public class BasicToolTipUI extends ToolTipUI private String text; /** + * Handles property changes. + */ + private PropertyChangeListener propertyChangeHandler; + + /** * Creates a new BasicToolTipUI object. */ public BasicToolTipUI() @@ -98,7 +126,12 @@ public class BasicToolTipUI extends ToolTipUI */ public Dimension getMaximumSize(JComponent c) { - return getPreferredSize(c); + Dimension d = getPreferredSize(c); + View view = (View) c.getClientProperty(BasicHTML.propertyKey); + if (view != null) + d.width += view.getMaximumSpan(View.X_AXIS) + - view.getPreferredSpan(View.X_AXIS); + return d; } /** @@ -110,7 +143,12 @@ public class BasicToolTipUI extends ToolTipUI */ public Dimension getMinimumSize(JComponent c) { - return getPreferredSize(c); + Dimension d = getPreferredSize(c); + View view = (View) c.getClientProperty(BasicHTML.propertyKey); + if (view != null) + d.width -= view.getPreferredSpan(View.X_AXIS) + - view.getMinimumSpan(View.X_AXIS); + return d; } /** @@ -123,22 +161,25 @@ public class BasicToolTipUI extends ToolTipUI public Dimension getPreferredSize(JComponent c) { JToolTip tip = (JToolTip) c; - FontMetrics fm; - Toolkit g = tip.getToolkit(); - text = tip.getTipText(); - - Rectangle vr = new Rectangle(); - Rectangle ir = new Rectangle(); - Rectangle tr = new Rectangle(); - Insets insets = tip.getInsets(); - fm = g.getFontMetrics(tip.getFont()); - SwingUtilities.layoutCompoundLabel(tip, fm, text, null, - SwingConstants.CENTER, - SwingConstants.CENTER, - SwingConstants.CENTER, - SwingConstants.CENTER, vr, ir, tr, 0); - return new Dimension(insets.left + tr.width + insets.right, - insets.top + tr.height + insets.bottom); + String str = tip.getTipText(); + FontMetrics fm = c.getFontMetrics(c.getFont()); + Insets i = c.getInsets(); + Dimension d = new Dimension(i.left + i.right, i.top + i.bottom); + if (str != null && ! str.equals("")) + { + View view = (View) c.getClientProperty(BasicHTML.propertyKey); + if (view != null) + { + d.width += (int) view.getPreferredSpan(View.X_AXIS); + d.height += (int) view.getPreferredSpan(View.Y_AXIS); + } + else + { + d.width += fm.stringWidth(str) + 6; + d.height += fm.getHeight(); + } + } + return d; } /** @@ -160,7 +201,8 @@ public class BasicToolTipUI extends ToolTipUI */ protected void installListeners(JComponent c) { - // TODO: Implement this properly. + propertyChangeHandler = new PropertyChangeHandler(); + c.addPropertyChangeListener(propertyChangeHandler); } /** @@ -172,6 +214,7 @@ public class BasicToolTipUI extends ToolTipUI { c.setOpaque(true); installDefaults(c); + BasicHTML.updateRenderer(c, ((JToolTip) c).getTipText()); installListeners(c); } @@ -186,26 +229,25 @@ public class BasicToolTipUI extends ToolTipUI JToolTip tip = (JToolTip) c; String text = tip.getTipText(); - Toolkit t = tip.getToolkit(); - if (text == null) - return; - - Rectangle vr = new Rectangle(); - vr = SwingUtilities.calculateInnerArea(tip, vr); - Rectangle ir = new Rectangle(); - Rectangle tr = new Rectangle(); - FontMetrics fm = t.getFontMetrics(tip.getFont()); + Font font = c.getFont(); + FontMetrics fm = c.getFontMetrics(font); int ascent = fm.getAscent(); - SwingUtilities.layoutCompoundLabel(tip, fm, text, null, - SwingConstants.CENTER, - SwingConstants.CENTER, - SwingConstants.CENTER, - SwingConstants.CENTER, vr, ir, tr, 0); + Insets i = c.getInsets(); + Dimension size = c.getSize(); + Rectangle paintR = new Rectangle(i.left, i.top, + size.width - i.left - i.right, + size.height - i.top - i.bottom); Color saved = g.getColor(); + Font oldFont = g.getFont(); g.setColor(Color.BLACK); - g.drawString(text, vr.x, vr.y + ascent); + View view = (View) c.getClientProperty(BasicHTML.propertyKey); + if (view != null) + view.paint(g, paintR); + else + g.drawString(text, paintR.x + 3, paintR.y + ascent); + g.setFont(oldFont); g.setColor(saved); } @@ -229,7 +271,11 @@ public class BasicToolTipUI extends ToolTipUI */ protected void uninstallListeners(JComponent c) { - // TODO: Implement this properly. + if (propertyChangeHandler != null) + { + c.removePropertyChangeListener(propertyChangeHandler); + propertyChangeHandler = null; + } } /** @@ -240,6 +286,7 @@ public class BasicToolTipUI extends ToolTipUI public void uninstallUI(JComponent c) { uninstallDefaults(c); + BasicHTML.updateRenderer(c, ""); uninstallListeners(c); } } diff --git a/javax/swing/plaf/basic/BasicTreeUI.java b/javax/swing/plaf/basic/BasicTreeUI.java index f61824bb7..ef46efdd2 100644 --- a/javax/swing/plaf/basic/BasicTreeUI.java +++ b/javax/swing/plaf/basic/BasicTreeUI.java @@ -38,17 +38,16 @@ package javax.swing.plaf.basic; -import gnu.classpath.NotImplementedException; import gnu.javax.swing.tree.GnuPath; import java.awt.Color; import java.awt.Component; +import java.awt.Container; import java.awt.Dimension; -import java.awt.Font; -import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Insets; import java.awt.Label; +import java.awt.Point; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; @@ -275,13 +274,6 @@ public class BasicTreeUI TreeModelListener treeModelListener; /** - * This timer fires the editing action after about 1200 ms if not reset during - * that time. It handles the editing start with the single mouse click (and - * not the double mouse click) on the selected tree node. - */ - Timer startEditTimer; - - /** * The zero size icon, used for expand controls, if they are not visible. */ static Icon nullIcon; @@ -428,6 +420,7 @@ public class BasicTreeUI { if (largeModel != this.largeModel) { + completeEditing(); tree.removeComponentListener(componentListener); this.largeModel = largeModel; tree.addComponentListener(componentListener); @@ -451,6 +444,7 @@ public class BasicTreeUI */ protected void setRowHeight(int rowHeight) { + completeEditing(); if (rowHeight == 0) rowHeight = getMaxHeight(tree); treeState.setRowHeight(rowHeight); @@ -544,6 +538,7 @@ public class BasicTreeUI */ protected void setRootVisible(boolean newValue) { + completeEditing(); tree.setRootVisible(newValue); } @@ -590,8 +585,7 @@ public class BasicTreeUI */ protected void setCellEditor(TreeCellEditor editor) { - cellEditor = editor; - createdCellEditor = true; + updateCellEditor(); } /** @@ -611,7 +605,7 @@ public class BasicTreeUI */ protected void setEditable(boolean newValue) { - tree.setEditable(newValue); + updateCellEditor(); } /** @@ -632,6 +626,7 @@ public class BasicTreeUI */ protected void setSelectionModel(TreeSelectionModel newLSM) { + completeEditing(); if (newLSM != null) { treeSelectionModel = newLSM; @@ -787,12 +782,13 @@ public class BasicTreeUI */ public boolean stopEditing(JTree tree) { - if (isEditing(tree)) + boolean ret = false; + if (editingComponent != null && cellEditor.stopCellEditing()) { completeEditing(false, false, true); - finish(); + ret = true; } - return ! isEditing(tree); + return ret; } /** @@ -805,8 +801,8 @@ public class BasicTreeUI // There is no need to send the cancel message to the editor, // as the cancellation event itself arrives from it. This would // only be necessary when cancelling the editing programatically. - completeEditing(false, false, false); - finish(); + if (editingComponent != null) + completeEditing(false, true, false); } /** @@ -818,7 +814,9 @@ public class BasicTreeUI */ public void startEditingAtPath(JTree tree, TreePath path) { - startEditing(path, null); + tree.scrollPathToVisible(path); + if (path != null && tree.isVisible(path)) + startEditing(path, null); } /** @@ -842,6 +840,7 @@ public class BasicTreeUI preferredSize = new Dimension(); largeModel = tree.isLargeModel(); preferredSize = new Dimension(); + stopEditingInCompleteEditing = true; setModel(tree.getModel()); } @@ -1136,6 +1135,7 @@ public class BasicTreeUI */ protected void updateExpandedDescendants(TreePath path) { + completeEditing(); Enumeration expanded = tree.getExpandedDescendants(path); while (expanded.hasMoreElements()) treeState.setExpandedState((TreePath) expanded.nextElement(), true); @@ -1167,9 +1167,33 @@ public class BasicTreeUI */ protected void updateCellEditor() { - if (tree.isEditable() && cellEditor == null) - setCellEditor(createDefaultCellEditor()); - createdCellEditor = true; + completeEditing(); + TreeCellEditor newEd = null; + if (tree != null && tree.isEditable()) + { + newEd = tree.getCellEditor(); + if (newEd == null) + { + newEd = createDefaultCellEditor(); + if (newEd != null) + { + tree.setCellEditor(newEd); + createdCellEditor = true; + } + } + } + // Update listeners. + if (newEd != cellEditor) + { + if (cellEditor != null && cellEditorListener != null) + cellEditor.removeCellEditorListener(cellEditorListener); + cellEditor = newEd; + if (cellEditorListener == null) + cellEditorListener = createCellEditorListener(); + if (cellEditor != null && cellEditorListener != null) + cellEditor.addCellEditorListener(cellEditorListener); + createdCellEditor = false; + } } /** @@ -1719,6 +1743,10 @@ public class BasicTreeUI */ protected void completeEditing() { + if (tree.getInvokesStopCellEditing() && stopEditingInCompleteEditing + && editingComponent != null) + cellEditor.stopCellEditing(); + completeEditing(false, true, false); } @@ -1736,28 +1764,35 @@ public class BasicTreeUI boolean messageTree) { // Make no attempt to complete the non existing editing session. - if (!isEditing(tree)) - return; - - if (messageStop) - { - getCellEditor().stopCellEditing(); - stopEditingInCompleteEditing = true; - } - - if (messageCancel) + if (stopEditingInCompleteEditing && editingComponent != null) { - getCellEditor().cancelCellEditing(); - stopEditingInCompleteEditing = true; - } + Component comp = editingComponent; + TreePath p = editingPath; + editingComponent = null; + editingPath = null; + if (messageStop) + cellEditor.stopCellEditing(); + else if (messageCancel) + cellEditor.cancelCellEditing(); + + tree.remove(comp); + + if (editorHasDifferentSize) + { + treeState.invalidatePathBounds(p); + updateSize(); + } + else + { + // Need to refresh the tree. + Rectangle b = getPathBounds(tree, p); + tree.repaint(0, b.y, tree.getWidth(), b.height); + } - if (messageTree) - { - TreeCellEditor editor = getCellEditor(); - if (editor != null) + if (messageTree) { - Object value = editor.getCellEditorValue(); - treeModel.valueForPathChanged(tree.getLeadSelectionPath(), value); + Object value = cellEditor.getCellEditorValue(); + treeModel.valueForPathChanged(p, value); } } } @@ -1772,47 +1807,105 @@ public class BasicTreeUI */ protected boolean startEditing(TreePath path, MouseEvent event) { - updateCellEditor(); - TreeCellEditor ed = getCellEditor(); + // Maybe cancel editing. + if (isEditing(tree) && tree.getInvokesStopCellEditing() + && ! stopEditing(tree)) + return false; - if (ed != null && (event == EDIT || ed.shouldSelectCell(event)) - && ed.isCellEditable(event)) + completeEditing(); + TreeCellEditor ed = cellEditor; + if (ed != null && tree.isPathEditable(path)) { - Rectangle bounds = getPathBounds(tree, path); - - // Extend the right boundary till the tree width. - bounds.width = tree.getWidth() - bounds.x; - - editingPath = path; - editingRow = tree.getRowForPath(editingPath); - - Object value = editingPath.getLastPathComponent(); - - stopEditingInCompleteEditing = false; - boolean expanded = tree.isExpanded(editingPath); - isEditing = true; - editingComponent = ed.getTreeCellEditorComponent(tree, value, true, - expanded, - isLeaf(editingRow), - editingRow); - - // Remove all previous components (if still present). Only one - // container with the editing component inside is allowed in the tree. - tree.removeAll(); - - // The editing component must be added to its container. We add the - // container, not the editing component itself. - Component container = editingComponent.getParent(); - container.setBounds(bounds); - tree.add(container); - editingComponent.requestFocus(); + if (ed.isCellEditable(event)) + { + editingRow = getRowForPath(tree, path); + Object value = path.getLastPathComponent(); + boolean isSelected = tree.isPathSelected(path); + boolean isExpanded = tree.isExpanded(editingPath); + boolean isLeaf = treeModel.isLeaf(value); + editingComponent = ed.getTreeCellEditorComponent(tree, value, + isSelected, + isExpanded, + isLeaf, + editingRow); + + Rectangle bounds = getPathBounds(tree, path); + + Dimension size = editingComponent.getPreferredSize(); + int rowHeight = getRowHeight(); + if (size.height != bounds.height && rowHeight > 0) + size.height = rowHeight; + + if (size.width != bounds.width || size.height != bounds.height) + { + editorHasDifferentSize = true; + treeState.invalidatePathBounds(path); + updateSize(); + } + else + editorHasDifferentSize = false; + + // The editing component must be added to its container. We add the + // container, not the editing component itself. + tree.add(editingComponent); + editingComponent.setBounds(bounds.x, bounds.y, size.width, + size.height); + editingComponent.validate(); + editingPath = path; + + if (ed.shouldSelectCell(event)) + { + stopEditingInCompleteEditing = false; + tree.setSelectionRow(editingRow); + stopEditingInCompleteEditing = true; + } + + editorRequestFocus(editingComponent); + // Register MouseInputHandler to redispatch initial mouse events + // correctly. + if (event instanceof MouseEvent) + { + Point p = SwingUtilities.convertPoint(tree, event.getX(), event.getY(), + editingComponent); + Component active = + SwingUtilities.getDeepestComponentAt(editingComponent, p.x, p.y); + if (active != null) + { + MouseInputHandler ih = new MouseInputHandler(tree, active, event); + + } + } - return true; + return true; + } + else + editingComponent = null; } return false; } /** + * Requests focus on the editor. The method is necessary since the + * DefaultTreeCellEditor returns a container that contains the + * actual editor, and we want to request focus on the editor, not the + * container. + */ + private void editorRequestFocus(Component c) + { + if (c instanceof Container) + { + // TODO: Maybe do something more reasonable here, like queriying the + // FocusTraversalPolicy. + Container cont = (Container) c; + if (cont.getComponentCount() > 0) + cont.getComponent(0).requestFocus(); + } + else if (c.isFocusable()) + c.requestFocus(); + + } + + /** * If the <code>mouseX</code> and <code>mouseY</code> are in the expand or * collapse region of the row, this will toggle the row. * @@ -2180,7 +2273,7 @@ public class BasicTreeUI */ public void editingStopped(ChangeEvent e) { - stopEditing(tree); + completeEditing(false, false, true); } /** @@ -2191,7 +2284,7 @@ public class BasicTreeUI */ public void editingCanceled(ChangeEvent e) { - cancelEditing(tree); + completeEditing(false, false, false); } } // CellEditorHandler @@ -2347,9 +2440,15 @@ public class BasicTreeUI * events. */ public class MouseHandler - extends MouseAdapter - implements MouseMotionListener + extends MouseAdapter + implements MouseMotionListener { + + /** + * If the cell has been selected on mouse press. + */ + private boolean selectedOnPress; + /** * Constructor */ @@ -2365,76 +2464,15 @@ public class BasicTreeUI */ public void mousePressed(MouseEvent e) { - // Any mouse click cancels the previous waiting edit action, initiated - // by the single click on the selected node. - if (startEditTimer != null) + if (! e.isConsumed()) { - startEditTimer.stop(); - startEditTimer = null; + handleEvent(e); + selectedOnPress = true; } - - if (tree != null && tree.isEnabled()) + else { - // Always end the current editing session if clicked on the - // tree and outside the bounds of the editing component. - if (isEditing(tree)) - if (!stopEditing(tree)) - // Return if we have failed to cancel the editing session. - return; - - int x = e.getX(); - int y = e.getY(); - TreePath path = getClosestPathForLocation(tree, x, y); - - if (path != null) - { - Rectangle bounds = getPathBounds(tree, path); - if (SwingUtilities.isLeftMouseButton(e)) - checkForClickInExpandControl(path, x, y); - - if (x > bounds.x && x <= (bounds.x + bounds.width)) - { - TreePath currentLead = tree.getLeadSelectionPath(); - if (currentLead != null && currentLead.equals(path) - && e.getClickCount() == 1 && tree.isEditable()) - { - // Schedule the editing session. - final TreePath editPath = path; - - // The code below handles the required click-pause-click - // functionality which must be present in the tree UI. - // If the next click comes after the - // time longer than the double click interval AND - // the same node stays focused for the WAIT_TILL_EDITING - // duration, the timer starts the editing session. - if (startEditTimer != null) - startEditTimer.stop(); - - startEditTimer = new Timer(WAIT_TILL_EDITING, - new ActionListener() - { - public void actionPerformed(ActionEvent e) - { - startEditing(editPath, EDIT); - } - }); - - startEditTimer.setRepeats(false); - startEditTimer.start(); - } - else - { - if (e.getClickCount() == 2) - toggleExpandState(path); - else - selectPathForEvent(path, e); - } - } - } + selectedOnPress = false; } - - // We need to request the focus. - tree.requestFocusInWindow(); } /** @@ -2446,9 +2484,8 @@ public class BasicTreeUI * @param e is the mouse event that occured */ public void mouseDragged(MouseEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** @@ -2458,9 +2495,8 @@ public class BasicTreeUI * @param e the mouse event that occured */ public void mouseMoved(MouseEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** @@ -2469,9 +2505,46 @@ public class BasicTreeUI * @param e is the mouse event that occured */ public void mouseReleased(MouseEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + if (! e.isConsumed() && ! selectedOnPress) + handleEvent(e); + } + + /** + * Handles press and release events. + * + * @param e the mouse event + */ + private void handleEvent(MouseEvent e) + { + if (tree != null && tree.isEnabled()) + { + // Maybe stop editing. + if (isEditing(tree) && tree.getInvokesStopCellEditing() + && ! stopEditing(tree)) + return; + + // Explicitly request focus. + tree.requestFocusInWindow(); + + int x = e.getX(); + int y = e.getY(); + TreePath path = getClosestPathForLocation(tree, x, y); + if (path != null) + { + Rectangle b = getPathBounds(tree, path); + if (y <= b.y + b.height) + { + if (SwingUtilities.isLeftMouseButton(e)) + checkForClickInExpandControl(path, x, y); + if (x > b.x && x <= b.x + b.width) + { + if (! startEditing(path, e)) + selectPathForEvent(path, e); + } + } + } + } } } @@ -2501,6 +2574,9 @@ public class BasicTreeUI { this.source = source; this.destination = destination; + source.addMouseListener(this); + source.addMouseMotionListener(this); + dispatch(e); } /** @@ -2510,9 +2586,8 @@ public class BasicTreeUI * @param e mouse event that occured */ public void mouseClicked(MouseEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + dispatch(e); } /** @@ -2521,9 +2596,8 @@ public class BasicTreeUI * @param e mouse event that occured */ public void mousePressed(MouseEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + // Nothing to do here. } /** @@ -2532,9 +2606,9 @@ public class BasicTreeUI * @param e mouse event that occured */ public void mouseReleased(MouseEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + dispatch(e); + removeFromSource(); } /** @@ -2543,9 +2617,9 @@ public class BasicTreeUI * @param e mouse event that occured */ public void mouseEntered(MouseEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + if (! SwingUtilities.isLeftMouseButton(e)) + removeFromSource(); } /** @@ -2554,9 +2628,9 @@ public class BasicTreeUI * @param e mouse event that occured */ public void mouseExited(MouseEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + if (! SwingUtilities.isLeftMouseButton(e)) + removeFromSource(); } /** @@ -2568,9 +2642,8 @@ public class BasicTreeUI * @param e mouse event that occured */ public void mouseDragged(MouseEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + dispatch(e); } /** @@ -2580,18 +2653,37 @@ public class BasicTreeUI * @param e mouse event that occured */ public void mouseMoved(MouseEvent e) - throws NotImplementedException { - // TODO: What should be done here, if anything? + removeFromSource(); } /** * Removes event from the source */ protected void removeFromSource() - throws NotImplementedException { - // TODO: Implement this properly. + if (source != null) + { + source.removeMouseListener(this); + source.removeMouseMotionListener(this); + } + source = null; + destination = null; + } + + /** + * Redispatches mouse events to the destination. + * + * @param e the mouse event to redispatch + */ + private void dispatch(MouseEvent e) + { + if (destination != null) + { + MouseEvent e2 = SwingUtilities.convertMouseEvent(source, e, + destination); + destination.dispatchEvent(e2); + } } } @@ -2627,24 +2719,42 @@ public class BasicTreeUI public Rectangle getNodeDimensions(Object cell, int row, int depth, boolean expanded, Rectangle size) { - if (size == null || cell == null) - return null; - - String s = cell.toString(); - Font f = tree.getFont(); - FontMetrics fm = tree.getToolkit().getFontMetrics(f); - - if (s != null) + Dimension prefSize; + if (editingComponent != null && editingRow == row) + { + // Editing, ask editor for preferred size. + prefSize = editingComponent.getPreferredSize(); + int rowHeight = getRowHeight(); + if (rowHeight > 0 && rowHeight != prefSize.height) + prefSize.height = rowHeight; + } + else + { + // Not editing, ask renderer for preferred size. + Component rend = + currentCellRenderer.getTreeCellRendererComponent(tree, cell, + tree.isRowSelected(row), + expanded, + treeModel.isLeaf(cell), + row, false); + // Make sure the layout is valid. + rendererPane.add(rend); + rend.validate(); + prefSize = rend.getPreferredSize(); + } + if (size != null) { - TreePath path = treeState.getPathForRow(row); size.x = getRowX(row, depth); - size.width = SwingUtilities.computeStringWidth(fm, s); - size.width = size.width + getCurrentControlIcon(path).getIconWidth() - + gap + getNodeIcon(path).getIconWidth(); - size.height = getMaxHeight(tree); - size.y = size.height * row; + // FIXME: This should be handled by the layout cache. + size.y = prefSize.height * row; + size.width = prefSize.width; + size.height = prefSize.height; } - + else + // FIXME: The y should be handled by the layout cache. + size = new Rectangle(getRowX(row, depth), prefSize.height * row, prefSize.width, + prefSize.height); + return size; } @@ -2706,6 +2816,9 @@ public class BasicTreeUI if (treeState != null) treeState.invalidateSizes(); } + else if (property.equals(JTree.EDITABLE_PROPERTY)) + setEditable(((Boolean) event.getNewValue()).booleanValue()); + } } @@ -2714,7 +2827,7 @@ public class BasicTreeUI * properties of the model change. */ public class SelectionModelPropertyChangeHandler - implements PropertyChangeListener + implements PropertyChangeListener { /** @@ -2732,9 +2845,8 @@ public class BasicTreeUI * the property that has changed. */ public void propertyChange(PropertyChangeEvent event) - throws NotImplementedException { - // TODO: What should be done here, if anything? + treeSelectionModel.resetRowSelection(); } } @@ -2804,6 +2916,7 @@ public class BasicTreeUI */ public void treeCollapsed(TreeExpansionEvent event) { + completeEditing(); validCachedPreferredSize = false; treeState.setExpandedState(event.getPath(), false); // The maximal cell height may change @@ -3269,8 +3382,7 @@ public class BasicTreeUI */ public void valueChanged(TreeSelectionEvent event) { - if (tree.isEditing()) - tree.cancelEditing(); + completeEditing(); TreePath op = event.getOldLeadSelectionPath(); TreePath np = event.getNewLeadSelectionPath(); @@ -3808,25 +3920,6 @@ public class BasicTreeUI } /** - * Finish the editing session. - */ - void finish() - { - treeState.invalidatePathBounds(treeState.getPathForRow(editingRow)); - editingPath = null; - editingRow = - 1; - stopEditingInCompleteEditing = false; - isEditing = false; - Rectangle bounds = editingComponent.getParent().getBounds(); - tree.removeAll(); - validCachedPreferredSize = false; - // Repaint the region, where was the editing component. - tree.repaint(bounds); - editingComponent = null; - tree.requestFocus(); - } - - /** * Returns the amount to indent the given row * * @return amount to indent the given row. diff --git a/javax/swing/plaf/metal/MetalButtonUI.java b/javax/swing/plaf/metal/MetalButtonUI.java index 8addfc66c..be9607927 100644 --- a/javax/swing/plaf/metal/MetalButtonUI.java +++ b/javax/swing/plaf/metal/MetalButtonUI.java @@ -54,7 +54,6 @@ import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.UIResource; -import javax.swing.plaf.basic.BasicButtonListener; import javax.swing.plaf.basic.BasicButtonUI; /** @@ -66,24 +65,46 @@ public class MetalButtonUI extends BasicButtonUI { - /** The color used to draw the focus rectangle around the text and/or icon. */ + /** + * The shared button UI. + */ + private static MetalButtonUI sharedUI; + + /** + * The color used to draw the focus rectangle around the text and/or icon. + */ protected Color focusColor; - /** The background color for the button when it is pressed. */ + /** + * The background color for the button when it is pressed. + */ protected Color selectColor; - /** The color for disabled button labels. */ + /** + * The color for disabled button labels. + */ protected Color disabledTextColor; /** + * Returns a UI delegate for the specified component. + * + * @param c the component (should be a subclass of {@link AbstractButton}). + * + * @return A new instance of <code>MetalButtonUI</code>. + */ + public static ComponentUI createUI(JComponent c) + { + if (sharedUI == null) + sharedUI = new MetalButtonUI(); + return sharedUI; + } + + /** * Creates a new instance. */ public MetalButtonUI() { super(); - focusColor = UIManager.getColor(getPropertyPrefix() + "focus"); - selectColor = UIManager.getColor(getPropertyPrefix() + "select"); - disabledTextColor = UIManager.getColor(getPropertyPrefix() + "disabledText"); } /** @@ -93,6 +114,7 @@ public class MetalButtonUI */ protected Color getFocusColor() { + focusColor = UIManager.getColor(getPropertyPrefix() + "focus"); return focusColor; } @@ -103,6 +125,7 @@ public class MetalButtonUI */ protected Color getSelectColor() { + selectColor = UIManager.getColor(getPropertyPrefix() + "select"); return selectColor; } @@ -113,22 +136,12 @@ public class MetalButtonUI */ protected Color getDisabledTextColor() { + disabledTextColor = UIManager.getColor(getPropertyPrefix() + + "disabledText"); return disabledTextColor; } /** - * Returns a UI delegate for the specified component. - * - * @param c the component (should be a subclass of {@link AbstractButton}). - * - * @return A new instance of <code>MetalButtonUI</code>. - */ - public static ComponentUI createUI(JComponent c) - { - return new MetalButtonUI(); - } - - /** * Installs the default settings for the specified button. * * @param button the button. @@ -137,33 +150,20 @@ public class MetalButtonUI */ public void installDefaults(AbstractButton button) { + // This is overridden to be public, for whatever reason. super.installDefaults(button); - button.setRolloverEnabled(UIManager.getBoolean( - getPropertyPrefix() + "rollover")); } - + /** * Removes the defaults added by {@link #installDefaults(AbstractButton)}. */ public void uninstallDefaults(AbstractButton button) { + // This is overridden to be public, for whatever reason. super.uninstallDefaults(button); - button.setRolloverEnabled(false); } /** - * Returns a button listener for the specified button. - * - * @param button the button. - * - * @return A button listener. - */ - protected BasicButtonListener createButtonListener(AbstractButton button) - { - return new MetalButtonListener(button); - } - - /** * Paints the background of the button to indicate that it is in the * "pressed" state. * @@ -175,7 +175,7 @@ public class MetalButtonUI if (b.isContentAreaFilled()) { Rectangle area = b.getVisibleRect(); - g.setColor(selectColor); + g.setColor(getSelectColor()); g.fillRect(area.x, area.y, area.width, area.height); } } diff --git a/javax/swing/plaf/metal/MetalCheckBoxIcon.java b/javax/swing/plaf/metal/MetalCheckBoxIcon.java index fb8280e44..30ee93162 100644 --- a/javax/swing/plaf/metal/MetalCheckBoxIcon.java +++ b/javax/swing/plaf/metal/MetalCheckBoxIcon.java @@ -1,5 +1,5 @@ /* MetalCheckBoxIcon.java -- An icon for JCheckBoxes in the Metal L&F - Copyright (C) 2005 Free Software Foundation, Inc. + Copyright (C) 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -42,8 +42,8 @@ import java.awt.Component; import java.awt.Graphics; import java.io.Serializable; +import javax.swing.AbstractButton; import javax.swing.Icon; -import javax.swing.JCheckBox; import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.plaf.UIResource; @@ -134,8 +134,9 @@ public class MetalCheckBoxIcon MetalUtils.paintGradient(g, x, y, getIconWidth(), getIconHeight(), SwingConstants.VERTICAL, "CheckBox.gradient"); border.paintBorder(c, g, x, y, getIconWidth(), getIconHeight()); - JCheckBox cb = (JCheckBox) c; - if (cb.isSelected()) - drawCheck(c, g, x, y); + + AbstractButton b = (AbstractButton) c; + if (b.isSelected()) + drawCheck(b, g, x, y); } } diff --git a/javax/swing/plaf/metal/MetalLookAndFeel.java b/javax/swing/plaf/metal/MetalLookAndFeel.java index da16159c9..ff26aa232 100644 --- a/javax/swing/plaf/metal/MetalLookAndFeel.java +++ b/javax/swing/plaf/metal/MetalLookAndFeel.java @@ -38,6 +38,8 @@ exception statement from your version. */ package javax.swing.plaf.metal; +import gnu.classpath.SystemProperties; + import java.awt.Color; import java.awt.Font; @@ -81,16 +83,15 @@ public class MetalLookAndFeel extends BasicLookAndFeel */ public MetalLookAndFeel() { - createDefaultTheme(); + // Nothing to do here. } /** - * Sets the current theme to a new instance of {@link OceanTheme}. + * Sets the current theme to a new instance of {@link DefaultMetalTheme}. */ protected void createDefaultTheme() { - if (theme == null) - setCurrentTheme(new OceanTheme()); + getCurrentTheme(); } /** @@ -149,6 +150,7 @@ public class MetalLookAndFeel extends BasicLookAndFeel public UIDefaults getDefaults() { + createDefaultTheme(); if (LAF_defaults == null) { LAF_defaults = super.getDefaults(); @@ -1354,7 +1356,14 @@ public class MetalLookAndFeel extends BasicLookAndFeel public static MetalTheme getCurrentTheme() { if (theme == null) - theme = new OceanTheme(); + { + // swing.metalTheme property documented here: + // http://java.sun.com/j2se/1.5.0/docs/guide/swing/1.5/index.html + if ("steel".equals(SystemProperties.getProperty("swing.metalTheme"))) + theme = new DefaultMetalTheme(); + else + theme = new OceanTheme(); + } return theme; } diff --git a/javax/swing/plaf/metal/MetalSplitPaneDivider.java b/javax/swing/plaf/metal/MetalSplitPaneDivider.java index 6081c355c..a3069daa9 100644 --- a/javax/swing/plaf/metal/MetalSplitPaneDivider.java +++ b/javax/swing/plaf/metal/MetalSplitPaneDivider.java @@ -38,18 +38,14 @@ exception statement from your version. */ package javax.swing.plaf.metal; import java.awt.Color; -import java.awt.Component; -import java.awt.Container; import java.awt.Dimension; import java.awt.Graphics; -import java.awt.LayoutManager; -import java.awt.Point; +import java.awt.Insets; +import javax.swing.JButton; import javax.swing.JSplitPane; -import javax.swing.SwingConstants; import javax.swing.UIManager; import javax.swing.border.Border; -import javax.swing.plaf.basic.BasicArrowButton; import javax.swing.plaf.basic.BasicSplitPaneDivider; /** @@ -59,6 +55,143 @@ import javax.swing.plaf.basic.BasicSplitPaneDivider; */ class MetalSplitPaneDivider extends BasicSplitPaneDivider { + /** + * The button pixel data, as indices into the colors array below. + * This is the version for 'left' buttons. + * + * This is slightly different from the icon in Sun's version, it is + * one pixel smaller and is more consistent with BUTTON_SPRITE_R. + */ + static final byte[][] BUTTON_SPRITE_L = {{ 0, 0, 0, 2, 0, 0, 0, 0 }, + { 0, 0, 2, 1, 1, 0, 0, 0 }, + { 0, 2, 1, 1, 1, 1, 0, 0 }, + { 2, 1, 1, 1, 1, 1, 1, 0 }, + { 0, 3, 3, 3, 3, 3, 3, 3 }}; + + /** + * The button pixel data, as indices into the colors array below. + * This is the version for 'right' buttons. + */ + static final byte[][] BUTTON_SPRITE_R = {{ 2, 2, 2, 2, 2, 2, 2, 2 }, + { 0, 1, 1, 1, 1, 1, 1, 3 }, + { 0, 0, 1, 1, 1, 1, 3, 0 }, + { 0, 0, 0, 1, 1, 3, 0, 0 }, + { 0, 0, 0, 0, 3, 0, 0, 0 }}; + + private class MetalOneTouchButton + extends JButton + { + /** + * Denotes a left button. + */ + static final int LEFT = 0; + + /** + * Denotes a right button. + */ + static final int RIGHT = 1; + + /** + * The colors for the button sprite. + */ + private Color[] colors; + + /** + * Either LEFT or RIGHT. + */ + private int direction; + + /** + * Creates a new instance. + * + * @param dir either LEFT or RIGHT + */ + MetalOneTouchButton(int dir) + { + direction = dir; + colors = new Color[4]; + } + + /** + * Never allow borders. + */ + public void setBorder(Border b) + { + } + + /** + * Never allow focus traversal. + */ + public boolean isFocusTraversable() + { + return false; + } + + /** + * Paints the one touch button. + */ + public void paint(Graphics g) + { + if (splitPane != null) + { + // Update colors here to reflect dynamic changes to the theme. + colors[0] = getBackground(); + colors[1] = MetalLookAndFeel.getPrimaryControlDarkShadow(); + colors[2] = MetalLookAndFeel.getPrimaryControlInfo(); + colors[3] = MetalLookAndFeel.getPrimaryControlHighlight(); + + // Fill background. + g.setColor(getBackground()); + g.fillRect(0, 0, getWidth(), getHeight()); + + // Pressed buttons have slightly different color mapping. + if (getModel().isPressed()) + colors[1] = colors[2]; + + byte[][] sprite; + if (direction == LEFT) + sprite = BUTTON_SPRITE_L; + else + sprite = BUTTON_SPRITE_R; + + if (orientation == JSplitPane.VERTICAL_SPLIT) + { + // Draw the sprite as it is. + for (int y = 0; y < sprite.length; y++) + { + byte[] line = sprite[y]; + for (int x = 0; x < line.length; x++) + { + int c = line[x]; + if (c != 0) + { + g.setColor(colors[c]); + g.fillRect(x + 1, y + 1, 1, 1); + } + } + } + } + else + { + // Draw the sprite with swapped X and Y axis. + for (int y = 0; y < sprite.length; y++) + { + byte[] line = sprite[y]; + for (int x = 0; x < line.length; x++) + { + int c = line[x]; + if (c != 0) + { + g.setColor(colors[c]); + g.fillRect(y + 1, x + 1, 1, 1); + } + } + } + } + } + } + } + /** The dark color in the pattern. */ Color dark; @@ -79,7 +212,6 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider public MetalSplitPaneDivider(MetalSplitPaneUI ui, Color light, Color dark) { super(ui); - setLayout(new MetalDividerLayout()); this.splitPane = super.splitPane; this.orientation = super.orientation; this.light = light; @@ -106,126 +238,27 @@ class MetalSplitPaneDivider extends BasicSplitPaneDivider if (border != null) border.paintBorder(this, g, 0, 0, s.width, s.height); - MetalUtils.fillMetalPattern(splitPane, g, 2, 2, s.width - 4, s.height - 4, + Insets i = getInsets(); + MetalUtils.fillMetalPattern(splitPane, g, i.left + 2, i.top + 2, + s.width - i.left - i.right - 4, + s.height - i.top - i.bottom - 4, light, dark); - if (splitPane.isOneTouchExpandable()) - { - ((BasicArrowButton) rightButton).paint(g); - ((BasicArrowButton) leftButton).paint(g); - } + super.paint(g); } - - /** - * This helper class acts as the Layout Manager for the divider. - */ - public class MetalDividerLayout implements LayoutManager - { - /** The right button. */ - BasicArrowButton rb; - - /** The left button. */ - BasicArrowButton lb; - - /** - * Creates a new DividerLayout object. - */ - public MetalDividerLayout() - { - // Nothing to do here - } - - /** - * This method is called when a Component is added. - * - * @param string The constraints string. - * @param c The Component to add. - */ - public void addLayoutComponent(String string, Component c) - { - // Nothing to do here, constraints are set depending on - // orientation in layoutContainer - } - - /** - * This method is called to lay out the container. - * - * @param c The container to lay out. - */ - public void layoutContainer(Container c) - { - // The only components we care about setting up are the - // one touch buttons. - if (splitPane.isOneTouchExpandable()) - { - if (c.getComponentCount() == 2) - { - Component c1 = c.getComponent(0); - Component c2 = c.getComponent(1); - if ((c1 instanceof BasicArrowButton) - && (c2 instanceof BasicArrowButton)) - { - lb = (BasicArrowButton) c1; - rb = (BasicArrowButton) c2; - } - } - if (rb != null && lb != null) - { - Point p = getLocation(); - lb.setSize(lb.getPreferredSize()); - rb.setSize(rb.getPreferredSize()); - lb.setLocation(p.x, p.y); - - if (orientation == JSplitPane.HORIZONTAL_SPLIT) - { - rb.setDirection(SwingConstants.EAST); - lb.setDirection(SwingConstants.WEST); - rb.setLocation(p.x, p.y + lb.getHeight()); - } - else - { - rb.setDirection(SwingConstants.SOUTH); - lb.setDirection(SwingConstants.NORTH); - rb.setLocation(p.x + lb.getWidth(), p.y); - } - } - } - } - - /** - * This method returns the minimum layout size. - * - * @param c The container to calculate for. - * - * @return The minimum layout size. - */ - public Dimension minimumLayoutSize(Container c) - { - return preferredLayoutSize(c); - } - /** - * This method returns the preferred layout size. - * - * @param c The container to calculate for. - * - * @return The preferred layout size. - */ - public Dimension preferredLayoutSize(Container c) - { - int dividerSize = getDividerSize(); - return new Dimension(dividerSize, dividerSize); - } + protected JButton createLeftOneTouchButton() + { + JButton b = new MetalOneTouchButton(MetalOneTouchButton.LEFT); + b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE)); + b.setRequestFocusEnabled(false); + return b; + } - /** - * This method is called when a component is removed. - * - * @param c The component to remove. - */ - public void removeLayoutComponent(Component c) - { - // Nothing to do here. If buttons are removed - // they will not be layed out when layoutContainer is - // called. - } + protected JButton createRightOneTouchButton() + { + JButton b = new MetalOneTouchButton(MetalOneTouchButton.RIGHT); + b.setMinimumSize(new Dimension(ONE_TOUCH_SIZE, ONE_TOUCH_SIZE)); + b.setRequestFocusEnabled(false); + return b; } } diff --git a/javax/swing/plaf/metal/MetalTabbedPaneUI.java b/javax/swing/plaf/metal/MetalTabbedPaneUI.java index 20135fc85..53eaa3cac 100644 --- a/javax/swing/plaf/metal/MetalTabbedPaneUI.java +++ b/javax/swing/plaf/metal/MetalTabbedPaneUI.java @@ -1159,7 +1159,7 @@ public class MetalTabbedPaneUI extends BasicTabbedPaneUI g.drawLine(x + 1, y + 1, x + 1, rect.y + 1); if (rect.y + rect.height < y + h - 2) { - g.drawLine(x + y, rect.y + rect.height + 1, x + 1, y + h + 2); + g.drawLine(x + 1, rect.y + rect.height + 1, x + 1, y + h + 2); } } } diff --git a/javax/swing/plaf/metal/MetalToolTipUI.java b/javax/swing/plaf/metal/MetalToolTipUI.java index d1040347f..6647cc02d 100644 --- a/javax/swing/plaf/metal/MetalToolTipUI.java +++ b/javax/swing/plaf/metal/MetalToolTipUI.java @@ -43,9 +43,6 @@ import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; -import java.awt.Insets; -import java.awt.Rectangle; -import java.awt.Toolkit; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; @@ -54,8 +51,6 @@ import javax.swing.JComponent; import javax.swing.JMenuItem; import javax.swing.JToolTip; import javax.swing.KeyStroke; -import javax.swing.SwingConstants; -import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.plaf.ComponentUI; @@ -192,32 +187,14 @@ public class MetalToolTipUI */ public Dimension getPreferredSize(JComponent c) { - if (isAcceleratorHidden()) - return super.getPreferredSize(c); - else + Dimension d = super.getPreferredSize(c); + String acc = getAcceleratorString(); + if (acc != null && ! acc.equals("")) { - Insets insets = c.getInsets(); - JToolTip tt = (JToolTip) c; - String tipText = tt.getTipText(); - if (tipText != null) - { - FontMetrics fm = c.getFontMetrics(c.getFont()); - int prefH = fm.getHeight() + insets.top + insets.bottom; - int prefW = fm.stringWidth(tipText) + insets.left + insets.right; - - // this seems to be the first opportunity we have to get the - // accelerator string from the component (if it has one) - acceleratorString = fetchAcceleratorString(c); - if (acceleratorString != null) - { - prefW += padSpaceBetweenStrings; - fm = c.getFontMetrics(acceleratorFont); - prefW += fm.stringWidth(acceleratorString); - } - return new Dimension(prefW, prefH); - } - else return new Dimension(0, 0); + FontMetrics fm = c.getFontMetrics(c.getFont()); + d.width += fm.stringWidth(acc); } + return d; } /** @@ -228,39 +205,8 @@ public class MetalToolTipUI */ public void paint(Graphics g, JComponent c) { - JToolTip tip = (JToolTip) c; - - String text = tip.getTipText(); - Toolkit t = tip.getToolkit(); - if (text == null) - return; - - Rectangle vr = new Rectangle(); - vr = SwingUtilities.calculateInnerArea(tip, vr); - Rectangle ir = new Rectangle(); - Rectangle tr = new Rectangle(); - FontMetrics fm = t.getFontMetrics(tip.getFont()); - int ascent = fm.getAscent(); - SwingUtilities.layoutCompoundLabel(tip, fm, text, null, - SwingConstants.CENTER, SwingConstants.LEFT, - SwingConstants.CENTER, SwingConstants.CENTER, vr, ir, tr, 0); - Color saved = g.getColor(); - g.setColor(Color.BLACK); - - g.drawString(text, vr.x, vr.y + ascent); - - // paint accelerator - if (acceleratorString != null) - { - g.setFont(acceleratorFont); - g.setColor(acceleratorForeground); - fm = t.getFontMetrics(acceleratorFont); - int width = fm.stringWidth(acceleratorString); - g.drawString(acceleratorString, vr.x + vr.width - width - - padSpaceBetweenStrings / 2, vr.y + vr.height - fm.getDescent()); - } - - g.setColor(saved); + super.paint(g, c); + // Somehow paint accelerator. Keep care for possible HTML rendering. } /** diff --git a/javax/swing/plaf/metal/MetalTreeUI.java b/javax/swing/plaf/metal/MetalTreeUI.java index 3ea37c82f..ed1e5b4d8 100644 --- a/javax/swing/plaf/metal/MetalTreeUI.java +++ b/javax/swing/plaf/metal/MetalTreeUI.java @@ -38,14 +38,15 @@ exception statement from your version. */ package javax.swing.plaf.metal; -import gnu.classpath.NotImplementedException; - import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JTree; +import javax.swing.UIManager; import javax.swing.tree.TreePath; import javax.swing.plaf.ComponentUI; import javax.swing.plaf.basic.BasicTreeUI; @@ -56,6 +57,68 @@ import javax.swing.plaf.basic.BasicTreeUI; public class MetalTreeUI extends BasicTreeUI { /** + * Listens for property changes of the line style and updates the + * internal setting. + */ + private class LineStyleListener + implements PropertyChangeListener + { + + public void propertyChange(PropertyChangeEvent e) + { + if (e.getPropertyName().equals(LINE_STYLE_PROPERTY)) + decodeLineStyle(e.getNewValue()); + } + + } + + /** + * The key to the lineStyle client property. + */ + private static final String LINE_STYLE_PROPERTY = "JTree.lineStyle"; + + /** + * The property value indicating no line style. + */ + private static final String LINE_STYLE_VALUE_NONE = "None"; + + /** + * The property value indicating angled line style. + */ + private static final String LINE_STYLE_VALUE_ANGLED = "Angled"; + + /** + * The property value indicating horizontal line style. + */ + private static final String LINE_STYLE_VALUE_HORIZONTAL = "Horizontal"; + + /** + * The line style for None. + */ + private static final int LINE_STYLE_NONE = 0; + + /** + * The line style for Angled. + */ + private static final int LINE_STYLE_ANGLED = 1; + + /** + * The line style for Horizontal. + */ + private static final int LINE_STYLE_HORIZONTAL = 2; + + /** + * The current line style. + */ + private int lineStyle; + + /** + * Listens for changes on the line style property and updates the + * internal settings. + */ + private PropertyChangeListener lineStyleListener; + + /** * Constructs a new instance of <code>MetalTreeUI</code>. */ public MetalTreeUI() @@ -103,8 +166,13 @@ public class MetalTreeUI extends BasicTreeUI */ public void installUI(JComponent c) { - // TODO: What to do here, if anything? super.installUI(c); + + Object lineStyleProp = c.getClientProperty(LINE_STYLE_PROPERTY); + decodeLineStyle(lineStyleProp); + if (lineStyleListener == null) + lineStyleListener = new LineStyleListener(); + c.addPropertyChangeListener(lineStyleListener); } /** @@ -124,8 +192,10 @@ public class MetalTreeUI extends BasicTreeUI */ public void uninstallUI(JComponent c) { - // TODO: What to do here? super.uninstallUI(c); + if (lineStyleListener != null) + c.removePropertyChangeListener(lineStyleListener); + lineStyleListener = null; } /** @@ -135,9 +205,15 @@ public class MetalTreeUI extends BasicTreeUI * @param lineStyleFlag - String representation */ protected void decodeLineStyle(Object lineStyleFlag) - throws NotImplementedException { - // FIXME: not implemented + if (lineStyleFlag == null || lineStyleFlag.equals(LINE_STYLE_VALUE_ANGLED)) + lineStyle = LINE_STYLE_ANGLED; + else if (lineStyleFlag.equals(LINE_STYLE_VALUE_HORIZONTAL)) + lineStyle = LINE_STYLE_HORIZONTAL; + else if (lineStyleFlag.equals(LINE_STYLE_VALUE_NONE)) + lineStyle = LINE_STYLE_NONE; + else + lineStyle = LINE_STYLE_ANGLED; } /** @@ -170,6 +246,9 @@ public class MetalTreeUI extends BasicTreeUI // Calls BasicTreeUI's paint since it takes care of painting all // types of icons. super.paint(g, c); + + if (lineStyle == LINE_STYLE_HORIZONTAL) + paintHorizontalSeparators(g, c); } /** @@ -179,9 +258,28 @@ public class MetalTreeUI extends BasicTreeUI * @param c - the current component to draw */ protected void paintHorizontalSeparators(Graphics g, JComponent c) - throws NotImplementedException { - // FIXME: not implemented + g.setColor(UIManager.getColor("Tree.line")); + Rectangle clip = g.getClipBounds(); + int row0 = getRowForPath(tree, getClosestPathForLocation(tree, 0, clip.y)); + int row1 = + getRowForPath(tree, getClosestPathForLocation(tree, 0, + clip.y + clip.height - 1)); + if (row0 >= 0 && row1 >= 0) + { + for (int i = row0; i <= row1; i++) + { + TreePath p = getPathForRow(tree, i); + if (p != null && p.getPathCount() == 2) + { + Rectangle r = getPathBounds(tree, getPathForRow(tree, i)); + if (r != null) + { + g.drawLine(clip.x, r.y, clip.x + clip.width, r.y); + } + } + } + } } @@ -197,7 +295,8 @@ public class MetalTreeUI extends BasicTreeUI protected void paintVerticalPartOfLeg(Graphics g, Rectangle clipBounds, Insets insets, TreePath path) { - super.paintVerticalPartOfLeg(g, clipBounds, insets, path); + if (lineStyle == LINE_STYLE_ANGLED) + super.paintVerticalPartOfLeg(g, clipBounds, insets, path); } /** @@ -211,7 +310,8 @@ public class MetalTreeUI extends BasicTreeUI boolean isExpanded, boolean hasBeenExpanded, boolean isLeaf) { - super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, path, row, - isExpanded, hasBeenExpanded, isLeaf); + if (lineStyle == LINE_STYLE_ANGLED) + super.paintHorizontalPartOfLeg(g, clipBounds, insets, bounds, path, row, + isExpanded, hasBeenExpanded, isLeaf); } } diff --git a/javax/swing/table/DefaultTableModel.java b/javax/swing/table/DefaultTableModel.java index 79285903c..1b68ce2be 100644 --- a/javax/swing/table/DefaultTableModel.java +++ b/javax/swing/table/DefaultTableModel.java @@ -625,7 +625,7 @@ public class DefaultTableModel extends AbstractTableModel if (columnCount > columnIdentifiers.size()) columnIdentifiers.setSize(columnCount); - if (rowCount > dataVector.size()) + if (dataVector != null && rowCount > dataVector.size()) { int rowsToAdd = rowCount - dataVector.size(); addExtraRows(rowsToAdd, columnCount); diff --git a/javax/swing/text/BoxView.java b/javax/swing/text/BoxView.java index 7e8f19f74..962d06219 100644 --- a/javax/swing/text/BoxView.java +++ b/javax/swing/text/BoxView.java @@ -92,11 +92,6 @@ public class BoxView private int[] span = new int[2]; /** - * The SizeRequirements of the child views along the X_AXIS and Y_AXIS. - */ - private SizeRequirements[][] childReqs = new SizeRequirements[2][]; - - /** * Creates a new <code>BoxView</code> for the given * <code>Element</code> and axis. Valid values for the axis are * {@link View#X_AXIS} and {@link View#Y_AXIS}. @@ -227,48 +222,30 @@ public class BoxView */ public void replace(int offset, int length, View[] views) { - int numViews = 0; - if (views != null) - numViews = views.length; - - // Resize and copy data for cache arrays. - // The spansX cache. - int oldSize = getViewCount(); - - int[] newSpansX = new int[oldSize - length + numViews]; - System.arraycopy(spans[X_AXIS], 0, newSpansX, 0, offset); - System.arraycopy(spans[X_AXIS], offset + length, newSpansX, - offset + numViews, - oldSize - (offset + length)); - spans[X_AXIS] = newSpansX; - - // The spansY cache. - int[] newSpansY = new int[oldSize - length + numViews]; - System.arraycopy(spans[Y_AXIS], 0, newSpansY, 0, offset); - System.arraycopy(spans[Y_AXIS], offset + length, newSpansY, - offset + numViews, - oldSize - (offset + length)); - spans[Y_AXIS] = newSpansY; - - // The offsetsX cache. - int[] newOffsetsX = new int[oldSize - length + numViews]; - System.arraycopy(offsets[X_AXIS], 0, newOffsetsX, 0, offset); - System.arraycopy(offsets[X_AXIS], offset + length, newOffsetsX, - offset + numViews, - oldSize - (offset + length)); - offsets[X_AXIS] = newOffsetsX; - - // The offsetsY cache. - int[] newOffsetsY = new int[oldSize - length + numViews]; - System.arraycopy(offsets[Y_AXIS], 0, newOffsetsY, 0, offset); - System.arraycopy(offsets[Y_AXIS], offset + length, newOffsetsY, - offset + numViews, - oldSize - (offset + length)); - offsets[Y_AXIS] = newOffsetsY; + int oldNumChildren = getViewCount(); // Actually perform the replace. super.replace(offset, length, views); + // Resize and copy data for cache arrays. + int newItems = views != null ? views.length : 0; + int delta = newItems - length; + int src = offset + length; + int numMove = oldNumChildren - src; + int dst = src + delta; + offsets[X_AXIS] = replaceLayoutArray(offsets[X_AXIS], offset, + oldNumChildren, delta, src, dst, + numMove); + spans[X_AXIS] = replaceLayoutArray(spans[X_AXIS], offset, + oldNumChildren, delta, src, dst, + numMove); + offsets[Y_AXIS] = replaceLayoutArray(offsets[Y_AXIS], offset, + oldNumChildren, delta, src, dst, + numMove); + spans[Y_AXIS] = replaceLayoutArray(spans[Y_AXIS], offset, + oldNumChildren, delta, src, dst, + numMove); + // Invalidate layout information. layoutValid[X_AXIS] = false; requirementsValid[X_AXIS] = false; @@ -277,6 +254,34 @@ public class BoxView } /** + * Helper method. This updates the layout cache arrays in response + * to a call to {@link #replace(int, int, View[])}. + * + * @param oldArray the old array + * + * @return the replaced array + */ + private int[] replaceLayoutArray(int[] oldArray, int offset, int numChildren, + int delta, int src, int dst, int numMove) + + { + int[] newArray; + if (numChildren + delta > oldArray.length) + { + int newLength = Math.max(2 * oldArray.length, numChildren + delta); + newArray = new int[newLength]; + System.arraycopy(oldArray, 0, newArray, 0, offset); + System.arraycopy(oldArray, src, newArray, dst, numMove); + } + else + { + newArray = oldArray; + System.arraycopy(newArray, src, newArray, dst, numMove); + } + return newArray; + } + + /** * Renders the <code>Element</code> that is associated with this * <code>View</code>. * @@ -373,9 +378,9 @@ public class BoxView } /** - * This method is obsolete and no longer in use. It is replaced by - * {@link #calculateMajorAxisRequirements(int, SizeRequirements)} and - * {@link #calculateMinorAxisRequirements(int, SizeRequirements)}. + * Calculates size requirements for a baseline layout. This is not + * used by the BoxView itself, but by subclasses that wish to perform + * a baseline layout, like the FlowView's rows. * * @param axis the axis that is examined * @param sr the <code>SizeRequirements</code> object to hold the result, @@ -387,50 +392,94 @@ public class BoxView protected SizeRequirements baselineRequirements(int axis, SizeRequirements sr) { - updateChildRequirements(axis); + // Create new instance if sr == null. + if (sr == null) + sr = new SizeRequirements(); + sr.alignment = 0.5F; + + // Calculate overall ascent and descent. + int totalAscentMin = 0; + int totalAscentPref = 0; + int totalAscentMax = 0; + int totalDescentMin = 0; + int totalDescentPref = 0; + int totalDescentMax = 0; + + int count = getViewCount(); + for (int i = 0; i < count; i++) + { + View v = getView(i); + float align = v.getAlignment(axis); + int span = (int) v.getPreferredSpan(axis); + int ascent = (int) (align * span); + int descent = span - ascent; + + totalAscentPref = Math.max(ascent, totalAscentPref); + totalDescentPref = Math.max(descent, totalDescentPref); + if (v.getResizeWeight(axis) > 0) + { + // If the view is resizable, then use the min and max size + // of the view. + span = (int) v.getMinimumSpan(axis); + ascent = (int) (align * span); + descent = span - ascent; + totalAscentMin = Math.max(ascent, totalAscentMin); + totalDescentMin = Math.max(descent, totalDescentMin); + + span = (int) v.getMaximumSpan(axis); + ascent = (int) (align * span); + descent = span - ascent; + totalAscentMax = Math.max(ascent, totalAscentMax); + totalDescentMax = Math.max(descent, totalDescentMax); + } + else + { + // If the view is not resizable, use the preferred span. + totalAscentMin = Math.max(ascent, totalAscentMin); + totalDescentMin = Math.max(descent, totalDescentMin); + totalAscentMax = Math.max(ascent, totalAscentMax); + totalDescentMax = Math.max(descent, totalDescentMax); + } + } - SizeRequirements res = sr; - if (res == null) - res = new SizeRequirements(); + // Preferred overall span is the sum of the preferred ascent and descent. + // With overflow check. + sr.preferred = (int) Math.min((long) totalAscentPref + + (long) totalDescentPref, + Integer.MAX_VALUE); - float minLeft = 0; - float minRight = 0; - float prefLeft = 0; - float prefRight = 0; - float maxLeft = 0; - float maxRight = 0; - for (int i = 0; i < childReqs[axis].length; i++) + // Align along the baseline. + if (sr.preferred > 0) + sr.alignment = (float) totalAscentPref / sr.preferred; + + if (sr.alignment == 0) { - float myMinLeft = childReqs[axis][i].minimum * childReqs[axis][i].alignment; - float myMinRight = childReqs[axis][i].minimum - myMinLeft; - minLeft = Math.max(myMinLeft, minLeft); - minRight = Math.max(myMinRight, minRight); - float myPrefLeft = childReqs[axis][i].preferred * childReqs[axis][i].alignment; - float myPrefRight = childReqs[axis][i].preferred - myPrefLeft; - prefLeft = Math.max(myPrefLeft, prefLeft); - prefRight = Math.max(myPrefRight, prefRight); - float myMaxLeft = childReqs[axis][i].maximum * childReqs[axis][i].alignment; - float myMaxRight = childReqs[axis][i].maximum - myMaxLeft; - maxLeft = Math.max(myMaxLeft, maxLeft); - maxRight = Math.max(myMaxRight, maxRight); + // Nothing above the baseline, use the descent. + sr.minimum = totalDescentMin; + sr.maximum = totalDescentMax; } - int minSize = (int) (minLeft + minRight); - int prefSize = (int) (prefLeft + prefRight); - int maxSize = (int) (maxLeft + maxRight); - float align = prefLeft / (prefRight + prefLeft); - if (Float.isNaN(align)) - align = 0; - - res.alignment = align; - res.maximum = maxSize; - res.preferred = prefSize; - res.minimum = minSize; - return res; + else if (sr.alignment == 1.0F) + { + // Nothing below the baseline, use the descent. + sr.minimum = totalAscentMin; + sr.maximum = totalAscentMax; + } + else + { + sr.minimum = Math.max((int) (totalAscentMin / sr.alignment), + (int) (totalDescentMin / (1.0F - sr.alignment))); + sr.maximum = Math.min((int) (totalAscentMax / sr.alignment), + (int) (totalDescentMax / (1.0F - sr.alignment))); + } + return sr; } /** - * Calculates the layout of the children of this <code>BoxView</code> along - * the specified axis. + * Calculates the baseline layout of the children of this + * <code>BoxView</code> along the specified axis. + * + * This is not used by the BoxView itself, but by subclasses that wish to + * perform a baseline layout, like the FlowView's rows. * * @param span the target span * @param axis the axis that is examined @@ -440,13 +489,36 @@ public class BoxView protected void baselineLayout(int span, int axis, int[] offsets, int[] spans) { - updateChildRequirements(axis); - updateRequirements(axis); + int totalAscent = (int) (span * getAlignment(axis)); + int totalDescent = span - totalAscent; - // Calculate the spans and offsets using the SizeRequirements uility - // methods. - SizeRequirements.calculateAlignedPositions(span, requirements[axis], - childReqs[axis], offsets, spans); + int count = getViewCount(); + for (int i = 0; i < count; i++) + { + View v = getView(i); + float align = v.getAlignment(axis); + int viewSpan; + if (v.getResizeWeight(axis) > 0) + { + // If possible, then resize for best fit. + int min = (int) v.getMinimumSpan(axis); + int max = (int) v.getMaximumSpan(axis); + if (align == 0.0F) + viewSpan = Math.max(Math.min(max, totalDescent), min); + else if (align == 1.0F) + viewSpan = Math.max(Math.min(max, totalAscent), min); + else + { + int fit = (int) Math.min(totalAscent / align, + totalDescent / (1.0F - align)); + viewSpan = Math.max(Math.min(max, fit), min); + } + } + else + viewSpan = (int) v.getPreferredSpan(axis); + offsets[i] = totalAscent - (int) (viewSpan * align); + spans[i] = viewSpan; + } } /** @@ -509,7 +581,7 @@ public class BoxView res.minimum = 0; res.preferred = 0; - res.maximum = 0; + res.maximum = Integer.MAX_VALUE; res.alignment = 0.5F; int n = getViewCount(); for (int i = 0; i < n; i++) @@ -589,24 +661,54 @@ public class BoxView { View result = null; int count = getViewCount(); - Rectangle copy = new Rectangle(r); - - for (int i = 0; i < count; ++i) + if (myAxis == X_AXIS) { - copy.setBounds(r); - // The next call modifies copy. - childAllocation(i, copy); - if (copy.contains(x, y)) + // Border case. Requested point is left from the box. + if (x < r.x + offsets[X_AXIS][0]) { - // Modify r on success. - r.setBounds(copy); - result = getView(i); - break; + childAllocation(0, r); + result = getView(0); + } + else + { + // Search views inside box. + for (int i = 0; i < count && result == null; i++) + { + if (x < r.x + offsets[X_AXIS][i]) + { + childAllocation(i - 1, r); + result = getView(i - 1); + } + } } } - - if (result == null && count > 0) - return getView(count - 1); + else // Same algorithm for Y_AXIS. + { + // Border case. Requested point is above the box. + if (y < r.y + offsets[Y_AXIS][0]) + { + childAllocation(0, r); + result = getView(0); + } + else + { + // Search views inside box. + for (int i = 0; i < count && result == null; i++) + { + if (y < r.y + offsets[Y_AXIS][i]) + { + childAllocation(i - 1, r); + result = getView(i - 1); + } + } + } + } + // Not found, other border case: point is right from or below the box. + if (result == null) + { + childAllocation(count - 1, r); + result = getView(count - 1); + } return result; } @@ -808,7 +910,9 @@ public class BoxView */ public int getWidth() { - return span[X_AXIS] + getLeftInset() - getRightInset(); + // The RI returns the following here, however, I'd think that is a bug. + // return span[X_AXIS] + getLeftInset() - getRightInset(); + return span[X_AXIS] + getLeftInset() + getRightInset(); } /** @@ -818,7 +922,9 @@ public class BoxView */ public int getHeight() { - return span[Y_AXIS] + getTopInset() - getBottomInset(); + // The RI returns the following here, however, I'd think that is a bug. + // return span[Y_AXIS] + getTopInset() - getBottomInset(); + return span[Y_AXIS] + getTopInset() + getBottomInset(); } /** @@ -977,7 +1083,11 @@ public class BoxView public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) { - // FIXME: What to do here? + if (! isAllocationValid()) + { + Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + setSize(r.width, r.height); + } return super.viewToModel(x, y, a, bias); } @@ -988,32 +1098,6 @@ public class BoxView } /** - * Updates the child requirements along the specified axis. The requirements - * are only updated if the layout for the specified axis is marked as - * invalid. - * - * @param axis the axis to be updated - */ - private void updateChildRequirements(int axis) - { - if (! isLayoutValid(axis)) - { - int numChildren = getViewCount(); - if (childReqs[axis] == null || childReqs[axis].length != numChildren) - childReqs[axis] = new SizeRequirements[numChildren]; - for (int i = 0; i < numChildren; ++i) - { - View child = getView(i); - childReqs[axis][i] = - new SizeRequirements((int) child.getMinimumSpan(axis), - (int) child.getPreferredSpan(axis), - (int) child.getMaximumSpan(axis), - child.getAlignment(axis)); - } - } - } - - /** * Updates the view's cached requirements along the specified axis if * necessary. The requirements are only updated if the layout for the * specified axis is marked as invalid. diff --git a/javax/swing/text/ComponentView.java b/javax/swing/text/ComponentView.java index a7d237ab7..555120396 100644 --- a/javax/swing/text/ComponentView.java +++ b/javax/swing/text/ComponentView.java @@ -39,11 +39,11 @@ package javax.swing.text; import java.awt.Component; import java.awt.Container; +import java.awt.Dimension; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; -import javax.swing.SwingConstants; import javax.swing.SwingUtilities; /** @@ -62,11 +62,161 @@ public class ComponentView extends View { /** + * A special container that sits between the component and the hosting + * container. This is used to propagate invalidate requests and cache + * the component's layout sizes. + */ + private class Interceptor + extends Container + { + Dimension min; + Dimension pref; + Dimension max; + float alignX; + float alignY; + + /** + * Creates a new instance that hosts the specified component. + */ + Interceptor(Component c) + { + setLayout(null); + add(c); + cacheComponentSizes(); + } + + /** + * Intercepts the normal invalidate call and propagates the invalidate + * request up using the View's preferenceChanged(). + */ + public void invalidate() + { + super.invalidate(); + if (getParent() != null) + preferenceChanged(null, true, true); + } + + /** + * This is overridden to simply cache the layout sizes. + */ + public void doLayout() + { + cacheComponentSizes(); + } + + /** + * Overridden to also reshape the component itself. + */ + public void reshape(int x, int y, int w, int h) + { + super.reshape(x, y, w, h); + if (getComponentCount() > 0) + getComponent(0).setSize(w, h); + cacheComponentSizes(); + } + + /** + * Overridden to also show the component. + */ + public void show() + { + super.show(); + if (getComponentCount() > 0) + getComponent(0).setVisible(true); + } + + /** + * Overridden to also hide the component. + */ + public void hide() + { + super.hide(); + if (getComponentCount() > 0) + getComponent(0).setVisible(false); + } + + /** + * Overridden to return the cached value. + */ + public Dimension getMinimumSize() + { + maybeValidate(); + return min; + } + + /** + * Overridden to return the cached value. + */ + public Dimension getPreferredSize() + { + maybeValidate(); + return pref; + } + + /** + * Overridden to return the cached value. + */ + public Dimension getMaximumSize() + { + maybeValidate(); + return max; + } + + /** + * Overridden to return the cached value. + */ + public float getAlignmentX() + { + maybeValidate(); + return alignX; + } + + /** + * Overridden to return the cached value. + */ + public float getAlignmentY() + { + maybeValidate(); + return alignY; + } + + /** + * Validates the container only when necessary. + */ + private void maybeValidate() + { + if (! isValid()) + validate(); + } + + /** + * Fetches the component layout sizes into the cache. + */ + private void cacheComponentSizes() + { + if (getComponentCount() > 0) + { + Component c = getComponent(0); + min = c.getMinimumSize(); + pref = c.getPreferredSize(); + max = c.getMaximumSize(); + alignX = c.getAlignmentX(); + alignY = c.getAlignmentY(); + } + } + } + + /** * The component that is displayed by this view. */ private Component comp; /** + * The intercepting container. + */ + private Interceptor interceptor; + + /** * Creates a new instance of <code>ComponentView</code> for the specified * <code>Element</code>. * @@ -99,13 +249,20 @@ public class ComponentView extends View */ public float getAlignment(int axis) { - float align; - if (axis == X_AXIS) - align = getComponent().getAlignmentX(); - else if (axis == Y_AXIS) - align = getComponent().getAlignmentY(); + float align = 0.0F; + // I'd rather throw an IllegalArgumentException for illegal axis, + // but the Harmony testsuite indicates fallback to super behaviour. + if (interceptor != null && (axis == X_AXIS || axis == Y_AXIS)) + { + if (axis == X_AXIS) + align = interceptor.getAlignmentX(); + else if (axis == Y_AXIS) + align = interceptor.getAlignmentY(); + else + assert false : "Must not reach here"; + } else - throw new IllegalArgumentException(); + align = super.getAlignment(axis); return align; } @@ -118,8 +275,6 @@ public class ComponentView extends View */ public final Component getComponent() { - if (comp == null) - comp = createComponent(); return comp; } @@ -135,49 +290,70 @@ public class ComponentView extends View */ public float getMaximumSpan(int axis) { - float span; - if (axis == X_AXIS) - span = getComponent().getMaximumSize().width; - else if (axis == Y_AXIS) - span = getComponent().getMaximumSize().height; - else - throw new IllegalArgumentException(); + if (axis != X_AXIS && axis != Y_AXIS) + throw new IllegalArgumentException("Illegal axis"); + float span = 0; + if (interceptor != null) + { + if (axis == X_AXIS) + span = interceptor.getMaximumSize().width; + else if (axis == Y_AXIS) + span = interceptor.getMaximumSize().height; + else + assert false : "Must not reach here"; + } return span; } public float getMinimumSpan(int axis) { - float span; - if (axis == X_AXIS) - span = getComponent().getMinimumSize().width; - else if (axis == Y_AXIS) - span = getComponent().getMinimumSize().height; - else - throw new IllegalArgumentException(); + if (axis != X_AXIS && axis != Y_AXIS) + throw new IllegalArgumentException("Illegal axis"); + float span = 0; + if (interceptor != null) + { + if (axis == X_AXIS) + span = interceptor.getMinimumSize().width; + else if (axis == Y_AXIS) + span = interceptor.getMinimumSize().height; + else + assert false : "Must not reach here"; + } return span; } public float getPreferredSpan(int axis) { - float span; - if (axis == X_AXIS) - span = getComponent().getPreferredSize().width; - else if (axis == Y_AXIS) - span = getComponent().getPreferredSize().height; - else - throw new IllegalArgumentException(); + if (axis != X_AXIS && axis != Y_AXIS) + throw new IllegalArgumentException("Illegal axis"); + float span = 0; + if (interceptor != null) + { + if (axis == X_AXIS) + span = interceptor.getPreferredSize().width; + else if (axis == Y_AXIS) + span = interceptor.getPreferredSize().height; + else + assert false : "Must not reach here"; + } return span; } public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException { - Element el = getElement(); - if (pos < el.getStartOffset() || pos >= el.getEndOffset()) - throw new BadLocationException("Illegal offset for this view", pos); - Rectangle r = a.getBounds(); - Component c = getComponent(); - return new Rectangle(r.x, r.y, c.getWidth(), c.getHeight()); + int p0 = getStartOffset(); + int p1 = getEndOffset(); + if (pos >= p0 && pos <= p1) + { + Rectangle viewRect = a.getBounds(); + if (pos == p1) + viewRect.x += viewRect.width; + viewRect.width = 0; + return viewRect; + } + else + throw new BadLocationException("Illegal position", pos); } /** @@ -191,8 +367,11 @@ public class ComponentView extends View */ public void paint(Graphics g, Shape a) { - Rectangle r = a.getBounds(); - getComponent().setBounds(r.x, r.y, r.width, r.height); + if (interceptor != null) + { + Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + interceptor.setBounds(r.x, r.y, r.width, r.height); + } } /** @@ -209,15 +388,16 @@ public class ComponentView extends View */ public void setParent(final View p) { + super.setParent(p); if (SwingUtilities.isEventDispatchThread()) - setParentImpl(p); + setParentImpl(); else SwingUtilities.invokeLater (new Runnable() { public void run() { - setParentImpl(p); + setParentImpl(); } }); } @@ -225,23 +405,41 @@ public class ComponentView extends View /** * The implementation of {@link #setParent}. This is package private to * avoid a synthetic accessor method. - * - * @param p the parent view to set */ - private void setParentImpl(View p) + void setParentImpl() { - super.setParent(p); + View p = getParent(); if (p != null) { - Component c = getComponent(); - p.getContainer().add(c); + Container c = getContainer(); + if (c != null) + { + if (interceptor == null) + { + // Create component and put it inside the interceptor. + Component created = createComponent(); + if (created != null) + { + comp = created; + interceptor = new Interceptor(comp); + } + } + if (interceptor != null) + { + // Add the interceptor to the hosting container. + if (interceptor.getParent() == null) + c.add(interceptor, this); + } + } } else { - Component c = getComponent(); - Container parent = c.getParent(); - parent.remove(c); - comp = null; + if (interceptor != null) + { + Container parent = interceptor.getParent(); + if (parent != null) + parent.remove(interceptor); + } } } @@ -259,10 +457,21 @@ public class ComponentView extends View */ public int viewToModel(float x, float y, Shape a, Position.Bias[] b) { - // The element should only have one character position and it is clear - // that this position is the position that best matches the given screen - // coordinates, simply because this view has only this one position. - Element el = getElement(); - return el.getStartOffset(); + int pos; + // I'd rather do the following. The harmony testsuite indicates + // that a simple cast is performed. + //Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + Rectangle r = (Rectangle) a; + if (x < r.x + r.width / 2) + { + b[0] = Position.Bias.Forward; + pos = getStartOffset(); + } + else + { + b[0] = Position.Bias.Backward; + pos = getEndOffset(); + } + return pos; } } diff --git a/javax/swing/text/CompositeView.java b/javax/swing/text/CompositeView.java index 6f487b898..ab587a9e1 100644 --- a/javax/swing/text/CompositeView.java +++ b/javax/swing/text/CompositeView.java @@ -38,7 +38,6 @@ exception statement from your version. */ package javax.swing.text; -import java.awt.Insets; import java.awt.Rectangle; import java.awt.Shape; @@ -57,7 +56,12 @@ public abstract class CompositeView /** * The child views of this <code>CompositeView</code>. */ - View[] children; + private View[] children; + + /** + * The number of child views. + */ + private int numChildren; /** * The allocation of this <code>View</code> minus its insets. This is @@ -70,7 +74,10 @@ public abstract class CompositeView * The insets of this <code>CompositeView</code>. This is initialized * in {@link #setInsets}. */ - Insets insets; + private short top; + private short bottom; + private short left; + private short right; /** * Creates a new <code>CompositeView</code> for the given @@ -82,7 +89,10 @@ public abstract class CompositeView { super(element); children = new View[0]; - insets = new Insets(0, 0, 0, 0); + top = 0; + bottom = 0; + left = 0; + right = 0; } /** @@ -96,16 +106,22 @@ public abstract class CompositeView */ protected void loadChildren(ViewFactory f) { - Element el = getElement(); - int count = el.getElementCount(); - View[] newChildren = new View[count]; - for (int i = 0; i < count; ++i) + if (f != null) { - Element child = el.getElement(i); - View view = f.create(child); - newChildren[i] = view; + Element el = getElement(); + int count = el.getElementCount(); + View[] newChildren = new View[count]; + for (int i = 0; i < count; ++i) + { + Element child = el.getElement(i); + View view = f.create(child); + newChildren[i] = view; + } + // I'd have called replace(0, getViewCount(), newChildren) here + // in order to replace all existing views. However according to + // Harmony's tests this is not what the RI does. + replace(0, 0, newChildren); } - replace(0, getViewCount(), newChildren); } /** @@ -129,7 +145,7 @@ public abstract class CompositeView */ public int getViewCount() { - return children.length; + return numChildren; } /** @@ -156,24 +172,42 @@ public abstract class CompositeView */ public void replace(int offset, int length, View[] views) { - // Check for null views to add. - for (int i = 0; i < views.length; ++i) - if (views[i] == null) - throw new NullPointerException("Added views must not be null"); - - int endOffset = offset + length; + // Make sure we have an array. The Harmony testsuite indicates that we + // have to do something like this. + if (views == null) + views = new View[0]; // First we set the parent of the removed children to null. + int endOffset = offset + length; for (int i = offset; i < endOffset; ++i) - children[i].setParent(null); + { + if (children[i].getParent() == this) + children[i].setParent(null); + children[i] = null; + } - View[] newChildren = new View[children.length - length + views.length]; - System.arraycopy(children, 0, newChildren, 0, offset); - System.arraycopy(views, 0, newChildren, offset, views.length); - System.arraycopy(children, offset + length, newChildren, - offset + views.length, - children.length - (offset + length)); - children = newChildren; + // Update the children array. + int delta = views.length - length; + int src = offset + length; + int numMove = numChildren - src; + int dst = src + delta; + if (numChildren + delta > children.length) + { + // Grow array. + int newLength = Math.max(2 * children.length, numChildren + delta); + View[] newChildren = new View[newLength]; + System.arraycopy(children, 0, newChildren, 0, offset); + System.arraycopy(views, 0, newChildren, offset, views.length); + System.arraycopy(children, src, newChildren, dst, numMove); + children = newChildren; + } + else + { + // Patch existing array. + System.arraycopy(children, src, children, dst, numMove); + System.arraycopy(views, 0, children, offset, views.length); + } + numChildren += delta; // Finally we set the parent of the added children to this. for (int i = 0; i < views.length; ++i) @@ -258,27 +292,6 @@ public abstract class CompositeView } /** - * A helper method for {@link #modelToView(int, Position.Bias, int, - * Position.Bias, Shape)}. This creates a default location when there is - * no child view that can take responsibility for mapping the position to - * view coordinates. Depending on the specified bias this will be the - * left or right edge of this view's allocation. - * - * @param a the allocation for this view - * @param bias the bias - * - * @return a default location - */ - private Shape createDefaultLocation(Shape a, Position.Bias bias) - { - Rectangle alloc = a.getBounds(); - Rectangle location = new Rectangle(alloc.x, alloc.y, 1, alloc.height); - if (bias == Position.Bias.Forward) - location.x = alloc.x + alloc.width; - return location; - } - - /** * Maps a region in the document into the coordinate space of the View. * * @param p1 the beginning position inside the document @@ -394,7 +407,7 @@ public abstract class CompositeView */ public int getViewIndex(int pos, Position.Bias b) { - if (b == Position.Bias.Backward && pos != 0) + if (b == Position.Bias.Backward) pos -= 1; int i = -1; if (pos >= getStartOffset() && pos < getEndOffset()) @@ -528,10 +541,10 @@ public abstract class CompositeView insideAllocation = inside; } } - inside.x = alloc.x + insets.left; - inside.y = alloc.y + insets.top; - inside.width = alloc.width - insets.left - insets.right; - inside.height = alloc.height - insets.top - insets.bottom; + inside.x = alloc.x + left; + inside.y = alloc.y + top; + inside.width = alloc.width - left - right; + inside.height = alloc.height - top - bottom; return inside; } @@ -546,39 +559,26 @@ public abstract class CompositeView */ protected void setParagraphInsets(AttributeSet attributes) { - Float l = (Float) attributes.getAttribute(StyleConstants.LeftIndent); - short left = 0; - if (l != null) - left = l.shortValue(); - Float r = (Float) attributes.getAttribute(StyleConstants.RightIndent); - short right = 0; - if (r != null) - right = r.shortValue(); - Float t = (Float) attributes.getAttribute(StyleConstants.SpaceAbove); - short top = 0; - if (t != null) - top = t.shortValue(); - Float b = (Float) attributes.getAttribute(StyleConstants.SpaceBelow); - short bottom = 0; - if (b != null) - bottom = b.shortValue(); - setInsets(top, left, bottom, right); + top = (short) StyleConstants.getSpaceAbove(attributes); + bottom = (short) StyleConstants.getSpaceBelow(attributes); + left = (short) StyleConstants.getLeftIndent(attributes); + right = (short) StyleConstants.getRightIndent(attributes); } /** * Sets the insets of this <code>CompositeView</code>. * - * @param top the top inset - * @param left the left inset - * @param bottom the bottom inset - * @param right the right inset + * @param t the top inset + * @param l the left inset + * @param b the bottom inset + * @param r the right inset */ - protected void setInsets(short top, short left, short bottom, short right) + protected void setInsets(short t, short l, short b, short r) { - insets.top = top; - insets.left = left; - insets.bottom = bottom; - insets.right = right; + top = t; + left = l; + bottom = b; + right = r; } /** @@ -588,7 +588,7 @@ public abstract class CompositeView */ protected short getLeftInset() { - return (short) insets.left; + return left; } /** @@ -598,7 +598,7 @@ public abstract class CompositeView */ protected short getRightInset() { - return (short) insets.right; + return right; } /** @@ -608,7 +608,7 @@ public abstract class CompositeView */ protected short getTopInset() { - return (short) insets.top; + return top; } /** @@ -618,7 +618,7 @@ public abstract class CompositeView */ protected short getBottomInset() { - return (short) insets.bottom; + return bottom; } /** diff --git a/javax/swing/text/DefaultEditorKit.java b/javax/swing/text/DefaultEditorKit.java index 8602e69f8..aa69deca5 100644 --- a/javax/swing/text/DefaultEditorKit.java +++ b/javax/swing/text/DefaultEditorKit.java @@ -38,7 +38,6 @@ exception statement from your version. */ package javax.swing.text; -import java.awt.Point; import java.awt.Toolkit; import java.awt.event.ActionEvent; @@ -312,19 +311,21 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - int offs = t.getDocument().getLength(); - Caret c = t.getCaret(); - c.setDot(0); - c.moveDot(offs); - - try - { - c.setMagicCaretPosition(t.modelToView(offs).getLocation()); - } - catch(BadLocationException ble) - { - // Can't happen. - } + if (t != null) + { + int offs = t.getDocument().getLength(); + Caret c = t.getCaret(); + c.setDot(0); + c.moveDot(offs); + try + { + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } + catch(BadLocationException ble) + { + // Can't happen. + } + } } } @@ -339,15 +340,18 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - Caret c = t.getCaret(); - c.moveDot(0); - try - { - c.setMagicCaretPosition(t.modelToView(0).getLocation()); - } - catch(BadLocationException ble) + if (t != null) { - // Can't happen. + Caret c = t.getCaret(); + c.moveDot(0); + try + { + c.setMagicCaretPosition(t.modelToView(0).getLocation()); + } + catch(BadLocationException ble) + { + // Can't happen. + } } } } @@ -363,16 +367,19 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - int offs = t.getDocument().getLength(); - Caret c = t.getCaret(); - c.moveDot(offs); - try - { - c.setMagicCaretPosition(t.modelToView(offs).getLocation()); - } - catch(BadLocationException ble) + if (t != null) { - // Can't happen. + int offs = t.getDocument().getLength(); + Caret c = t.getCaret(); + c.moveDot(offs); + try + { + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } + catch(BadLocationException ble) + { + // Can't happen. + } } } } @@ -389,17 +396,19 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - Caret c = t.getCaret(); - try + if (t != null) { - int offs = Utilities.getRowStart(t, c.getDot()); - c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + Caret c = t.getCaret(); + try + { + int offs = Utilities.getRowStart(t, c.getDot()); + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } + catch(BadLocationException ble) + { + // Can't happen. + } } - catch(BadLocationException ble) - { - // Can't happen. - } - } } @@ -414,17 +423,19 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - Caret c = t.getCaret(); - try - { - int offs = Utilities.getRowEnd(t, c.getDot()); - c.setMagicCaretPosition(t.modelToView(offs).getLocation()); - } - catch(BadLocationException ble) + if (t != null) { - // Can't happen. + Caret c = t.getCaret(); + try + { + int offs = Utilities.getRowEnd(t, c.getDot()); + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } + catch(BadLocationException ble) + { + // Can't happen. + } } - } } @@ -438,20 +449,21 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - Caret c = t.getCaret(); - try - { - int offs1 = Utilities.getRowStart(t, c.getDot()); - int offs2 = Utilities.getRowEnd(t, c.getDot()); - - c.setDot(offs2); - c.moveDot(offs1); - - c.setMagicCaretPosition(t.modelToView(offs2).getLocation()); - } - catch(BadLocationException ble) + if (t != null) { - // Can't happen. + Caret c = t.getCaret(); + try + { + int offs1 = Utilities.getRowStart(t, c.getDot()); + int offs2 = Utilities.getRowEnd(t, c.getDot()); + c.setDot(offs2); + c.moveDot(offs1); + c.setMagicCaretPosition(t.modelToView(offs2).getLocation()); + } + catch(BadLocationException ble) + { + // Can't happen. + } } } } @@ -466,51 +478,52 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - Caret c = t.getCaret(); - int dot = c.getDot(); - - try + if (t != null) { - int wordStart = Utilities.getWordStart(t, dot); - - if (dot == wordStart) - { - // Current cursor position is on the first character in a word. - c.setDot(wordStart); - c.moveDot(Utilities.getWordEnd(t, wordStart)); - } - else + Caret c = t.getCaret(); + int dot = c.getDot(); + try { - // Current cursor position is not on the first character - // in a word. - int nextWord = Utilities.getNextWord(t, dot); - int previousWord = Utilities.getPreviousWord(t, dot); - int previousWordEnd = Utilities.getWordEnd(t, previousWord); - - // Cursor position is in the space between two words. In such a - // situation just select the space. - if (dot >= previousWordEnd && dot <= nextWord) + int wordStart = Utilities.getWordStart(t, dot); + + if (dot == wordStart) { - c.setDot(previousWordEnd); - c.moveDot(nextWord); + // Current cursor position is on the first character in a word. + c.setDot(wordStart); + c.moveDot(Utilities.getWordEnd(t, wordStart)); } else { - // Cursor position is inside a word. Just select it then. - c.setDot(previousWord); - c.moveDot(previousWordEnd); + // Current cursor position is not on the first character + // in a word. + int nextWord = Utilities.getNextWord(t, dot); + int previousWord = Utilities.getPreviousWord(t, dot); + int previousWordEnd = Utilities.getWordEnd(t, previousWord); + + // Cursor position is in the space between two words. In such a + // situation just select the space. + if (dot >= previousWordEnd && dot <= nextWord) + { + c.setDot(previousWordEnd); + c.moveDot(nextWord); + } + else + { + // Cursor position is inside a word. Just select it then. + c.setDot(previousWord); + c.moveDot(previousWordEnd); + } } - } - // If the position was updated change the magic caret position - // as well. - if (c.getDot() != dot) - c.setMagicCaretPosition(t.modelToView(c.getDot()).getLocation()); - - } - catch(BadLocationException ble) - { - // Can't happen. + // If the position was updated change the magic caret position + // as well. + if (c.getDot() != dot) + c.setMagicCaretPosition(t.modelToView(c.getDot()).getLocation()); + } + catch(BadLocationException ble) + { + // Can't happen. + } } } } @@ -715,21 +728,23 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - try - { - int offs = Utilities.getRowEnd(t, t.getCaretPosition()); - - if (offs > -1) - { - Caret c = t.getCaret(); - c.setDot(offs); - c.setMagicCaretPosition(t.modelToView(offs).getLocation()); - } - } - catch (BadLocationException ble) - { - // Nothing to do here - } + if (t != null) + { + try + { + int offs = Utilities.getRowEnd(t, t.getCaretPosition()); + if (offs > -1) + { + Caret c = t.getCaret(); + c.setDot(offs); + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } + } + catch (BadLocationException ble) + { + // Nothing to do here + } + } } } @@ -744,21 +759,23 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - try - { - int offs = Utilities.getRowStart(t, t.getCaretPosition()); - - if (offs > -1) - { - Caret c = t.getCaret(); - c.setDot(offs); - c.setMagicCaretPosition(t.modelToView(offs).getLocation()); - } - } - catch (BadLocationException ble) - { - // Do nothing here. - } + if (t != null) + { + try + { + int offs = Utilities.getRowStart(t, t.getCaretPosition()); + if (offs > -1) + { + Caret c = t.getCaret(); + c.setDot(offs); + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } + } + catch (BadLocationException ble) + { + // Do nothing here. + } + } } } @@ -773,16 +790,19 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - Caret c = t.getCaret(); - c.setDot(0); - try - { - c.setMagicCaretPosition(t.modelToView(0).getLocation()); - } - catch(BadLocationException ble) - { - // Can't happen. - } + if (t != null) + { + Caret c = t.getCaret(); + c.setDot(0); + try + { + c.setMagicCaretPosition(t.modelToView(0).getLocation()); + } + catch(BadLocationException ble) + { + // Can't happen. + } + } } } @@ -797,16 +817,19 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - int offs = t.getDocument().getLength(); - Caret c = t.getCaret(); - c.setDot(offs); - try - { - c.setMagicCaretPosition(t.modelToView(offs).getLocation()); - } - catch(BadLocationException ble) + if (t != null) { - // Can't happen. + int offs = t.getDocument().getLength(); + Caret c = t.getCaret(); + c.setDot(offs); + try + { + c.setMagicCaretPosition(t.modelToView(offs).getLocation()); + } + catch(BadLocationException ble) + { + // Can't happen. + } } } } @@ -862,7 +885,9 @@ public class DefaultEditorKit extends EditorKit */ public void actionPerformed(ActionEvent event) { - getTextComponent(event).copy(); + JTextComponent target = getTextComponent(event); + if (target != null) + target.copy(); } } @@ -893,7 +918,9 @@ public class DefaultEditorKit extends EditorKit */ public void actionPerformed(ActionEvent event) { - getTextComponent(event).cut(); + JTextComponent target = getTextComponent(event); + if (target != null) + target.cut(); } } @@ -922,7 +949,9 @@ public class DefaultEditorKit extends EditorKit */ public void actionPerformed(ActionEvent event) { - getTextComponent(event).paste(); + JTextComponent target = getTextComponent(event); + if (target != null) + target.paste(); } } @@ -957,14 +986,26 @@ public class DefaultEditorKit extends EditorKit { // first we filter the following events: // - control characters - // - key events with the ALT modifier (FIXME: filter that too!) - int cp = event.getActionCommand().codePointAt(0); - if (Character.isISOControl(cp)) - return; - - JTextComponent t = getTextComponent(event); - if (t != null && t.isEnabled() && t.isEditable()) - t.replaceSelection(event.getActionCommand()); + // - key events with the ALT modifier + JTextComponent target = getTextComponent(event); + if ((target != null) && (event != null)) + { + if ((target.isEditable()) && (target.isEnabled())) + { + String content = event.getActionCommand(); + int mod = event.getModifiers(); + if ((content != null) && (content.length() > 0) + && (mod & ActionEvent.ALT_MASK) == 0 + && (mod & ActionEvent.CTRL_MASK) == 0) + { + char c = content.charAt(0); + if ((c >= 0x20) && (c != 0x7F)) + { + target.replaceSelection(content); + } + } + } + } } } @@ -992,7 +1033,8 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - t.replaceSelection("\n"); + if (t != null) + t.replaceSelection("\n"); } } @@ -1047,7 +1089,8 @@ public class DefaultEditorKit extends EditorKit public void actionPerformed(ActionEvent event) { JTextComponent t = getTextComponent(event); - t.replaceSelection("\t"); + if (t != null) + t.replaceSelection("\t"); } } diff --git a/javax/swing/text/DefaultFormatter.java b/javax/swing/text/DefaultFormatter.java index 19994e21b..bf7c02a00 100644 --- a/javax/swing/text/DefaultFormatter.java +++ b/javax/swing/text/DefaultFormatter.java @@ -216,7 +216,7 @@ public class DefaultFormatter extends JFormattedTextField.AbstractFormatter */ public DefaultFormatter() { - commitsOnValidEdit = true; + commitsOnValidEdit = false; overwriteMode = true; allowsInvalid = true; } diff --git a/javax/swing/text/DefaultStyledDocument.java b/javax/swing/text/DefaultStyledDocument.java index 5705bde17..367666053 100644 --- a/javax/swing/text/DefaultStyledDocument.java +++ b/javax/swing/text/DefaultStyledDocument.java @@ -497,11 +497,6 @@ public class DefaultStyledDocument extends AbstractDocument implements private int pos; /** - * The ElementChange that describes the latest changes. - */ - private DefaultDocumentEvent documentEvent; - - /** * The parent of the fracture. */ private Element fracturedParent; @@ -835,13 +830,9 @@ public class DefaultStyledDocument extends AbstractDocument implements */ public void change(int offset, int length, DefaultDocumentEvent ev) { - if (length == 0) - return; - this.offset = offset; - this.pos = offset; - this.length = length; - documentEvent = ev; + prepareEdit(offset, length); changeUpdate(); + finishEdit(ev); } /** @@ -1258,8 +1249,6 @@ public class DefaultStyledDocument extends AbstractDocument implements { int len = tag.getLength(); int dir = tag.getDirection(); - AttributeSet tagAtts = tag.getAttributes(); - if (dir == ElementSpec.JoinNextDirection) { if (! edit.isFracture) @@ -1603,56 +1592,6 @@ public class DefaultStyledDocument extends AbstractDocument implements } - /** - * Recreates all the elements from the parent to the element on the top of - * the stack, starting from startFrom with the starting offset of - * startOffset. - * - * @param recreate - - * the elements to recreate - * @param parent - - * the element to add the new elements to - * @param startFrom - - * where to start recreating from - * @param startOffset - - * the offset of the first element - * @return the array of added elements - */ - private Element[] recreateAfterFracture(Element[] recreate, - BranchElement parent, int startFrom, - int startOffset) - { - Element[] added = new Element[recreate.length - startFrom]; - int j = 0; - for (int i = startFrom; i < recreate.length; i++) - { - Element curr = recreate[i]; - int len = curr.getEndOffset() - curr.getStartOffset(); - if (curr instanceof LeafElement) - added[j] = createLeafElement(parent, curr.getAttributes(), - startOffset, startOffset + len); - else - { - BranchElement br = - (BranchElement) createBranchElement(parent, - curr.getAttributes()); - int bSize = curr.getElementCount(); - for (int k = 0; k < bSize; k++) - { - Element bCurr = curr.getElement(k); - Element[] add = recreateAfterFracture(new Element[] { bCurr }, br, 0, - startOffset); - br.replace(0, 0, add); - - } - added[j] = br; - } - startOffset += len; - j++; - } - - return added; - } } @@ -1985,7 +1924,6 @@ public class DefaultStyledDocument extends AbstractDocument implements // start and ends at an element end. buffer.change(offset, length, ev); - Element root = getDefaultRootElement(); // Visit all paragraph elements within the specified interval int end = offset + length; Element curr; diff --git a/javax/swing/text/FieldView.java b/javax/swing/text/FieldView.java index f41f90130..0a078e53d 100644 --- a/javax/swing/text/FieldView.java +++ b/javax/swing/text/FieldView.java @@ -45,8 +45,6 @@ import java.awt.Graphics; import java.awt.Insets; import java.awt.Rectangle; import java.awt.Shape; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import javax.swing.BoundedRangeModel; import javax.swing.JTextField; @@ -225,7 +223,7 @@ public class FieldView extends PlainView public int getResizeWeight(int axis) { - return axis = axis == X_AXIS ? 1 : 0; + return axis == X_AXIS ? 1 : 0; } public Shape modelToView(int pos, Shape a, Position.Bias bias) diff --git a/javax/swing/text/FlowView.java b/javax/swing/text/FlowView.java index 3de95ed7f..085b0ac45 100644 --- a/javax/swing/text/FlowView.java +++ b/javax/swing/text/FlowView.java @@ -329,6 +329,16 @@ public abstract class FlowView extends BoxView { super(el, axis); } + + /** + * Overridden to return the attributes of the parent + * (== the FlowView instance). + */ + public AttributeSet getAttributes() + { + View p = getParent(); + return p != null ? p.getAttributes() : null; + } } /** diff --git a/javax/swing/text/GapContent.java b/javax/swing/text/GapContent.java index 7b1502777..990e9d464 100644 --- a/javax/swing/text/GapContent.java +++ b/javax/swing/text/GapContent.java @@ -165,7 +165,7 @@ public class GapContent */ int getOffset() { - assert mark == 0 || mark < gapStart || mark >= gapEnd : + assert mark == 0 || mark <= gapStart || mark >= gapEnd : "Invalid mark: " + mark + ", gapStart: " + gapStart + ", gapEnd: " + gapEnd; @@ -1013,7 +1013,7 @@ public class GapContent * * @return the index of the first occurance of o in l, or -i + 1 if not found */ - private int search(List l, Object o) + int search(List l, Object o) { int i = Collections.binarySearch(l, o); while (i > 0) diff --git a/javax/swing/text/GlyphView.java b/javax/swing/text/GlyphView.java index 65025dd08..385f50bf6 100644 --- a/javax/swing/text/GlyphView.java +++ b/javax/swing/text/GlyphView.java @@ -46,7 +46,6 @@ import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; import java.awt.Toolkit; -import java.text.BreakIterator; import javax.swing.SwingConstants; import javax.swing.event.DocumentEvent; @@ -300,28 +299,19 @@ public class GlyphView extends View implements TabableView, Cloneable g.setColor(view.getForeground()); g.setFont(view.getFont()); int ascent = g.getFontMetrics().getAscent(); - if (view.isSuperscript()) - // TODO: Adjust font for superscripting. - Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent - height / 2, - g, tabEx, txt.offset); - else if (view.isSubscript()) - // TODO: Adjust font for subscripting. - Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent + height / 2, - g, tabEx, txt.offset); - else - Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent, g, tabEx, - txt.offset); + Utilities.drawTabbedText(txt, bounds.x, bounds.y + ascent, g, tabEx, + txt.offset); if (view.isStrikeThrough()) { int strikeHeight = (int) (getAscent(view) / 2); - g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.height + width, + g.drawLine(bounds.x, bounds.y + strikeHeight, bounds.x + width, bounds.y + strikeHeight); } if (view.isUnderline()) { int lineHeight = (int) getAscent(view); - g.drawLine(bounds.x, bounds.y + lineHeight, bounds.height + width, + g.drawLine(bounds.x, bounds.y + lineHeight, bounds.x + width, bounds.y + lineHeight); } g.setColor(oldColor); @@ -385,7 +375,6 @@ public class GlyphView extends View implements TabableView, Cloneable public float getSpan(GlyphView view, int p0, int p1, TabExpander te, float x) { - Element el = view.getElement(); Font font = view.getFont(); FontMetrics fm = Toolkit.getDefaultToolkit().getFontMetrics(font); Segment txt = view.getText(p0, p1); @@ -466,7 +455,7 @@ public class GlyphView extends View implements TabableView, Cloneable { Rectangle b = a.getBounds(); int pos = getBoundedPosition(v, v.getStartOffset(), b.x, x - b.x); - return pos; + return pos + v.getStartOffset(); } } @@ -574,19 +563,24 @@ public class GlyphView extends View implements TabableView, Cloneable float span = 0; checkPainter(); GlyphPainter painter = getGlyphPainter(); - if (axis == X_AXIS) + switch (axis) { - Element el = getElement(); + case X_AXIS: TabExpander tabEx = null; View parent = getParent(); if (parent instanceof TabExpander) tabEx = (TabExpander) parent; span = painter.getSpan(this, getStartOffset(), getEndOffset(), tabEx, 0.F); + break; + case Y_AXIS: + span = painter.getHeight(this); + if (isSuperscript()) + span += span / 3; + break; + default: + throw new IllegalArgumentException("Illegal axis"); } - else - span = painter.getHeight(this); - return span; } @@ -762,16 +756,19 @@ public class GlyphView extends View implements TabableView, Cloneable */ public Font getFont() { - Element el = getElement(); - AttributeSet atts = el.getAttributes(); - String family = StyleConstants.getFontFamily(atts); - int size = StyleConstants.getFontSize(atts); - int style = Font.PLAIN; - if (StyleConstants.isBold(atts)) - style |= Font.BOLD; - if (StyleConstants.isItalic(atts)) - style |= Font.ITALIC; - Font font = new Font(family, style, size); + Document doc = getDocument(); + Font font = null; + if (doc instanceof StyledDocument) + { + StyledDocument styledDoc = (StyledDocument) doc; + font = styledDoc.getFont(getAttributes()); + } + else + { + Container c = getContainer(); + if (c != null) + font = c.getFont(); + } return font; } @@ -908,10 +905,8 @@ public class GlyphView extends View implements TabableView, Cloneable return this; checkPainter(); - GlyphPainter painter = getGlyphPainter(); // Try to find a suitable line break. - BreakIterator lineBreaker = BreakIterator.getLineInstance(); Segment txt = new Segment(); try { @@ -1050,14 +1045,21 @@ public class GlyphView extends View implements TabableView, Cloneable */ public float getAlignment(int axis) { + checkPainter(); float align; if (axis == Y_AXIS) { - checkPainter(); GlyphPainter painter = getGlyphPainter(); float height = painter.getHeight(this); float descent = painter.getDescent(this); - align = (height - descent) / height; + float ascent = painter.getAscent(this); + if (isSuperscript()) + align = 1.0F; + else if (isSubscript()) + align = height > 0 ? (height - (descent + (ascent / 2))) / height + : 0; + else + align = height > 0 ? (height - descent) / height : 0; } else align = super.getAlignment(axis); diff --git a/javax/swing/text/InternationalFormatter.java b/javax/swing/text/InternationalFormatter.java index 8db435c18..d6f2359e6 100644 --- a/javax/swing/text/InternationalFormatter.java +++ b/javax/swing/text/InternationalFormatter.java @@ -285,7 +285,7 @@ public class InternationalFormatter if (minimum != null && minimum.compareTo(o) > 0) throw new ParseException("The value may not be less than the" + " specified minimum", 0); - if (maximum != null && minimum.compareTo(o) < 0) + if (maximum != null && maximum.compareTo(o) < 0) throw new ParseException("The value may not be greater than the" + " specified maximum", 0); return o; diff --git a/javax/swing/text/LabelView.java b/javax/swing/text/LabelView.java index a00a49c24..7cfeae862 100644 --- a/javax/swing/text/LabelView.java +++ b/javax/swing/text/LabelView.java @@ -114,28 +114,24 @@ public class LabelView extends GlyphView */ protected void setPropertiesFromAttributes() { - Element el = getElement(); - AttributeSet atts = el.getAttributes(); - // We cannot use StyleConstants.getBackground() here, because that returns - // BLACK as default (when background == null). What we need is the - // background setting of the text component instead, which is what we get - // when background == null anyway. - background = (Color) atts.getAttribute(StyleConstants.Background); - foreground = StyleConstants.getForeground(atts); + AttributeSet atts = getAttributes(); setStrikeThrough(StyleConstants.isStrikeThrough(atts)); setSubscript(StyleConstants.isSubscript(atts)); setSuperscript(StyleConstants.isSuperscript(atts)); setUnderline(StyleConstants.isUnderline(atts)); - // Determine the font. - String family = StyleConstants.getFontFamily(atts); - int size = StyleConstants.getFontSize(atts); - int style = Font.PLAIN; - if (StyleConstants.isBold(atts)) - style |= Font.BOLD; - if (StyleConstants.isItalic(atts)) - style |= Font.ITALIC; - font = new Font(family, style, size); + // Determine the font and colors. + Document d = getDocument(); + if (d instanceof StyledDocument) + { + StyledDocument doc = (StyledDocument) d; + font = doc.getFont(atts); + if (atts.isDefined(StyleConstants.Background)) + background = doc.getBackground(atts); + else + background = null; + foreground = doc.getForeground(atts); + } valid = true; } @@ -258,6 +254,8 @@ public class LabelView extends GlyphView */ public boolean isSubscript() { + if (! valid) + setPropertiesFromAttributes(); return subscript; } diff --git a/javax/swing/text/MaskFormatter.java b/javax/swing/text/MaskFormatter.java index d12b9ea29..581cceb61 100644 --- a/javax/swing/text/MaskFormatter.java +++ b/javax/swing/text/MaskFormatter.java @@ -110,9 +110,7 @@ public class MaskFormatter extends DefaultFormatter */ public MaskFormatter (String mask) throws java.text.ParseException { - // Override super's default behaviour, in MaskFormatter the default - // is not to allow invalid values - setAllowsInvalid(false); + this(); setMask (mask); } @@ -307,60 +305,124 @@ public class MaskFormatter extends DefaultFormatter */ public Object stringToValue (String value) throws ParseException { - int vLength = value.length(); - - // For value to be a valid it must be the same length as the mask - // note this doesn't take into account symbols that occupy more than - // one character, this is something we may possibly need to fix. - if (maskLength != vLength) - throw new ParseException ("stringToValue passed invalid value", vLength); - - // Check if the value is valid according to the mask and valid/invalid - // sets. - try - { - convertValue(value, false); - } - catch (ParseException pe) - { - throw new ParseException("stringToValue passed invalid value", - pe.getErrorOffset()); - } - - if (!getValueContainsLiteralCharacters()) - value = stripLiterals(value); - return super.stringToValue(value); + return super.stringToValue(convertStringToValue(value)); } - /** - * Strips the literal characters from the given String. - * @param value the String to strip - * @return the stripped String - */ - String stripLiterals(String value) + private String convertStringToValue(String value) + throws ParseException { StringBuffer result = new StringBuffer(); - for (int i = 0; i < value.length(); i++) + char valueChar; + boolean isPlaceHolder; + + int length = mask.length(); + for (int i = 0, j = 0; j < length; j++) { - // Only append the characters that don't correspond to literal - // characters in the mask. - switch (mask.charAt(i)) + char maskChar = mask.charAt(j); + + if (i < value.length()) + { + isPlaceHolder = false; + valueChar = value.charAt(i); + if (maskChar != ESCAPE_CHAR && maskChar != valueChar) + { + if (invalidChars != null + && invalidChars.indexOf(valueChar) != -1) + throw new ParseException("Invalid character: " + valueChar, i); + if (validChars != null + && validChars.indexOf(valueChar) == -1) + throw new ParseException("Invalid character: " + valueChar, i); + } + } + else if (placeHolder != null && i < placeHolder.length()) + { + isPlaceHolder = true; + valueChar = placeHolder.charAt(i); + } + else + { + isPlaceHolder = true; + valueChar = placeHolderChar; + } + + // This switch block on the mask character checks that the character + // within <code>value</code> at that point is valid according to the + // mask and also converts to upper/lowercase as needed. + switch (maskChar) { case NUM_CHAR: + if (! Character.isDigit(valueChar)) + throw new ParseException("Number expected: " + valueChar, i); + result.append(valueChar); + i++; + break; case UPPERCASE_CHAR: + if (! Character.isLetter(valueChar)) + throw new ParseException("Letter expected", i); + result.append(Character.toUpperCase(valueChar)); + i++; + break; case LOWERCASE_CHAR: + if (! Character.isLetter(valueChar)) + throw new ParseException("Letter expected", i); + result.append(Character.toLowerCase(valueChar)); + i++; + break; case ALPHANUM_CHAR: + if (! Character.isLetterOrDigit(valueChar)) + throw new ParseException("Letter or number expected", i); + result.append(valueChar); + i++; + break; case LETTER_CHAR: + if (! Character.isLetter(valueChar)) + throw new ParseException("Letter expected", i); + result.append(valueChar); + i++; + break; case HEX_CHAR: + if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder) + throw new ParseException("Hexadecimal character expected", i); + result.append(valueChar); + i++; + break; case ANYTHING_CHAR: - result.append(value.charAt(i)); + result.append(valueChar); + i++; + break; + case ESCAPE_CHAR: + // Escape character, check the next character to make sure that + // the literals match + j++; + if (j < length) + { + maskChar = mask.charAt(j); + if (! isPlaceHolder && getValueContainsLiteralCharacters() + && valueChar != maskChar) + throw new ParseException ("Invalid character: "+ valueChar, i); + if (getValueContainsLiteralCharacters()) + { + result.append(maskChar); + } + i++; + } + else if (! isPlaceHolder) + throw new ParseException("Bad match at trailing escape: ", i); break; default: + if (! isPlaceHolder && getValueContainsLiteralCharacters() + && valueChar != maskChar) + throw new ParseException ("Invalid character: "+ valueChar, i); + if (getValueContainsLiteralCharacters()) + { + result.append(maskChar); + } + i++; } } return result.toString(); } - + /** * Returns a String representation of the Object value based on the mask. * @@ -368,21 +430,10 @@ public class MaskFormatter extends DefaultFormatter * @throws ParseException if value is invalid for this mask and valid/invalid * character sets */ - public String valueToString (Object value) throws ParseException + public String valueToString(Object value) throws ParseException { - String result = super.valueToString(value); - int rLength = result.length(); - - // If value is longer than the mask, truncate it. Note we may need to - // account for symbols that are more than one character long. - if (rLength > maskLength) - result = result.substring(0, maskLength); - - // Verify the validity and convert to upper/lowercase as needed. - result = convertValue(result, true); - if (rLength < maskLength) - return pad(result, rLength); - return result; + String string = value != null ? value.toString() : ""; + return convertValueToString(string); } /** @@ -390,194 +441,116 @@ public class MaskFormatter extends DefaultFormatter * sure that it is valid. If <code>convert</code> is true, it also * converts letters to upper/lowercase as required by the mask. * @param value the String to convert - * @param convert true if we should convert letters to upper/lowercase * @return the converted String * @throws ParseException if the given String isn't valid for the mask */ - String convertValue(String value, boolean convert) throws ParseException + private String convertValueToString(String value) + throws ParseException { - StringBuffer result = new StringBuffer(value); - char markChar; - char resultChar; - boolean literal; - - // this boolean is specifically to avoid calling the isCharValid method - // when neither invalidChars or validChars has been set - boolean checkCharSets = (invalidChars != null || validChars != null); + StringBuffer result = new StringBuffer(); + char valueChar; + boolean isPlaceHolder; - for (int i = 0, j = 0; i < value.length(); i++, j++) + int length = mask.length(); + for (int i = 0, j = 0; j < length; j++) { - literal = false; - resultChar = result.charAt(i); + char maskChar = mask.charAt(j); + if (i < value.length()) + { + isPlaceHolder = false; + valueChar = value.charAt(i); + if (maskChar != ESCAPE_CHAR && valueChar != maskChar) + { + if (invalidChars != null + && invalidChars.indexOf(valueChar) != -1) + throw new ParseException("Invalid character: " + valueChar, + i); + if (validChars != null && validChars.indexOf(valueChar) == -1) + throw new ParseException("Invalid character: " + valueChar +" maskChar: " + maskChar, + i); + } + } + else if (placeHolder != null && i < placeHolder.length()) + { + isPlaceHolder = true; + valueChar = placeHolder.charAt(i); + } + else + { + isPlaceHolder = true; + valueChar = placeHolderChar; + } + // This switch block on the mask character checks that the character // within <code>value</code> at that point is valid according to the // mask and also converts to upper/lowercase as needed. - switch (mask.charAt(j)) + switch (maskChar) { case NUM_CHAR: - if (!Character.isDigit(resultChar)) - throw new ParseException("Number expected", i); + if ( ! isPlaceHolder && ! Character.isDigit(valueChar)) + throw new ParseException("Number expected: " + valueChar, i); + result.append(valueChar); + i++; break; case UPPERCASE_CHAR: - if (!Character.isLetter(resultChar)) + if (! Character.isLetter(valueChar)) throw new ParseException("Letter expected", i); - if (convert) - result.setCharAt(i, Character.toUpperCase(resultChar)); + result.append(Character.toUpperCase(valueChar)); + i++; break; case LOWERCASE_CHAR: - if (!Character.isLetter(resultChar)) + if (! Character.isLetter(valueChar)) throw new ParseException("Letter expected", i); - if (convert) - result.setCharAt(i, Character.toLowerCase(resultChar)); + result.append(Character.toLowerCase(valueChar)); + i++; break; case ALPHANUM_CHAR: - if (!Character.isLetterOrDigit(resultChar)) + if (! Character.isLetterOrDigit(valueChar)) throw new ParseException("Letter or number expected", i); + result.append(valueChar); + i++; break; case LETTER_CHAR: - if (!Character.isLetter(resultChar)) + if (! Character.isLetter(valueChar)) throw new ParseException("Letter expected", i); + result.append(valueChar); + i++; break; case HEX_CHAR: - if (hexString.indexOf(resultChar) == -1) + if (hexString.indexOf(valueChar) == -1 && ! isPlaceHolder) throw new ParseException("Hexadecimal character expected", i); + result.append(valueChar); + i++; break; case ANYTHING_CHAR: + result.append(valueChar); + i++; break; case ESCAPE_CHAR: // Escape character, check the next character to make sure that // the literals match j++; - literal = true; - if (resultChar != mask.charAt(j)) - throw new ParseException ("Invalid character: "+resultChar, i); + if (j < length) + { + maskChar = mask.charAt(j); + if (! isPlaceHolder && getValueContainsLiteralCharacters() + && valueChar != maskChar) + throw new ParseException ("Invalid character: "+ valueChar, i); + if (getValueContainsLiteralCharacters()) + i++; + result.append(maskChar); + } break; default: - literal = true; - if (!getValueContainsLiteralCharacters() && convert) - throw new ParseException ("Invalid character: "+resultChar, i); - else if (resultChar != mask.charAt(j)) - throw new ParseException ("Invalid character: "+resultChar, i); + if (! isPlaceHolder && getValueContainsLiteralCharacters() + && valueChar != maskChar) + throw new ParseException ("Invalid character: "+ valueChar, i); + if (getValueContainsLiteralCharacters()) + i++; + result.append(maskChar); } - // If necessary, check if the character is valid. - if (!literal && checkCharSets && !isCharValid(resultChar)) - throw new ParseException("invalid character: "+resultChar, i); - - } - return result.toString(); - } - - /** - * Convenience method used by many other methods to check if a character is - * valid according to the mask, the validChars, and the invalidChars. To - * be valid a character must: - * 1. be allowed by the mask - * 2. be present in any non-null validChars String - * 3. not be present in any non-null invalidChars String - * @param testChar the character to test - * @return true if the character is valid - */ - boolean isCharValid(char testChar) - { - char lower = Character.toLowerCase(testChar); - char upper = Character.toUpperCase(testChar); - // If validChars isn't null, the character must appear in it. - if (validChars != null) - if (validChars.indexOf(lower) == -1 && validChars.indexOf(upper) == -1) - return false; - // If invalidChars isn't null, the character must not appear in it. - if (invalidChars != null) - if (invalidChars.indexOf(lower) != -1 - || invalidChars.indexOf(upper) != -1) - return false; - return true; - } - - /** - * Pads the value with literals, the placeholder String and/or placeholder - * character as appropriate. - * @param value the value to pad - * @param currLength the current length of the value - * @return the padded String - */ - String pad (String value, int currLength) - { - StringBuffer result = new StringBuffer(value); - int index = currLength; - while (result.length() < maskLength) - { - // The character used to pad may be a literal, a character from the - // place holder string, or the place holder character. getPadCharAt - // will find the proper one for us. - result.append (getPadCharAt(index)); - index++; } return result.toString(); } - /** - * Returns the character with which to pad the value at the given index - * position. If the mask has a literal at this position, this is returned - * otherwise if the place holder string is initialized and is longer than - * <code>i</code> characters then the character at position <code>i</code> - * from this String is returned. Else, the place holder character is - * returned. - * @param i the index at which we want to pad the value - * @return the character with which we should pad the value - */ - char getPadCharAt(int i) - { - boolean escaped = false; - int target = i; - char maskChar; - int holderLength = placeHolder == null ? -1 : placeHolder.length(); - // We must iterate through the mask from the beginning, because the given - // index doesn't account for escaped characters. For example, with the - // mask "1A'A''A1" index 2 refers to the literalized A, not to the - // single quotation. - for (int n = 0; n < mask.length(); n++) - { - maskChar = mask.charAt(n); - if (maskChar == ESCAPE_CHAR && !escaped) - { - target++; - escaped = true; - } - else if (escaped == true) - { - // Check if target == n which means we've come to the character - // we want to return and since it is a literal (because escaped - // is true), we return it. - if (target == n) - return maskChar; - escaped = false; - } - if (target == n) - { - // We've come to the character we want to return. It wasn't - // escaped so if it isn't a literal we should return either - // the character from place holder string or the place holder - // character, depending on whether or not the place holder - // string is long enough. - switch (maskChar) - { - case NUM_CHAR: - case UPPERCASE_CHAR: - case LOWERCASE_CHAR: - case ALPHANUM_CHAR: - case LETTER_CHAR: - case HEX_CHAR: - case ANYTHING_CHAR: - if (holderLength > i) - return placeHolder.charAt(i); - else - return placeHolderChar; - default: - return maskChar; - } - } - } - // This shouldn't happen - throw new AssertionError("MaskFormatter.getMaskCharAt failed"); - } } diff --git a/javax/swing/text/ParagraphView.java b/javax/swing/text/ParagraphView.java index c4857863d..b0b4b246e 100644 --- a/javax/swing/text/ParagraphView.java +++ b/javax/swing/text/ParagraphView.java @@ -40,6 +40,7 @@ package javax.swing.text; import java.awt.Shape; +import javax.swing.SizeRequirements; import javax.swing.event.DocumentEvent; /** @@ -64,11 +65,39 @@ public class ParagraphView extends FlowView implements TabExpander super(el, X_AXIS); } + /** + * Overridden to adjust when we are the first line, and firstLineIndent + * is not 0. + */ + public short getLeftInset() + { + short leftInset = super.getLeftInset(); + View parent = getParent(); + if (parent != null) + { + if (parent.getView(0) == this) + leftInset += firstLineIndent; + } + return leftInset; + } + public float getAlignment(int axis) { float align; if (axis == X_AXIS) - align = 0.0F; // TODO: Implement according to justification + switch (justification) + { + case StyleConstants.ALIGN_RIGHT: + align = 1.0F; + break; + case StyleConstants.ALIGN_CENTER: + case StyleConstants.ALIGN_JUSTIFIED: + align = 0.5F; + break; + case StyleConstants.ALIGN_LEFT: + default: + align = 0.0F; + } else align = super.getAlignment(axis); return align; @@ -107,6 +136,27 @@ public class ParagraphView extends FlowView implements TabExpander return index; } + + /** + * Overridden to perform a baseline layout. The normal BoxView layout + * isn't completely suitable for rows. + */ + protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, + int[] spans) + { + baselineLayout(targetSpan, axis, offsets, spans); + } + + /** + * Overridden to perform a baseline layout. The normal BoxView layout + * isn't completely suitable for rows. + */ + protected SizeRequirements calculateMinorAxisRequirements(int axis, + SizeRequirements r) + { + return baselineRequirements(axis, r); + } + protected void loadChildren(ViewFactory vf) { // Do nothing here. The children are added while layouting. @@ -192,11 +242,14 @@ public class ParagraphView extends FlowView implements TabExpander * * @param ev the document event * @param a the allocation of this view - * @param fv the view factory to use for creating new child views + * @param vf the view factory to use for creating new child views */ - public void changedUpdate(DocumentEvent ev, Shape a, ViewFactory fv) + public void changedUpdate(DocumentEvent ev, Shape a, ViewFactory vf) { setPropertiesFromAttributes(); + layoutChanged(X_AXIS); + layoutChanged(Y_AXIS); + super.changedUpdate(ev, a, vf); } /** diff --git a/javax/swing/text/PlainView.java b/javax/swing/text/PlainView.java index 5d0ce4a37..e048d5f71 100644 --- a/javax/swing/text/PlainView.java +++ b/javax/swing/text/PlainView.java @@ -280,7 +280,6 @@ public class PlainView extends View implements TabExpander // FIXME: Text may be scrolled. Document document = textComponent.getDocument(); Element root = getElement(); - int y = rect.y + metrics.getAscent(); int height = metrics.getHeight(); // For layered highlighters we need to paint the layered highlights @@ -292,7 +291,16 @@ public class PlainView extends View implements TabExpander int count = root.getElementCount(); - for (int i = 0; i < count; i++) + // Determine first and last line inside the clip. + Rectangle clip = g.getClipBounds(); + SwingUtilities.computeIntersection(rect.x, rect.y, rect.width, rect.height, + clip); + int line0 = (clip.y - rect.y) / height; + line0 = Math.max(0, Math.min(line0, count - 1)); + int line1 = (clip.y + clip.height - rect.y) / height; + line1 = Math.max(0, Math.min(line1, count - 1)); + int y = rect.y + metrics.getAscent() + height * line0; + for (int i = line0; i <= line1; i++) { if (hl != null) { diff --git a/javax/swing/text/Position.java b/javax/swing/text/Position.java index bb1449e18..d02eb834d 100644 --- a/javax/swing/text/Position.java +++ b/javax/swing/text/Position.java @@ -42,8 +42,8 @@ public interface Position { static final class Bias { - public static final Bias Backward = new Bias("backward"); - public static final Bias Forward = new Bias("forward"); + public static final Bias Backward = new Bias("Backward"); + public static final Bias Forward = new Bias("Forward"); private String name; diff --git a/javax/swing/text/SimpleAttributeSet.java b/javax/swing/text/SimpleAttributeSet.java index 85556b5da..701fa8a7c 100644 --- a/javax/swing/text/SimpleAttributeSet.java +++ b/javax/swing/text/SimpleAttributeSet.java @@ -123,9 +123,17 @@ public class SimpleAttributeSet */ public Object clone() { - SimpleAttributeSet s = new SimpleAttributeSet(); - s.tab = (Hashtable) tab.clone(); - return s; + SimpleAttributeSet attr = null; + try + { + attr = (SimpleAttributeSet) super.clone(); + attr.tab = (Hashtable) tab.clone(); + } + catch (CloneNotSupportedException ex) + { + assert false; + } + return attr; } /** diff --git a/javax/swing/text/StringContent.java b/javax/swing/text/StringContent.java index 8014dc3bc..4a3f9d752 100644 --- a/javax/swing/text/StringContent.java +++ b/javax/swing/text/StringContent.java @@ -39,6 +39,9 @@ exception statement from your version. */ package javax.swing.text; import java.io.Serializable; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.WeakReference; import java.util.Iterator; import java.util.Vector; @@ -57,6 +60,76 @@ import javax.swing.undo.UndoableEdit; public final class StringContent implements AbstractDocument.Content, Serializable { + /** + * Stores a reference to a mark that can be resetted to the original value + * after a mark has been moved. This is used for undoing actions. + */ + private class UndoPosRef + { + /** + * The mark that might need to be reset. + */ + private Mark mark; + + /** + * The original offset to reset the mark to. + */ + private int undoOffset; + + /** + * Creates a new UndoPosRef. + * + * @param m the mark + */ + UndoPosRef(Mark m) + { + mark = m; + undoOffset = mark.mark; + } + + /** + * Resets the position of the mark to the value that it had when + * creating this UndoPosRef. + */ + void reset() + { + mark.mark = undoOffset; + } + } + + /** + * Holds a mark into the buffer that is used by StickyPosition to find + * the actual offset of the position. This is pulled out of the + * GapContentPosition object so that the mark and position can be handled + * independently, and most important, so that the StickyPosition can + * be garbage collected while we still hold a reference to the Mark object. + */ + private class Mark + { + /** + * The actual mark into the buffer. + */ + int mark; + + + /** + * The number of GapContentPosition object that reference this mark. If + * it reaches zero, it get's deleted by + * {@link StringContent#garbageCollect()}. + */ + int refCount; + + /** + * Creates a new Mark object for the specified offset. + * + * @param offset the offset + */ + Mark(int offset) + { + mark = offset; + } + } + /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = 4755994433709540381L; @@ -65,7 +138,12 @@ public final class StringContent private int count; - private Vector positions = new Vector(); + /** + * Holds the marks for the positions. + * + * This is package private to avoid accessor methods. + */ + Vector marks; private class InsertUndo extends AbstractUndoableEdit { @@ -75,6 +153,8 @@ public final class StringContent private String redoContent; + private Vector positions; + public InsertUndo(int start, int length) { super(); @@ -87,10 +167,10 @@ public final class StringContent super.undo(); try { - StringContent.this.checkLocation(this.start, this.length); - this.redoContent = new String(StringContent.this.content, this.start, - this.length); - StringContent.this.remove(this.start, this.length); + if (marks != null) + positions = getPositionsInRange(null, start, length); + redoContent = getString(start, length); + remove(start, length); } catch (BadLocationException b) { @@ -103,7 +183,13 @@ public final class StringContent super.redo(); try { - StringContent.this.insertString(this.start, this.redoContent); + insertString(start, redoContent); + redoContent = null; + if (positions != null) + { + updateUndoPositions(positions); + positions = null; + } } catch (BadLocationException b) { @@ -115,14 +201,19 @@ public final class StringContent private class RemoveUndo extends AbstractUndoableEdit { private int start; - + private int len; private String undoString; + Vector positions; + public RemoveUndo(int start, String str) { super(); this.start = start; + len = str.length(); this.undoString = str; + if (marks != null) + positions = getPositionsInRange(null, start, str.length()); } public void undo() @@ -131,6 +222,12 @@ public final class StringContent try { StringContent.this.insertString(this.start, this.undoString); + if (positions != null) + { + updateUndoPositions(positions); + positions = null; + } + undoString = null; } catch (BadLocationException bad) { @@ -143,8 +240,10 @@ public final class StringContent super.redo(); try { - int end = this.undoString.length(); - StringContent.this.remove(this.start, end); + undoString = getString(start, len); + if (marks != null) + positions = getPositionsInRange(null, start, len); + remove(this.start, len); } catch (BadLocationException bad) { @@ -155,17 +254,18 @@ public final class StringContent private class StickyPosition implements Position { - private int offset = -1; + Mark mark; public StickyPosition(int offset) { - this.offset = offset; - } + // Try to make space. + garbageCollect(); - // This is package-private to avoid an accessor method. - void setOffset(int offset) - { - this.offset = this.offset >= 0 ? offset : -1; + mark = new Mark(offset); + mark.refCount++; + marks.add(mark); + + new WeakReference(this, queueOfDeath); } /** @@ -173,11 +273,25 @@ public final class StringContent */ public int getOffset() { - return offset < 0 ? 0 : offset; + return mark.mark; } } /** + * Used in {@link #remove(int,int)}. + */ + private static final char[] EMPTY = new char[0]; + + /** + * Queues all references to GapContentPositions that are about to be + * GC'ed. This is used to remove the corresponding marks from the + * positionMarks array if the number of references to that mark reaches zero. + * + * This is package private to avoid accessor synthetic methods. + */ + ReferenceQueue queueOfDeath; + + /** * Creates a new instance containing the string "\n". This is equivalent * to calling {@link #StringContent(int)} with an <code>initialLength</code> * of 10. @@ -196,6 +310,7 @@ public final class StringContent public StringContent(int initialLength) { super(); + queueOfDeath = new ReferenceQueue(); if (initialLength < 1) initialLength = 1; this.content = new char[initialLength]; @@ -207,14 +322,13 @@ public final class StringContent int offset, int length) { - Vector refPos = new Vector(); - Iterator iter = this.positions.iterator(); + Vector refPos = v == null ? new Vector() : v; + Iterator iter = marks.iterator(); while(iter.hasNext()) { - Position p = (Position) iter.next(); - if ((offset <= p.getOffset()) - && (p.getOffset() <= (offset + length))) - refPos.add(p); + Mark m = (Mark) iter.next(); + if (offset <= m.mark && m.mark <= offset + length) + refPos.add(new UndoPosRef(m)); } return refPos; } @@ -231,10 +345,10 @@ public final class StringContent */ public Position createPosition(int offset) throws BadLocationException { - if (offset < this.count || offset > this.count) - checkLocation(offset, 0); + // Lazily create marks vector. + if (marks == null) + marks = new Vector(); StickyPosition sp = new StickyPosition(offset); - this.positions.add(sp); return sp; } @@ -246,7 +360,7 @@ public final class StringContent */ public int length() { - return this.count; + return count; } /** @@ -268,27 +382,23 @@ public final class StringContent if (str == null) throw new NullPointerException(); char[] insert = str.toCharArray(); - char[] temp = new char[this.content.length + insert.length]; - this.count += insert.length; - // Copy array and insert the string. - if (where > 0) - System.arraycopy(this.content, 0, temp, 0, where); - System.arraycopy(insert, 0, temp, where, insert.length); - System.arraycopy(this.content, where, temp, (where + insert.length), - (temp.length - where - insert.length)); - if (this.content.length < temp.length) - this.content = new char[temp.length]; - // Copy the result in the original char array. - System.arraycopy(temp, 0, this.content, 0, temp.length); + replace(where, 0, insert); + // Move all the positions. - Vector refPos = getPositionsInRange(this.positions, where, - temp.length - where); - Iterator iter = refPos.iterator(); - while (iter.hasNext()) + if (marks != null) { - StickyPosition p = (StickyPosition)iter.next(); - p.setOffset(p.getOffset() + str.length()); + Iterator iter = marks.iterator(); + int start = where; + if (start == 0) + start = 1; + while (iter.hasNext()) + { + Mark m = (Mark) iter.next(); + if (m.mark >= start) + m.mark += str.length(); + } } + InsertUndo iundo = new InsertUndo(where, insert.length); return iundo; } @@ -308,32 +418,51 @@ public final class StringContent public UndoableEdit remove(int where, int nitems) throws BadLocationException { checkLocation(where, nitems + 1); - char[] temp = new char[(this.content.length - nitems)]; - this.count = this.count - nitems; RemoveUndo rundo = new RemoveUndo(where, new String(this.content, where, nitems)); - // Copy array. - System.arraycopy(this.content, 0, temp, 0, where); - System.arraycopy(this.content, where + nitems, temp, where, - this.content.length - where - nitems); - this.content = new char[temp.length]; - // Then copy the result in the original char array. - System.arraycopy(temp, 0, this.content, 0, this.content.length); + + replace(where, nitems, EMPTY); // Move all the positions. - Vector refPos = getPositionsInRange(this.positions, where, - this.content.length + nitems - where); - Iterator iter = refPos.iterator(); - while (iter.hasNext()) + if (marks != null) { - StickyPosition p = (StickyPosition)iter.next(); - int result = p.getOffset() - nitems; - p.setOffset(result); - if (result < 0) - this.positions.remove(p); + Iterator iter = marks.iterator(); + while (iter.hasNext()) + { + Mark m = (Mark) iter.next(); + if (m.mark >= where + nitems) + m.mark -= nitems; + else if (m.mark >= where) + m.mark = where; + } } return rundo; } - + + private void replace(int offs, int numRemove, char[] insert) + { + int insertLength = insert.length; + int delta = insertLength - numRemove; + int src = offs + numRemove; + int numMove = count - src; + int dest = src + delta; + if (count + delta >= content.length) + { + // Grow data array. + int newLength = Math.max(2 * content.length, count + delta); + char[] newContent = new char[newLength]; + System.arraycopy(content, 0, newContent, 0, offs); + System.arraycopy(insert, 0, newContent, offs, insertLength); + System.arraycopy(content, src, newContent, dest, numMove); + content = newContent; + } + else + { + System.arraycopy(content, src, content, dest, numMove); + System.arraycopy(insert, 0, content, offs, insertLength); + } + count += delta; + } + /** * Returns a new <code>String</code> containing the characters in the * specified range. @@ -348,6 +477,8 @@ public final class StringContent */ public String getString(int where, int len) throws BadLocationException { + // The RI throws a StringIndexOutOfBoundsException here, which + // smells like a bug. We throw a BadLocationException instead. checkLocation(where, len); return new String(this.content, where, len); } @@ -368,22 +499,28 @@ public final class StringContent public void getChars(int where, int len, Segment txt) throws BadLocationException { - checkLocation(where, len); - txt.array = this.content; + if (where + len > count) + throw new BadLocationException("Invalid location", where + len); + txt.array = content; txt.offset = where; txt.count = len; } /** - * @specnote This method is not very well specified and the positions vector - * is implementation specific. The undo positions are managed - * differently in this implementation, this method is only here - * for binary compatibility. + * Resets the positions in the specified vector to their original offset + * after a undo operation is performed. For example, after removing some + * content, the positions in the removed range will all be set to one + * offset. This method restores the positions to their original offsets + * after an undo. */ protected void updateUndoPositions(Vector positions) { - // We do nothing here. + for (Iterator i = positions.iterator(); i.hasNext();) + { + UndoPosRef pos = (UndoPosRef) i.next(); + pos.reset(); + } } /** @@ -405,6 +542,29 @@ public final class StringContent else if ((where + len) > this.count) throw new BadLocationException("Invalid range", this.count); } - + + /** + * Polls the queue of death for GapContentPositions, updates the + * corresponding reference count and removes the corresponding mark + * if the refcount reaches zero. + * + * This is package private to avoid accessor synthetic methods. + */ + void garbageCollect() + { + Reference ref = queueOfDeath.poll(); + while (ref != null) + { + if (ref != null) + { + StickyPosition pos = (StickyPosition) ref.get(); + Mark m = pos.mark; + m.refCount--; + if (m.refCount == 0) + marks.remove(m); + } + ref = queueOfDeath.poll(); + } + } } diff --git a/javax/swing/text/StyleConstants.java b/javax/swing/text/StyleConstants.java index c7906b8ad..4e5005c6b 100644 --- a/javax/swing/text/StyleConstants.java +++ b/javax/swing/text/StyleConstants.java @@ -40,6 +40,7 @@ package javax.swing.text; import java.awt.Color; import java.awt.Component; +import java.util.ArrayList; import javax.swing.Icon; @@ -163,6 +164,12 @@ public class StyleConstants public static final Object ResolveAttribute = new StyleConstants("resolver"); + /** + * All StyleConstants keys. This is used in StyleContext to register + * all known keys as static attribute keys for serialization. + */ + static ArrayList keys; + String keyname; // Package-private to avoid accessor constructor for use by @@ -170,6 +177,9 @@ public class StyleConstants StyleConstants(String k) { keyname = k; + if (keys == null) + keys = new ArrayList(); + keys.add(this); } /** @@ -729,6 +739,7 @@ public class StyleConstants */ public static void setIcon(MutableAttributeSet a, Icon c) { + a.addAttribute(AbstractDocument.ElementNameAttribute, IconElementName); a.addAttribute(IconAttribute, c); } diff --git a/javax/swing/text/StyleContext.java b/javax/swing/text/StyleContext.java index 1e869485c..b01d1060f 100644 --- a/javax/swing/text/StyleContext.java +++ b/javax/swing/text/StyleContext.java @@ -43,19 +43,23 @@ import java.awt.Font; import java.awt.FontMetrics; import java.awt.Toolkit; import java.io.IOException; +import java.io.NotSerializableException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.lang.ref.WeakReference; import java.util.Enumeration; import java.util.EventListener; import java.util.Hashtable; +import java.util.Iterator; +import java.util.WeakHashMap; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.event.EventListenerList; public class StyleContext - implements Serializable, AbstractDocument.AttributeContext + implements Serializable, AbstractDocument.AttributeContext { /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = 8042858831190784241L; @@ -66,11 +70,10 @@ public class StyleContext /** The serialization UID (compatible with JDK1.5). */ private static final long serialVersionUID = -6690628971806226374L; - protected ChangeEvent changeEvent; + protected transient ChangeEvent changeEvent; protected EventListenerList listenerList; - AttributeSet attributes; - String name; + private transient AttributeSet attributes; public NamedStyle() { @@ -84,22 +87,26 @@ public class StyleContext public NamedStyle(String name, Style parent) { - this.name = name; - this.attributes = getEmptySet(); - this.changeEvent = new ChangeEvent(this); - this.listenerList = new EventListenerList(); - setResolveParent(parent); + attributes = getEmptySet(); + listenerList = new EventListenerList(); + if (name != null) + setName(name); + if (parent != null) + setResolveParent(parent); } public String getName() { + String name = null; + if (isDefined(StyleConstants.NameAttribute)) + name = getAttribute(StyleConstants.NameAttribute).toString(); return name; } public void setName(String n) { - name = n; - fireStateChanged(); + if (n != null) + addAttribute(StyleConstants.NameAttribute, n); } public void addChangeListener(ChangeListener l) @@ -127,6 +134,9 @@ public class StyleContext ChangeListener[] listeners = getChangeListeners(); for (int i = 0; i < listeners.length; ++i) { + // Lazily create event. + if (changeEvent == null) + changeEvent = new ChangeEvent(this); listeners[i].stateChanged(changeEvent); } } @@ -155,7 +165,10 @@ public class StyleContext public AttributeSet copyAttributes() { - return attributes.copyAttributes(); + // The RI returns a NamedStyle as copy, so do we. + NamedStyle copy = new NamedStyle(); + copy.attributes = attributes.copyAttributes(); + return copy; } public Object getAttribute(Object attrName) @@ -210,112 +223,125 @@ public class StyleContext public void setResolveParent(AttributeSet parent) { if (parent != null) - { - attributes = StyleContext.this.addAttribute - (attributes, ResolveAttribute, parent); - } - fireStateChanged(); + addAttribute(StyleConstants.ResolveAttribute, parent); + else + removeAttribute(StyleConstants.ResolveAttribute); } public String toString() { - return ("[NamedStyle: name=" + name + ", attrs=" + attributes.toString() + "]"); - } + return "NamedStyle:" + getName() + " " + attributes; + } + + private void writeObject(ObjectOutputStream s) + throws IOException + { + s.defaultWriteObject(); + writeAttributeSet(s, attributes); + } + + private void readObject(ObjectInputStream s) + throws ClassNotFoundException, IOException + { + s.defaultReadObject(); + attributes = SimpleAttributeSet.EMPTY; + readAttributeSet(s, this); + } } public class SmallAttributeSet implements AttributeSet { final Object [] attrs; + private AttributeSet resolveParent; public SmallAttributeSet(AttributeSet a) { - if (a == null) - attrs = new Object[0]; - else + int n = a.getAttributeCount(); + int i = 0; + attrs = new Object[n * 2]; + Enumeration e = a.getAttributeNames(); + while (e.hasMoreElements()) { - int n = a.getAttributeCount(); - int i = 0; - attrs = new Object[n * 2]; - Enumeration e = a.getAttributeNames(); - while (e.hasMoreElements()) - { - Object name = e.nextElement(); - attrs[i++] = name; - attrs[i++] = a.getAttribute(name); - } + Object name = e.nextElement(); + Object value = a.getAttribute(name); + if (name == ResolveAttribute) + resolveParent = (AttributeSet) value; + attrs[i++] = name; + attrs[i++] = value; } } public SmallAttributeSet(Object [] a) { - if (a == null) - attrs = new Object[0]; - else + attrs = a; + for (int i = 0; i < attrs.length; i += 2) { - attrs = new Object[a.length]; - System.arraycopy(a, 0, attrs, 0, a.length); + if (attrs[i] == ResolveAttribute) + resolveParent = (AttributeSet) attrs[i + 1]; } } public Object clone() { - return new SmallAttributeSet(this.attrs); + return this; } public boolean containsAttribute(Object name, Object value) { - for (int i = 0; i < attrs.length; i += 2) - { - if (attrs[i].equals(name) && - attrs[i+1].equals(value)) - return true; - } - return false; + return value.equals(getAttribute(name)); } public boolean containsAttributes(AttributeSet a) { + boolean res = true; Enumeration e = a.getAttributeNames(); - while (e.hasMoreElements()) + while (e.hasMoreElements() && res) { Object name = e.nextElement(); - Object val = a.getAttribute(name); - if (!containsAttribute(name, val)) - return false; + res = a.getAttribute(name).equals(getAttribute(name)); } - return true; + return res; } public AttributeSet copyAttributes() { - return (AttributeSet) clone(); + return this; } public boolean equals(Object obj) { - return - (obj instanceof AttributeSet) - && this.isEqual((AttributeSet)obj); + boolean eq = false; + if (obj instanceof AttributeSet) + { + AttributeSet atts = (AttributeSet) obj; + eq = getAttributeCount() == atts.getAttributeCount() + && containsAttributes(atts); + } + return eq; } public Object getAttribute(Object key) { - for (int i = 0; i < attrs.length; i += 2) + Object att = null; + if (key == StyleConstants.ResolveAttribute) + att = resolveParent; + + for (int i = 0; i < attrs.length && att == null; i += 2) { if (attrs[i].equals(key)) - return attrs[i+1]; + att = attrs[i + 1]; } - + // Check the resolve parent, unless we're looking for the - // ResolveAttribute, which would cause an infinite loop - if (!(key.equals(ResolveAttribute))) + // ResolveAttribute, which must not be looked up + if (att == null) { - Object p = getResolveParent(); - if (p != null && p instanceof AttributeSet) - return (((AttributeSet)p).getAttribute(key)); + AttributeSet parent = getResolveParent(); + if (parent != null) + att = parent.getAttribute(key); } - return null; + return att; } public int getAttributeCount() @@ -342,7 +368,7 @@ public class StyleContext public AttributeSet getResolveParent() { - return (AttributeSet) getAttribute(ResolveAttribute); + return resolveParent; } public int hashCode() @@ -362,68 +388,95 @@ public class StyleContext public boolean isEqual(AttributeSet attr) { - return getAttributeCount() == attr.getAttributeCount() + boolean eq; + // If the other one is also a SmallAttributeSet, it is only considered + // equal if it's the same instance. + if (attr instanceof SmallAttributeSet) + eq = attr == this; + else + eq = getAttributeCount() == attr.getAttributeCount() && this.containsAttributes(attr); + return eq; } public String toString() { - StringBuffer sb = new StringBuffer(); - sb.append("[StyleContext.SmallattributeSet:"); - for (int i = 0; i < attrs.length - 1; ++i) + StringBuilder sb = new StringBuilder(); + sb.append('{'); + for (int i = 0; i < attrs.length; i += 2) { - sb.append(" ("); - sb.append(attrs[i].toString()); - sb.append("="); - sb.append(attrs[i+1].toString()); - sb.append(")"); + if (attrs[i + 1] instanceof AttributeSet) + { + sb.append(attrs[i]); + sb.append("=AttributeSet,"); + } + else + { + sb.append(attrs[i]); + sb.append('='); + sb.append(attrs[i + 1]); + sb.append(','); + } } - sb.append("]"); + sb.append("}"); return sb.toString(); } } - // FIXME: official javadocs suggest that these might be more usefully - // implemented using a WeakHashMap, but not sure if that works most - // places or whether it really matters anyways. - // - // FIXME: also not sure if these tables ought to be static (singletons), - // shared across all StyleContexts. I think so, but it's not clear in - // docs. revert to non-shared if you think it matters. - /** - * The name of the default style. + * Register StyleConstant keys as static attribute keys for serialization. */ - public static final String DEFAULT_STYLE = "default"; - + static + { + // Don't let problems while doing this prevent class loading. + try + { + for (Iterator i = StyleConstants.keys.iterator(); i.hasNext();) + registerStaticAttributeKey(i.next()); + } + catch (Throwable t) + { + t.printStackTrace(); + } + } + /** - * The default style for this style context. + * The name of the default style. */ - NamedStyle defaultStyle = new NamedStyle(DEFAULT_STYLE, null); + public static final String DEFAULT_STYLE = "default"; static Hashtable sharedAttributeSets = new Hashtable(); static Hashtable sharedFonts = new Hashtable(); - static StyleContext defaultStyleContext = new StyleContext(); + static StyleContext defaultStyleContext; static final int compressionThreshold = 9; /** * These attribute keys are handled specially in serialization. */ - private static Hashtable staticAttributeKeys = new Hashtable(); + private static Hashtable writeAttributeKeys; + private static Hashtable readAttributeKeys; + + private NamedStyle styles; + + /** + * Used for searching attributes in the pool. + */ + private transient MutableAttributeSet search = new SimpleAttributeSet(); + + /** + * A pool of immutable AttributeSets. + */ + private transient WeakHashMap attributeSetPool = new WeakHashMap(); - EventListenerList listenerList; - Hashtable styleTable; - /** * Creates a new instance of the style context. Add the default style * to the style table. */ public StyleContext() { - listenerList = new EventListenerList(); - styleTable = new Hashtable(); - styleTable.put(DEFAULT_STYLE, defaultStyle); + styles = new NamedStyle(null); + addStyle(DEFAULT_STYLE, null); } protected SmallAttributeSet createSmallAttributeSet(AttributeSet a) @@ -438,30 +491,30 @@ public class StyleContext public void addChangeListener(ChangeListener listener) { - listenerList.add(ChangeListener.class, listener); + styles.addChangeListener(listener); } public void removeChangeListener(ChangeListener listener) { - listenerList.remove(ChangeListener.class, listener); + styles.removeChangeListener(listener); } public ChangeListener[] getChangeListeners() { - return (ChangeListener[]) listenerList.getListeners(ChangeListener.class); + return styles.getChangeListeners(); } public Style addStyle(String name, Style parent) { Style newStyle = new NamedStyle(name, parent); if (name != null) - styleTable.put(name, newStyle); + styles.addAttribute(name, newStyle); return newStyle; } public void removeStyle(String name) { - styleTable.remove(name); + styles.removeAttribute(name); } /** @@ -476,7 +529,7 @@ public class StyleContext */ public Style getStyle(String name) { - return (Style) styleTable.get(name); + return (Style) styles.getAttribute(name); } /** @@ -485,7 +538,22 @@ public class StyleContext */ public Enumeration<?> getStyleNames() { - return styleTable.keys(); + return styles.getAttributeNames(); + } + + private void readObject(ObjectInputStream in) + throws ClassNotFoundException, IOException + { + search = new SimpleAttributeSet(); + attributeSetPool = new WeakHashMap(); + in.defaultReadObject(); + } + + private void writeObject(ObjectOutputStream out) + throws IOException + { + cleanupPool(); + out.defaultWriteObject(); } // @@ -577,132 +645,120 @@ public class StyleContext public static StyleContext getDefaultStyleContext() { + if (defaultStyleContext == null) + defaultStyleContext = new StyleContext(); return defaultStyleContext; } public AttributeSet addAttribute(AttributeSet old, Object name, Object value) { - if (old instanceof MutableAttributeSet) + AttributeSet ret; + if (old.getAttributeCount() + 1 < getCompressionThreshold()) { - ((MutableAttributeSet)old).addAttribute(name, value); - return old; + search.removeAttributes(search); + search.addAttributes(old); + search.addAttribute(name, value); + reclaim(old); + ret = searchImmutableSet(); } - else + else { - MutableAttributeSet mutable = createLargeAttributeSet(old); - mutable.addAttribute(name, value); - if (mutable.getAttributeCount() >= getCompressionThreshold()) - return mutable; - else - { - SmallAttributeSet small = createSmallAttributeSet(mutable); - if (sharedAttributeSets.containsKey(small)) - small = (SmallAttributeSet) sharedAttributeSets.get(small); - else - sharedAttributeSets.put(small,small); - return small; - } + MutableAttributeSet mas = getMutableAttributeSet(old); + mas.addAttribute(name, value); + ret = mas; } + return ret; } public AttributeSet addAttributes(AttributeSet old, AttributeSet attributes) { - if (old instanceof MutableAttributeSet) + AttributeSet ret; + if (old.getAttributeCount() + attributes.getAttributeCount() + < getCompressionThreshold()) { - ((MutableAttributeSet)old).addAttributes(attributes); - return old; + search.removeAttributes(search); + search.addAttributes(old); + search.addAttributes(attributes); + reclaim(old); + ret = searchImmutableSet(); } - else + else { - MutableAttributeSet mutable = createLargeAttributeSet(old); - mutable.addAttributes(attributes); - if (mutable.getAttributeCount() >= getCompressionThreshold()) - return mutable; - else - { - SmallAttributeSet small = createSmallAttributeSet(mutable); - if (sharedAttributeSets.containsKey(small)) - small = (SmallAttributeSet) sharedAttributeSets.get(small); - else - sharedAttributeSets.put(small,small); - return small; - } + MutableAttributeSet mas = getMutableAttributeSet(old); + mas.addAttributes(attributes); + ret = mas; } + return ret; } public AttributeSet getEmptySet() { - AttributeSet e = createSmallAttributeSet(null); - if (sharedAttributeSets.containsKey(e)) - e = (AttributeSet) sharedAttributeSets.get(e); - else - sharedAttributeSets.put(e, e); - return e; + return SimpleAttributeSet.EMPTY; } public void reclaim(AttributeSet attributes) { - if (sharedAttributeSets.containsKey(attributes)) - sharedAttributeSets.remove(attributes); + cleanupPool(); } public AttributeSet removeAttribute(AttributeSet old, Object name) { - if (old instanceof MutableAttributeSet) + AttributeSet ret; + if (old.getAttributeCount() - 1 <= getCompressionThreshold()) { - ((MutableAttributeSet)old).removeAttribute(name); - if (old.getAttributeCount() < getCompressionThreshold()) - { - SmallAttributeSet small = createSmallAttributeSet(old); - if (!sharedAttributeSets.containsKey(small)) - sharedAttributeSets.put(small,small); - old = (AttributeSet) sharedAttributeSets.get(small); - } - return old; + search.removeAttributes(search); + search.addAttributes(old); + search.removeAttribute(name); + reclaim(old); + ret = searchImmutableSet(); } - else - { - MutableAttributeSet mutable = createLargeAttributeSet(old); - mutable.removeAttribute(name); - SmallAttributeSet small = createSmallAttributeSet(mutable); - if (sharedAttributeSets.containsKey(small)) - small = (SmallAttributeSet) sharedAttributeSets.get(small); - else - sharedAttributeSets.put(small,small); - return small; + else + { + MutableAttributeSet mas = getMutableAttributeSet(old); + mas.removeAttribute(name); + ret = mas; } + return ret; } public AttributeSet removeAttributes(AttributeSet old, AttributeSet attributes) { - return removeAttributes(old, attributes.getAttributeNames()); + AttributeSet ret; + if (old.getAttributeCount() <= getCompressionThreshold()) + { + search.removeAttributes(search); + search.addAttributes(old); + search.removeAttributes(attributes); + reclaim(old); + ret = searchImmutableSet(); + } + else + { + MutableAttributeSet mas = getMutableAttributeSet(old); + mas.removeAttributes(attributes); + ret = mas; + } + return ret; } public AttributeSet removeAttributes(AttributeSet old, Enumeration<?> names) { - if (old instanceof MutableAttributeSet) + AttributeSet ret; + if (old.getAttributeCount() <= getCompressionThreshold()) { - ((MutableAttributeSet)old).removeAttributes(names); - if (old.getAttributeCount() < getCompressionThreshold()) - { - SmallAttributeSet small = createSmallAttributeSet(old); - if (!sharedAttributeSets.containsKey(small)) - sharedAttributeSets.put(small,small); - old = (AttributeSet) sharedAttributeSets.get(small); - } - return old; + search.removeAttributes(search); + search.addAttributes(old); + search.removeAttributes(names); + reclaim(old); + ret = searchImmutableSet(); } - else - { - MutableAttributeSet mutable = createLargeAttributeSet(old); - mutable.removeAttributes(names); - SmallAttributeSet small = createSmallAttributeSet(mutable); - if (sharedAttributeSets.containsKey(small)) - small = (SmallAttributeSet) sharedAttributeSets.get(small); - else - sharedAttributeSets.put(small,small); - return small; - } + else + { + MutableAttributeSet mas = getMutableAttributeSet(old); + mas.removeAttributes(names); + ret = mas; + } + return ret; } /** @@ -715,7 +771,7 @@ public class StyleContext { if (key == null) return null; - return staticAttributeKeys.get(key); + return readAttributeKeys.get(key); } /** @@ -742,27 +798,25 @@ public class StyleContext * stream * @throws IOException - any I/O error */ - public static void readAttributeSet(ObjectInputStream in, MutableAttributeSet a) + public static void readAttributeSet(ObjectInputStream in, + MutableAttributeSet a) throws ClassNotFoundException, IOException { - if (in == null || a == null) - return; - - Object key = in.readObject(); - Object val = in.readObject(); - while (key != null && val != null) + int count = in.readInt(); + for (int i = 0; i < count; i++) { - Object staticKey = staticAttributeKeys.get(key); - Object staticVal = staticAttributeKeys.get(val); - - if (staticKey != null) - key = staticKey; - if (staticVal != null) - val = staticVal; - + Object key = in.readObject(); + Object val = in.readObject(); + if (readAttributeKeys != null) + { + Object staticKey = readAttributeKeys.get(key); + if (staticKey != null) + key = staticKey; + Object staticVal = readAttributeKeys.get(val); + if (staticVal != null) + val = staticVal; + } a.addAttribute(key, val); - key = in.readObject(); - val = in.readObject(); } } @@ -778,18 +832,35 @@ public class StyleContext public static void writeAttributeSet(ObjectOutputStream out, AttributeSet a) throws IOException { + int count = a.getAttributeCount(); + out.writeInt(count); Enumeration e = a.getAttributeNames(); while (e.hasMoreElements()) { - Object oldKey = e.nextElement(); - Object newKey = getStaticAttribute(oldKey); - Object key = (newKey == null) ? oldKey : newKey; - - out.writeObject(key); - out.writeObject(a.getAttribute(oldKey)); + Object key = e.nextElement(); + // Write key. + if (key instanceof Serializable) + out.writeObject(key); + else + { + Object io = writeAttributeKeys.get(key); + if (io == null) + throw new NotSerializableException(key.getClass().getName() + + ", key: " + key); + out.writeObject(io); + } + // Write value. + Object val = a.getAttribute(key); + Object io = writeAttributeKeys.get(val); + if (val instanceof Serializable) + out.writeObject(io != null ? io : val); + else + { + if (io == null) + throw new NotSerializableException(val.getClass().getName()); + out.writeObject(io); + } } - out.writeObject(null); - out.writeObject(null); } /** @@ -833,8 +904,79 @@ public class StyleContext */ public static void registerStaticAttributeKey(Object key) { - if (key != null) - staticAttributeKeys.put(key.getClass().getName() + "." + key.toString(), - key); + String io = key.getClass().getName() + "." + key.toString(); + if (writeAttributeKeys == null) + writeAttributeKeys = new Hashtable(); + if (readAttributeKeys == null) + readAttributeKeys = new Hashtable(); + writeAttributeKeys.put(key, io); + readAttributeKeys.put(io, key); + } + + /** + * Returns a string representation of this StyleContext. + * + * @return a string representation of this StyleContext + */ + public String toString() + { + cleanupPool(); + StringBuilder b = new StringBuilder(); + Iterator i = attributeSetPool.keySet().iterator(); + while (i.hasNext()) + { + Object att = i.next(); + b.append(att); + b.append('\n'); + } + return b.toString(); + } + + /** + * Searches the AttributeSet pool and returns a pooled instance if available, + * or pool a new one. + * + * @return an immutable attribute set that equals the current search key + */ + private AttributeSet searchImmutableSet() + { + SmallAttributeSet k = createSmallAttributeSet(search); + WeakReference ref = (WeakReference) attributeSetPool.get(k); + SmallAttributeSet a; + if (ref == null || (a = (SmallAttributeSet) ref.get()) == null) + { + a = k; + attributeSetPool.put(a, new WeakReference(a)); + } + return a; + } + + /** + * Cleans up the attribute set pool from entries that are no longer + * referenced. + */ + private void cleanupPool() + { + // TODO: How else can we force cleaning up the WeakHashMap? + attributeSetPool.size(); + } + + /** + * Returns a MutableAttributeSet that holds a. If a itself is mutable, + * this returns a itself, otherwise it creates a new SimpleAtttributeSet + * via {@link #createLargeAttributeSet(AttributeSet)}. + * + * @param a the AttributeSet to create a mutable set for + * + * @return a mutable attribute set that corresponds to a + */ + private MutableAttributeSet getMutableAttributeSet(AttributeSet a) + { + MutableAttributeSet mas; + if (a instanceof MutableAttributeSet) + mas = (MutableAttributeSet) a; + else + mas = createLargeAttributeSet(a); + return mas; } } diff --git a/javax/swing/text/StyledEditorKit.java b/javax/swing/text/StyledEditorKit.java index c4eef4463..568694387 100644 --- a/javax/swing/text/StyledEditorKit.java +++ b/javax/swing/text/StyledEditorKit.java @@ -142,7 +142,7 @@ public class StyledEditorKit extends DefaultEditorKit Element el = doc.getCharacterElement(editor.getSelectionStart()); boolean isBold = StyleConstants.isBold(el.getAttributes()); SimpleAttributeSet atts = new SimpleAttributeSet(); - StyleConstants.setItalic(atts, ! isBold); + StyleConstants.setBold(atts, ! isBold); setCharacterAttributes(editor, atts, false); } } @@ -335,35 +335,21 @@ public class StyledEditorKit extends DefaultEditorKit AttributeSet atts, boolean replace) { - Document doc = editor.getDocument(); - if (doc instanceof StyledDocument) - { - StyledDocument styleDoc = (StyledDocument) editor.getDocument(); - EditorKit kit = editor.getEditorKit(); - if (!(kit instanceof StyledEditorKit)) - { - StyledEditorKit styleKit = (StyledEditorKit) kit; - int start = editor.getSelectionStart(); - int end = editor.getSelectionEnd(); - int dot = editor.getCaret().getDot(); - if (start == dot && end == dot) - { - // If there is no selection, then we only update the - // input attributes. - MutableAttributeSet inputAttributes = - styleKit.getInputAttributes(); - inputAttributes.addAttributes(atts); - } - else - styleDoc.setCharacterAttributes(start, end, atts, replace); - } - else - throw new AssertionError("The EditorKit for StyledTextActions " - + "is expected to be a StyledEditorKit"); - } - else - throw new AssertionError("The Document for StyledTextActions is " - + "expected to be a StyledDocument."); + int p0 = editor.getSelectionStart(); + int p1 = editor.getSelectionEnd(); + if (p0 != p1) + { + StyledDocument doc = getStyledDocument(editor); + doc.setCharacterAttributes(p0, p1 - p0, atts, replace); + } + // Update input attributes. + StyledEditorKit kit = getStyledEditorKit(editor); + MutableAttributeSet inputAtts = kit.getInputAttributes(); + if (replace) + { + inputAtts.removeAttributes(inputAtts); + } + inputAtts.addAttributes(atts); } /** diff --git a/javax/swing/text/TextAction.java b/javax/swing/text/TextAction.java index 28dbff00a..49c49cb9d 100644 --- a/javax/swing/text/TextAction.java +++ b/javax/swing/text/TextAction.java @@ -38,14 +38,15 @@ exception statement from your version. */ package javax.swing.text; +import java.awt.Component; +import java.awt.KeyboardFocusManager; import java.awt.Point; import java.awt.event.ActionEvent; -import java.util.ArrayList; -import java.util.HashSet; +import java.util.HashMap; +import java.util.Iterator; import javax.swing.AbstractAction; import javax.swing.Action; -import javax.swing.SwingConstants; /** * TextAction @@ -73,10 +74,16 @@ public abstract class TextAction extends AbstractAction */ protected final JTextComponent getTextComponent(ActionEvent event) { - if (event.getSource() instanceof JTextComponent) - return (JTextComponent) event.getSource(); - - return getFocusedComponent(); + JTextComponent target = null; + if (event != null) + { + Object source = event.getSource(); + if (source instanceof JTextComponent) + target = (JTextComponent) source; + } + if (target == null) + target = getFocusedComponent(); + return target; } /** @@ -89,16 +96,28 @@ public abstract class TextAction extends AbstractAction */ public static final Action[] augmentList(Action[] list1, Action[] list2) { - HashSet<Action> actionSet = new HashSet<Action>(); + HashMap<Object,Action> actions = new HashMap<Object,Action>(); for (int i = 0; i < list1.length; ++i) - actionSet.add(list1[i]); + { + Action a = list1[i]; + Object name = a.getValue(Action.NAME); + actions.put(name != null ? name : "", a); + } for (int i = 0; i < list2.length; ++i) - actionSet.add(list2[i]); + { + Action a = list2[i]; + Object name = a.getValue(Action.NAME); + actions.put(name != null ? name : "", a); + } + Action[] augmented = new Action[actions.size()]; + + int i = 0; + for (Iterator<Action> it = actions.values().iterator(); it.hasNext(); i++) + augmented[i] = it.next(); + return augmented; - ArrayList<Action> list = new ArrayList<Action>(actionSet); - return list.toArray(new Action[actionSet.size()]); } /** @@ -108,7 +127,13 @@ public abstract class TextAction extends AbstractAction */ protected final JTextComponent getFocusedComponent() { - return null; // TODO + KeyboardFocusManager kfm = + KeyboardFocusManager.getCurrentKeyboardFocusManager(); + Component focused = kfm.getPermanentFocusOwner(); + JTextComponent textComp = null; + if (focused instanceof JTextComponent) + textComp = (JTextComponent) focused; + return textComp; } /** Abstract helper class which implements everything needed for an diff --git a/javax/swing/text/Utilities.java b/javax/swing/text/Utilities.java index f75906a0f..8ddf97a12 100644 --- a/javax/swing/text/Utilities.java +++ b/javax/swing/text/Utilities.java @@ -54,10 +54,6 @@ import javax.swing.text.Position.Bias; */ public class Utilities { - /** - * The length of the char buffer that holds the characters to be drawn. - */ - private static final int BUF_LENGTH = 64; /** * Creates a new <code>Utilities</code> object. @@ -125,8 +121,8 @@ public class Utilities // In case we have a tab, we just 'jump' over the tab. // When we have no tab expander we just use the width of ' '. if (e != null) - pixelX = (int) e.nextTabStop((float) pixelX, - startOffset + offset - s.offset); + pixelX = (int) e.nextTabStop(pixelX, + startOffset + offset - s.offset); else pixelX += metrics.charWidth(' '); break; @@ -176,7 +172,7 @@ public class Utilities // In case we have a tab, we just 'jump' over the tab. // When we have no tab expander we just use the width of 'm'. if (e != null) - pixelX = (int) e.nextTabStop((float) pixelX, + pixelX = (int) e.nextTabStop(pixelX, startOffset + offset - s.offset); else pixelX += metrics.charWidth(' '); @@ -269,7 +265,7 @@ public class Utilities currentX += width; } - return pos + p0; + return pos; } /** @@ -537,28 +533,39 @@ public class Utilities int x0, int x, TabExpander e, int startOffset) { - int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset, false); - BreakIterator breaker = BreakIterator.getWordInstance(); - breaker.setText(s); - - // If startOffset and s.offset differ then we need to use - // that difference two convert the offset between the two metrics. - int shift = startOffset - s.offset; - + int mark = Utilities.getTabbedTextOffset(s, metrics, x0, x, e, startOffset, + false); + int breakLoc = mark; // If mark is equal to the end of the string, just use that position. - if (mark >= shift + s.count) - return mark; - - // Try to find a word boundary previous to the mark at which we - // can break the text. - int preceding = breaker.preceding(mark + 1 - shift); - - if (preceding != 0) - return preceding + shift; - - // If preceding is 0 we couldn't find a suitable word-boundary so - // just break it on the character boundary - return mark; + if (mark < s.count - 1) + { + for (int i = s.offset + mark; i >= s.offset; i--) + { + char ch = s.array[i]; + if (ch < 256) + { + // For ASCII simply scan backwards for whitespace. + if (Character.isWhitespace(ch)) + { + breakLoc = i - s.offset + 1; + break; + } + } + else + { + // Only query BreakIterator for complex chars. + BreakIterator bi = BreakIterator.getLineInstance(); + bi.setText(s); + int pos = bi.preceding(i + 1); + if (pos > s.offset) + { + breakLoc = breakLoc - s.offset; + } + break; + } + } + } + return breakLoc; } /** @@ -706,12 +713,12 @@ public class Utilities offset, Bias.Forward, direction, - null) + new Position.Bias[1]) : t.getUI().getNextVisualPositionFrom(t, offset, Bias.Forward, direction, - null); + new Position.Bias[1]); } catch (BadLocationException ble) { diff --git a/javax/swing/text/View.java b/javax/swing/text/View.java index 55a63f6b6..aafd76a4f 100644 --- a/javax/swing/text/View.java +++ b/javax/swing/text/View.java @@ -57,7 +57,6 @@ public abstract class View implements SwingConstants public static final int X_AXIS = 0; public static final int Y_AXIS = 1; - private float width, height; private Element elt; private View parent; @@ -307,15 +306,16 @@ public abstract class View implements SwingConstants { int index = getViewIndex(x, y, allocation); - if (index < -1) - return null; - - Shape childAllocation = getChildAllocation(index, allocation); - - if (childAllocation.getBounds().contains(x, y)) - return getView(index).getToolTipText(x, y, childAllocation); - - return null; + String text = null; + if (index >= 0) + { + allocation = getChildAllocation(index, allocation); + Rectangle r = allocation instanceof Rectangle ? (Rectangle) allocation + : allocation.getBounds(); + if (r.contains(x, y)) + text = getView(index).getToolTipText(x, y, allocation); + } + return text; } /** @@ -334,7 +334,10 @@ public abstract class View implements SwingConstants public int getBreakWeight(int axis, float pos, float len) { - return BadBreakWeight; + int weight = BadBreakWeight; + if (len > getPreferredSpan(axis)) + weight = GoodBreakWeight; + return weight; } public View breakView(int axis, int offset, float pos, float len) @@ -370,12 +373,18 @@ public abstract class View implements SwingConstants */ public void insertUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) { - Element el = getElement(); - DocumentEvent.ElementChange ec = ev.getChange(el); - if (ec != null) - updateChildren(ec, ev, vf); - forwardUpdate(ec, ev, shape, vf); - updateLayout(ec, ev, shape); + if (getViewCount() > 0) + { + Element el = getElement(); + DocumentEvent.ElementChange ec = ev.getChange(el); + if (ec != null) + { + if (! updateChildren(ec, ev, vf)) + ec = null; + } + forwardUpdate(ec, ev, shape, vf); + updateLayout(ec, ev, shape); + } } /** @@ -429,12 +438,18 @@ public abstract class View implements SwingConstants */ public void changedUpdate(DocumentEvent ev, Shape shape, ViewFactory vf) { - Element el = getElement(); - DocumentEvent.ElementChange ec = ev.getChange(el); - if (ec != null) - updateChildren(ec, ev, vf); - forwardUpdate(ec, ev, shape, vf); - updateLayout(ec, ev, shape); + if (getViewCount() > 0) + { + Element el = getElement(); + DocumentEvent.ElementChange ec = ev.getChange(el); + if (ec != null) + { + if (! updateChildren(ec, ev, vf)) + ec = null; + } + forwardUpdate(ec, ev, shape, vf); + updateLayout(ec, ev, shape); + } } /** @@ -465,10 +480,15 @@ public abstract class View implements SwingConstants Element[] removed = ec.getChildrenRemoved(); int index = ec.getIndex(); - View[] newChildren = new View[added.length]; - for (int i = 0; i < added.length; ++i) - newChildren[i] = vf.create(added[i]); - replace(index, removed.length, newChildren); + View[] newChildren = null; + if (added != null) + { + newChildren = new View[added.length]; + for (int i = 0; i < added.length; ++i) + newChildren[i] = vf.create(added[i]); + } + int numRemoved = removed != null ? removed.length : 0; + replace(index, numRemoved, newChildren); return true; } @@ -750,7 +770,9 @@ public abstract class View implements SwingConstants */ public int viewToModel(float x, float y, Shape a) { - return viewToModel(x, y, a, new Position.Bias[0]); + Position.Bias[] biasRet = new Position.Bias[1]; + biasRet[0] = Position.Bias.Forward; + return viewToModel(x, y, a, biasRet); } /** diff --git a/javax/swing/text/WrappedPlainView.java b/javax/swing/text/WrappedPlainView.java index 8cb2f4fb5..00e12b112 100644 --- a/javax/swing/text/WrappedPlainView.java +++ b/javax/swing/text/WrappedPlainView.java @@ -83,7 +83,17 @@ public class WrappedPlainView extends BoxView implements TabExpander /** The height of the line (used while painting) **/ int lineHeight; - + + /** + * The base offset for tab calculations. + */ + private int tabBase; + + /** + * The tab size. + */ + private int tabSize; + /** * The instance returned by {@link #getLineBuffer()}. */ @@ -121,10 +131,13 @@ public class WrappedPlainView extends BoxView implements TabExpander */ public float nextTabStop(float x, int tabStop) { - JTextComponent host = (JTextComponent)getContainer(); - float tabSizePixels = getTabSize() - * host.getFontMetrics(host.getFont()).charWidth('m'); - return (float) (Math.floor(x / tabSizePixels) + 1) * tabSizePixels; + int next = (int) x; + if (tabSize != 0) + { + int numTabs = ((int) x - tabBase) / tabSize; + next = tabBase + (numTabs + 1) * tabSize; + } + return next; } /** @@ -274,44 +287,32 @@ public class WrappedPlainView extends BoxView implements TabExpander */ protected int calculateBreakPosition(int p0, int p1) { - Container c = getContainer(); - - int li = getLeftInset(); - int ti = getTopInset(); - - Rectangle alloc = new Rectangle(li, ti, - getWidth()-getRightInset()-li, - getHeight()-getBottomInset()-ti); - - // Mimic a behavior observed in the RI. - if (alloc.isEmpty()) - return 0; - - updateMetrics(); - + Segment s = new Segment(); try { - getDocument().getText(p0, p1 - p0, getLineBuffer()); + getDocument().getText(p0, p1 - p0, s); } - catch (BadLocationException ble) + catch (BadLocationException ex) { - // this shouldn't happen - throw new InternalError("Invalid offsets p0: " + p0 + " - p1: " + p1); + assert false : "Couldn't load text"; } - + int width = getWidth(); + int pos; if (wordWrap) - return Utilities.getBreakLocation(lineBuffer, metrics, alloc.x, - alloc.x + alloc.width, this, p0); + pos = p0 + Utilities.getBreakLocation(s, metrics, tabBase, + tabBase + width, this, p0); else - return p0 + Utilities.getTabbedTextOffset(lineBuffer, metrics, alloc.x, - alloc.x + alloc.width, this, 0, - true); + pos = p0 + Utilities.getTabbedTextOffset(s, metrics, tabBase, + tabBase + width, this, p0, + false); + return pos; } void updateMetrics() { Container component = getContainer(); metrics = component.getFontMetrics(component.getFont()); + tabSize = getTabSize()* metrics.charWidth('m'); } /** @@ -350,9 +351,15 @@ public class WrappedPlainView extends BoxView implements TabExpander */ public void insertUpdate (DocumentEvent e, Shape a, ViewFactory f) { - super.insertUpdate(e, a, viewFactory); + // Update children efficiently. + updateChildren(e, a); - // No repaint needed, as this is done by the WrappedLine instances. + // Notify children. + Rectangle r = a != null && isAllocationValid() ? getInsideAllocation(a) + : null; + View v = getViewAtPosition(e.getOffset(), r); + if (v != null) + v.insertUpdate(e, r, f); } /** @@ -361,9 +368,15 @@ public class WrappedPlainView extends BoxView implements TabExpander */ public void removeUpdate (DocumentEvent e, Shape a, ViewFactory f) { - super.removeUpdate(e, a, viewFactory); - - // No repaint needed, as this is done by the WrappedLine instances. + // Update children efficiently. + updateChildren(e, a); + + // Notify children. + Rectangle r = a != null && isAllocationValid() ? getInsideAllocation(a) + : null; + View v = getViewAtPosition(e.getOffset(), r); + if (v != null) + v.removeUpdate(e, r, f); } /** @@ -373,11 +386,39 @@ public class WrappedPlainView extends BoxView implements TabExpander */ public void changedUpdate (DocumentEvent e, Shape a, ViewFactory f) { - super.changedUpdate(e, a, viewFactory); - - // No repaint needed, as this is done by the WrappedLine instances. + // Update children efficiently. + updateChildren(e, a); } - + + /** + * Helper method. Updates the child views in response to + * insert/remove/change updates. This is here to be a little more efficient + * than the BoxView implementation. + * + * @param ev the document event + * @param a the shape + */ + private void updateChildren(DocumentEvent ev, Shape a) + { + Element el = getElement(); + DocumentEvent.ElementChange ec = ev.getChange(el); + if (ec != null) + { + Element[] removed = ec.getChildrenRemoved(); + Element[] added = ec.getChildrenAdded(); + View[] addedViews = new View[added.length]; + for (int i = 0; i < added.length; i++) + addedViews[i] = new WrappedLine(added[i]); + replace(ec.getIndex(), removed.length, addedViews); + if (a != null) + { + preferenceChanged(null, true, true); + getContainer().repaint(); + } + } + updateMetrics(); + } + class WrappedLineCreator implements ViewFactory { // Creates a new WrappedLine @@ -397,6 +438,9 @@ public class WrappedPlainView extends BoxView implements TabExpander */ public void paint(Graphics g, Shape a) { + Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + tabBase = r.x; + JTextComponent comp = (JTextComponent)getContainer(); // Ensure metrics are up-to-date. updateMetrics(); @@ -434,7 +478,6 @@ public class WrappedPlainView extends BoxView implements TabExpander public WrappedLine(Element elem) { super(elem); - determineNumLines(); } /** @@ -496,37 +539,29 @@ public class WrappedPlainView extends BoxView implements TabExpander } } - + /** * Calculates the number of logical lines that the Element * needs to be displayed and updates the variable numLines * accordingly. */ - void determineNumLines() + private int determineNumLines() { - numLines = 0; + int nLines = 0; int end = getEndOffset(); - if (end == 0) - return; - - int breakPoint; for (int i = getStartOffset(); i < end;) { - numLines ++; + nLines++; // careful: check that there's no off-by-one problem here // depending on which position calculateBreakPosition returns - breakPoint = calculateBreakPosition(i, end); + int breakPoint = calculateBreakPosition(i, end); - if (breakPoint == 0) - return; - - // If breakPoint is equal to the current index no further - // line is needed and we can end the loop. if (breakPoint == i) - break; + i = breakPoint + 1; else i = breakPoint; } + return nLines; } /** @@ -571,7 +606,7 @@ public class WrappedPlainView extends BoxView implements TabExpander // Throwing a BadLocationException is an observed behavior of the RI. if (rect.isEmpty()) throw new BadLocationException("Unable to calculate view coordinates " - + "when allocation area is empty.", 5); + + "when allocation area is empty.", pos); Segment s = getLineBuffer(); int lineHeight = metrics.getHeight(); @@ -648,7 +683,7 @@ public class WrappedPlainView extends BoxView implements TabExpander return currLineStart; if (y > rect.y + rect.height) - return end; + return end - 1; // Note: rect.x and rect.width do not represent the width of painted // text but the area where text *may* be painted. This means the width @@ -709,22 +744,14 @@ public class WrappedPlainView extends BoxView implements TabExpander */ void updateDamage (Rectangle a) { - // If the allocation area is empty we can't do anything useful. - // As determining the number of lines is impossible in that state we - // reset it to an invalid value which can then be recalculated at a - // later point. - if (a == null || a.isEmpty()) + int nLines = determineNumLines(); + if (numLines != nLines) { - numLines = 1; - return; + numLines = nLines; + preferenceChanged(this, false, true); + getContainer().repaint(); } - - int oldNumLines = numLines; - determineNumLines(); - - if (numLines != oldNumLines) - preferenceChanged(this, false, true); - else + else if (a != null) getContainer().repaint(a.x, a.y, a.width, a.height); } @@ -738,7 +765,8 @@ public class WrappedPlainView extends BoxView implements TabExpander */ public void insertUpdate (DocumentEvent changes, Shape a, ViewFactory f) { - updateDamage((Rectangle)a); + Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + updateDamage(r); } /** @@ -760,7 +788,8 @@ public class WrappedPlainView extends BoxView implements TabExpander // However this seems to cause no trouble and as it reduces the // number of method calls it can stay this way. - updateDamage((Rectangle)a); + Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + updateDamage(r); } } } diff --git a/javax/swing/text/ZoneView.java b/javax/swing/text/ZoneView.java new file mode 100644 index 000000000..6cabc6c20 --- /dev/null +++ b/javax/swing/text/ZoneView.java @@ -0,0 +1,442 @@ +/* ZoneView.java -- An effective BoxView subclass + 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 javax.swing.text; + +import java.awt.Shape; +import java.util.ArrayList; +import java.util.LinkedList; + +import javax.swing.event.DocumentEvent; + +/** + * A View implementation that delays loading of sub views until they are + * needed for display or internal transformations. This can be used for + * editors that need to handle large documents more effectivly than the + * standard {@link BoxView}. + * + * @author Roman Kennke (kennke@aicas.com) + * + * @since 1.3 + */ +public class ZoneView + extends BoxView +{ + + /** + * The default zone view implementation. The specs suggest that this is + * a subclass of AsyncBoxView, so do we. + */ + static class Zone + extends AsyncBoxView + { + /** + * The start position for this zone. + */ + private Position p0; + + /** + * The end position for this zone. + */ + private Position p1; + + /** + * Creates a new Zone for the specified element, start and end positions. + * + * @param el the element + * @param pos0 the start position + * @param pos1 the end position + * @param axis the major axis + */ + Zone(Element el, Position pos0, Position pos1, int axis) + { + super(el, axis); + p0 = pos0; + p1 = pos1; + } + + /** + * Returns the start offset of the zone. + * + * @return the start offset of the zone + */ + public int getStartOffset() + { + return p0.getOffset(); + } + + /** + * Returns the end offset of the zone. + * + * @return the end offset of the zone + */ + public int getEndOffset() + { + return p1.getOffset(); + } + } + + /** + * The maximumZoneSize. + */ + private int maximumZoneSize; + + /** + * The maximum number of loaded zones. + */ + private int maxZonesLoaded; + + /** + * A queue of loaded zones. When the number of loaded zones exceeds the + * maximum number of zones, the oldest zone(s) get unloaded. + */ + private LinkedList loadedZones; + + /** + * Creates a new ZoneView for the specified element and axis. + * + * @param element the element for which to create a ZoneView + * @param axis the major layout axis for the box + */ + public ZoneView(Element element, int axis) + { + super(element, axis); + maximumZoneSize = 8192; + maxZonesLoaded = 3; + loadedZones = new LinkedList(); + } + + /** + * Sets the maximum zone size. Note that zones might still become larger + * then the size specified when a singe child view is larger for itself, + * because zones are formed on child view boundaries. + * + * @param size the maximum zone size to set + * + * @see #getMaximumZoneSize() + */ + public void setMaximumZoneSize(int size) + { + maximumZoneSize = size; + } + + /** + * Returns the maximum zone size. Note that zones might still become larger + * then the size specified when a singe child view is larger for itself, + * because zones are formed on child view boundaries. + * + * @return the maximum zone size + * + * @see #setMaximumZoneSize(int) + */ + public int getMaximumZoneSize() + { + return maximumZoneSize; + } + + /** + * Sets the maximum number of zones that are allowed to be loaded at the + * same time. If the new number of allowed zones is smaller then the + * previous settings, this unloads all zones the aren't allowed to be + * loaded anymore. + * + * @param num the number of zones allowed to be loaded at the same time + * + * @throws IllegalArgumentException if <code>num <= 0</code> + * + * @see #getMaxZonesLoaded() + */ + public void setMaxZonesLoaded(int num) + { + if (num < 1) + throw new IllegalArgumentException("Illegal number of zones"); + maxZonesLoaded = num; + unloadOldestZones(); + } + + /** + * Returns the number of zones that are allowed to be loaded. + * + * @return the number of zones that are allowed to be loaded + * + * @see #setMaxZonesLoaded(int) + */ + public int getMaxZonesLoaded() + { + return maxZonesLoaded; + } + + /** + * Gets called after a zone has been loaded. This unloads the oldest zone(s) + * when the maximum number of zones is reached. + * + * @param zone the zone that has been loaded + */ + protected void zoneWasLoaded(View zone) + { + loadedZones.addLast(zone); + unloadOldestZones(); + } + + /** + * This unloads the specified zone. This is implemented to simply remove + * all child views from that zone. + * + * @param zone the zone to be unloaded + */ + protected void unloadZone(View zone) + { + zone.removeAll(); + } + + /** + * Returns <code>true</code> when the specified zone is loaded, + * <code>false</code> otherwise. The default implementation checks if + * the zone view has child elements. + * + * @param zone the zone view to check + * + * @return <code>true</code> when the specified zone is loaded, + * <code>false</code> otherwise + */ + protected boolean isZoneLoaded(View zone) + { + return zone.getViewCount() > 0; + } + + /** + * Creates a zone for the specified range. Subclasses can override this + * to provide a custom implementation for the zones. + * + * @param p0 the start of the range + * @param p1 the end of the range + * + * @return the zone + */ + protected View createZone(int p0, int p1) + { + Document doc = getDocument(); + Position pos0 = null; + Position pos1 = null; + try + { + pos0 = doc.createPosition(p0); + pos1 = doc.createPosition(p1); + } + catch (BadLocationException ex) + { + assert false : "Must not happen"; + } + Zone zone = new Zone(getElement(), pos0, pos1, getAxis()); + return zone; + } + + // -------------------------------------------------------------------------- + // CompositeView methods. + // -------------------------------------------------------------------------- + + /** + * Overridden to not load all the child views. This methods creates + * initial zones without actually loading them. + * + * @param vf not used + */ + protected void loadChildren(ViewFactory vf) + { + int p0 = getStartOffset(); + int p1 = getEndOffset(); + append(createZone(p0, p1)); + checkZoneAt(p0); + } + + /** + * Returns the index of the child view at the document position + * <code>pos</code>. + * + * This overrides the CompositeView implementation because the ZoneView does + * not provide a one to one mapping from Elements to Views. + * + * @param pos the document position + * + * @return the index of the child view at the document position + * <code>pos</code> + */ + protected int getViewIndexAtPosition(int pos) + { + int index = -1; + boolean found = false; + if (pos >= getStartOffset() && pos <= getEndOffset()) + { + int upper = getViewCount() - 1; + int lower = 0; + index = (upper - lower) / 2 + lower; + int bias = 0; + do + { + View child = getView(index); + int childStart = child.getStartOffset(); + int childEnd = child.getEndOffset(); + if (pos >= childStart && pos < childEnd) + found = true; + else if (pos < childStart) + { + upper = index; + bias = -1; + } + else if (pos >= childEnd) + { + lower = index; + bias = 1; + } + if (! found) + { + int newIndex = (upper - lower) / 2 + lower; + if (newIndex == index) + index = newIndex + bias; + else + index = newIndex; + } + } while (upper != lower && ! found); + } + // If no child view actually covers the specified offset, reset index to + // -1. + if (! found) + index = -1; + return index; + } + + // -------------------------------------------------------------------------- + // View methods. + // -------------------------------------------------------------------------- + + public void insertUpdate(DocumentEvent e, Shape a, ViewFactory vf) + { + // TODO: Implement this. + } + + public void removeUpdate(DocumentEvent e, Shape a, ViewFactory vf) + { + // TODO: Implement this. + } + + protected boolean updateChildren(DocumentEvent.ElementChange ec, + DocumentEvent e, ViewFactory vf) + { + // TODO: Implement this. + return false; + } + + // -------------------------------------------------------------------------- + // Internal helper methods. + // -------------------------------------------------------------------------- + + /** + * A helper method to unload the oldest zones when there are more loaded + * zones then allowed. + */ + private void unloadOldestZones() + { + int maxZones = getMaxZonesLoaded(); + while (loadedZones.size() > maxZones) + { + View zone = (View) loadedZones.removeFirst(); + unloadZone(zone); + } + } + + /** + * Checks if the zone view at position <code>pos</code> should be split + * (its size is greater than maximumZoneSize) and tries to split it. + * + * @param pos the document position to check + */ + private void checkZoneAt(int pos) + { + int viewIndex = getViewIndexAtPosition(pos); //, Position.Bias.Forward); + View view = getView(viewIndex); + int p0 = view.getStartOffset(); + int p1 = view.getEndOffset(); + if (p1 - p0 > maximumZoneSize) + splitZone(viewIndex, p0, p1); + } + + /** + * Tries to break the view at the specified index and inside the specified + * range into pieces that are acceptable with respect to the maximum zone + * size. + * + * @param index the index of the view to split + * @param p0 the start offset + * @param p1 the end offset + */ + private void splitZone(int index, int p0, int p1) + { + ArrayList newZones = new ArrayList(); + int p = p0; + do + { + p0 = p; + p = Math.min(getPreferredZoneEnd(p0), p1); + newZones.add(createZone(p0, p)); + } while (p < p1); + View[] newViews = new View[newZones.size()]; + newViews = (View[]) newZones.toArray(newViews); + replace(index, 1, newViews); + } + + /** + * Calculates the positions at which a zone split is performed. This + * tries to create zones sized close to half the maximum zone size. + * + * @param start the start offset + * + * @return the preferred end offset + */ + private int getPreferredZoneEnd(int start) + { + Element el = getElement(); + int index = el.getElementIndex(start + (maximumZoneSize / 2)); + Element child = el.getElement(index); + int p0 = child.getStartOffset(); + int p1 = child.getEndOffset(); + int end = p1; + if (p0 - start > maximumZoneSize && p0 > start) + end = p0; + return end; + } +} diff --git a/javax/swing/text/html/CSS.java b/javax/swing/text/html/CSS.java index c248e758e..20a2debbc 100644 --- a/javax/swing/text/html/CSS.java +++ b/javax/swing/text/html/CSS.java @@ -37,6 +37,12 @@ exception statement from your version. */ package javax.swing.text.html; +import gnu.javax.swing.text.html.css.CSSColor; +import gnu.javax.swing.text.html.css.FontSize; +import gnu.javax.swing.text.html.css.FontStyle; +import gnu.javax.swing.text.html.css.FontWeight; +import gnu.javax.swing.text.html.css.Length; + import java.io.Serializable; import java.util.HashMap; @@ -459,4 +465,33 @@ public class CSS implements Serializable return defaultValue; } } + + /** + * Maps attribute values (String) to some converter class, based on the + * key. + * + * @param att the key + * @param v the value + * + * @return the wrapped value + */ + static Object getValue(Attribute att, String v) + { + Object o; + if (att == Attribute.FONT_SIZE) + o = new FontSize(v); + else if (att == Attribute.FONT_WEIGHT) + o = new FontWeight(v); + else if (att == Attribute.FONT_STYLE) + o = new FontStyle(v); + else if (att == Attribute.COLOR || att == Attribute.BACKGROUND_COLOR) + o = new CSSColor(v); + else if (att == Attribute.MARGIN || att == Attribute.MARGIN_BOTTOM + || att == Attribute.MARGIN_LEFT || att == Attribute.MARGIN_RIGHT + || att == Attribute.MARGIN_TOP) + o = new Length(v); + else + o = v; + return o; + } } diff --git a/javax/swing/text/html/HTMLDocument.java b/javax/swing/text/html/HTMLDocument.java index f7b081c6a..00372cd36 100644 --- a/javax/swing/text/html/HTMLDocument.java +++ b/javax/swing/text/html/HTMLDocument.java @@ -39,7 +39,6 @@ exception statement from your version. */ package javax.swing.text.html; import gnu.classpath.NotImplementedException; -import gnu.javax.swing.text.html.CharacterAttributeTranslator; import gnu.javax.swing.text.html.parser.htmlAttributeSet; import java.io.IOException; @@ -50,8 +49,6 @@ import java.util.Stack; import java.util.Vector; import javax.swing.JEditorPane; -import javax.swing.event.DocumentEvent; -import javax.swing.event.HyperlinkEvent.EventType; import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; @@ -87,8 +84,6 @@ public class HTMLDocument extends DefaultStyledDocument boolean preservesUnknownTags = true; int tokenThreshold = Integer.MAX_VALUE; HTMLEditorKit.Parser parser; - StyleSheet styleSheet; - AbstractDocument.Content content; /** * Constructs an HTML document using the default buffer size and a default @@ -96,7 +91,7 @@ public class HTMLDocument extends DefaultStyledDocument */ public HTMLDocument() { - this(null); + this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleSheet()); } /** @@ -119,14 +114,7 @@ public class HTMLDocument extends DefaultStyledDocument */ public HTMLDocument(AbstractDocument.Content c, StyleSheet styles) { - this.content = c; - if (styles == null) - { - styles = new StyleSheet(); - styles.importStyleSheet(getClass().getResource(HTMLEditorKit. - DEFAULT_CSS)); - } - this.styleSheet = styles; + super(c, styles); } /** @@ -137,7 +125,7 @@ public class HTMLDocument extends DefaultStyledDocument */ public StyleSheet getStyleSheet() { - return styleSheet; + return (StyleSheet) getAttributeContext(); } /** @@ -269,7 +257,7 @@ public class HTMLDocument extends DefaultStyledDocument public void setBase(URL u) { baseURL = u; - styleSheet.setBase(u); + getStyleSheet().setBase(u); } /** @@ -633,13 +621,8 @@ public class HTMLDocument extends DefaultStyledDocument // Put the old attribute set on the stack. pushCharacterStyle(); - // Translate tag.. return if succesful. - if(CharacterAttributeTranslator.translateTag(charAttr, t, a)) - return; - // Just add the attributes in <code>a</code>. - if (a != null) - charAttr.addAttribute(t, a.copyAttributes()); + charAttr.addAttribute(t, a.copyAttributes()); } /** @@ -812,7 +795,42 @@ public class HTMLDocument extends DefaultStyledDocument print ("AreaAction.end not implemented"); } } - + + /** + * Converts HTML tags to CSS attributes. + */ + class ConvertAction + extends TagAction + { + + public void start(HTML.Tag tag, MutableAttributeSet atts) + { + pushCharacterStyle(); + charAttr.addAttribute(tag, atts.copyAttributes()); + StyleSheet styleSheet = getStyleSheet(); + // TODO: Add other tags here. + if (tag == HTML.Tag.FONT) + { + String color = (String) atts.getAttribute(HTML.Attribute.COLOR); + if (color != null) + styleSheet.addCSSAttribute(charAttr, CSS.Attribute.COLOR, color); + String face = (String) atts.getAttribute(HTML.Attribute.FACE); + if (face != null) + styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_FAMILY, + face); + String size = (String) atts.getAttribute(HTML.Attribute.SIZE); + if (size != null) + styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_SIZE, + size); + } + } + + public void end(HTML.Tag tag) + { + popCharacterStyle(); + } + } + class BaseAction extends TagAction { /** @@ -1028,7 +1046,7 @@ public class HTMLDocument extends DefaultStyledDocument StyleAction styleAction = new StyleAction(); TitleAction titleAction = new TitleAction(); - + ConvertAction convertAction = new ConvertAction(); tagToAction.put(HTML.Tag.A, characterAction); tagToAction.put(HTML.Tag.ADDRESS, characterAction); tagToAction.put(HTML.Tag.APPLET, hiddenAction); @@ -1051,7 +1069,7 @@ public class HTMLDocument extends DefaultStyledDocument tagToAction.put(HTML.Tag.DL, blockAction); tagToAction.put(HTML.Tag.DT, paragraphAction); tagToAction.put(HTML.Tag.EM, characterAction); - tagToAction.put(HTML.Tag.FONT, characterAction); + tagToAction.put(HTML.Tag.FONT, convertAction); tagToAction.put(HTML.Tag.FORM, blockAction); tagToAction.put(HTML.Tag.FRAME, specialAction); tagToAction.put(HTML.Tag.FRAMESET, blockAction); @@ -1163,7 +1181,7 @@ public class HTMLDocument extends DefaultStyledDocument */ public void handleText(char[] data, int pos) { - if (data != null && data.length > 0) + if (shouldInsert() && data != null && data.length > 0) addContent(data, 0, data.length); } @@ -1728,4 +1746,19 @@ public void setOuterHTML(Element elem, String htmlText) // TODO charset getParser().parse(new StringReader(htmlText), reader, true); } + + /** + * Overridden to tag content with the synthetic HTML.Tag.CONTENT + * tag. + */ + protected void insertUpdate(DefaultDocumentEvent evt, AttributeSet att) + { + if (att == null) + { + SimpleAttributeSet sas = new SimpleAttributeSet(); + sas.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT); + att = sas; + } + super.insertUpdate(evt, att); + } } diff --git a/javax/swing/text/html/HTMLEditorKit.java b/javax/swing/text/html/HTMLEditorKit.java index b852d568c..3bf380c6f 100644 --- a/javax/swing/text/html/HTMLEditorKit.java +++ b/javax/swing/text/html/HTMLEditorKit.java @@ -48,6 +48,8 @@ import java.awt.event.MouseMotionListener; import java.awt.Cursor; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.Reader; import java.io.Serializable; import java.io.StringReader; @@ -64,7 +66,6 @@ import javax.swing.text.EditorKit; import javax.swing.text.Element; import javax.swing.text.MutableAttributeSet; import javax.swing.text.StyleConstants; -import javax.swing.text.StyleContext; import javax.swing.text.StyledEditorKit; import javax.swing.text.TextAction; import javax.swing.text.View; @@ -804,7 +805,7 @@ public class HTMLEditorKit /** * The current style sheet. */ - StyleSheet styleSheet; + private StyleSheet styleSheet; /** * The ViewFactory for HTMLFactory. @@ -831,11 +832,6 @@ public class HTMLEditorKit */ LinkController mouseListener; - /** - * Style context for this editor. - */ - StyleContext styleContext; - /** The content type */ String contentType = "text/html"; @@ -850,11 +846,7 @@ public class HTMLEditorKit */ public HTMLEditorKit() { - super(); - styleContext = new StyleContext(); - styleSheet = new StyleSheet(); - styleSheet.importStyleSheet(getClass().getResource(DEFAULT_CSS)); - // FIXME: Set inputAttributes with default.css + // Nothing to do here. } /** @@ -923,8 +915,7 @@ public class HTMLEditorKit if (parser == null) throw new IOException("Parser is null."); - ParserCallback pc = ((HTMLDocument) doc).getReader - (offset, popDepth, pushDepth, insertTag); + ParserCallback pc = doc.getReader(offset, popDepth, pushDepth, insertTag); // FIXME: What should ignoreCharSet be set to? @@ -1154,8 +1145,18 @@ public class HTMLEditorKit { if (styleSheet == null) { - styleSheet = new StyleSheet(); - styleSheet.importStyleSheet(getClass().getResource(DEFAULT_CSS)); + try + { + styleSheet = new StyleSheet(); + InputStream in = getClass().getResourceAsStream(DEFAULT_CSS); + InputStreamReader r = new InputStreamReader(in); + styleSheet.loadRules(r, null); + r.close(); + } + catch (IOException ex) + { + // No style available. + } } return styleSheet; } diff --git a/javax/swing/text/html/InlineView.java b/javax/swing/text/html/InlineView.java index 77ec86e82..31eaa129c 100644 --- a/javax/swing/text/html/InlineView.java +++ b/javax/swing/text/html/InlineView.java @@ -60,6 +60,11 @@ public class InlineView { /** + * The attributes used by this view. + */ + private AttributeSet attributes; + + /** * Creates a new <code>InlineView</code> that renders the specified element. * * @param element the element for this view @@ -115,7 +120,9 @@ public class InlineView public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) { super.changedUpdate(e, a, f); - setPropertiesFromAttributes(); + StyleSheet ss = getStyleSheet(); + attributes = ss.getViewAttributes(this); + preferenceChanged(null, true, true); } /** @@ -126,8 +133,12 @@ public class InlineView */ public AttributeSet getAttributes() { - // FIXME: Implement this. - return super.getAttributes(); + if (attributes == null) + { + StyleSheet ss = getStyleSheet(); + attributes = ss.getViewAttributes(this); + } + return attributes; } @@ -143,10 +154,43 @@ public class InlineView return super.breakView(axis, offset, pos, len); } + /** + * Loads the character style properties from the stylesheet. + */ protected void setPropertiesFromAttributes() { - // FIXME: Implement this. super.setPropertiesFromAttributes(); + AttributeSet atts = getAttributes(); + Object o = atts.getAttribute(CSS.Attribute.TEXT_DECORATION); + + // Check for underline. + boolean b = false; + if (o != null && o.toString().contains("underline")) + b = true; + setUnderline(b); + + // Check for line-through. + b = false; + if (o != null && o.toString().contains("line-through")) + b = true; + setStrikeThrough(b); + + // Check for vertical alignment (subscript/superscript). + o = atts.getAttribute(CSS.Attribute.VERTICAL_ALIGN); + + // Subscript. + b = false; + if (o != null && o.toString().contains("sub")) + b = true; + setSubscript(b); + + // Superscript. + b = false; + if (o != null && o.toString().contains("sup")) + b = true; + setSuperscript(b); + + // TODO: Handle white-space: nowrap property. } /** diff --git a/javax/swing/text/html/MultiAttributeSet.java b/javax/swing/text/html/MultiAttributeSet.java new file mode 100644 index 000000000..0f1145084 --- /dev/null +++ b/javax/swing/text/html/MultiAttributeSet.java @@ -0,0 +1,213 @@ +/* MultiAttributeSet.java -- Multiplexes between a set of AttributeSets + 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 javax.swing.text.html; + +import java.util.Enumeration; +import java.util.NoSuchElementException; + +import javax.swing.text.AttributeSet; +import javax.swing.text.SimpleAttributeSet; + +/** + * An AttributeSet impl that multiplexes between a set of other AttributeSets. + * + * @author Roman Kennke (kennke@aicas.com) + */ +class MultiAttributeSet + implements AttributeSet +{ + + /** + * The Enumeration for the multiplexed names. + */ + private class MultiNameEnumeration + implements Enumeration + { + /** + * The index of the current AttributeSet. + */ + private int index; + + /** + * The names Enumeration of the current AttributeSet. + */ + private Enumeration current; + + /** + * Creates a new instance. + */ + MultiNameEnumeration() + { + index = 0; + current = multi[0].getAttributeNames(); + } + + public boolean hasMoreElements() + { + return current.hasMoreElements() || index < multi.length - 1; + } + + public Object nextElement() + { + if (! current.hasMoreElements()) + { + if (index < multi.length - 1) + { + index++; + current = multi[index].getAttributeNames(); + } + else + throw new NoSuchElementException(); + } + return current.nextElement(); + } + + } + + /** + * The AttributeSets to multiplex. + */ + AttributeSet[] multi; + + /** + * Provided for subclasses that need to initialize via {@link #init}. + */ + MultiAttributeSet() + { + // Nothing to do here. + } + + /** + * Creates a new instance. + * + * @param m the AttributeSets to multiplex + */ + MultiAttributeSet(AttributeSet[] m) + { + init(m); + } + + /** + * Provided for subclasses to initialize the attribute set. + * + * @param m the attributes to multiplex + */ + void init(AttributeSet[] m) + { + multi = m; + } + + public boolean containsAttribute(Object name, Object value) + { + boolean ret = false; + for (int i = 0; i < multi.length && ret == false; i++) + { + if (multi[i].containsAttribute(name, value)) + ret = true; + } + return ret; + } + + public boolean containsAttributes(AttributeSet attributes) + { + boolean ret = true; + Enumeration e = attributes.getAttributeNames(); + while (ret && e.hasMoreElements()) + { + Object key = e.nextElement(); + ret = attributes.getAttribute(key).equals(getAttribute(key)); + } + return ret; + } + + public AttributeSet copyAttributes() + { + SimpleAttributeSet copy = new SimpleAttributeSet(); + for (int i = 0; i < multi.length; i++) + { + copy.addAttributes(multi[i]); + } + return copy; + } + + public Object getAttribute(Object key) + { + Object ret = null; + for (int i = 0; i < multi.length && ret == null; i++) + { + ret = multi[i].getAttribute(key); + } + return ret; + } + + public int getAttributeCount() + { + int n = 0; + for (int i = 0; i < multi.length; i++) + { + n += multi[i].getAttributeCount(); + } + return n; + } + + public Enumeration getAttributeNames() + { + return new MultiNameEnumeration(); + } + + public AttributeSet getResolveParent() + { + return null; + } + + public boolean isDefined(Object attrName) + { + boolean ret = false; + for (int i = 0; i < multi.length && ! ret; i++) + ret = multi[i].isDefined(attrName); + return ret; + } + + public boolean isEqual(AttributeSet attr) + { + return getAttributeCount() == attr.getAttributeCount() + && containsAttributes(attr); + } + +} diff --git a/javax/swing/text/html/MultiStyle.java b/javax/swing/text/html/MultiStyle.java new file mode 100644 index 000000000..3937bff75 --- /dev/null +++ b/javax/swing/text/html/MultiStyle.java @@ -0,0 +1,136 @@ +/* MultiStyle.java -- Multiplexes between several Styles + 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 javax.swing.text.html; + +import java.util.Enumeration; + +import javax.swing.event.ChangeListener; +import javax.swing.text.AttributeSet; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.Style; + +/** + * A Style implementation that is able to multiplex between several other + * Styles. This is used for CSS style resolving. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class MultiStyle + extends MultiAttributeSet + implements Style +{ + + // FIXME: Fix the implementation to also return attributes that + // are added to this style, etc. However, this is not really needed + // now for CSS, but would be nice for correctness. + + /** + * The name of the style. + */ + private String name; + + /** + * The attributes added to this style. + */ + private SimpleAttributeSet attributes; + + /** + * Creates a new instance. + * + * @param n the name + * @param m the styles to multiplex + */ + public MultiStyle(String n, AttributeSet[] m) + { + super(m); + name = n; + attributes = new SimpleAttributeSet(); + } + + /** + * Returns the name of the style. + * + * @return the name of the style + */ + public String getName() + { + return name; + } + + public void addChangeListener(ChangeListener listener) + { + // TODO: Implement. + } + + public void removeChangeListener(ChangeListener listener) + { + // TODO: Implement. + } + + public void addAttribute(Object name, Object value) + { + attributes.addAttribute(name, value); + } + + public void addAttributes(AttributeSet atts) + { + attributes.addAttributes(atts); + } + + public void removeAttribute(Object name) + { + attributes.removeAttribute(name); + } + + public void removeAttributes(Enumeration names) + { + attributes.removeAttribute(names); + } + + public void removeAttributes(AttributeSet atts) + { + attributes.removeAttribute(atts); + } + + public void setResolveParent(AttributeSet parent) + { + // TODO: Implement. + } + +} diff --git a/javax/swing/text/html/ParagraphView.java b/javax/swing/text/html/ParagraphView.java index 2339f4e66..951f70b60 100644 --- a/javax/swing/text/html/ParagraphView.java +++ b/javax/swing/text/html/ParagraphView.java @@ -39,12 +39,14 @@ exception statement from your version. */ package javax.swing.text.html; import java.awt.Graphics; +import java.awt.Rectangle; import java.awt.Shape; import javax.swing.SizeRequirements; import javax.swing.text.AttributeSet; import javax.swing.text.Document; import javax.swing.text.Element; +import javax.swing.text.StyleConstants; import javax.swing.text.View; /** @@ -55,10 +57,20 @@ import javax.swing.text.View; * @author Roman Kennke (kennke@aicas.com) */ public class ParagraphView - extends javax.swing.text.ParagraphView + extends javax.swing.text.ParagraphView { /** + * The attributes used by this view. + */ + private AttributeSet attributes; + + /** + * The stylesheet's box painter. + */ + private StyleSheet.BoxPainter painter; + + /** * Creates a new ParagraphView for the specified element. * * @param element the element @@ -88,8 +100,11 @@ public class ParagraphView */ public AttributeSet getAttributes() { - // FIXME: Implement this multiplexing thing. - return super.getAttributes(); + if (attributes == null) + { + attributes = getStyleSheet().getViewAttributes(this); + } + return attributes; } /** @@ -98,7 +113,32 @@ public class ParagraphView */ protected void setPropertiesFromAttributes() { - // FIXME: Implement this. + super.setPropertiesFromAttributes(); + + // Fetch CSS attributes. + AttributeSet atts = getAttributes(); + Object o = atts.getAttribute(CSS.Attribute.TEXT_ALIGN); + if (o != null) + { + String align = o.toString(); + if (align.equals("left")) + setJustification(StyleConstants.ALIGN_LEFT); + else if (align.equals("right")) + setJustification(StyleConstants.ALIGN_RIGHT); + else if (align.equals("center")) + setJustification(StyleConstants.ALIGN_CENTER); + else if (align.equals("justify")) + setJustification(StyleConstants.ALIGN_JUSTIFIED); + } + + // Fetch StyleSheet's box painter. + painter = getStyleSheet().getBoxPainter(atts); + setInsets((short) painter.getInset(TOP, this), + (short) painter.getInset(LEFT, this), + (short) painter.getInset(BOTTOM, this), + (short) painter.getInset(RIGHT, this)); + + // TODO: Handle CSS width and height attributes somehow. } /** @@ -147,15 +187,20 @@ public class ParagraphView } /** - * Paints this view. This delegates to the superclass after the coordinates - * have been updated for tab calculations. + * Paints this view. This paints the box using the stylesheet's + * box painter for this view and delegates to the super class paint() + * afterwards. * * @param g the graphics object * @param a the current allocation of this view */ public void paint(Graphics g, Shape a) { - // FIXME: Implement the above specified behaviour. + if (a != null) + { + Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + painter.paint(g, r.x, r.y, r.width, r.height, this); + } super.paint(g, a); } diff --git a/javax/swing/text/html/StyleSheet.java b/javax/swing/text/html/StyleSheet.java index 01853d163..520076652 100644 --- a/javax/swing/text/html/StyleSheet.java +++ b/javax/swing/text/html/StyleSheet.java @@ -38,28 +38,35 @@ exception statement from your version. */ package javax.swing.text.html; -import gnu.javax.swing.text.html.CharacterAttributeTranslator; +import gnu.classpath.NotImplementedException; +import gnu.javax.swing.text.html.css.CSSColor; +import gnu.javax.swing.text.html.css.CSSParser; +import gnu.javax.swing.text.html.css.CSSParserCallback; +import gnu.javax.swing.text.html.css.FontSize; +import gnu.javax.swing.text.html.css.FontStyle; +import gnu.javax.swing.text.html.css.FontWeight; +import gnu.javax.swing.text.html.css.Length; import java.awt.Color; import java.awt.Font; import java.awt.Graphics; - import java.io.IOException; import java.io.Reader; import java.io.Serializable; -import java.io.StringReader; - -import java.net.MalformedURLException; import java.net.URL; - +import java.util.ArrayList; import java.util.Enumeration; -import java.util.Vector; +import java.util.HashMap; +import java.util.List; +import java.util.StringTokenizer; +import javax.swing.event.ChangeListener; import javax.swing.text.AttributeSet; import javax.swing.text.Element; import javax.swing.text.MutableAttributeSet; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.Style; +import javax.swing.text.StyleConstants; import javax.swing.text.StyleContext; import javax.swing.text.View; @@ -85,6 +92,91 @@ import javax.swing.text.View; public class StyleSheet extends StyleContext { + /** + * Parses CSS stylesheets using the parser in gnu/javax/swing/html/css. + */ + private class CSSStyleSheetParserCallback + implements CSSParserCallback + { + /** + * The selector for which the rules are currently parsed. + */ + private String[] selector; + + /** + * Called at the beginning of a statement. + * + * @param sel the selector + */ + public void startStatement(String sel) + { + StringTokenizer tokens = new StringTokenizer(sel); + selector = new String[tokens.countTokens()]; + for (int index = 0; tokens.hasMoreTokens(); index++) + selector[index] = tokens.nextToken(); + } + + /** + * Called at the end of a statement. + */ + public void endStatement() + { + selector = null; + } + + /** + * Called when a declaration is parsed. + * + * @param property the property + * @param value the value + */ + public void declaration(String property, String value) + { + for (int i = 0; i < selector.length; i++) + { + CSSStyle style = (CSSStyle) css.get(selector[i]); + if (style == null) + { + style = new CSSStyle(); + css.put(selector[i], style); + } + CSS.Attribute cssAtt = CSS.getAttribute(property); + Object val = CSS.getValue(cssAtt, value); + if (cssAtt != null) + style.addAttribute(cssAtt, val); + // else // For debugging only. + // System.err.println("no mapping for: " + property); + } + } + + } + + /** + * Represents a style that is defined by a CSS rule. + */ + private class CSSStyle + extends SimpleAttributeSet + implements Style + { + + public String getName() + { + // TODO: Implement this for correctness. + return null; + } + + public void addChangeListener(ChangeListener listener) + { + // TODO: Implement this for correctness. + } + + public void removeChangeListener(ChangeListener listener) + { + // TODO: Implement this for correctness. + } + + } + /** The base URL */ URL base; @@ -93,7 +185,18 @@ public class StyleSheet extends StyleContext /** The style sheets stored. */ StyleSheet[] styleSheet; - + + /** + * Maps element names (selectors) to AttributSet (the corresponding style + * information). + */ + HashMap css = new HashMap(); + + /** + * Maps selectors to their resolved styles. + */ + private HashMap resolvedStyles; + /** * Constructs a StyleSheet. */ @@ -101,6 +204,7 @@ public class StyleSheet extends StyleContext { super(); baseFontSize = 4; // Default font size from CSS + resolvedStyles = new HashMap(); } /** @@ -114,10 +218,171 @@ public class StyleSheet extends StyleContext */ public Style getRule(HTML.Tag t, Element e) { - // FIXME: Not implemented. - return null; + // Create list of the element and all of its parents, starting + // with the bottommost element. + ArrayList path = new ArrayList(); + Element el; + AttributeSet atts; + for (el = e; el != null; el = el.getParentElement()) + path.add(el); + + // Create fully qualified selector. + StringBuilder selector = new StringBuilder(); + int count = path.size(); + // We append the actual element after this loop. + for (int i = count - 1; i > 0; i--) + { + el = (Element) path.get(i); + atts = el.getAttributes(); + Object name = atts.getAttribute(StyleConstants.NameAttribute); + selector.append(name.toString()); + if (atts.isDefined(HTML.Attribute.ID)) + { + selector.append('#'); + selector.append(atts.getAttribute(HTML.Attribute.ID)); + } + else if (atts.isDefined(HTML.Attribute.CLASS)) + { + selector.append('.'); + selector.append(atts.getAttribute(HTML.Attribute.CLASS)); + } + selector.append(' '); + } + selector.append(t.toString()); + el = (Element) path.get(0); + atts = el.getAttributes(); + // For leaf elements, we have to fetch the tag specific attributes. + if (el.isLeaf()) + { + Object o = atts.getAttribute(t); + if (o instanceof AttributeSet) + atts = (AttributeSet) o; + else + atts = null; + } + if (atts != null) + { + if (atts.isDefined(HTML.Attribute.ID)) + { + selector.append('#'); + selector.append(atts.getAttribute(HTML.Attribute.ID)); + } + else if (atts.isDefined(HTML.Attribute.CLASS)) + { + selector.append('.'); + selector.append(atts.getAttribute(HTML.Attribute.CLASS)); + } + } + return getResolvedStyle(selector.toString(), path, t); } - + + /** + * Fetches a resolved style. If there is no resolved style for the + * specified selector, the resolve the style using + * {@link #resolveStyle(String, List, HTML.Tag)}. + * + * @param selector the selector for which to resolve the style + * @param path the Element path, used in the resolving algorithm + * @param tag the tag for which to resolve + * + * @return the resolved style + */ + private Style getResolvedStyle(String selector, List path, HTML.Tag tag) + { + Style style = (Style) resolvedStyles.get(selector); + if (style == null) + style = resolveStyle(selector, path, tag); + return style; + } + + /** + * Resolves a style. This creates arrays that hold the tag names, + * class and id attributes and delegates the work to + * {@link #resolveStyle(String, String[], String[], String[])}. + * + * @param selector the selector + * @param path the Element path + * @param tag the tag + * + * @return the resolved style + */ + private Style resolveStyle(String selector, List path, HTML.Tag tag) + { + int count = path.size(); + String[] tags = new String[count]; + String[] ids = new String[count]; + String[] classes = new String[count]; + for (int i = 0; i < count; i++) + { + Element el = (Element) path.get(i); + AttributeSet atts = el.getAttributes(); + if (i == 0 && el.isLeaf()) + { + Object o = atts.getAttribute(tag); + if (o instanceof AttributeSet) + atts = (AttributeSet) o; + else + atts = null; + } + if (atts != null) + { + HTML.Tag t = + (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute); + if (t != null) + tags[i] = t.toString(); + else + tags[i] = null; + if (atts.isDefined(HTML.Attribute.CLASS)) + classes[i] = atts.getAttribute(HTML.Attribute.CLASS).toString(); + else + classes[i] = null; + if (atts.isDefined(HTML.Attribute.ID)) + ids[i] = atts.getAttribute(HTML.Attribute.ID).toString(); + else + ids[i] = null; + } + else + { + tags[i] = null; + classes[i] = null; + ids[i] = null; + } + } + tags[0] = tag.toString(); + return resolveStyle(selector, tags, ids, classes); + } + + /** + * Performs style resolving. + * + * @param selector the selector + * @param tags the tags + * @param ids the corresponding ID attributes + * @param classes the corresponding CLASS attributes + * + * @return the resolved style + */ + private Style resolveStyle(String selector, String[] tags, String[] ids, + String[] classes) + { + // FIXME: This style resolver is not correct. But it works good enough for + // the default.css. + int count = tags.length; + ArrayList styles = new ArrayList(); + for (int i = 0; i < count; i++) + { + Style style = (Style) css.get(tags[i]); + if (style != null) + styles.add(style); + // FIXME: Handle ID and CLASS attributes. + } + Style[] styleArray = new Style[styles.size()]; + Style resolved = new MultiStyle(selector, + (Style[]) styles.toArray(styleArray)); + resolvedStyles.put(selector, resolved); + return resolved; + } + /** * Gets the rule that best matches the selector. selector is a space * separated String of element names. The attributes of the returned @@ -128,27 +393,21 @@ public class StyleSheet extends StyleContext */ public Style getRule(String selector) { - // FIXME: Not implemented. - return null; + // FIXME: This is a very rudimentary implementation. Should + // be extended to conform to the CSS spec. + return (Style) css.get(selector); } /** - * Adds a set if rules to the sheet. The rules are expected to be in valid + * Adds a set of rules to the sheet. The rules are expected to be in valid * CSS format. This is called as a result of parsing a <style> tag * * @param rule - the rule to add to the sheet */ public void addRule(String rule) + throws NotImplementedException { - CssParser cp = new CssParser(); - try - { - cp.parse(base, new StringReader(rule), false, false); - } - catch (IOException io) - { - // Do nothing here. - } + // FIXME: Implement. } /** @@ -176,10 +435,13 @@ public class StyleSheet extends StyleContext * parameter. * @throws IOException - For any IO error while reading */ - public void loadRules(Reader in, URL ref) throws IOException + public void loadRules(Reader in, URL ref) + throws IOException { - CssParser cp = new CssParser(); - cp.parse(ref, in, false, false); + CSSStyleSheetParserCallback cb = new CSSStyleSheetParserCallback(); + // FIXME: Handle ref. + CSSParser parser = new CSSParser(in, cb); + parser.parse(); } /** @@ -191,8 +453,7 @@ public class StyleSheet extends StyleContext */ public AttributeSet getViewAttributes(View v) { - // FIXME: Not implemented. - return null; + return new ViewAttributeSet(v, this); } /** @@ -310,7 +571,8 @@ public class StyleSheet extends StyleContext public void addCSSAttribute(MutableAttributeSet attr, CSS.Attribute key, String value) { - attr.addAttribute(key, value); + Object val = CSS.getValue(key, value); + attr.addAttribute(key, val); } /** @@ -340,8 +602,11 @@ public class StyleSheet extends StyleContext */ public AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet) { - // FIXME: Not implemented. - return null; + // FIXME: Really convert HTML to CSS here. + AttributeSet cssAttr = htmlAttrSet.copyAttributes(); + MutableAttributeSet cssStyle = addStyle(null, null); + cssStyle.addAttributes(cssAttr); + return cssStyle; } /** @@ -455,7 +720,31 @@ public class StyleSheet extends StyleContext */ public Font getFont(AttributeSet a) { - return super.getFont(a); + FontSize size = (FontSize) a.getAttribute(CSS.Attribute.FONT_SIZE); + int realSize = 12; + if (size != null) + realSize = size.getValue(); + + // Decrement size for subscript and superscript. + Object valign = a.getAttribute(CSS.Attribute.VERTICAL_ALIGN); + if (valign != null) + { + String v = valign.toString(); + if (v.contains("sup") || v.contains("sub")) + realSize -= 2; + } + + // TODO: Convert font family. + String family = "SansSerif"; + + int style = Font.PLAIN; + FontWeight weight = (FontWeight) a.getAttribute(CSS.Attribute.FONT_WEIGHT); + if (weight != null) + style |= weight.getValue(); + FontStyle fStyle = (FontStyle) a.getAttribute(CSS.Attribute.FONT_STYLE); + if (fStyle != null) + style |= fStyle.getValue(); + return new Font(family, style, realSize); } /** @@ -468,7 +757,11 @@ public class StyleSheet extends StyleContext */ public Color getForeground(AttributeSet a) { - return super.getForeground(a); + CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.COLOR); + Color color = null; + if (c != null) + color = c.getValue(); + return color; } /** @@ -481,7 +774,11 @@ public class StyleSheet extends StyleContext */ public Color getBackground(AttributeSet a) { - return super.getBackground(a); + CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.BACKGROUND_COLOR); + Color color = null; + if (c != null) + color = c.getValue(); + return color; } /** @@ -595,7 +892,7 @@ public class StyleSheet extends StyleContext */ public Color stringToColor(String colorName) { - return CharacterAttributeTranslator.getColor(colorName); + return CSSColor.convertValue(colorName); } /** @@ -609,12 +906,12 @@ public class StyleSheet extends StyleContext */ public static class BoxPainter extends Object implements Serializable { - - /** - * Attribute set for painter - */ - AttributeSet as; - + + private float leftInset; + private float rightInset; + private float topInset; + private float bottomInset; + /** * Package-private constructor. * @@ -622,9 +919,21 @@ public class StyleSheet extends StyleContext */ BoxPainter(AttributeSet as) { - this.as = as; + Length l = (Length) as.getAttribute(CSS.Attribute.MARGIN_LEFT); + if (l != null) + leftInset = l.getValue(); + l = (Length) as.getAttribute(CSS.Attribute.MARGIN_RIGHT); + if (l != null) + rightInset = l.getValue(); + l = (Length) as.getAttribute(CSS.Attribute.MARGIN_TOP); + if (l != null) + topInset = l.getValue(); + l = (Length) as.getAttribute(CSS.Attribute.MARGIN_BOTTOM); + if (l != null) + bottomInset = l.getValue(); } + /** * Gets the inset needed on a given side to account for the margin, border * and padding. @@ -638,8 +947,25 @@ public class StyleSheet extends StyleContext */ public float getInset(int size, View v) { - // FIXME: Not implemented. - return 0; + float inset; + switch (size) + { + case View.TOP: + inset = topInset; + break; + case View.BOTTOM: + inset = bottomInset; + break; + case View.LEFT: + inset = leftInset; + break; + case View.RIGHT: + inset = rightInset; + break; + default: + inset = 0.0F; + } + return inset; } /** @@ -701,207 +1027,5 @@ public class StyleSheet extends StyleContext // FIXME: Not implemented. } } - - /** - * The parser callback for the CSSParser. - */ - class CssParser implements CSSParser.CSSParserCallback - { - /** - * A vector of all the selectors. - * Each element is an array of all the selector tokens - * in a single rule. - */ - Vector selectors; - - /** A vector of all the selector tokens in a rule. */ - Vector selectorTokens; - /** Name of the current property. */ - String propertyName; - - /** The set of CSS declarations */ - MutableAttributeSet declaration; - - /** - * True if parsing a declaration, that is the Reader will not - * contain a selector. - */ - boolean parsingDeclaration; - - /** True if the attributes are coming from a linked/imported style. */ - boolean isLink; - - /** The base URL */ - URL base; - - /** The parser */ - CSSParser parser; - - /** - * Constructor - */ - CssParser() - { - selectors = new Vector(); - selectorTokens = new Vector(); - parser = new CSSParser(); - base = StyleSheet.this.base; - declaration = new SimpleAttributeSet(); - } - - /** - * Parses the passed in CSS declaration into an AttributeSet. - * - * @param s - the declaration - * @return the set of attributes containing the property and value. - */ - public AttributeSet parseDeclaration(String s) - { - try - { - return parseDeclaration(new StringReader(s)); - } - catch (IOException e) - { - // Do nothing here. - } - return null; - } - - /** - * Parses the passed in CSS declaration into an AttributeSet. - * - * @param r - the reader - * @return the attribute set - * @throws IOException from the reader - */ - public AttributeSet parseDeclaration(Reader r) throws IOException - { - parse(base, r, true, false); - return declaration; - } - - /** - * Parse the given CSS stream - * - * @param base - the url - * @param r - the reader - * @param parseDec - True if parsing a declaration - * @param isLink - True if parsing a link - */ - public void parse(URL base, Reader r, boolean parseDec, boolean isLink) throws IOException - { - parsingDeclaration = parseDec; - this.isLink = isLink; - this.base = base; - - // flush out all storage - propertyName = null; - selectors.clear(); - selectorTokens.clear(); - declaration.removeAttributes(declaration); - - parser.parse(r, this, parseDec); - } - - /** - * Invoked when a valid @import is encountered, - * will call importStyleSheet if a MalformedURLException - * is not thrown in creating the URL. - * - * @param s - the string after @import - */ - public void handleImport(String s) - { - if (s != null) - { - try - { - if (s.startsWith("url(") && s.endsWith(")")) - s = s.substring(4, s.length() - 1); - if (s.indexOf("\"") >= 0) - s = s.replaceAll("\"",""); - - URL url = new URL(s); - if (url == null && base != null) - url = new URL(base, s); - - importStyleSheet(url); - } - catch (MalformedURLException e) - { - // Do nothing here. - } - } - } - - /** - * A selector has been encountered. - * - * @param s - a selector (e.g. P or UL or even P,) - */ - public void handleSelector(String s) - { - if (s.endsWith(",")) - s = s.substring(0, s.length() - 1); - - selectorTokens.addElement(s); - addSelector(); - } - - /** - * Invoked when the start of a rule is encountered. - */ - public void startRule() - { - addSelector(); - } - - /** - * Invoked when a property name is encountered. - * - * @param s - the property - */ - public void handleProperty(String s) - { - propertyName = s; - } - - /** - * Invoked when a property value is encountered. - * - * @param s - the value - */ - public void handleValue(String s) - { - // call addCSSAttribute - // FIXME: Not implemented - } - - /** - * Invoked when the end of a rule is encountered. - */ - public void endRule() - { - // FIXME: Not implemented - // add rules - propertyName = null; - } - - /** - * Adds the selector to the vector. - */ - private void addSelector() - { - int length = selectorTokens.size(); - if (length > 0) - { - Object[] sel = new Object[length]; - System.arraycopy(selectorTokens.toArray(), 0, sel, 0, length); - selectors.add(sel); - selectorTokens.clear(); - } - } - } } diff --git a/javax/swing/text/html/ViewAttributeSet.java b/javax/swing/text/html/ViewAttributeSet.java new file mode 100644 index 000000000..25db89fc4 --- /dev/null +++ b/javax/swing/text/html/ViewAttributeSet.java @@ -0,0 +1,163 @@ +/* ViewAttributeSet.java -- The AttributeSet used by HTML views + 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 javax.swing.text.html; + +import java.util.ArrayList; +import java.util.Enumeration; + +import javax.swing.text.AttributeSet; +import javax.swing.text.Element; +import javax.swing.text.StyleConstants; +import javax.swing.text.View; + +/** + * An AttributeSet implemenation that is used by the HTML views. This + * AttributeSet is created by StyleSheet.getViewAttributes() and combines + * the following attributes: + * - The original attributes of the View's element. + * - Any translated (HTML->CSS) attributes, as returned by + * StyleSheet.translateHTMLToCS(). + * - CSS Styles as resolved by the CSS stylesheet. + * + * In addition to that, it resolves attributes to the parent views, if + * a CSS attribute is requested that is inheritable. + * + * @author Roman Kennke (kennke@aicas.com) + */ +class ViewAttributeSet + extends MultiAttributeSet +{ + + /** + * The view for which we are the AttributeSet. + */ + private View view; + + /** + * The stylesheet to use. + */ + private StyleSheet styleSheet; + + /** + * Creates a new instance. + * + * @param v the view for which to do the AttributeSet + */ + ViewAttributeSet(View v, StyleSheet ss) + { + styleSheet = ss; + view = v; + ArrayList atts = new ArrayList(); + + Element el = v.getElement(); + AttributeSet elAtts = el.getAttributes(); + AttributeSet htmlAtts = styleSheet.translateHTMLToCSS(elAtts); + if (htmlAtts.getAttributeCount() > 0) + atts.add(htmlAtts); + + if (el.isLeaf()) + { + Enumeration n = elAtts.getAttributeNames(); + while (n.hasMoreElements()) + { + Object key = n.nextElement(); + if (key instanceof HTML.Tag) + { + AttributeSet rule = styleSheet.getRule((HTML.Tag) key, el); + if (rule != null) + atts.add(rule); + } + } + } + else + { + HTML.Tag tag = + (HTML.Tag) elAtts.getAttribute(StyleConstants.NameAttribute); + AttributeSet rule = styleSheet.getRule(tag, el); + if (rule != null) + atts.add(rule); + } + + AttributeSet[] atts1 = new AttributeSet[atts.size()]; + atts1 = (AttributeSet[]) atts.toArray(atts1); + init(atts1); + } + + /** + * Fetches the attribute for the specific ckey. If the attribute + * can't be found and the key is a CSS.Attribute that is inherited, + * then the attribute is looked up in the resolve parent. + */ + public Object getAttribute(Object key) + { + Object val = super.getAttribute(key); + if (val == null) + { + // Didn't find value. If the key is a CSS.Attribute, and is + // inherited, then ask the resolve parent. + if (key instanceof CSS.Attribute) + { + CSS.Attribute cssKey = (CSS.Attribute) key; + if (cssKey.isInherited()) + { + AttributeSet resolveParent = getResolveParent(); + if (resolveParent != null) + val = resolveParent.getAttribute(cssKey); + } + } + } + return val; + } + + /** + * Returns the resolve parent of this AttributeSet. This is the AttributeSet + * returned by the parent view if available. + */ + public AttributeSet getResolveParent() + { + AttributeSet parent = null; + if (view != null) + { + View parentView = view.getParent(); + if (parentView != null) + parent = parentView.getAttributes(); + } + return parent; + } +} diff --git a/javax/swing/tree/DefaultTreeCellEditor.java b/javax/swing/tree/DefaultTreeCellEditor.java index b0a4d8db8..4c10bfe1a 100644 --- a/javax/swing/tree/DefaultTreeCellEditor.java +++ b/javax/swing/tree/DefaultTreeCellEditor.java @@ -43,7 +43,6 @@ import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; -import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.event.ActionEvent; @@ -59,10 +58,10 @@ import javax.swing.Icon; import javax.swing.JTextField; import javax.swing.JTree; import javax.swing.SwingUtilities; +import javax.swing.Timer; import javax.swing.UIManager; import javax.swing.border.Border; import javax.swing.event.CellEditorListener; -import javax.swing.event.ChangeEvent; import javax.swing.event.EventListenerList; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; @@ -77,12 +76,6 @@ public class DefaultTreeCellEditor implements ActionListener, TreeCellEditor, TreeSelectionListener { /** - * The number of the fast mouse clicks, required to start the editing - * session. - */ - static int CLICK_COUNT_TO_START = 3; - - /** * This container that appears on the tree during editing session. * It contains the editing component displays various other editor - * specific parts like editing icon. @@ -99,7 +92,7 @@ public class DefaultTreeCellEditor */ public EditorContainer() { - // Do nothing here. + setLayout(null); } /** @@ -111,12 +104,6 @@ public class DefaultTreeCellEditor // Do nothing here. } - public void setBounds(Rectangle bounds) - { - super.setBounds(bounds); - doLayout(); - } - /** * Overrides Container.paint to paint the node's icon and use the selection * color for the background. @@ -126,11 +113,20 @@ public class DefaultTreeCellEditor */ public void paint(Graphics g) { + // Paint editing icon. if (editingIcon != null) { // From the previous version, the left margin is taken as half // of the icon width. - editingIcon.paintIcon(this, g, 0, 0); + int y = Math.max(0, (getHeight() - editingIcon.getIconHeight()) / 2); + editingIcon.paintIcon(this, g, 0, y); + } + // Paint border. + Color c = getBorderSelectionColor(); + if (c != null) + { + g.setColor(c); + g.drawRect(0, 0, getWidth() - 1, getHeight() - 1); } super.paint(g); } @@ -141,27 +137,33 @@ public class DefaultTreeCellEditor */ public void doLayout() { - // The offset of the editing component. - int eOffset; + if (editingComponent != null) + { + editingComponent.getPreferredSize(); + editingComponent.setBounds(offset, 0, getWidth() - offset, + getHeight()); + } + } - // Move the component to the left, leaving room for the editing icon: - if (editingIcon != null) - eOffset = editingIcon.getIconWidth(); + public Dimension getPreferredSize() + { + Dimension dim; + if (editingComponent != null) + { + dim = editingComponent.getPreferredSize(); + dim.width += offset + 5; + if (renderer != null) + { + Dimension r = renderer.getPreferredSize(); + dim.height = Math.max(dim.height, r.height); + } + if (editingIcon != null) + dim.height = Math.max(dim.height, editingIcon.getIconHeight()); + dim.width = Math.max(100, dim.width); + } else - eOffset = 0; - - Rectangle bounds = getBounds(); - Component c = getComponent(0); - c.setLocation(eOffset, 0); - - // Span the editing component near over all window width. - c.setSize(bounds.width - eOffset, bounds.height); - /* - * @specnote the Sun sets some more narrow editing component width (it is - * not documented how does it is calculated). However as our text field is - * still not able to auto - scroll horizontally, replicating such strategy - * would prevent adding extra characters to the text being edited. - */ + dim = new Dimension(0, 0); + return dim; } } @@ -227,46 +229,15 @@ public class DefaultTreeCellEditor */ public Dimension getPreferredSize() { - String s = getText(); - - Font f = getFont(); - - if (f != null) + Dimension size = super.getPreferredSize(); + if (renderer != null && DefaultTreeCellEditor.this.getFont() == null) { - FontMetrics fm = getToolkit().getFontMetrics(f); - - return new Dimension(SwingUtilities.computeStringWidth(fm, s), - fm.getHeight()); + size.height = renderer.getPreferredSize().height; } return renderer.getPreferredSize(); } } - /** - * Listens for the events from the realEditor. - */ - class RealEditorListener implements CellEditorListener - { - /** - * The method is called when the editing has been cancelled. - * @param event unused - */ - public void editingCanceled(ChangeEvent event) - { - cancelCellEditing(); - } - - /** - * The method is called after completing the editing session. - * - * @param event unused - */ - public void editingStopped(ChangeEvent event) - { - stopCellEditing(); - } - } - private EventListenerList listenerList = new EventListenerList(); /** @@ -367,21 +338,14 @@ public class DefaultTreeCellEditor public DefaultTreeCellEditor(JTree tree, DefaultTreeCellRenderer renderer, TreeCellEditor editor) { - setTree(tree); this.renderer = renderer; - - if (editor == null) - editor = createTreeCellEditor(); - else - editor.addCellEditorListener(new RealEditorListener()); - realEditor = editor; - - lastPath = tree.getLeadSelectionPath(); - tree.addTreeSelectionListener(this); + if (realEditor == null) + realEditor = createTreeCellEditor(); editingContainer = createContainer(); - setFont(UIManager.getFont("Tree.font")); - setBorderSelectionColor(UIManager.getColor("Tree.selectionBorderColor")); + setTree(tree); + Color c = UIManager.getColor("Tree.editorBorderSelectionColor"); + setBorderSelectionColor(c); } /** @@ -505,19 +469,36 @@ public class DefaultTreeCellEditor * @return the component for editing */ public Component getTreeCellEditorComponent(JTree tree, Object value, - boolean isSelected, boolean expanded, + boolean isSelected, + boolean expanded, boolean leaf, int row) { - if (realEditor == null) - realEditor = createTreeCellEditor(); - - return realEditor.getTreeCellEditorComponent(tree, value, isSelected, - expanded, leaf, row); + setTree(tree); + lastRow = row; + determineOffset(tree, value, isSelected, expanded, leaf, row); + if (editingComponent != null) + editingContainer.remove(editingComponent); + + editingComponent = realEditor.getTreeCellEditorComponent(tree, value, + isSelected, + expanded, leaf, + row); + Font f = getFont(); + if (f == null) + { + if (renderer != null) + f = renderer.getFont(); + if (f == null) + f = tree.getFont(); + } + editingContainer.setFont(f); + prepareForEditing(); + return editingContainer; } /** * Returns the value currently being edited (requests it from the - * {@link realEditor}. + * {@link #realEditor}. * * @return the value currently being edited */ @@ -535,16 +516,48 @@ public class DefaultTreeCellEditor * @return true if editing can be started */ public boolean isCellEditable(EventObject event) - { - if (editingComponent == null) - configureEditingComponent(tree, renderer, realEditor); - - if (editingComponent != null && realEditor.isCellEditable(event)) + { + boolean ret = false; + boolean ed = false; + if (event != null) { - prepareForEditing(); - return true; + if (event.getSource() instanceof JTree) + { + setTree((JTree) event.getSource()); + if (event instanceof MouseEvent) + { + MouseEvent me = (MouseEvent) event; + TreePath path = tree.getPathForLocation(me.getX(), me.getY()); + ed = lastPath != null && path != null && lastPath.equals(path); + if (path != null) + { + lastRow = tree.getRowForPath(path); + Object val = path.getLastPathComponent(); + boolean isSelected = tree.isRowSelected(lastRow); + boolean isExpanded = tree.isExpanded(path); + TreeModel m = tree.getModel(); + boolean isLeaf = m.isLeaf(val); + determineOffset(tree, val, isSelected, isExpanded, isLeaf, + lastRow); + } + } + } } - return false; + if (! realEditor.isCellEditable(event)) + ret = false; + else + { + if (canEditImmediately(event)) + ret = true; + else if (ed && shouldStartEditingTimer(event)) + startEditingTimer(); + else if (timer != null && timer.isRunning()) + timer.stop(); + } + if (ret) + prepareForEditing(); + return ret; + } /** @@ -567,14 +580,13 @@ public class DefaultTreeCellEditor */ public boolean stopCellEditing() { - if (editingComponent != null) + boolean ret = false; + if (realEditor.stopCellEditing()) { - stopEditingTimer(); - tree.stopEditing(); - editingComponent = null; - return true; + finish(); + ret = true; } - return false; + return ret; } /** @@ -583,21 +595,15 @@ public class DefaultTreeCellEditor */ public void cancelCellEditing() { - if (editingComponent != null) - { - tree.cancelEditing(); - editingComponent = null; - } - stopEditingTimer(); + realEditor.cancelCellEditing(); + finish(); } - - /** - * Stop the editing timer, if it is installed and running. - */ - private void stopEditingTimer() + + private void finish() { - if (timer != null && timer.isRunning()) - timer.stop(); + if (editingComponent != null) + editingContainer.remove(editingComponent); + editingComponent = null; } /** @@ -640,10 +646,18 @@ public class DefaultTreeCellEditor */ public void valueChanged(TreeSelectionEvent e) { - tPath = lastPath; - lastPath = e.getNewLeadSelectionPath(); - lastRow = tree.getRowForPath(lastPath); - stopCellEditing(); + if (tree != null) + { + if (tree.getSelectionCount() == 1) + lastPath = tree.getSelectionPath(); + else + lastPath = null; + } + // TODO: We really should do the following here, but can't due + // to buggy DefaultTreeSelectionModel. This selection model + // should only fire if the selection actually changes. +// if (timer != null) +// timer.stop(); } /** @@ -653,6 +667,8 @@ public class DefaultTreeCellEditor */ public void actionPerformed(ActionEvent e) { + if (tree != null && lastPath != null) + tree.startEditingAtPath(lastPath); } /** @@ -664,7 +680,17 @@ public class DefaultTreeCellEditor */ protected void setTree(JTree newTree) { - tree = newTree; + if (tree != newTree) + { + if (tree != null) + tree.removeTreeSelectionListener(this); + tree = newTree; + if (tree != null) + tree.addTreeSelectionListener(this); + + if (timer != null) + timer.stop(); + } } /** @@ -675,10 +701,14 @@ public class DefaultTreeCellEditor */ protected boolean shouldStartEditingTimer(EventObject event) { - if ((event instanceof MouseEvent) && - ((MouseEvent) event).getClickCount() == 1) - return true; - return false; + boolean ret = false; + if (event instanceof MouseEvent) + { + MouseEvent me = (MouseEvent) event; + ret = SwingUtilities.isLeftMouseButton(me) && me.getClickCount() == 1 + && inHitRegion(me.getX(), me.getY()); + } + return ret; } /** @@ -686,8 +716,12 @@ public class DefaultTreeCellEditor */ protected void startEditingTimer() { - if (timer != null) - timer.start(); + if (timer == null) + { + timer = new Timer(1200, this); + timer.setRepeats(false); + } + timer.start(); } /** @@ -723,7 +757,6 @@ public class DefaultTreeCellEditor protected boolean inHitRegion(int x, int y) { Rectangle bounds = tree.getPathBounds(lastPath); - return bounds.contains(x, y); } @@ -739,13 +772,24 @@ public class DefaultTreeCellEditor protected void determineOffset(JTree tree, Object value, boolean isSelected, boolean expanded, boolean leaf, int row) { - renderer.getTreeCellRendererComponent(tree, value, isSelected, expanded, - leaf, row, true); - Icon c = renderer.getIcon(); - if (c != null) - offset = renderer.getIconTextGap() + c.getIconWidth(); + if (renderer != null) + { + if (leaf) + editingIcon = renderer.getLeafIcon(); + else if (expanded) + editingIcon = renderer.getOpenIcon(); + else + editingIcon = renderer.getClosedIcon(); + if (editingIcon != null) + offset = renderer.getIconTextGap() + editingIcon.getIconWidth(); + else + offset = renderer.getIconTextGap(); + } else - offset = 0; + { + editingIcon = null; + offset = 0; + } } /** @@ -754,8 +798,8 @@ public class DefaultTreeCellEditor */ protected void prepareForEditing() { - editingContainer.removeAll(); - editingContainer.add(editingComponent); + if (editingComponent != null) + editingContainer.add(editingComponent); } /** @@ -776,10 +820,10 @@ public class DefaultTreeCellEditor */ protected TreeCellEditor createTreeCellEditor() { - DefaultCellEditor editor = new DefaultCellEditor(new DefaultTreeCellEditor.DefaultTextField( - UIManager.getBorder("Tree.selectionBorder"))); - editor.addCellEditorListener(new RealEditorListener()); - editor.setClickCountToStart(CLICK_COUNT_TO_START); + Border border = UIManager.getBorder("Tree.editorBorder"); + JTextField tf = new DefaultTreeCellEditor.DefaultTextField(border); + DefaultCellEditor editor = new DefaultCellEditor(tf); + editor.setClickCountToStart(1); realEditor = editor; return editor; } diff --git a/javax/swing/tree/DefaultTreeCellRenderer.java b/javax/swing/tree/DefaultTreeCellRenderer.java index e120b71c1..26a30f0cc 100644 --- a/javax/swing/tree/DefaultTreeCellRenderer.java +++ b/javax/swing/tree/DefaultTreeCellRenderer.java @@ -547,19 +547,9 @@ public class DefaultTreeCellRenderer */ public Dimension getPreferredSize() { - Rectangle vr = new Rectangle(); - Rectangle ir = new Rectangle(); - Rectangle tr = new Rectangle(); - - FontMetrics fm = getToolkit().getFontMetrics(getFont()); - SwingUtilities.layoutCompoundLabel((JLabel) this, fm, getText(), - getIcon(), getVerticalAlignment(), - getHorizontalAlignment(), - getVerticalTextPosition(), - getHorizontalTextPosition(), vr, ir, tr, - getIconTextGap()); - Rectangle cr = ir.union(tr); - return new Dimension(cr.width, cr.height); + Dimension size = super.getPreferredSize(); + size.width += 3; + return size; } /** diff --git a/javax/swing/tree/VariableHeightLayoutCache.java b/javax/swing/tree/VariableHeightLayoutCache.java index 11509b1b0..03251eb1f 100644 --- a/javax/swing/tree/VariableHeightLayoutCache.java +++ b/javax/swing/tree/VariableHeightLayoutCache.java @@ -451,8 +451,8 @@ public class VariableHeightLayoutCache { if (y < r.y) return r.y - y; - else if (y > r.y + r.height) - return y - (r.y + r.height); + else if (y > r.y + r.height - 1) + return y - (r.y + r.height - 1); else return 0; } diff --git a/lib/Makefile.am b/lib/Makefile.am index 3192372d9..52c49186d 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -7,6 +7,7 @@ sinclude $(JAVA_DEPEND) propertydirs := $(shell cd $(top_srcdir)/resource && $(FIND) gnu java javax org META-INF -type d ! -name CVS -print) propertyfiles := $(shell cd $(top_srcdir)/resource && $(FIND) gnu java javax org -name \*\.properties -print) +cssfiles := $(shell cd $(top_srcdir) && $(FIND) gnu java javax org -name \*\.css -print) metafiles := $(shell cd $(top_srcdir)/resource && $(FIND) META-INF -name CVS -prune -o -name \*\.in -prune -o -type f -print) iconfiles := $(shell cd $(top_srcdir) && $(FIND) gnu/javax/swing/plaf/gtk/icons -name *.png -type f -print) @@ -103,16 +104,19 @@ glibj.zip: classes compile-classes resources endif # USE_PREBUILT_GLIBJ_ZIP resources: copy-vmresources.sh - if ! [ -e gnu ]; then mkdir gnu; fi - if ! [ -e gnu/java ]; then mkdir gnu/java; fi - if ! [ -e gnu/java/locale ]; then mkdir gnu/java/locale; fi - if ! [ -e gnu/javax/swing/plaf/gtk/icons ]; then mkdir -p gnu/javax/swing/plaf/gtk/icons; fi + if ! test -d gnu; then mkdir gnu; fi + if ! test -d gnu/java; then mkdir gnu/java; fi + if ! test -d gnu/java/locale; then mkdir gnu/java/locale; fi + if ! test -d gnu/javax/swing/plaf/gtk/icons; then mkdir -p gnu/javax/swing/plaf/gtk/icons; fi @list='$(propertydirs)'; for p in $$list; do \ - if ! [ -e $$p ]; then mkdir $$p; fi; \ + if ! test -d $$p; then mkdir $$p; fi; \ done @list='$(propertyfiles)'; for p in $$list; do \ cp $(top_srcdir)/resource/$$p $$p; \ done + @list='$(cssfiles)'; for p in $$list; do \ + cp $(top_srcdir)/$$p $$p; \ + done @list='$(metafiles)'; for p in $$list; do \ cp $(top_srcdir)/resource/$$p $$p; \ done diff --git a/m4/gcc_attribute.m4 b/m4/gcc_attribute.m4 new file mode 100644 index 000000000..f0c25722b --- /dev/null +++ b/m4/gcc_attribute.m4 @@ -0,0 +1,133 @@ +dnl CACHED_TRY_COMPILE(<description>,<cachevar>,<include>,<program>,<ifyes>,<ifno>) +AC_DEFUN([CACHED_TRY_COMPILE],[ + AC_MSG_CHECKING($1) + AC_CACHE_VAL($2,[ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[$3]], [[$4]])],[$2=yes],[$2=no]) + ]) + if test "x$$2" = xyes; then + true + $5 + else + true + $6 + fi +]) + +dnl GCC_ATTRIBUTE(<short-label>,<cachevar>,<func-params>,<attribute>,<HAVE>,<desc>,[<true-cmds>],[<false-cmds>]) +AC_DEFUN([GCC_ATTRIBUTE],[ + CACHED_TRY_COMPILE(__attribute__(($1)),cv_c_gcc_attribute_$2,, + [extern int testfunction($3) __attribute__(($4))], + AC_MSG_RESULT(yes) + AC_DEFINE(HAVE_GNUC25_$5,,$6) + $7, + AC_MSG_RESULT(no) + $8) +]) + + +AC_DEFUN([GCC_ATTRIBUTE_SUPPORTED],[ + GCC_ATTRIBUTE([,,],supported,[int x],[,,],ATTRIB,[Define if function attributes a la GCC 2.5 and higher are available.]) + AH_BOTTOM([/* GNU C attributes. */ +#ifndef FUNCATTR +#ifdef HAVE_GNUC25_ATTRIB +#define FUNCATTR(x) __attribute__(x) +#else +#define FUNCATTR(x) +#endif +#endif]) + +]) +AC_DEFUN([GCC_ATTRIBUTE_CONST],[ + AC_REQUIRE([GCC_ATTRIBUTE_SUPPORTED]) + GCC_ATTRIBUTE(const,const,[int x],const,CONST,[Define if constant functions a la GCC 2.5 and higher are available.]) + AH_BOTTOM([/* GNU C constant functions, or null. */ +#ifndef ATTRCONST +#ifdef HAVE_GNUC25_CONST +#define ATTRCONST const +#else +#define ATTRCONST +#endif +#endif +#ifndef CONSTANT +#define CONSTANT FUNCATTR((ATTRCONST)) +#endif]) +]) +AC_DEFUN([GCC_ATTRIBUTE_NORETURN],[ + AC_REQUIRE([GCC_ATTRIBUTE_SUPPORTED]) + GCC_ATTRIBUTE(noreturn,noreturn,[int x],noreturn,NORETURN,[Define if nonreturning functions a la GCC 2.5 and higher are available.]) + AH_BOTTOM([/* GNU C nonreturning functions, or null. */ +#ifndef ATTRNORETURN +#ifdef HAVE_GNUC25_NORETURN +#define ATTRNORETURN noreturn +#else /* ! HAVE_GNUC25_NORETURN */ +#define ATTRNORETURN +#endif /* HAVE_GNUC25_NORETURN */ +#endif /* ATTRNORETURN */ +#ifndef NONRETURNING +#define NONRETURNING FUNCATTR((ATTRNORETURN)) +#endif /* NONRETURNING */]) +]) +AC_DEFUN([GCC_ATTRIBUTE_UNUSED],[ + AC_REQUIRE([GCC_ATTRIBUTE_SUPPORTED]) + GCC_ATTRIBUTE(unused,unused,[int x],unused,UNUSED,[Define if unused variables la GCC 2.5 and higher are available.]) + AH_BOTTOM([/* GNU C unused functions, or null. */ +#ifndef ATTRUNUSED +#ifdef HAVE_GNUC25_UNUSED +#define ATTRUNUSED unused +#else +#define ATTRUNUSED +#endif +#endif +#ifndef UNUSED +#define UNUSED FUNCATTR((ATTRUNUSED)) +#endif]) +]) +AC_DEFUN([GCC_ATTRIBUTE_FORMAT],[ + AC_REQUIRE([GCC_ATTRIBUTE_SUPPORTED]) + GCC_ATTRIBUTE(format...,format,[char *y, ...],[format(printf,1,2)],PRINTFFORMAT,[Define if printf-format argument lists a la GCC are available.]) + AH_BOTTOM([/* GNU C printf formats, or null. */ +#ifndef ATTRPRINTF +#ifdef HAVE_GNUC25_PRINTFFORMAT +#define ATTRPRINTF(si,tc) format(printf,si,tc) +#else +#define ATTRPRINTF(si,tc) +#endif +#endif +#ifndef PRINTFFORMAT +#define PRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc))) +#endif + +#ifndef NONRETURNPRINTFFORMAT +#define NONRETURNPRINTFFORMAT(si,tc) FUNCATTR((ATTRPRINTF(si,tc),ATTRNORETURN)) +#endif]) +]) +AC_DEFUN([GCC_ATTRIBUTE_ALWAYS_INLINE],[ + AC_REQUIRE([GCC_ATTRIBUTE_SUPPORTED]) + GCC_ATTRIBUTE(always_inline,always_inline,[int x],always_inline,ALWAYS_INLINE,[Define if unconditional inlining of functions a la GCC 3.1 and higher are available.]) + AH_BOTTOM([/* GNU C constant functions, or null. */ +#ifndef ATTRALWAYS_INLINE +#ifdef HAVE_GNUC25_ALWAYS_INLINE +#define ATTRALWAYS_INLINE always_inline +#else +#define ATTRALWAYS_INLINE +#endif +#endif +#ifndef ALWAYS_INLINE +#define ALWAYS_INLINE FUNCATTR((ATTRALWAYS_INLINE)) +#endif]) +]) +AC_DEFUN([GCC_ATTRIBUTE_PACKED],[ + AC_REQUIRE([GCC_ATTRIBUTE_SUPPORTED]) + GCC_ATTRIBUTE(packed,packed,[int x],packed,PACKED,[Define if packing of struct members a la GCC 2.5 and higher is available.]) + AH_BOTTOM([/* GNU C constant functions, or null. */ +#ifndef ATTRPACKED +#ifdef HAVE_GNUC25_PACKED +#define ATTRPACKED packed +#else +#define ATTRPACKED +#endif +#endif +#ifndef PACKED +#define PACKED FUNCATTR((ATTRPACKED)) +#endif]) +]) diff --git a/native/Makefile.am b/native/Makefile.am index 25aa364d1..89b859eb7 100644 --- a/native/Makefile.am +++ b/native/Makefile.am @@ -12,6 +12,6 @@ if CREATE_PLUGIN PLUGINDIR = plugin endif -SUBDIRS = fdlibm $(JNIDIR) $(JAWTDIR) $(PLUGINDIR) target -DIST_SUBDIRS = fdlibm jni jawt plugin target +SUBDIRS = fdlibm $(JNIDIR) $(JAWTDIR) $(PLUGINDIR) +DIST_SUBDIRS = fdlibm jni jawt plugin diff --git a/native/jni/Makefile.am b/native/jni/Makefile.am index 2b205826c..110540799 100644 --- a/native/jni/Makefile.am +++ b/native/jni/Makefile.am @@ -1,7 +1,7 @@ ## Input file for automake to generate the Makefile.in used by configure if CREATE_CORE_JNI_LIBRARIES - JNIDIRS = java-io java-lang java-net java-nio java-util + JNIDIRS = native-lib java-io java-lang java-net java-nio java-util endif if CREATE_ALSA_LIBRARIES @@ -32,7 +32,8 @@ SUBDIRS = classpath $(JNIDIRS) \ $(ALSADIR) $(DSSIDIR) $(GTKDIR) $(CLASSPATH_QT_PEER_DIR) $(XMLJDIR) \ $(CLASSPATH_GCONF_PEER_DIR) DIST_SUBDIRS = classpath java-io java-lang java-net java-nio java-util \ - gtk-peer gconf-peer qt-peer xmlj midi-alsa midi-dssi + gtk-peer gconf-peer qt-peer xmlj midi-alsa midi-dssi \ + native-lib all-local: cd $(top_srcdir) && $(SHELL) ./scripts/check_jni_methods.sh diff --git a/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c b/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c index c8d9fb551..b92d017a5 100644 --- a/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c +++ b/native/jni/gtk-peer/gnu_java_awt_peer_gtk_GdkPixbufDecoder.c @@ -210,7 +210,8 @@ query_formats (JNIEnv *env, jclass clazz) jobject jformat; GSList *formats, *f; GdkPixbufFormat *format; - char **ch, *name; + gchar **ch, *name; + gint count; jclass formatClass; jmethodID addExtensionID; @@ -240,14 +241,16 @@ query_formats (JNIEnv *env, jclass clazz) string = (*env)->NewStringUTF(env, name); g_assert(string != NULL); - jformat = (*env)->CallStaticObjectMethod + jformat = (*env)->CallStaticObjectMethod (env, clazz, registerFormatID, string, (jboolean) gdk_pixbuf_format_is_writable(format)); (*env)->DeleteLocalRef(env, string); + g_free(name); g_assert(jformat != NULL); - + ch = gdk_pixbuf_format_get_extensions(format); + count = 0; while (*ch) { string = (*env)->NewStringUTF(env, *ch); @@ -255,9 +258,12 @@ query_formats (JNIEnv *env, jclass clazz) (*env)->CallVoidMethod (env, jformat, addExtensionID, string); (*env)->DeleteLocalRef(env, string); ++ch; + ++count; } - + g_strfreev(ch - count); + ch = gdk_pixbuf_format_get_mime_types(format); + count = 0; while (*ch) { string = (*env)->NewStringUTF(env, *ch); @@ -265,12 +271,13 @@ query_formats (JNIEnv *env, jclass clazz) (*env)->CallVoidMethod (env, jformat, addMimeTypeID, string); (*env)->DeleteLocalRef(env, string); ++ch; + ++count; } - + g_strfreev(ch - count); (*env)->DeleteLocalRef(env, jformat); } - - g_slist_free(formats); + + g_slist_free(formats); } diff --git a/native/jni/java-io/Makefile.am b/native/jni/java-io/Makefile.am index ac7cf2872..637c74c61 100644 --- a/native/jni/java-io/Makefile.am +++ b/native/jni/java-io/Makefile.am @@ -4,7 +4,8 @@ libjavaio_la_SOURCES = java_io_VMFile.c \ java_io_VMObjectInputStream.c \ java_io_VMObjectStreamClass.c -libjavaio_la_LIBADD = $(top_builddir)/native/jni/classpath/jcl.lo +libjavaio_la_LIBADD = $(top_builddir)/native/jni/classpath/jcl.lo \ + $(top_builddir)/native/jni/native-lib/libclasspathnative.la AM_LDFLAGS = @CLASSPATH_MODULE@ AM_CPPFLAGS = @CLASSPATH_INCLUDES@ diff --git a/native/jni/java-io/java_io_VMFile.c b/native/jni/java-io/java_io_VMFile.c index 7a3fdaf43..3f9282049 100644 --- a/native/jni/java-io/java_io_VMFile.c +++ b/native/jni/java-io/java_io_VMFile.c @@ -38,17 +38,20 @@ exception statement from your version. */ /* do not move; needed here because of some macro definitions */ #include <config.h> +#include <assert.h> #include <stdio.h> #include <stdlib.h> +#if defined (HAVE_LSTAT) && defined (HAVE_READLINK) +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#endif + #include <jni.h> #include <jcl.h> - -#include "target_native.h" -#ifndef WITHOUT_FILESYSTEM -#include "target_native_file.h" -#endif -#include "target_native_math_int.h" +#include "cpio.h" +#include "cpnative.h" #include "java_io_VMFile.h" @@ -75,26 +78,25 @@ Java_java_io_VMFile_create (JNIEnv * env, filename = JCL_jstring_to_cstring (env, name); if (filename == NULL) { - return (0); + return 0; } - TARGET_NATIVE_FILE_OPEN_CREATE (filename, fd, result); - if (result != TARGET_NATIVE_OK) + result = cpio_openFile (filename, &fd, CPFILE_FLAG_CREATE|CPFILE_FLAG_WRITE, CPFILE_PERMISSION_NORMAL); + if (result != CPNATIVE_OK) { - /* XXX ??? NYI */ - if (errno != EEXIST) + if (result != EEXIST) JCL_ThrowException (env, "java/io/IOException", - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); JCL_free_cstring (env, name, filename); - return (0); + return 0; } - TARGET_NATIVE_FILE_CLOSE (fd, result); + cpio_closeFile (fd); JCL_free_cstring (env, name, filename); - return (1); + return 1; #else /* not WITHOUT_FILESYSTEM */ - return (0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } @@ -123,22 +125,20 @@ Java_java_io_VMFile_canRead (JNIEnv * env, filename = (*env)->GetStringUTFChars (env, name, 0); if (filename == NULL) { - return (0); + return 0; } /* The lazy man's way out. We actually do open the file for reading briefly to verify it can be done */ - TARGET_NATIVE_FILE_OPEN_READ (filename, fd, result); + result = cpio_openFile (filename, &fd, CPFILE_FLAG_READ, 0); (*env)->ReleaseStringUTFChars (env, name, filename); - if (result != TARGET_NATIVE_OK) - { - return (0); - } - TARGET_NATIVE_FILE_CLOSE (fd, result); + if (result != CPNATIVE_OK) + return 0; + cpio_closeFile (fd); - return (1); + return 1; #else /* not WITHOUT_FILESYSTEM */ - return (0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } @@ -167,22 +167,22 @@ Java_java_io_VMFile_canWrite (JNIEnv * env, filename = (*env)->GetStringUTFChars (env, name, 0); if (filename == NULL) { - return (0); + return 0; } /* The lazy man's way out. We actually do open the file for writing briefly to verify it can be done */ - TARGET_NATIVE_FILE_OPEN_READWRITE (filename, fd, result); + result = cpio_openFile (filename, &fd, CPFILE_FLAG_READWRITE, 0); (*env)->ReleaseStringUTFChars (env, name, filename); - if (result != TARGET_NATIVE_OK) + if (result != CPNATIVE_OK) { - return (0); + return 0; } - TARGET_NATIVE_FILE_CLOSE (fd, result); + cpio_closeFile (fd); - return (1); + return 1; #else /* not WITHOUT_FILESYSTEM */ - return (0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } @@ -210,15 +210,15 @@ Java_java_io_VMFile_setReadOnly (JNIEnv * env, filename = (*env)->GetStringUTFChars (env, name, 0); if (filename == NULL) { - return (0); + return 0; } - TARGET_NATIVE_FILE_SET_MODE_READONLY (filename, result); + result = cpio_setFileReadonly (filename); (*env)->ReleaseStringUTFChars (env, name, filename); - return ((result == TARGET_NATIVE_OK) ? 1 : 0); + return result == CPNATIVE_OK ? 1 : 0; #else /* not WITHOUT_FILESYSTEM */ - return (0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } @@ -246,15 +246,15 @@ Java_java_io_VMFile_exists (JNIEnv * env, filename = (*env)->GetStringUTFChars (env, name, 0); if (filename == NULL) { - return (0); + return 0; } - TARGET_NATIVE_FILE_EXISTS (filename, result); + result = cpio_isFileExists (filename); (*env)->ReleaseStringUTFChars (env, name, filename); - return ((result == TARGET_NATIVE_OK) ? 1 : 0); + return result == CPNATIVE_OK ? 1 : 0; #else /* not WITHOUT_FILESYSTEM */ - return (0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } @@ -277,21 +277,22 @@ Java_java_io_VMFile_isFile (JNIEnv * env, #ifndef WITHOUT_FILESYSTEM const char *filename; int result; + jint entryType; /* Don't use the JCL convert function because it throws an exception on failure */ filename = (*env)->GetStringUTFChars (env, name, 0); if (filename == NULL) { - return (0); + return 0; } - TARGET_NATIVE_FILE_IS_FILE (filename, result); + result = cpio_checkType (filename, &entryType); (*env)->ReleaseStringUTFChars (env, name, filename); - return ((result == TARGET_NATIVE_OK) ? 1 : 0); + return result == CPNATIVE_OK && entryType == CPFILE_FILE ? 1 : 0; #else /* not WITHOUT_FILESYSTEM */ - return (0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } @@ -313,21 +314,22 @@ Java_java_io_VMFile_isDirectory (JNIEnv * env, #ifndef WITHOUT_FILESYSTEM const char *filename; int result; + jint entryType; /* Don't use the JCL convert function because it throws an exception on failure */ filename = (*env)->GetStringUTFChars (env, name, 0); if (filename == NULL) { - return (0); + return 0; } - - TARGET_NATIVE_FILE_IS_DIRECTORY (filename, result); + + result = cpio_checkType (filename, &entryType); (*env)->ReleaseStringUTFChars (env, name, filename); - return ((result == TARGET_NATIVE_OK) ? 1 : 0); + return result == CPNATIVE_OK && entryType == CPFILE_DIRECTORY ? 1 : 0; #else /* not WITHOUT_FILESYSTEM */ - return (0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } @@ -356,29 +358,26 @@ Java_java_io_VMFile_length (JNIEnv * env, on failure */ filename = (*env)->GetStringUTFChars (env, name, 0); if (filename == NULL) - { - return (TARGET_NATIVE_MATH_INT_INT64_CONST_0); - } + return 0; /* open file for reading, get size and close file */ - TARGET_NATIVE_FILE_OPEN_READ (filename, tmpfd, result); - if (result != TARGET_NATIVE_OK) - { - return (TARGET_NATIVE_MATH_INT_INT64_CONST_0); - } - TARGET_NATIVE_FILE_SIZE (tmpfd, length, result); - if (result != TARGET_NATIVE_OK) + result = cpio_openFile (filename, &tmpfd, CPFILE_FLAG_READ, 0); + if (result != CPNATIVE_OK) + return 0; + + result = cpio_getFileSize (tmpfd, &length); + if (result != CPNATIVE_OK) { - TARGET_NATIVE_FILE_CLOSE (tmpfd, result); - return (TARGET_NATIVE_MATH_INT_INT64_CONST_0); + cpio_closeFile (tmpfd); + return 0; } - TARGET_NATIVE_FILE_CLOSE (tmpfd, result); + + result = cpio_closeFile (tmpfd); (*env)->ReleaseStringUTFChars (env, name, filename); - return ((result == - TARGET_NATIVE_OK) ? length : TARGET_NATIVE_MATH_INT_INT64_CONST_0); + return result == CPNATIVE_OK ? length : 0; #else /* not WITHOUT_FILESYSTEM */ - return (TARGET_NATIVE_MATH_INT_INT64_CONST_0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } @@ -407,16 +406,15 @@ Java_java_io_VMFile_lastModified (JNIEnv * env, filename = (*env)->GetStringUTFChars (env, name, 0); if (filename == NULL) { - return (TARGET_NATIVE_MATH_INT_INT64_CONST_0); + return 0; } - TARGET_NATIVE_FILE_GET_LAST_MODIFIED (filename, mtime, result); + result = cpio_getModificationTime (filename, &mtime); (*env)->ReleaseStringUTFChars (env, name, filename); - return ((result == - TARGET_NATIVE_OK) ? mtime : TARGET_NATIVE_MATH_INT_INT64_CONST_0); + return result == CPNATIVE_OK ? mtime : 0; #else /* not WITHOUT_FILESYSTEM */ - return (TARGET_NATIVE_MATH_INT_INT64_CONST_0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } @@ -444,15 +442,15 @@ Java_java_io_VMFile_setLastModified (JNIEnv * env, filename = (*env)->GetStringUTFChars (env, name, 0); if (filename == NULL) { - return (0); + return 0; } - TARGET_NATIVE_FILE_SET_LAST_MODIFIED (filename, newtime, result); + result = cpio_setModificationTime (filename, newtime); (*env)->ReleaseStringUTFChars (env, name, filename); - return ((result == TARGET_NATIVE_OK) ? 1 : 0); + return result == CPNATIVE_OK ? 1 : 0; #else /* not WITHOUT_FILESYSTEM */ - return (0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } @@ -481,15 +479,15 @@ Java_java_io_VMFile_delete (JNIEnv * env, filename = (*env)->GetStringUTFChars (env, name, 0); if (filename == NULL) { - return (0); + return 0; } - TARGET_NATIVE_FILE_DELETE (filename, result); + result = cpio_removeFile (filename); (*env)->ReleaseStringUTFChars (env, name, filename); - return ((result == TARGET_NATIVE_OK) ? 1 : 0); + return result == CPNATIVE_OK ? 1 : 0; #else /* not WITHOUT_FILESYSTEM */ - return (0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } @@ -517,15 +515,15 @@ Java_java_io_VMFile_mkdir (JNIEnv * env, pathname = (*env)->GetStringUTFChars (env, name, 0); if (pathname == NULL) { - return (0); + return 0; } - TARGET_NATIVE_FILE_MAKE_DIR (pathname, result); + result = cpio_mkdir (pathname); (*env)->ReleaseStringUTFChars (env, name, pathname); - return ((result == TARGET_NATIVE_OK) ? 1 : 0); + return (result == CPNATIVE_OK) ? 1 : 0; #else /* not WITHOUT_FILESYSTEM */ - return (0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } @@ -553,23 +551,23 @@ Java_java_io_VMFile_renameTo (JNIEnv * env, old_filename = (*env)->GetStringUTFChars (env, t, 0); if (old_filename == NULL) { - return (0); + return 0; } new_filename = (*env)->GetStringUTFChars (env, d, 0); if (new_filename == NULL) { (*env)->ReleaseStringUTFChars (env, t, old_filename); - return (0); + return 0; } - TARGET_NATIVE_FILE_RENAME (old_filename, new_filename, result); + result = cpio_rename (old_filename, new_filename); (*env)->ReleaseStringUTFChars (env, d, new_filename); (*env)->ReleaseStringUTFChars (env, t, old_filename); - return ((result == TARGET_NATIVE_OK) ? 1 : 0); + return (result == CPNATIVE_OK) ? 1 : 0; #else /* not WITHOUT_FILESYSTEM */ - return (0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } @@ -608,32 +606,32 @@ Java_java_io_VMFile_list (JNIEnv * env, jobject obj dirname = (*env)->GetStringUTFChars (env, name, 0); if (dirname == NULL) { - return (0); + return 0; } /* open directory for reading */ - TARGET_NATIVE_FILE_OPEN_DIR (dirname, handle, result); + result = cpio_openDir (dirname, &handle); (*env)->ReleaseStringUTFChars (env, name, dirname); - if (result != TARGET_NATIVE_OK) + if (result != CPNATIVE_OK) { - return (0); + return 0; } /* allocate filelist */ filelist = (char **) JCL_malloc (env, sizeof (char *) * REALLOC_SIZE); if (filelist == NULL) { - TARGET_NATIVE_FILE_CLOSE_DIR (handle, result); - return (0); + result = cpio_closeDir (handle); + return 0; } filelist_count = 0; max_filelist_count = REALLOC_SIZE; /* read the files from the directory */ - TARGET_NATIVE_FILE_READ_DIR (handle, filename, result); - while (result == TARGET_NATIVE_OK) + result = cpio_readDir (handle, &filename); + while (result == CPNATIVE_OK) { if ((strcmp (filename, ".") != 0) && (strcmp (filename, "..") != 0)) { @@ -652,8 +650,8 @@ Java_java_io_VMFile_list (JNIEnv * env, jobject obj JCL_free (env, filelist[i]); } JCL_free (env, filelist); - TARGET_NATIVE_FILE_CLOSE_DIR (handle, result); - return (0); + result = cpio_closeDir (handle); + return 0; } filelist = tmp_filelist; max_filelist_count += REALLOC_SIZE; @@ -668,11 +666,11 @@ Java_java_io_VMFile_list (JNIEnv * env, jobject obj } /* read next directory entry */ - TARGET_NATIVE_FILE_READ_DIR (handle, filename, result); + result = cpio_readDir (handle, &filename); } /* close directory */ - TARGET_NATIVE_FILE_CLOSE_DIR (handle, result); + result = cpio_closeDir (handle); /* put the list of files into a Java String array and return it */ str_clazz = (*env)->FindClass (env, "java/lang/String"); @@ -683,7 +681,7 @@ Java_java_io_VMFile_list (JNIEnv * env, jobject obj JCL_free (env, filelist[i]); } JCL_free (env, filelist); - return (0); + return 0; } filearray = (*env)->NewObjectArray (env, filelist_count, str_clazz, 0); if (filearray == NULL) @@ -693,7 +691,7 @@ Java_java_io_VMFile_list (JNIEnv * env, jobject obj JCL_free (env, filelist[i]); } JCL_free (env, filelist); - return (0); + return 0; } for (i = 0; i < filelist_count; i++) { @@ -708,7 +706,7 @@ Java_java_io_VMFile_list (JNIEnv * env, jobject obj JCL_free (env, filelist[i]); } JCL_free (env, filelist); - return (0); + return 0; } /* save into array */ @@ -725,9 +723,9 @@ Java_java_io_VMFile_list (JNIEnv * env, jobject obj } JCL_free (env, filelist); - return (filearray); + return filearray; #else /* not WITHOUT_FILESYSTEM */ - return (0); + return 0; #endif /* not WITHOUT_FILESYSTEM */ } diff --git a/native/jni/java-lang/Makefile.am b/native/jni/java-lang/Makefile.am index 06deb62b8..366d72e4c 100644 --- a/native/jni/java-lang/Makefile.am +++ b/native/jni/java-lang/Makefile.am @@ -7,7 +7,8 @@ libjavalang_la_SOURCES = java_lang_VMSystem.c \ java_lang_VMProcess.c libjavalang_la_LIBADD = $(wildcard $(top_builddir)/native/fdlibm/*.lo) \ - $(top_builddir)/native/jni/classpath/jcl.lo + $(top_builddir)/native/jni/classpath/jcl.lo \ + $(top_builddir)/native/jni/native-lib/libclasspathnative.la libjavalangreflect_la_SOURCES = java_lang_reflect_VMArray.c diff --git a/native/jni/java-lang/java_lang_VMProcess.c b/native/jni/java-lang/java_lang_VMProcess.c index f13a94f18..e9d85fa65 100644 --- a/native/jni/java-lang/java_lang_VMProcess.c +++ b/native/jni/java-lang/java_lang_VMProcess.c @@ -50,10 +50,8 @@ exception statement from your version. */ #include <fcntl.h> #include <stdio.h> -#include <jcl.h> - -#include "target_native.h" -#include "target_native_misc.h" +#include "cpnative.h" +#include "cpproc.h" /* Internal functions */ static char *copy_string (JNIEnv * env, jobject string); @@ -65,7 +63,6 @@ static char *copy_elem (JNIEnv * env, jobject stringArray, jint i); static char * copy_string (JNIEnv * env, jobject string) { - char errbuf[64]; const char *utf; jclass clazz; char *copy; @@ -89,12 +86,10 @@ copy_string (JNIEnv * env, jobject string) /* Copy it */ if ((copy = strdup (utf)) == NULL) { - TARGET_NATIVE_MISC_FORMAT_STRING1 (errbuf, sizeof (errbuf), - "strdup: %s", strerror (errno)); clazz = (*env)->FindClass (env, "java/lang/InternalError"); if ((*env)->ExceptionOccurred (env)) return NULL; - (*env)->ThrowNew (env, clazz, errbuf); + (*env)->ThrowNew (env, clazz, "strdup returned NULL"); (*env)->DeleteLocalRef (env, clazz); } @@ -131,8 +126,8 @@ Java_java_lang_VMProcess_nativeSpawn (JNIEnv * env, jobject this, jobjectArray envArray, jobject dirFile, jboolean redirect) { - int fds[3][2] = { {-1, -1}, {-1, -1}, {-1, -1} }; - jobject streams[3] = { NULL, NULL, NULL }; + int fds[CPIO_EXEC_NUM_PIPES]; + jobject streams[CPIO_EXEC_NUM_PIPES] = { NULL, NULL, NULL }; jobject dirString = NULL; char **newEnviron = NULL; jsize cmdArrayLen = 0; @@ -146,6 +141,7 @@ Java_java_lang_VMProcess_nativeSpawn (JNIEnv * env, jobject this, jclass clazz; int i; int pipe_count = redirect ? 2 : 3; + int err; /* Check for null */ if (cmdArray == NULL) @@ -182,9 +178,7 @@ Java_java_lang_VMProcess_nativeSpawn (JNIEnv * env, jobject this, + (dirString != NULL ? 1 : 0)) * sizeof (*strings))) == NULL) { - TARGET_NATIVE_MISC_FORMAT_STRING1 (errbuf, - sizeof (errbuf), "malloc: %s", - strerror (errno)); + strncpy (errbuf, "malloc failed", sizeof(errbuf)); goto out_of_memory; } @@ -209,103 +203,16 @@ Java_java_lang_VMProcess_nativeSpawn (JNIEnv * env, jobject this, { if ((dir = copy_string (env, dirString)) == NULL) goto done; - strings[num_strings++] = dir; } /* Create inter-process pipes */ - for (i = 0; i < pipe_count; i++) - { - if (pipe (fds[i]) == -1) - { - TARGET_NATIVE_MISC_FORMAT_STRING1 (errbuf, - sizeof (errbuf), "pipe: %s", - strerror (errno)); - goto system_error; - } - } - - /* Set close-on-exec flag for parent's ends of pipes */ - (void) fcntl (fds[0][1], F_SETFD, 1); - (void) fcntl (fds[1][0], F_SETFD, 1); - if (pipe_count == 3) - (void) fcntl (fds[2][0], F_SETFD, 1); - - /* Fork into parent and child processes */ - if ((pid = fork ()) == (pid_t) - 1) + err = cpproc_forkAndExec(strings, newEnviron, fds, pipe_count, &pid, dir); + if (err != 0) { - TARGET_NATIVE_MISC_FORMAT_STRING1 (errbuf, - sizeof (errbuf), "fork: %s", - strerror (errno)); + strncpy(errbuf, cpnative_getErrorString (err), sizeof(errbuf)); goto system_error; } - /* Child becomes the new process */ - if (pid == 0) - { - char *const path = strings[0]; - - /* Move file descriptors to standard locations */ - if (fds[0][0] != 0) - { - if (dup2 (fds[0][0], 0) == -1) - { - fprintf (stderr, "dup2: %s", strerror (errno)); - exit (127); - } - close (fds[0][0]); - } - if (fds[1][1] != 1) - { - if (dup2 (fds[1][1], 1) == -1) - { - fprintf (stderr, "dup2: %s", strerror (errno)); - exit (127); - } - close (fds[1][1]); - } - if (pipe_count == 2) - { - /* Duplicate stdout to stderr. */ - if (dup2 (1, 2) == -1) - { - fprintf (stderr, "dup2: %s", strerror (errno)); - exit (127); - } - } - else if (fds[2][1] != 2) - { - if (dup2 (fds[2][1], 2) == -1) - { - fprintf (stderr, "dup2: %s", strerror (errno)); - exit (127); - } - close (fds[2][1]); - } - - /* Change into destination directory */ - if (dir != NULL && chdir (dir) == -1) - { - fprintf (stderr, "%s: %s", dir, strerror (errno)); - exit (127); - } - - /* Make argv[0] last component of executable pathname */ - /* XXX should use "file.separator" property here XXX */ - for (i = strlen (path); i > 0 && path[i - 1] != '/'; i--); - strings[0] = path + i; - - /* Set new environment */ - if (newEnviron != NULL) - environ = newEnviron; - - /* Execute new program (this will close the parent end of the pipes) */ - execvp (path, strings); - - /* Failed */ - fprintf (stderr, "%s: %s", path, strerror (errno)); - exit (127); - } - /* Create Input/OutputStream objects around parent file descriptors */ clazz = (*env)->FindClass (env, "gnu/java/nio/channels/FileChannelImpl"); if ((*env)->ExceptionOccurred (env)) @@ -316,10 +223,8 @@ Java_java_lang_VMProcess_nativeSpawn (JNIEnv * env, jobject this, for (i = 0; i < pipe_count; i++) { /* Mode is WRITE (2) for in and READ (1) for out and err. */ - const int fd = fds[i][i == 0]; - const int mode = ((i == 0) - ? gnu_java_nio_channels_FileChannelImpl_WRITE - : gnu_java_nio_channels_FileChannelImpl_READ); + const int fd = fds[i]; + const int mode = ((i == CPIO_EXEC_STDIN) ? 2 : 1); jclass sclazz; jmethodID smethod; @@ -355,7 +260,10 @@ Java_java_lang_VMProcess_nativeSpawn (JNIEnv * env, jobject this, if ((*env)->ExceptionOccurred (env)) goto done; (*env)->CallVoidMethod (env, this, method, - streams[0], streams[1], streams[2], (jlong) pid); + streams[CPIO_EXEC_STDIN], + streams[CPIO_EXEC_STDOUT], + streams[CPIO_EXEC_STDERR], + (jlong) pid); if ((*env)->ExceptionOccurred (env)) goto done; @@ -365,15 +273,6 @@ done: * parent process. Our goal is to clean up the mess we created. */ - /* Close child's ends of pipes */ - for (i = 0; i < pipe_count; i++) - { - const int fd = fds[i][i != 0]; - - if (fd != -1) - close (fd); - } - /* * Close parent's ends of pipes if Input/OutputStreams never got created. * This can only happen in a failure case. If a Stream object @@ -382,7 +281,7 @@ done: */ for (i = 0; i < pipe_count; i++) { - const int fd = fds[i][i == 0]; + const int fd = fds[i]; if (fd != -1 && streams[i] == NULL) close (fd); @@ -392,7 +291,8 @@ done: while (num_strings > 0) free (strings[--num_strings]); free (strings); - + if (dir != NULL) + free(dir); /* Done */ return; @@ -431,19 +331,20 @@ Java_java_lang_VMProcess_nativeReap (JNIEnv * env, jclass clazz) jfieldID field; jint status; pid_t pid; + int err; /* Try to reap a child process, but don't block */ - if ((pid = waitpid ((pid_t) - 1, &status, WNOHANG)) == 0) + err = cpproc_waitpid((pid_t)-1, &status, &pid, WNOHANG); + if (err == 0 && pid == 0) return JNI_FALSE; /* Check result from waitpid() */ - if (pid == (pid_t) - 1) + if (err != 0) { - if (errno == ECHILD || errno == EINTR) + if (err == ECHILD || err == EINTR) return JNI_FALSE; - TARGET_NATIVE_MISC_FORMAT_STRING2 (ebuf, - sizeof (ebuf), "waitpid(%ld): %s", - (long) pid, strerror (errno)); + snprintf(ebuf, sizeof (ebuf), "waitpid(%ld): %s", + (long) pid, cpnative_getErrorString(errno)); clazz = (*env)->FindClass (env, "java/lang/InternalError"); if ((*env)->ExceptionOccurred (env)) return JNI_FALSE; @@ -485,12 +386,13 @@ JNIEXPORT void JNICALL Java_java_lang_VMProcess_nativeKill (JNIEnv * env, jclass clazz, jlong pid) { char ebuf[64]; - - if (kill ((pid_t) pid, SIGKILL) == -1) + int err; + + err = cpproc_kill((pid_t) pid, SIGKILL); + if (err != 0) { - TARGET_NATIVE_MISC_FORMAT_STRING2 (ebuf, - sizeof (ebuf), "kill(%ld): %s", - (long) pid, strerror (errno)); + snprintf (ebuf, sizeof (ebuf), "kill(%ld): %s", + (long) pid, cpnative_getErrorString (err)); clazz = (*env)->FindClass (env, "java/lang/InternalError"); if ((*env)->ExceptionOccurred (env)) return; diff --git a/native/jni/java-net/Makefile.am b/native/jni/java-net/Makefile.am index 26bb64f73..b743f2ffe 100644 --- a/native/jni/java-net/Makefile.am +++ b/native/jni/java-net/Makefile.am @@ -17,7 +17,9 @@ libjavanet_la_SOURCES = javanet.c \ gnu_java_net_VMPlainSocketImpl.c \ $(local_sources) -libjavanet_la_LIBADD = $(top_builddir)/native/jni/classpath/jcl.lo $(LIBMAGIC) +libjavanet_la_LIBADD = $(top_builddir)/native/jni/classpath/jcl.lo \ + $(top_builddir)/native/jni/native-lib/libclasspathnative.la \ + $(LIBMAGIC) AM_LDFLAGS = @CLASSPATH_MODULE@ AM_CPPFLAGS = @CLASSPATH_INCLUDES@ diff --git a/native/jni/java-net/gnu_java_net_VMPlainDatagramSocketImpl.c b/native/jni/java-net/gnu_java_net_VMPlainDatagramSocketImpl.c index 1b3cb97e0..3a451c763 100644 --- a/native/jni/java-net/gnu_java_net_VMPlainDatagramSocketImpl.c +++ b/native/jni/java-net/gnu_java_net_VMPlainDatagramSocketImpl.c @@ -45,13 +45,10 @@ exception statement from your version. */ #include <jni.h> #include <jcl.h> -#include "javanet.h" - -#include "target_native.h" -#ifndef WITHOUT_NETWORK - #include "target_native_network.h" -#endif /* WITHOUT_NETWORK */ +#include "cpnative.h" +#include "cpnet.h" +#include "javanet.h" #include "gnu_java_net_VMPlainDatagramSocketImpl.h" @@ -188,19 +185,20 @@ Java_gnu_java_net_VMPlainDatagramSocketImpl_nativeReceive(JNIEnv *env, jintArray receivedLength) { #ifndef WITHOUT_NETWORK - int addr, *port, *bytes_read; - char *addressBytes; + jint *port, *bytes_read; + cpnet_address *addr; + jbyte *addressBytes; addr = 0; - port = (int*)(*env)->GetIntArrayElements(env, receivedFromPort, NULL); + port = (jint*)(*env)->GetIntArrayElements(env, receivedFromPort, NULL); if (port == NULL) { JCL_ThrowException(env, IO_EXCEPTION, "Internal error: could not access receivedFromPort array"); return; } - bytes_read = (int*)(*env)->GetIntArrayElements(env, receivedLength, NULL); + bytes_read = (jint*)(*env)->GetIntArrayElements(env, receivedLength, NULL); if (bytes_read == NULL) { (*env)->ReleaseIntArrayElements(env, receivedFromPort, (jint*)port, 0); @@ -210,7 +208,7 @@ Java_gnu_java_net_VMPlainDatagramSocketImpl_nativeReceive(JNIEnv *env, /* Receive the packet */ /* should we try some sort of validation on the length? */ - (*bytes_read) = _javanet_recvfrom(env, obj, arr, offset, length, &addr, port); + (*bytes_read) = _javanet_recvfrom(env, obj, arr, offset, length, &addr); /* Special case the strange situation where the receiver didn't want any bytes. */ @@ -225,27 +223,23 @@ Java_gnu_java_net_VMPlainDatagramSocketImpl_nativeReceive(JNIEnv *env, return; } - (*env)->ReleaseIntArrayElements(env, receivedFromPort, (jint*)port, 0); - (*env)->ReleaseIntArrayElements(env, receivedLength, (jint*)bytes_read, 0); - if ((*env)->ExceptionOccurred(env)) - { - return; - } + return; - DBG("PlainDatagramSocketImpl.receive(): Received packet\n"); - + *port = cpnet_addressGetPort (addr); /* Store the address */ - addressBytes = (char*)(*env)->GetPrimitiveArrayCritical(env, receivedFromAddress, NULL); - TARGET_NATIVE_NETWORK_INT_TO_IPADDRESS_BYTES(addr, - addressBytes[0], - addressBytes[1], - addressBytes[2], - addressBytes[3] - ); + addressBytes = (jbyte*)(*env)->GetPrimitiveArrayCritical(env, receivedFromAddress, NULL); + cpnet_IPV4AddressToBytes (addr, addressBytes); (*env)->ReleasePrimitiveArrayCritical(env, receivedFromAddress, addressBytes, 0); - + + cpnet_freeAddress (env, addr); + + (*env)->ReleaseIntArrayElements(env, receivedFromPort, (jint*)port, 0); + (*env)->ReleaseIntArrayElements(env, receivedLength, (jint*)bytes_read, 0); + + DBG("PlainDatagramSocketImpl.receive(): Received packet\n"); + #else /* not WITHOUT_NETWORK */ #endif /* not WITHOUT_NETWORK */ } @@ -266,25 +260,35 @@ Java_gnu_java_net_VMPlainDatagramSocketImpl_nativeSendTo(JNIEnv *env, jint len) { #ifndef WITHOUT_NETWORK - jint netAddress; + cpnet_address *netAddress; /* check if address given, tr 7.3.2005 */ - if (addr != NULL) + if (addr != NULL ) { - netAddress = _javanet_get_netaddr(env, addr); + netAddress = _javanet_get_ip_netaddr(env, addr); if ((*env)->ExceptionOccurred(env)) - { + { return; } + if (port == 0) + { + JCL_ThrowException(env, IO_EXCEPTION, + "Invalid port number"); + cpnet_freeAddress(env, netAddress); + return; + } + cpnet_addressSetPort (netAddress, port); } else { - netAddress = 0; + netAddress = NULL; } DBG("PlainDatagramSocketImpl.sendto(): have addr\n"); - _javanet_sendto(env, obj, buf, offset, len, netAddress, port); + _javanet_sendto(env, obj, buf, offset, len, netAddress); + if (netAddress != NULL) + cpnet_freeAddress(env, netAddress); if ((*env)->ExceptionOccurred(env)) { return; @@ -307,14 +311,14 @@ Java_gnu_java_net_VMPlainDatagramSocketImpl_join(JNIEnv *env, jobject addr) { #ifndef WITHOUT_NETWORK - jint netAddress; + cpnet_address *netAddress; int fd; int result; /* check if address given, tr 7.3.2005 */ if (addr != NULL) { - netAddress = _javanet_get_netaddr(env, addr); + netAddress = _javanet_get_ip_netaddr(env, addr); if ((*env)->ExceptionOccurred(env)) { JCL_ThrowException(env, IO_EXCEPTION, "Internal error"); @@ -323,7 +327,7 @@ Java_gnu_java_net_VMPlainDatagramSocketImpl_join(JNIEnv *env, } else { - netAddress = 0; + netAddress = NULL; } fd = _javanet_get_int_field(env, obj, "native_fd"); @@ -335,11 +339,11 @@ Java_gnu_java_net_VMPlainDatagramSocketImpl_join(JNIEnv *env, DBG("PlainDatagramSocketImpl.join(): have native fd\n"); - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_ADD_MEMBERSHIP(fd,netAddress,result); - - if (result != TARGET_NATIVE_OK) + result = cpnet_addMembership (env, fd, netAddress); + if (result != CPNATIVE_OK) { - JCL_ThrowException(env, IO_EXCEPTION, TARGET_NATIVE_LAST_ERROR_STRING()); + JCL_ThrowException(env, IO_EXCEPTION, + cpnative_getErrorString (result)); return; } @@ -360,14 +364,14 @@ Java_gnu_java_net_VMPlainDatagramSocketImpl_leave(JNIEnv *env, jobject addr) { #ifndef WITHOUT_NETWORK - jint netAddress; + cpnet_address *netAddress; int fd; int result; /* check if address given, tr 7.3.2005 */ if (addr != NULL) { - netAddress = _javanet_get_netaddr(env, addr); + netAddress = _javanet_get_ip_netaddr(env, addr); if ((*env)->ExceptionOccurred(env)) { JCL_ThrowException(env, IO_EXCEPTION, "Internal error"); @@ -381,14 +385,17 @@ Java_gnu_java_net_VMPlainDatagramSocketImpl_leave(JNIEnv *env, fd = _javanet_get_int_field(env, obj, "native_fd"); if ((*env)->ExceptionOccurred(env)) - { JCL_ThrowException(env, IO_EXCEPTION, "Internal error"); return; } + { + JCL_ThrowException(env, IO_EXCEPTION, "Internal error"); + return; + } DBG("PlainDatagramSocketImpl.leave(): have native fd\n"); - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_DROP_MEMBERSHIP(fd,netAddress,result); - if (result!=TARGET_NATIVE_OK) + result = cpnet_dropMembership (env, fd, netAddress); + if (result != CPNATIVE_OK) { - JCL_ThrowException(env, IO_EXCEPTION, TARGET_NATIVE_LAST_ERROR_STRING()); + JCL_ThrowException(env, IO_EXCEPTION, cpnative_getErrorString (result)); return; } diff --git a/native/jni/java-net/gnu_java_net_VMPlainSocketImpl.c b/native/jni/java-net/gnu_java_net_VMPlainSocketImpl.c index 3d48b9195..fb9855908 100644 --- a/native/jni/java-net/gnu_java_net_VMPlainSocketImpl.c +++ b/native/jni/java-net/gnu_java_net_VMPlainSocketImpl.c @@ -45,14 +45,10 @@ exception statement from your version. */ #include <jni.h> #include <jcl.h> +#include <cpnative.h> +#include <cpnet.h> #include "javanet.h" -#include "target_native.h" -#ifndef WITHOUT_NETWORK - #include "target_native_file.h" /* Get FIONREAD on Solaris. */ - #include "target_native_network.h" -#endif /* WITHOUT_NETWORK */ - #include "gnu_java_net_VMPlainSocketImpl.h" /* @@ -195,10 +191,10 @@ Java_gnu_java_net_VMPlainSocketImpl_available(JNIEnv *env, fd = (*env)->GetIntField(env, obj, fid); - TARGET_NATIVE_NETWORK_SOCKET_RECEIVE_AVAILABLE(fd,bytesAvailable,result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getAvailableBytes (env, fd, &bytesAvailable); + if (result != CPNATIVE_OK) { - JCL_ThrowException(env, IO_EXCEPTION, TARGET_NATIVE_LAST_ERROR_STRING()); + JCL_ThrowException(env, IO_EXCEPTION, cpnative_getErrorString (result)); return 0; } @@ -255,7 +251,7 @@ Java_gnu_java_net_VMPlainSocketImpl_read(JNIEnv *env, jint offset, jint len) { #ifndef WITHOUT_NETWORK - return(_javanet_recvfrom(env, obj, buf, offset, len, 0, 0)); + return(_javanet_recvfrom(env, obj, buf, offset, len, 0)); #else /* not WITHOUT_NETWORK */ return 0; #endif /* not WITHOUT_NETWORK */ @@ -273,7 +269,7 @@ Java_gnu_java_net_VMPlainSocketImpl_write(JNIEnv *env, jint offset, jint len) { #ifndef WITHOUT_NETWORK - _javanet_sendto(env, obj, buf, offset, len, 0, 0); + _javanet_sendto(env, obj, buf, offset, len, 0); #else /* not WITHOUT_NETWORK */ #endif /* not WITHOUT_NETWORK */ } diff --git a/native/jni/java-net/gnu_java_net_local_LocalSocketImpl.c b/native/jni/java-net/gnu_java_net_local_LocalSocketImpl.c index 35fb6bcdc..f2b2f8e63 100644 --- a/native/jni/java-net/gnu_java_net_local_LocalSocketImpl.c +++ b/native/jni/java-net/gnu_java_net_local_LocalSocketImpl.c @@ -38,9 +38,7 @@ exception statement from your version. */ #define _GNU_SOURCE -#ifdef HAVE_CONFIG_H #include "config.h" -#endif /* HAVE_CONFIG_H */ #include <gnu_java_net_local_LocalSocketImpl.h> diff --git a/native/jni/java-net/java_net_VMInetAddress.c b/native/jni/java-net/java_net_VMInetAddress.c index 86ac06e6f..2ca0545fe 100644 --- a/native/jni/java-net/java_net_VMInetAddress.c +++ b/native/jni/java-net/java_net_VMInetAddress.c @@ -45,13 +45,10 @@ exception statement from your version. */ #include <jni.h> #include <jcl.h> +#include "cpnative.h" +#include "cpnet.h" #include "javanet.h" -#include "target_native.h" -#ifndef WITHOUT_NETWORK -#include "target_native_network.h" -#endif /* WITHOUT_NETWORK */ - #include "java_net_VMInetAddress.h" /*************************************************************************/ @@ -69,8 +66,8 @@ Java_java_net_VMInetAddress_getLocalHostname (JNIEnv * env, jstring retval; #ifndef WITHOUT_NETWORK - TARGET_NATIVE_NETWORK_GET_HOSTNAME (hostname, sizeof (hostname), result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getHostname (env, hostname, sizeof (hostname)); + if (result != CPNATIVE_OK) { strcpy (hostname, "localhost"); } @@ -94,6 +91,7 @@ Java_java_net_VMInetAddress_lookupInaddrAny (JNIEnv * env, __attribute__ ((__unused__))) { jarray IParray; + cpnet_address *addr; jbyte *octets; /* Allocate an array for the IP address */ @@ -108,11 +106,10 @@ Java_java_net_VMInetAddress_lookupInaddrAny (JNIEnv * env, octets = (*env)->GetByteArrayElements (env, IParray, 0); #ifndef WITHOUT_NETWORK - TARGET_NATIVE_NETWORK_INT_TO_IPADDRESS_BYTES (INADDR_ANY, - octets[0], - octets[1], - octets[2], octets[3]); - (*env)->ReleaseByteArrayElements (env, IParray, octets, 0); + addr = cpnet_newIPV4Address (env); + cpnet_setIPV4Any (addr); + cpnet_IPV4AddressToBytes (addr, octets); + cpnet_freeAddress (env, addr); #else /* not WITHOUT_NETWORK */ octets[0] = 0; octets[1] = 0; @@ -120,6 +117,8 @@ Java_java_net_VMInetAddress_lookupInaddrAny (JNIEnv * env, octets[3] = 0; #endif /* not WITHOUT_NETWORK */ + (*env)->ReleaseByteArrayElements (env, IParray, octets, 0); + return (IParray); } @@ -138,14 +137,14 @@ Java_java_net_VMInetAddress_getHostByAddr (JNIEnv * env, #ifndef WITHOUT_NETWORK jbyte *octets; jsize len; - int addr; + cpnet_address *addr; char hostname[255]; int result; jstring retval; /* Grab the byte[] array with the IP out of the input data */ len = (*env)->GetArrayLength (env, arr); - if (len != 4) + if (len != 4 && len != 16) { JCL_ThrowException (env, UNKNOWN_HOST_EXCEPTION, "Bad IP Address"); return (jstring) NULL; @@ -158,21 +157,31 @@ Java_java_net_VMInetAddress_getHostByAddr (JNIEnv * env, return (jstring) NULL; } - /* Convert it to a 32 bit address */ - TARGET_NATIVE_NETWORK_IPADDRESS_BYTES_TO_INT (octets[0], - octets[1], - octets[2], octets[3], addr); + switch (len) + { + case 4: + addr = cpnet_newIPV4Address(env); + cpnet_bytesToIPV4Address (addr, octets); + break; + case 16: + addr = cpnet_newIPV6Address(env); + cpnet_bytesToIPV6Address (addr, octets); + break; + default: + JCL_ThrowException (env, UNKNOWN_HOST_EXCEPTION, "Bad IP Address"); + return (jstring) NULL; + + } /* Release some memory */ (*env)->ReleaseByteArrayElements (env, arr, octets, 0); /* Resolve the address and return the name */ - TARGET_NATIVE_NETWORK_GET_HOSTNAME_BY_ADDRESS (addr, hostname, - sizeof (hostname), result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getHostByAddr (env, addr, hostname, sizeof (hostname)); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, UNKNOWN_HOST_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return (jstring) NULL; } @@ -194,16 +203,14 @@ Java_java_net_VMInetAddress_getHostByName (JNIEnv * env, { #ifndef WITHOUT_NETWORK const char *hostname; -/* FIXME: limitation of max. 64 addresses - how to make it more flexibale? */ - int addresses[64]; + cpnet_address **addresses; jsize addresses_count; int result; jclass arr_class; jobjectArray addrs; - int i; + jint i; jbyte *octets; jarray ret_octets; - int max_addresses; /* Grab the hostname string */ hostname = (*env)->GetStringUTFChars (env, host, 0); @@ -213,12 +220,8 @@ Java_java_net_VMInetAddress_getHostByName (JNIEnv * env, return (jobjectArray) NULL; } - max_addresses = sizeof (addresses) / sizeof (addresses[0]); - TARGET_NATIVE_NETWORK_GET_HOSTNAME_BY_NAME (hostname, - addresses, - max_addresses, - addresses_count, result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getHostByName (env, hostname, &addresses, &addresses_count); + if (result != CPNATIVE_OK || addresses_count == 0) { JCL_ThrowException (env, UNKNOWN_HOST_EXCEPTION, (char *) hostname); return (jobjectArray) NULL; @@ -242,25 +245,54 @@ Java_java_net_VMInetAddress_getHostByName (JNIEnv * env, /* Now loop and copy in each address */ for (i = 0; i < addresses_count; i++) { - ret_octets = (*env)->NewByteArray (env, 4); - if (!ret_octets) + if (cpnet_isIPV6Address (addresses[i])) { - JCL_ThrowException (env, UNKNOWN_HOST_EXCEPTION, "Internal Error"); - return (jobjectArray) NULL; + ret_octets = (*env)->NewByteArray (env, 16); + + if (!ret_octets) + { + JCL_ThrowException (env, UNKNOWN_HOST_EXCEPTION, "Internal Error"); + cpnet_freeAddresses (env, addresses, addresses_count); + return (jobjectArray) NULL; + } + + octets = (*env)->GetByteArrayElements (env, ret_octets, 0); + + cpnet_IPV6AddressToBytes (addresses[i], octets); + + (*env)->ReleaseByteArrayElements (env, ret_octets, octets, 0); + + (*env)->SetObjectArrayElement (env, addrs, i, ret_octets); } + else if (cpnet_isIPV4Address (addresses[i])) + { + ret_octets = (*env)->NewByteArray (env, 4); - octets = (*env)->GetByteArrayElements (env, ret_octets, 0); + if (!ret_octets) + { + JCL_ThrowException (env, UNKNOWN_HOST_EXCEPTION, "Internal Error"); + cpnet_freeAddresses (env, addresses, addresses_count); + return (jobjectArray) NULL; + } + + octets = (*env)->GetByteArrayElements (env, ret_octets, 0); - TARGET_NATIVE_NETWORK_INT_TO_IPADDRESS_BYTES (addresses[i], - octets[0], - octets[1], - octets[2], octets[3]); + cpnet_IPV4AddressToBytes (addresses[i], octets); - (*env)->ReleaseByteArrayElements (env, ret_octets, octets, 0); + (*env)->ReleaseByteArrayElements (env, ret_octets, octets, 0); - (*env)->SetObjectArrayElement (env, addrs, i, ret_octets); + (*env)->SetObjectArrayElement (env, addrs, i, ret_octets); + } + else + { + JCL_ThrowException (env, UNKNOWN_HOST_EXCEPTION, "Internal Error"); + cpnet_freeAddresses (env, addresses, addresses_count); + return (jobjectArray) NULL; + } } + cpnet_freeAddresses (env, addresses, addresses_count); + return (addrs); #else /* not WITHOUT_NETWORK */ return (jobjectArray) NULL; diff --git a/native/jni/java-net/javanet.c b/native/jni/java-net/javanet.c index e500c6084..a8213044f 100644 --- a/native/jni/java-net/javanet.c +++ b/native/jni/java-net/javanet.c @@ -45,12 +45,10 @@ exception statement from your version. */ #include <jni.h> #include <jcl.h> -#include "javanet.h" +#include "cpnative.h" +#include "cpnet.h" -#include "target_native.h" -#ifndef WITHOUT_NETWORK -#include "target_native_network.h" -#endif /* WITHOUT_NETWORK */ +#include "javanet.h" #ifndef WITHOUT_NETWORK /* Need to have some value for SO_TIMEOUT */ @@ -235,22 +233,19 @@ _javanet_create_integer (JNIEnv * env, jint val) * Builds an InetAddress object from a 32 bit address in host byte order */ static jobject -_javanet_create_inetaddress (JNIEnv * env, int netaddr) +_javanet_create_inetaddress (JNIEnv * env, cpnet_address *netaddr) { #ifndef WITHOUT_NETWORK - unsigned char octets[4]; - char buf[16]; + jbyte octets[4]; + char buf[64]; jclass ia_cls; jmethodID mid; jstring ip_str; jobject ia; /* Build a string IP address */ - TARGET_NATIVE_NETWORK_INT_TO_IPADDRESS_BYTES (netaddr, - octets[0], - octets[1], - octets[2], octets[3]); - sprintf (buf, "%d.%d.%d.%d", octets[0], octets[1], octets[2], octets[3]); + cpnet_IPV4AddressToBytes(netaddr, octets); + sprintf (buf, "%d.%d.%d.%d", (int) (unsigned char)octets[0], (int)(unsigned char)octets[1], (int)(unsigned char)octets[2], (int)(unsigned char)octets[3]); DBG ("_javanet_create_inetaddress(): Created ip addr string\n"); /* Get an InetAddress object for this IP */ @@ -320,7 +315,7 @@ _javanet_set_remhost_addr (JNIEnv * env, jobject this, jobject ia) * InetAddress for the specified addr */ static void -_javanet_set_remhost (JNIEnv * env, jobject this, int netaddr) +_javanet_set_remhost (JNIEnv * env, jobject this, cpnet_address *netaddr) { jobject ia; @@ -338,19 +333,20 @@ _javanet_set_remhost (JNIEnv * env, jobject this, int netaddr) /*************************************************************************/ /* - * Returns a 32 bit Internet address for the passed in InetAddress object + * Returns an Internet address for the passed in InetAddress object */ -int -_javanet_get_netaddr (JNIEnv * env, jobject addr) +cpnet_address * +_javanet_get_ip_netaddr (JNIEnv * env, jobject addr) { #ifndef WITHOUT_NETWORK jclass cls = 0; jmethodID mid; jarray arr = 0; jbyte *octets; - int netaddr, len; + cpnet_address *netaddr; + jint len; - DBG ("_javanet_get_netaddr(): Entered _javanet_get_netaddr\n"); + DBG ("_javanet_get_ip_netaddr(): Entered _javanet_get_ip_netaddr\n"); if (addr == NULL) { @@ -368,36 +364,51 @@ _javanet_get_netaddr (JNIEnv * env, jobject addr) if (mid == NULL) return 0; - DBG ("_javanet_get_netaddr(): Got getAddress method\n"); + DBG ("_javanet_get_ip_netaddr(): Got getAddress method\n"); arr = (*env)->CallObjectMethod (env, addr, mid); if (arr == NULL) return 0; - DBG ("_javanet_get_netaddr(): Got the address\n"); + DBG ("_javanet_get_ip_netaddr(): Got the address\n"); - /* Turn the IP address into a 32 bit Internet address in network byte order */ + /* Turn the IP address into a system cpnet address. + * If the length is 4 then it is an IPV4 address, if it + * is 16 then it is an IPV6 address else it is an InternError. */ len = (*env)->GetArrayLength (env, arr); - if (len != 4) + if (len != 4 && len != 16) { JCL_ThrowException (env, IO_EXCEPTION, "Internal Error"); return 0; } - DBG ("_javanet_get_netaddr(): Length ok\n"); + DBG ("_javanet_get_ip_netaddr(): Length ok\n"); octets = (*env)->GetByteArrayElements (env, arr, 0); if (octets == NULL) return 0; - DBG ("_javanet_get_netaddr(): Grabbed bytes\n"); + DBG ("_javanet_get_ip_netaddr(): Grabbed bytes\n"); - TARGET_NATIVE_NETWORK_IPADDRESS_BYTES_TO_INT (octets[0], - octets[1], - octets[2], - octets[3], netaddr); + switch (len) + { + case 4: + netaddr = cpnet_newIPV4Address(env); + cpnet_bytesToIPV4Address(netaddr, octets); + break; + case 16: + netaddr = cpnet_newIPV6Address(env); + cpnet_bytesToIPV6Address(netaddr, octets); + break; + default: + /* This should not happen as we have checked before. + * But that way we shut the compiler warnings */ + JCL_ThrowException (env, IO_EXCEPTION, "Internal Error"); + return 0; + + } (*env)->ReleaseByteArrayElements (env, arr, octets, 0); - DBG ("_javanet_get_netaddr(): Done getting addr\n"); + DBG ("_javanet_get_ip_netaddr(): Done getting addr\n"); return netaddr; #else /* not WITHOUT_NETWORK */ @@ -419,29 +430,29 @@ _javanet_create (JNIEnv * env, jobject this, jboolean stream) if (stream) { /* create a stream socket */ - TARGET_NATIVE_NETWORK_SOCKET_OPEN_STREAM (fd, result); - if (result != TARGET_NATIVE_OK) + result = cpnet_openSocketStream(env, &fd, AF_INET); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return; } } else { /* create a datagram socket, set broadcast option */ - TARGET_NATIVE_NETWORK_SOCKET_OPEN_DATAGRAM (fd, result); - if (result != TARGET_NATIVE_OK) + result = cpnet_openSocketDatagram (env, &fd, AF_INET); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return; } - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_BROADCAST (fd, 1, result); - if (result != TARGET_NATIVE_OK) + result = cpnet_setBroadcast(env, fd, 1); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return; } } @@ -458,13 +469,11 @@ _javanet_create (JNIEnv * env, jobject this, jboolean stream) /* Try to make sure we close the socket since close() won't work. */ do { - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (fd, result); - if (result != TARGET_NATIVE_OK - && (TARGET_NATIVE_LAST_ERROR () - != TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL)) + result = cpnet_close(env, fd); + if (result != CPNATIVE_OK && result != CPNATIVE_EINTR) return; } - while (result != TARGET_NATIVE_OK); + while (result != CPNATIVE_OK); return; } @@ -498,18 +507,16 @@ _javanet_close (JNIEnv * env, jobject this, int stream) "native_fd", -1); do { - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (fd, result); - if (result != TARGET_NATIVE_OK) + result = cpnet_close (env, fd); + if (result != CPNATIVE_OK) { /* Only throw an error when a "real" error occurs. */ - error = TARGET_NATIVE_LAST_ERROR (); - if (error != TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL - && error != ENOTCONN && error != ECONNRESET && error != EBADF) + if (result != CPNATIVE_EINTR && result != ENOTCONN && result != ECONNRESET && result != EBADF) JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); } } - while (error == TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL); + while (error == CPNATIVE_EINTR); #else /* not WITHOUT_NETWORK */ #endif /* not WITHOUT_NETWORK */ @@ -525,20 +532,24 @@ _javanet_connect (JNIEnv * env, jobject this, jobject addr, jint port, jboolean stream) { #ifndef WITHOUT_NETWORK - int netaddr, fd; + cpnet_address *netaddr; + int fd; int result; - int local_address, local_port; - int remote_address, remote_port; + cpnet_address *local_addr; + cpnet_address *remote_addr; DBG ("_javanet_connect(): Entered _javanet_connect\n"); /* Pre-process input variables */ - netaddr = _javanet_get_netaddr (env, addr); + netaddr = _javanet_get_ip_netaddr (env, addr); if ((*env)->ExceptionOccurred (env)) return; if (port == -1) port = 0; + + cpnet_addressSetPort(netaddr, port); + DBG ("_javanet_connect(): Got network address\n"); /* Grab the real socket file descriptor */ @@ -554,29 +565,27 @@ _javanet_connect (JNIEnv * env, jobject this, jobject addr, jint port, /* Connect up */ do { - TARGET_NATIVE_NETWORK_SOCKET_CONNECT (fd, netaddr, port, result); - if (result != TARGET_NATIVE_OK - && (TARGET_NATIVE_LAST_ERROR () - != TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL)) + result = cpnet_connect (env, fd, netaddr); + if (result != CPNATIVE_OK && result != CPNATIVE_EINTR) { JCL_ThrowException (env, CONNECT_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return; } } - while (result != TARGET_NATIVE_OK); - + while (result != CPNATIVE_OK); + DBG ("_javanet_connect(): Connected successfully\n"); /* Populate instance variables */ - TARGET_NATIVE_NETWORK_SOCKET_GET_LOCAL_INFO (fd, local_address, local_port, - result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getLocalAddr (env, fd, &local_addr); + if (result != CPNATIVE_OK) { + cpnet_freeAddress(env, netaddr); JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); /* We don't care whether this succeeds. close() will cleanup later. */ - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (fd, result); + cpnet_close (env, fd); return; } @@ -584,63 +593,72 @@ _javanet_connect (JNIEnv * env, jobject this, jobject addr, jint port, if ((*env)->ExceptionOccurred (env)) { /* We don't care whether this succeeds. close() will cleanup later. */ - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (fd, result); + cpnet_freeAddress(env, netaddr); + cpnet_freeAddress(env, local_addr); + cpnet_close (env, fd); return; } DBG ("_javanet_connect(): Created fd\n"); if (stream) _javanet_set_int_field (env, this, "java/net/SocketImpl", "localport", - local_port); + cpnet_addressGetPort(local_addr)); else _javanet_set_int_field (env, this, "java/net/DatagramSocketImpl", - "localPort", local_port); + "localPort", cpnet_addressGetPort(local_addr)); + cpnet_freeAddress (env, local_addr); if ((*env)->ExceptionOccurred (env)) { /* We don't care whether this succeeds. close() will cleanup later. */ - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (fd, result); + cpnet_freeAddress(env, netaddr); + cpnet_close (env, fd); return; } DBG ("_javanet_connect(): Set the local port\n"); - TARGET_NATIVE_NETWORK_SOCKET_GET_REMOTE_INFO (fd, remote_address, - remote_port, result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getRemoteAddr (env, fd, &remote_addr); + if (result != CPNATIVE_OK) { + cpnet_freeAddress(env, netaddr); JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); /* We don't care whether this succeeds. close() will cleanup later. */ - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (fd, result); + cpnet_close (env, fd); return; } if (stream) { - if (remote_address == netaddr) + if (cpnet_isAddressEqual(remote_addr, netaddr)) { _javanet_set_remhost_addr (env, this, addr); } else { - _javanet_set_remhost (env, this, remote_address); + _javanet_set_remhost (env, this, remote_addr); } + cpnet_freeAddress(env, netaddr); + if ((*env)->ExceptionOccurred (env)) { /* We don't care whether this succeeds. close() will cleanup later. */ - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (fd, result); + cpnet_freeAddress (env, remote_addr); + cpnet_close (env, fd); return; } DBG ("_javanet_connect(): Set the remote host\n"); _javanet_set_int_field (env, this, "java/net/SocketImpl", "port", - remote_port); + cpnet_addressGetPort(remote_addr)); + cpnet_freeAddress (env, remote_addr); + if ((*env)->ExceptionOccurred (env)) { /* We don't care whether this succeeds. close() will cleanup later. */ - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (fd, result); + cpnet_close (env, fd); return; } DBG ("_javanet_connect(): Set the remote port\n"); @@ -661,98 +679,59 @@ _javanet_bind (JNIEnv * env, jobject this, jobject addr, jint port, int stream) { #ifndef WITHOUT_NETWORK - jclass cls; - jmethodID mid; - jbyteArray arr = 0; - jbyte *octets; jint fd; - int tmpaddr; + cpnet_address *tmpaddr; + cpnet_address *local_addr; int result; - int local_address, local_port; DBG ("_javanet_bind(): Entering native bind()\n"); - - /* Get the address to connect to */ - cls = (*env)->GetObjectClass (env, addr); - if (cls == NULL) - return; - - mid = (*env)->GetMethodID (env, cls, "getAddress", "()[B"); - if (mid == NULL) - return; - - DBG ("_javanet_bind(): Past getAddress method id\n"); - - arr = (*env)->CallObjectMethod (env, addr, mid); - if ((arr == NULL) || (*env)->ExceptionOccurred (env)) - { - JCL_ThrowException (env, IO_EXCEPTION, - "Internal error: _javanet_bind()"); - return; - } - - DBG ("_javanet_bind(): Past call object method\n"); - - octets = (*env)->GetByteArrayElements (env, arr, 0); - if (octets == NULL) - return; - - DBG ("_javanet_bind(): Past grab array\n"); - - /* Get the native socket file descriptor */ + + /* Grab the real socket file descriptor */ fd = _javanet_get_int_field (env, this, "native_fd"); if (fd == -1) { - (*env)->ReleaseByteArrayElements (env, arr, octets, 0); JCL_ThrowException (env, IO_EXCEPTION, - "Internal error: _javanet_bind(): no native file descriptor"); + "Internal error: _javanet_connect(): no native file descriptor"); return; } - DBG ("_javanet_bind(): Past native_fd lookup\n"); - - /* XXX NYI ??? */ - _javanet_set_option (env, this, SOCKOPT_SO_REUSEADDR, - _javanet_create_boolean (env, JNI_TRUE)); + cpnet_setReuseAddress (env, fd, 1); - /* Bind the socket */ - TARGET_NATIVE_NETWORK_IPADDRESS_BYTES_TO_INT (octets[0], - octets[1], - octets[2], - octets[3], tmpaddr); - TARGET_NATIVE_NETWORK_SOCKET_BIND (fd, tmpaddr, port, result); + /* Get the address to connect to */ + tmpaddr = _javanet_get_ip_netaddr (env, addr); + if ((*env)->ExceptionOccurred (env)) + return; - if (result != TARGET_NATIVE_OK) + cpnet_addressSetPort (tmpaddr, port); + result = cpnet_bind(env, fd, tmpaddr); + cpnet_freeAddress (env, tmpaddr); + if (result != CPNATIVE_OK) { - char *errorstr = TARGET_NATIVE_LAST_ERROR_STRING (); - (*env)->ReleaseByteArrayElements (env, arr, octets, 0); - JCL_ThrowException (env, BIND_EXCEPTION, - errorstr); + cpnative_getErrorString (result)); return; } DBG ("_javanet_bind(): Past bind\n"); - (*env)->ReleaseByteArrayElements (env, arr, octets, 0); - /* Update instance variables, specifically the local port number */ - TARGET_NATIVE_NETWORK_SOCKET_GET_LOCAL_INFO (fd, local_address, local_port, - result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getLocalAddr (env, fd, &local_addr); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return; } if (stream) _javanet_set_int_field (env, this, "java/net/SocketImpl", - "localport", local_port); + "localport", cpnet_addressGetPort (local_addr)); else _javanet_set_int_field (env, this, "java/net/DatagramSocketImpl", - "localPort", local_port); + "localPort", cpnet_addressGetPort (local_addr)); DBG ("_javanet_bind(): Past update port number\n"); + cpnet_freeAddress (env, local_addr); + return; #else /* not WITHOUT_NETWORK */ #endif /* not WITHOUT_NETWORK */ @@ -781,11 +760,11 @@ _javanet_listen (JNIEnv * env, jobject this, jint queuelen) } /* Start listening */ - TARGET_NATIVE_NETWORK_SOCKET_LISTEN (fd, queuelen, result); - if (result != TARGET_NATIVE_OK) + result = cpnet_listen (env, fd, queuelen); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return; } #else /* not WITHOUT_NETWORK */ @@ -804,8 +783,7 @@ _javanet_accept (JNIEnv * env, jobject this, jobject impl) #ifndef WITHOUT_NETWORK int fd, newfd; int result; - int local_address, local_port; - int remote_address, remote_port; + cpnet_address *remote_addr, *local_addr; /* Get the real file descriptor */ fd = _javanet_get_int_field (env, this, "native_fd"); @@ -819,24 +797,22 @@ _javanet_accept (JNIEnv * env, jobject this, jobject impl) /* Accept the connection */ do { - TARGET_NATIVE_NETWORK_SOCKET_ACCEPT (fd, newfd, result); - if (result != TARGET_NATIVE_OK - && (TARGET_NATIVE_LAST_ERROR () - != TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL)) + result = cpnet_accept (env, fd, &newfd); + if (result != CPNATIVE_OK && result != CPNATIVE_EINTR) { - if (TARGET_NATIVE_LAST_ERROR () == EAGAIN) + if (result == ETIMEDOUT || result == EAGAIN) JCL_ThrowException (env, "java/net/SocketTimeoutException", - "Timeout"); + "Accept operation timed out"); else JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return; } } - while (result != TARGET_NATIVE_OK); + while (result != CPNATIVE_OK); /* Reset the inherited timeout. */ - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_SO_TIMEOUT (newfd, 0, result); + cpnet_setSocketTimeout (env, newfd, 0); /* Populate instance variables */ _javanet_set_int_field (env, impl, "gnu/java/net/PlainSocketImpl", @@ -847,24 +823,21 @@ _javanet_accept (JNIEnv * env, jobject this, jobject impl) /* Try to make sure we close the socket since close() won't work. */ do { - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (newfd, result); - if (result != TARGET_NATIVE_OK - && (TARGET_NATIVE_LAST_ERROR () - != TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL)) + result = cpnet_close (env, newfd); + if (result != CPNATIVE_OK && result != CPNATIVE_EINTR) return; } - while (result != TARGET_NATIVE_OK); + while (result != CPNATIVE_OK); return; } - TARGET_NATIVE_NETWORK_SOCKET_GET_LOCAL_INFO (newfd, local_address, - local_port, result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getLocalAddr (env, newfd, &local_addr); + if (result != CPNATIVE_OK) { /* We don't care whether this succeeds. close() will cleanup later. */ - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (newfd, result); + cpnet_close (env, newfd); JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return; } @@ -872,44 +845,47 @@ _javanet_accept (JNIEnv * env, jobject this, jobject impl) if ((*env)->ExceptionOccurred (env)) { /* We don't care whether this succeeds. close() will cleanup later. */ - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (newfd, result); + cpnet_freeAddress (env, local_addr); + cpnet_close (env, newfd); return; } _javanet_set_int_field (env, impl, "java/net/SocketImpl", "localport", - local_port); + cpnet_addressGetPort (local_addr)); + cpnet_freeAddress (env, local_addr); if ((*env)->ExceptionOccurred (env)) { /* We don't care whether this succeeds. close() will cleanup later. */ - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (newfd, result); + cpnet_close (env, newfd); return; } - TARGET_NATIVE_NETWORK_SOCKET_GET_REMOTE_INFO (newfd, remote_address, - remote_port, result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getRemoteAddr (env, newfd, &remote_addr); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); /* We don't care whether this succeeds. close() will cleanup later. */ - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (newfd, result); + cpnet_close (env, newfd); return; } - _javanet_set_remhost (env, impl, remote_address); + _javanet_set_remhost (env, impl, remote_addr); if ((*env)->ExceptionOccurred (env)) { /* We don't care whether this succeeds. close() will cleanup later. */ - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (newfd, result); + cpnet_close (env, newfd); + cpnet_freeAddress (env, remote_addr); return; } _javanet_set_int_field (env, impl, "java/net/SocketImpl", "port", - remote_port); + cpnet_addressGetPort (remote_addr)); + cpnet_freeAddress (env, remote_addr); if ((*env)->ExceptionOccurred (env)) { /* We don't care whether this succeeds. close() will cleanup later. */ - TARGET_NATIVE_NETWORK_SOCKET_CLOSE (newfd, result); + cpnet_close (env, newfd); return; } #else /* not WITHOUT_NETWORK */ @@ -934,13 +910,14 @@ _javanet_accept (JNIEnv * env, jobject this, jobject impl) */ int _javanet_recvfrom (JNIEnv * env, jobject this, jarray buf, int offset, - int len, int *addr, int *port) + int len, cpnet_address **addr) { #ifndef WITHOUT_NETWORK int fd; jbyte *p; - int from_address, from_port; - int received_bytes; + cpnet_address *from_addr; + jint received_bytes; + int result; DBG ("_javanet_recvfrom(): Entered _javanet_recvfrom\n"); @@ -962,36 +939,26 @@ _javanet_recvfrom (JNIEnv * env, jobject this, jarray buf, int offset, DBG ("_javanet_recvfrom(): Got buffer\n"); /* Read the data */ - from_address = 0; - from_port = 0; + from_addr = NULL; do { if (addr != NULL) { - TARGET_NATIVE_NETWORK_SOCKET_RECEIVE_WITH_ADDRESS_PORT (fd, - p + offset, - len, - from_address, - from_port, - received_bytes); + result = cpnet_recvFrom (env, fd, p + offset, len, &from_addr, &received_bytes); } else { - TARGET_NATIVE_NETWORK_SOCKET_RECEIVE (fd, p + offset, len, - received_bytes); + result = cpnet_recv (env, fd, p + offset, len, &received_bytes); } } - while ((received_bytes == -1) && - (TARGET_NATIVE_LAST_ERROR () == - TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL)); - - if (received_bytes == -1) + while (result == CPNATIVE_EINTR); + if (result != 0) { - if (TARGET_NATIVE_LAST_ERROR () == EAGAIN) - JCL_ThrowException (env, "java/net/SocketTimeoutException", "Timeout"); + if (result == EAGAIN || result == ETIMEDOUT) + JCL_ThrowException (env, "java/net/SocketTimeoutException", "Receive operation timed out"); else JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); /* Cleanup and return. */ (*env)->ReleaseByteArrayElements (env, buf, p, 0); @@ -1003,9 +970,7 @@ _javanet_recvfrom (JNIEnv * env, jobject this, jarray buf, int offset, /* Handle return addr case */ if (addr != NULL) { - (*addr) = from_address; - if (port != NULL) - (*port) = from_port; + (*addr) = from_addr; } /* zero bytes received means recv() noticed the other side orderly @@ -1013,7 +978,7 @@ _javanet_recvfrom (JNIEnv * env, jobject this, jarray buf, int offset, if (received_bytes == 0) received_bytes = -1; - return (received_bytes); + return received_bytes; #else /* not WITHOUT_NETWORK */ #endif /* not WITHOUT_NETWORK */ } @@ -1031,12 +996,13 @@ _javanet_recvfrom (JNIEnv * env, jobject this, jarray buf, int offset, */ void _javanet_sendto (JNIEnv * env, jobject this, jarray buf, int offset, int len, - int addr, int port) + cpnet_address *addr) { #ifndef WITHOUT_NETWORK int fd; jbyte *p; - int bytes_sent; + jint bytes_sent; + int result; /* Get the real file descriptor */ fd = _javanet_get_int_field (env, this, "native_fd"); @@ -1056,26 +1022,30 @@ _javanet_sendto (JNIEnv * env, jobject this, jarray buf, int offset, int len, while (len > 0) { /* Send the data */ - if (addr == 0) + if (addr == NULL) { DBG ("_javanet_sendto(): Sending....\n"); - TARGET_NATIVE_NETWORK_SOCKET_SEND (fd, p + offset, len, bytes_sent); + result = cpnet_send (env, fd, p + offset, len, &bytes_sent); } else { DBG ("_javanet_sendto(): Sending....\n"); - TARGET_NATIVE_NETWORK_SOCKET_SEND_WITH_ADDRESS_PORT (fd, p + offset, - len, addr, port, - bytes_sent); + result = cpnet_sendTo (env, fd, p + offset, len, addr, &bytes_sent); } + if (result == EDESTADDRREQ) + { + JCL_ThrowException (env, NULL_EXCEPTION, + "Socket is not connected and no address is given"); + break; + } + if (bytes_sent < 0) { - if (TARGET_NATIVE_LAST_ERROR () - != TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL) + if (result != CPNATIVE_EINTR) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); break; } } @@ -1105,8 +1075,8 @@ _javanet_set_option (JNIEnv * env, jobject this, jint option_id, jobject val) int optval; jclass cls; jmethodID mid; - int address; - int result; + cpnet_address * address; + int result = CPNATIVE_OK; /* Get the real file descriptor */ fd = _javanet_get_int_field (env, this, "native_fd"); @@ -1123,7 +1093,6 @@ _javanet_set_option (JNIEnv * env, jobject this, jint option_id, jobject val) return; /* Process the option request */ - result = TARGET_NATIVE_ERROR; switch (option_id) { /* TCP_NODELAY case. val is a Boolean that tells us what to do */ @@ -1141,8 +1110,7 @@ _javanet_set_option (JNIEnv * env, jobject this, jint option_id, jobject val) if ((*env)->ExceptionOccurred (env)) return; - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_TCP_NODELAY (fd, optval, - result); + result = cpnet_setSocketTCPNoDelay (env, fd, optval); break; /* SO_LINGER case. If val is a boolean, then it will always be set @@ -1153,8 +1121,7 @@ _javanet_set_option (JNIEnv * env, jobject this, jint option_id, jobject val) if (mid) { /* We are disabling linger */ - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_SO_LINGER (fd, 1, 0, - result); + result = cpnet_setLinger (env, fd, JNI_FALSE, 0); } else { @@ -1175,15 +1142,13 @@ _javanet_set_option (JNIEnv * env, jobject this, jint option_id, jobject val) if ((*env)->ExceptionOccurred (env)) return; - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_SO_LINGER (fd, 0, optval, - result); + result = cpnet_setLinger(env, fd, JNI_TRUE, optval); } break; /* SO_TIMEOUT case. Val will be an integer with the new value */ /* Not writable on Linux */ case SOCKOPT_SO_TIMEOUT: -#ifdef SO_TIMEOUT mid = (*env)->GetMethodID (env, cls, "intValue", "()I"); if (mid == NULL) { @@ -1196,10 +1161,7 @@ _javanet_set_option (JNIEnv * env, jobject this, jint option_id, jobject val) if ((*env)->ExceptionOccurred (env)) return; - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_SO_TIMEOUT (fd, optval, result); -#else - result = TARGET_NATIVE_OK; -#endif + result = cpnet_setSocketTimeout (env, fd, optval); break; case SOCKOPT_SO_SNDBUF: @@ -1218,11 +1180,9 @@ _javanet_set_option (JNIEnv * env, jobject this, jint option_id, jobject val) return; if (option_id == SOCKOPT_SO_SNDBUF) - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_SO_SNDBUF (fd, optval, - result); + result = cpnet_setSendBuf (env, fd, optval); else - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_SO_RCDBUF (fd, optval, - result); + result = cpnet_setRecvBuf (env, fd, optval); break; /* TTL case. Val with be an Integer with the new time to live value */ @@ -1239,18 +1199,18 @@ _javanet_set_option (JNIEnv * env, jobject this, jint option_id, jobject val) if ((*env)->ExceptionOccurred (env)) return; - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_IP_TTL (fd, optval, result); + result = cpnet_setTTL (env, fd, optval); break; /* Multicast Interface case - val is InetAddress object */ case SOCKOPT_IP_MULTICAST_IF: - address = _javanet_get_netaddr (env, val); + address = _javanet_get_ip_netaddr (env, val); if ((*env)->ExceptionOccurred (env)) return; - - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_IP_MULTICAST_IF (fd, address, - result); + + result = cpnet_setMulticastIF (env, fd, address); + cpnet_freeAddress (env, address); break; case SOCKOPT_SO_REUSEADDR: @@ -1267,8 +1227,7 @@ _javanet_set_option (JNIEnv * env, jobject this, jint option_id, jobject val) if ((*env)->ExceptionOccurred (env)) return; - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_REUSE_ADDRESS (fd, optval, - result); + result = cpnet_setReuseAddress (env, fd, optval); break; case SOCKOPT_SO_KEEPALIVE: @@ -1285,7 +1244,7 @@ _javanet_set_option (JNIEnv * env, jobject this, jint option_id, jobject val) if ((*env)->ExceptionOccurred (env)) return; - TARGET_NATIVE_NETWORK_SOCKET_SET_OPTION_KEEP_ALIVE (fd, optval, result); + result = cpnet_setKeepAlive (env, fd, optval); break; case SOCKOPT_SO_BINDADDR: @@ -1298,10 +1257,10 @@ _javanet_set_option (JNIEnv * env, jobject this, jint option_id, jobject val) } /* Check to see if above operations succeeded */ - if (result != TARGET_NATIVE_OK) + if (result != CPNATIVE_OK) { JCL_ThrowException (env, SOCKET_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return; } #else /* not WITHOUT_NETWORK */ @@ -1319,8 +1278,9 @@ _javanet_get_option (JNIEnv * env, jobject this, jint option_id) #ifndef WITHOUT_NETWORK int fd; int flag, optval; - int address; + cpnet_address *address; int result; + jobject obj; /* Get the real file descriptor */ fd = _javanet_get_int_field (env, this, "native_fd"); @@ -1336,12 +1296,11 @@ _javanet_get_option (JNIEnv * env, jobject this, jint option_id) { /* TCP_NODELAY case. Return a Boolean indicating on or off */ case SOCKOPT_TCP_NODELAY: - TARGET_NATIVE_NETWORK_SOCKET_GET_OPTION_TCP_NODELAY (fd, optval, - result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getSocketTCPNoDelay (env, fd, &optval); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, SOCKET_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return (0); } @@ -1355,17 +1314,17 @@ _javanet_get_option (JNIEnv * env, jobject this, jint option_id) /* SO_LINGER case. If disabled, return a Boolean object that represents false, else return an Integer that is the value of SO_LINGER */ case SOCKOPT_SO_LINGER: - TARGET_NATIVE_NETWORK_SOCKET_GET_OPTION_SO_LINGER (fd, flag, optval, - result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getLinger (env, fd, &flag, &optval); + + if (result != CPNATIVE_OK) { JCL_ThrowException (env, SOCKET_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return (0); } - if (optval) - return (_javanet_create_integer (env, JNI_TRUE)); + if (flag) + return (_javanet_create_integer (env, optval)); else return (_javanet_create_boolean (env, JNI_FALSE)); @@ -1373,34 +1332,27 @@ _javanet_get_option (JNIEnv * env, jobject this, jint option_id) /* SO_TIMEOUT case. Return an Integer object with the timeout value */ case SOCKOPT_SO_TIMEOUT: -#ifdef SO_TIMEOUT - TARGET_NATIVE_NETWORK_SOCKET_GET_OPTION_SO_TIMEOUT (fd, optval, result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getSocketTimeout (env, fd, &optval); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, SOCKET_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return (0); } return (_javanet_create_integer (env, optval)); -#else - JCL_ThrowException (env, SOCKET_EXCEPTION, - "SO_TIMEOUT not supported on this platform"); - return (0); -#endif /* not SO_TIMEOUT */ break; case SOCKOPT_SO_SNDBUF: case SOCKOPT_SO_RCVBUF: if (option_id == SOCKOPT_SO_SNDBUF) - TARGET_NATIVE_NETWORK_SOCKET_GET_OPTION_SO_SNDBUF (fd, optval, - result); + result = cpnet_getSendBuf (env, fd, &optval); else - TARGET_NATIVE_NETWORK_SOCKET_GET_OPTION_SO_RCDBUF (fd, optval, - result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getRecvBuf (env, fd, &optval); + + if (result != CPNATIVE_OK) { JCL_ThrowException (env, SOCKET_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return (0); } @@ -1409,11 +1361,11 @@ _javanet_get_option (JNIEnv * env, jobject this, jint option_id) /* The TTL case. Return an Integer with the Time to Live value */ case SOCKOPT_IP_TTL: - TARGET_NATIVE_NETWORK_SOCKET_GET_OPTION_IP_TTL (fd, optval, result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getTTL (env, fd, &optval); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, SOCKET_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return (0); } @@ -1422,61 +1374,64 @@ _javanet_get_option (JNIEnv * env, jobject this, jint option_id) /* Multicast interface case */ case SOCKOPT_IP_MULTICAST_IF: - TARGET_NATIVE_NETWORK_SOCKET_GET_OPTION_IP_MULTICAST_IF (fd, address, - result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getMulticastIF (env, fd, &address); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, SOCKET_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return (0); } - return (_javanet_create_inetaddress (env, address)); + obj = _javanet_create_inetaddress (env, address); + cpnet_freeAddress (env, address); + + return obj; break; case SOCKOPT_SO_BINDADDR: - TARGET_NATIVE_NETWORK_SOCKET_GET_OPTION_BIND_ADDRESS (fd, address, - result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getLocalAddr (env, fd, &address); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, SOCKET_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return (0); } - return (_javanet_create_inetaddress (env, address)); + obj = _javanet_create_inetaddress (env, address); + cpnet_freeAddress (env, address); + + return obj; break; case SOCKOPT_SO_REUSEADDR: - TARGET_NATIVE_NETWORK_SOCKET_GET_OPTION_REUSE_ADDRESS (fd, optval, - result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getReuseAddress (env, fd, &optval); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, SOCKET_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return (0); } if (optval) - return (_javanet_create_boolean (env, JNI_TRUE)); + return _javanet_create_boolean (env, JNI_TRUE); else - return (_javanet_create_boolean (env, JNI_FALSE)); + return _javanet_create_boolean (env, JNI_FALSE); break; case SOCKOPT_SO_KEEPALIVE: - TARGET_NATIVE_NETWORK_SOCKET_GET_OPTION_KEEP_ALIVE (fd, optval, result); - if (result != TARGET_NATIVE_OK) + result = cpnet_getKeepAlive (env, fd, &optval); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, SOCKET_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return (0); } if (optval) - return (_javanet_create_boolean (env, JNI_TRUE)); + return _javanet_create_boolean (env, JNI_TRUE); else - return (_javanet_create_boolean (env, JNI_FALSE)); + return _javanet_create_boolean (env, JNI_FALSE); break; @@ -1493,6 +1448,7 @@ _javanet_get_option (JNIEnv * env, jobject this, jint option_id) void _javanet_shutdownInput (JNIEnv * env, jobject this) { + int result; int fd; /* Get the real file descriptor. */ @@ -1505,10 +1461,11 @@ _javanet_shutdownInput (JNIEnv * env, jobject this) } /* Shutdown input stream of socket. */ - if (shutdown (fd, SHUT_RD) == -1) + result = cpnet_shutdown (env, fd, CPNET_SHUTDOWN_READ); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, SOCKET_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING()); + cpnative_getErrorString (result)); return; } } @@ -1517,6 +1474,7 @@ void _javanet_shutdownOutput (JNIEnv * env, jobject this) { int fd; + int result; /* Get the real file descriptor. */ fd = _javanet_get_int_field (env, this, "native_fd"); @@ -1528,10 +1486,11 @@ _javanet_shutdownOutput (JNIEnv * env, jobject this) } /* Shutdown output stream of socket. */ - if (shutdown (fd, SHUT_WR) == -1) + result = cpnet_shutdown (env, fd, CPNET_SHUTDOWN_WRITE); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, SOCKET_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING()); + cpnative_getErrorString (result)); return; } } diff --git a/native/jni/java-net/javanet.h b/native/jni/java-net/javanet.h index 030d41282..411c6c651 100644 --- a/native/jni/java-net/javanet.h +++ b/native/jni/java-net/javanet.h @@ -41,6 +41,7 @@ exception statement from your version. */ #include <jni.h> #include "jcl.h" +#include "cpnet.h" /*************************************************************************/ @@ -54,6 +55,7 @@ exception statement from your version. */ #define CONNECT_EXCEPTION "java/net/ConnectException" #define SOCKET_EXCEPTION "java/net/SocketException" #define UNKNOWN_HOST_EXCEPTION "java/net/UnknownHostException" +#define NULL_EXCEPTION "java/lang/NullPointerException" /* Socket Option Identifiers - Don't change or binary compatibility with the JDK will be broken! These also need to @@ -78,15 +80,15 @@ exception statement from your version. */ */ extern int _javanet_get_int_field(JNIEnv *, jobject, const char *); -extern int _javanet_get_netaddr(JNIEnv *, jobject); +extern cpnet_address *_javanet_get_ip_netaddr(JNIEnv *, jobject); extern void _javanet_create(JNIEnv *, jobject, jboolean); extern void _javanet_close(JNIEnv *, jobject, int); extern void _javanet_connect(JNIEnv *, jobject, jobject, jint, jboolean); extern void _javanet_bind(JNIEnv *, jobject, jobject, jint, int); extern void _javanet_listen(JNIEnv *, jobject, jint); extern void _javanet_accept(JNIEnv *, jobject, jobject); -extern int _javanet_recvfrom(JNIEnv *, jobject, jarray, int, int, int *, int *); -extern void _javanet_sendto(JNIEnv *, jobject, jarray, int, int, int, int); +extern int _javanet_recvfrom(JNIEnv *, jobject, jarray, int, int, cpnet_address **); +extern void _javanet_sendto(JNIEnv *, jobject, jarray, int, int, cpnet_address *); extern jobject _javanet_get_option(JNIEnv *, jobject, jint); extern void _javanet_set_option(JNIEnv *, jobject, jint, jobject); extern void _javanet_shutdownInput (JNIEnv *, jobject); diff --git a/native/jni/java-net/local.c b/native/jni/java-net/local.c index cdddd89ef..c8ca91c2a 100644 --- a/native/jni/java-net/local.c +++ b/native/jni/java-net/local.c @@ -36,9 +36,7 @@ obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ -#ifdef HAVE_CONFIG_H #include "config.h" -#endif /* HAVE_CONFIG_H */ #ifdef ENABLE_LOCAL_SOCKETS diff --git a/native/jni/java-nio/Makefile.am b/native/jni/java-nio/Makefile.am index c3f6caf62..800d25be6 100644 --- a/native/jni/java-nio/Makefile.am +++ b/native/jni/java-nio/Makefile.am @@ -10,6 +10,7 @@ libjavanio_la_SOURCES = gnu_java_nio_VMPipe.c \ java_nio_VMDirectByteBuffer.c libjavanio_la_LIBADD = $(top_builddir)/native/jni/classpath/jcl.lo \ + $(top_builddir)/native/jni/native-lib/libclasspathnative.la \ $(LTLIBICONV) AM_LDFLAGS = @CLASSPATH_MODULE@ diff --git a/native/jni/java-nio/gnu_java_nio_channels_FileChannelImpl.c b/native/jni/java-nio/gnu_java_nio_channels_FileChannelImpl.c index 5aed63f35..644b334d3 100644 --- a/native/jni/java-nio/gnu_java_nio_channels_FileChannelImpl.c +++ b/native/jni/java-nio/gnu_java_nio_channels_FileChannelImpl.c @@ -44,11 +44,8 @@ exception statement from your version. */ #include <jni.h> #include <jcl.h> -#include "target_native.h" -#ifndef WITHOUT_FILESYSTEM -#include "target_native_file.h" -#endif -#include "target_native_math_int.h" +#include "cpnative.h" +#include "cpio.h" #include "gnu_java_nio_channels_FileChannelImpl.h" @@ -60,6 +57,10 @@ exception statement from your version. */ #include <sys/mman.h> #endif /* HAVE_SYS_MMAN_H */ +#if defined(HAVE_UNISTD_H) +#include <unistd.h> +#endif + /* These values must be kept in sync with FileChannelImpl.java. */ #define FILECHANNELIMPL_READ 1 #define FILECHANNELIMPL_WRITE 2 @@ -79,22 +80,6 @@ exception statement from your version. */ #define IO_EXCEPTION "java/io/IOException" -/* FIXME: This can't be right. Need converter macros. */ -#define CONVERT_JLONG_TO_INT(x) TARGET_NATIVE_MATH_INT_INT64_TO_INT32(x) -#define CONVERT_INT_TO_JLONG(x) TARGET_NATIVE_MATH_INT_INT32_TO_INT64(x) - -/* FIXME: This can't be right. Need converter macros. */ -#define CONVERT_JLONG_TO_OFF_T(x) TARGET_NATIVE_MATH_INT_INT64_TO_INT32(x) -#define CONVERT_OFF_T_TO_JLONG(x) TARGET_NATIVE_MATH_INT_INT32_TO_INT64(x) - -/* FIXME: This can't be right. Need converter macros */ -#define CONVERT_JINT_TO_INT(x) ((int)(x & 0xFFFFFFFF)) -#define CONVERT_INT_TO_JINT(x) ((int)(x & 0xFFFFFFFF)) - -/* FIXME: This can't be right. Need converter macros. */ -#define CONVERT_SSIZE_T_TO_JINT(x) ((jint)(x & 0xFFFFFFFF)) -#define CONVERT_JINT_TO_SSIZE_T(x) (x) - /* Align a value up or down to a multiple of the pagesize. */ #define ALIGN_DOWN(p,s) ((p) - ((p) % (s))) #define ALIGN_UP(p,s) ((p) + ((s) - ((p) % (s)))) @@ -162,53 +147,46 @@ Java_gnu_java_nio_channels_FileChannelImpl_open (JNIEnv * env, && (mode & FILECHANNELIMPL_FILEOPEN_FLAG_WRITE)) { /* read/write */ - flags = - TARGET_NATIVE_FILE_FILEFLAG_CREATE | - TARGET_NATIVE_FILE_FILEFLAG_READWRITE; - permissions = TARGET_NATIVE_FILE_FILEPERMISSION_NORMAL; + flags = CPFILE_FLAG_CREATE | CPFILE_FLAG_READWRITE; + permissions = CPFILE_PERMISSION_NORMAL; } else if ((mode & FILECHANNELIMPL_FILEOPEN_FLAG_READ)) { /* read */ - flags = TARGET_NATIVE_FILE_FILEFLAG_READ; - permissions = TARGET_NATIVE_FILE_FILEPERMISSION_NORMAL; + flags = CPFILE_FLAG_READ; + permissions = CPFILE_PERMISSION_NORMAL; } else { /* write */ - flags = - TARGET_NATIVE_FILE_FILEFLAG_CREATE | - TARGET_NATIVE_FILE_FILEFLAG_WRITE; + flags = CPFILE_FLAG_CREATE | CPFILE_FLAG_WRITE; if ((mode & FILECHANNELIMPL_FILEOPEN_FLAG_APPEND)) { - flags |= TARGET_NATIVE_FILE_FILEFLAG_APPEND; + flags |= CPFILE_FLAG_APPEND; } else { - flags |= TARGET_NATIVE_FILE_FILEFLAG_TRUNCATE; + flags |= CPFILE_FLAG_TRUNCATE; } - permissions = TARGET_NATIVE_FILE_FILEPERMISSION_NORMAL; + permissions = CPFILE_PERMISSION_NORMAL; } if ((mode & FILECHANNELIMPL_FILEOPEN_FLAG_SYNC)) { - flags |= TARGET_NATIVE_FILE_FILEFLAG_SYNC; + flags |= CPFILE_FLAG_SYNC; } if ((mode & FILECHANNELIMPL_FILEOPEN_FLAG_DSYNC)) { - flags |= TARGET_NATIVE_FILE_FILEFLAG_DSYNC; + flags |= CPFILE_FLAG_DSYNC; } -#ifdef O_BINARY - flags |= TARGET_NATIVE_FILE_FILEFLAG_BINARY; -#endif - - TARGET_NATIVE_FILE_OPEN (filename, native_fd, flags, permissions, result); + flags |= CPFILE_FLAG_BINARY; - if (result != TARGET_NATIVE_OK) + result = cpio_openFile (filename, &native_fd, flags, permissions); + if (result != CPNATIVE_OK) { char message[256]; /* Fixed size we don't need to malloc. */ - char *error_string = TARGET_NATIVE_LAST_ERROR_STRING (); + const char *error_string = cpnative_getErrorString (result); snprintf(message, 256, "%s: %s", error_string, filename); /* We are only allowed to throw FileNotFoundException. */ @@ -216,7 +194,7 @@ Java_gnu_java_nio_channels_FileChannelImpl_open (JNIEnv * env, "java/io/FileNotFoundException", message); JCL_free_cstring (env, name, filename); - return TARGET_NATIVE_MATH_INT_INT64_CONST_MINUS_1; + return -1; } JCL_free_cstring (env, name, filename); @@ -238,17 +216,15 @@ Java_gnu_java_nio_channels_FileChannelImpl_implCloseChannel (JNIEnv * env, do { - TARGET_NATIVE_FILE_CLOSE (native_fd, result); - if (result != TARGET_NATIVE_OK - && (TARGET_NATIVE_LAST_ERROR () - != TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL)) + result = cpio_closeFile (native_fd); + if (result != CPNATIVE_OK && result != CPNATIVE_EINTR) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return; } } - while (result != TARGET_NATIVE_OK); + while (result != CPNATIVE_OK); } /* @@ -267,20 +243,18 @@ Java_gnu_java_nio_channels_FileChannelImpl_available (JNIEnv * env, do { - TARGET_NATIVE_FILE_AVAILABLE (native_fd, bytes_available, result); - if (result != TARGET_NATIVE_OK - && (TARGET_NATIVE_LAST_ERROR () - != TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL)) + result = cpio_availableBytes (native_fd, &bytes_available); + if (result != CPNATIVE_OK && result != CPNATIVE_EINTR) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return 0; } } - while (result != TARGET_NATIVE_OK); + while (result != CPNATIVE_OK); /* FIXME NYI ??? why only jint and not jlong? */ - return TARGET_NATIVE_MATH_INT_INT64_TO_INT32 (bytes_available); + return (jint)bytes_available; } JNIEXPORT jlong JNICALL @@ -292,12 +266,12 @@ Java_gnu_java_nio_channels_FileChannelImpl_size (JNIEnv * env, jobject obj) native_fd = get_native_fd (env, obj); - TARGET_NATIVE_FILE_SIZE (native_fd, file_size, result); - if (result != TARGET_NATIVE_OK) + result = cpio_getFileSize (native_fd, &file_size); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); - return TARGET_NATIVE_MATH_INT_INT64_CONST_MINUS_1; + cpnative_getErrorString (result)); + return -1; } return file_size; @@ -317,12 +291,12 @@ Java_gnu_java_nio_channels_FileChannelImpl_implPosition (JNIEnv * env, native_fd = get_native_fd (env, obj); - TARGET_NATIVE_FILE_TELL (native_fd, current_offset, result); - if (result != TARGET_NATIVE_OK) + result = cpio_getFilePosition (native_fd, ¤t_offset); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); - return TARGET_NATIVE_MATH_INT_INT64_CONST_MINUS_1; + cpnative_getErrorString (result)); + return -1; } return current_offset; @@ -337,7 +311,6 @@ Java_gnu_java_nio_channels_FileChannelImpl_seek (JNIEnv * env, jobject obj, jlong offset) { int native_fd; - jlong new_offset; int result; native_fd = get_native_fd (env, obj); @@ -362,14 +335,11 @@ Java_gnu_java_nio_channels_FileChannelImpl_seek (JNIEnv * env, jobject obj, } #endif /* 0 */ - result = TARGET_NATIVE_ERROR; - new_offset = TARGET_NATIVE_MATH_INT_INT64_CONST_MINUS_1; - TARGET_NATIVE_FILE_SEEK_BEGIN (native_fd, offset, new_offset, result); - - if (result != TARGET_NATIVE_OK) + result = cpio_setFilePosition (native_fd, offset); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); } } @@ -383,10 +353,6 @@ Java_gnu_java_nio_channels_FileChannelImpl_implTruncate (JNIEnv * env, jlong len) { int native_fd; - jlong file_size; - int bytes_written; - jlong save_offset, new_offset; - char data; int result; native_fd = get_native_fd (env, obj); @@ -412,97 +378,11 @@ Java_gnu_java_nio_channels_FileChannelImpl_implTruncate (JNIEnv * env, } #endif /* 0 */ - /* get file size */ - TARGET_NATIVE_FILE_SIZE (native_fd, file_size, result); - if (result != TARGET_NATIVE_OK) + result = cpio_setFileSize (native_fd, len); + if (result != CPNATIVE_OK) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); - return; - } - - /* Save off current position */ - TARGET_NATIVE_FILE_TELL (native_fd, save_offset, result); - if (result != TARGET_NATIVE_OK) - { - JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); - return; - } - - if (TARGET_NATIVE_MATH_INT_INT64_LT (file_size, len)) - { - /* File is too short -- seek to one byte short of where we want, - * then write a byte */ - - /* move to position n-1 */ - TARGET_NATIVE_FILE_SEEK_BEGIN (native_fd, - TARGET_NATIVE_MATH_INT_INT64_SUB (len, - 1), - new_offset, result); - if (result != TARGET_NATIVE_OK) - { - JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); - return; - } - - /* write a byte - Note: This will fail if we somehow get here in read only mode - * That shouldn't happen */ - data = '\0'; - TARGET_NATIVE_FILE_WRITE (native_fd, &data, 1, bytes_written, result); - if (result != TARGET_NATIVE_OK) - { - JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); - return; - } - - /* Reposition file pointer to where we started if not beyond new len. */ - if (TARGET_NATIVE_MATH_INT_INT64_LT (save_offset, len)) - { - TARGET_NATIVE_FILE_SEEK_BEGIN (native_fd, save_offset, - new_offset, result); - if (result != TARGET_NATIVE_OK) - { - JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); - return; - } - } - } - else if (TARGET_NATIVE_MATH_INT_INT64_GT (file_size, len)) - { - /* File is too long - use ftruncate if available */ -#ifdef HAVE_FTRUNCATE - TARGET_NATIVE_FILE_TRUNCATE (native_fd, len, result); - if (result != TARGET_NATIVE_OK) - { - JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); - return; - } -#else /* HAVE_FTRUNCATE */ - /* FIXME: Probably operation isn't supported, but this exception - * is too harsh as it will probably crash the program without need - JCL_ThrowException(env, "java/lang/UnsupportedOperationException", - "not implemented - can't shorten files on this platform"); - */ - JCL_ThrowException (env, IO_EXCEPTION, "Unable to shorten file length"); -#endif /* HAVE_FTRUNCATE */ - - /* Reposition file pointer when it now is beyond the end of file. */ - if (TARGET_NATIVE_MATH_INT_INT64_GT (save_offset, len)) - { - TARGET_NATIVE_FILE_SEEK_BEGIN (native_fd, len, new_offset, result); - if (result != TARGET_NATIVE_OK) - { - JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); - return; - } - } + cpnative_getErrorString (result)); } } @@ -563,7 +443,7 @@ Java_gnu_java_nio_channels_FileChannelImpl_mapImpl (JNIEnv *env, jobject obj, fd, ALIGN_DOWN (position, pagesize)); if (p == MAP_FAILED) { - JCL_ThrowException (env, IO_EXCEPTION, strerror (errno)); + JCL_ThrowException (env, IO_EXCEPTION, cpnative_getErrorString (errno)); return NULL; } @@ -619,7 +499,7 @@ Java_gnu_java_nio_channels_FileChannelImpl_read__ (JNIEnv * env, jobject obj) { int native_fd; char data; - ssize_t bytes_read; + jint bytes_read; int result; native_fd = get_native_fd (env, obj); @@ -627,21 +507,18 @@ Java_gnu_java_nio_channels_FileChannelImpl_read__ (JNIEnv * env, jobject obj) bytes_read = 0; do { - TARGET_NATIVE_FILE_READ (native_fd, &data, 1, bytes_read, result); - if ((result == TARGET_NATIVE_OK) && (bytes_read == 0)) - { - return (-1); - } - if ((result != TARGET_NATIVE_OK) - && (TARGET_NATIVE_LAST_ERROR () != - TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL)) + result = cpio_read (native_fd, &data, 1, &bytes_read); + if ((result == CPNATIVE_OK) && (bytes_read == 0)) + return -1; + + if ((result != CPNATIVE_OK) && (result != CPNATIVE_EINTR)) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); - return (-1); + cpnative_getErrorString (result)); + return -1; } } - while (result != TARGET_NATIVE_OK); + while (result != CPNATIVE_OK); return ((jint) (data & 0xFF)); } @@ -659,8 +536,8 @@ Java_gnu_java_nio_channels_FileChannelImpl_read___3BII (JNIEnv * env, { int native_fd; jbyte *bufptr; - ssize_t bytes_read; - ssize_t n; + jint bytes_read; + jint n; int result; native_fd = get_native_fd (env, obj); @@ -692,32 +569,30 @@ Java_gnu_java_nio_channels_FileChannelImpl_read___3BII (JNIEnv * env, bytes_read = 0; do { - TARGET_NATIVE_FILE_READ (native_fd, (bufptr + offset + bytes_read), - (length - bytes_read), n, result); - if ((result == TARGET_NATIVE_OK) && (n == 0)) + result = cpio_read (native_fd, (bufptr + offset + bytes_read), + (length - bytes_read), &n); + if ((result == CPNATIVE_OK) && (n == 0)) { (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0); if (bytes_read == 0) return -1; /* Signal end of file to Java */ else - return CONVERT_SSIZE_T_TO_JINT (bytes_read); + return bytes_read; } - if ((result != TARGET_NATIVE_OK) - && (TARGET_NATIVE_LAST_ERROR () != - TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL)) + if ((result != CPNATIVE_OK) && (result != CPNATIVE_EINTR)) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0); return -1; } - if (result == TARGET_NATIVE_OK) + if (result == CPNATIVE_OK) bytes_read += n; } while (bytes_read < 1); (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0); - return CONVERT_SSIZE_T_TO_JINT (bytes_read); + return bytes_read; } /* @@ -730,26 +605,23 @@ Java_gnu_java_nio_channels_FileChannelImpl_write__I (JNIEnv * env, { int native_fd; char native_data; - ssize_t bytes_written; + jint bytes_written; int result; native_fd = get_native_fd (env, obj); - native_data = (char) (CONVERT_JINT_TO_INT (b) & 0xFF); + native_data = (char) (b & 0xFF); do { - TARGET_NATIVE_FILE_WRITE (native_fd, &native_data, 1, bytes_written, - result); - if ((result != TARGET_NATIVE_OK) - && (TARGET_NATIVE_LAST_ERROR () != - TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL)) + result = cpio_write (native_fd, &native_data, 1, &bytes_written); + if ((result != CPNATIVE_OK) && (result != CPNATIVE_EINTR)) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); return; } } - while (result != TARGET_NATIVE_OK); + while (result != CPNATIVE_OK); } /* @@ -762,10 +634,11 @@ Java_gnu_java_nio_channels_FileChannelImpl_force (JNIEnv * env, int native_fd; int result; native_fd = get_native_fd (env, obj); - TARGET_NATIVE_FILE_FSYNC (native_fd, result); - if (result != TARGET_NATIVE_OK) + + result = cpio_fsync (native_fd); + if (result != CPNATIVE_OK) JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); } /* @@ -781,8 +654,8 @@ Java_gnu_java_nio_channels_FileChannelImpl_write___3BII (JNIEnv * env, { int native_fd; jbyte *bufptr; - ssize_t bytes_written; - ssize_t n; + jint bytes_written; + jint n; int result; native_fd = get_native_fd (env, obj); @@ -799,20 +672,18 @@ Java_gnu_java_nio_channels_FileChannelImpl_write___3BII (JNIEnv * env, } bytes_written = 0; - while (bytes_written < CONVERT_JINT_TO_SSIZE_T (length)) + while (bytes_written < length) { - TARGET_NATIVE_FILE_WRITE (native_fd, (bufptr + offset + bytes_written), - (length - bytes_written), n, result); - if ((result != TARGET_NATIVE_OK) - && (TARGET_NATIVE_LAST_ERROR () != - TARGET_NATIVE_ERROR_INTERRUPT_FUNCTION_CALL)) + result = cpio_write (native_fd, (bufptr + offset + bytes_written), + (length - bytes_written), &n); + if ((result != CPNATIVE_OK) && (result != CPNATIVE_EINTR)) { JCL_ThrowException (env, IO_EXCEPTION, - TARGET_NATIVE_LAST_ERROR_STRING ()); + cpnative_getErrorString (result)); (*env)->ReleaseByteArrayElements (env, buffer, bufptr, 0); return; } - if (result == TARGET_NATIVE_OK) + if (result == CPNATIVE_OK) bytes_written += n; } @@ -849,7 +720,7 @@ Java_gnu_java_nio_channels_FileChannelImpl_lock (JNIEnv *env, jobject obj, if (errno != EACCES && errno != EAGAIN) { JCL_ThrowException (env, "java/lang/InternalError", - strerror (errno)); + cpnative_getErrorString (errno)); } return JNI_FALSE; } @@ -890,7 +761,7 @@ Java_gnu_java_nio_channels_FileChannelImpl_unlock (JNIEnv *env, if (ret) { JCL_ThrowException (env, "java/lang/InternalError", - strerror (errno)); + cpnative_getErrorString (errno)); } #else (void) obj; diff --git a/native/jni/midi-dssi/dssi_data.h b/native/jni/midi-dssi/dssi_data.h index 27a4e2831..f8243f29b 100644 --- a/native/jni/midi-dssi/dssi_data.h +++ b/native/jni/midi-dssi/dssi_data.h @@ -46,8 +46,6 @@ exception statement from your version. */ #include <stdio.h> -#include "target_native.h" -#include "target_native_misc.h" #include "../classpath/jcl.h" /* Specify the size of the circular buffer. It only needs to be big diff --git a/native/jni/native-lib/.cvsignore b/native/jni/native-lib/.cvsignore new file mode 100644 index 000000000..e9f2658a6 --- /dev/null +++ b/native/jni/native-lib/.cvsignore @@ -0,0 +1,8 @@ +*.o +*.a +*.lo +*.la +.libs +.deps +Makefile +Makefile.in diff --git a/native/jni/native-lib/Makefile.am b/native/jni/native-lib/Makefile.am new file mode 100644 index 000000000..beab77e57 --- /dev/null +++ b/native/jni/native-lib/Makefile.am @@ -0,0 +1,12 @@ +noinst_LTLIBRARIES = libclasspathnative.la +libclasspathnative_la_SOURCES = cpnet.c \ + cpnet.h \ + cpio.c \ + cpio.h \ + cpnative.h \ + cpproc.h \ + cpproc.c + +AM_LDFLAGS = @CLASSPATH_MODULE@ +AM_CPPFLAGS = @CLASSPATH_INCLUDES@ +AM_CFLAGS = @WARNING_CFLAGS@ @STRICT_WARNING_CFLAGS@ @ERROR_CFLAGS@ diff --git a/native/jni/native-lib/cpio.c b/native/jni/native-lib/cpio.c new file mode 100644 index 000000000..eb544dc83 --- /dev/null +++ b/native/jni/native-lib/cpio.c @@ -0,0 +1,461 @@ +/* cpio.c - Common java file IO native functions + 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. */ + +/* do not move; needed here because of some macro definitions */ +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <dirent.h> + +#include <jni.h> + +#if defined(HAVE_SYS_IOCTL_H) +#define BSD_COMP /* Get FIONREAD on Solaris2 */ +#include <sys/ioctl.h> +#endif +#if defined(HAVE_SYS_FILIO_H) /* Get FIONREAD on Solaris 2.5 */ +#include <sys/filio.h> +#endif + +#if defined(HAVE_SYS_STAT_H) +#include <sys/stat.h> +#endif + +#if defined(HAVE_FCNTL_H) +#include <fcntl.h> +#endif + +#if defined(HAVE_UNISTD_H) +#include <unistd.h> +#endif + +#if defined(HAVE_SYS_SELECT_H) +#include <sys/select.h> +#endif + +#include <utime.h> + +#include "cpnative.h" +#include "cpio.h" + +/* Some POSIX systems don't have O_SYNC and O_DYSNC so we define them here. */ +#if !defined (O_SYNC) && defined (O_FSYNC) +#define O_SYNC O_FSYNC +#endif +#if !defined (O_DSYNC) && defined (O_FSYNC) +#define O_DSYNC O_FSYNC +#endif +/* If O_DSYNC is still not defined, use O_SYNC (needed for newlib). */ +#if !defined (O_DSYNC) +#define O_DSYNC O_SYNC +#endif + +JNIEXPORT int cpio_openFile (const char *filename, int *fd, int flags, int permissions) +{ + int sflags = 0; + int rwflags = flags & CPFILE_FLAG_READWRITE; + int perms; + + if (flags & CPFILE_FLAG_CREATE) + sflags |= O_CREAT; + if (flags & CPFILE_FLAG_APPEND) + sflags |= O_APPEND; + if (flags & CPFILE_FLAG_TRUNCATE) + sflags |= O_TRUNC; + if (flags & CPFILE_FLAG_SYNC) + sflags |= O_SYNC; + if (flags & CPFILE_FLAG_DSYNC) + sflags |= O_DSYNC; +#if defined(O_BINARY) + if (flags & CPFILE_FLAG_BINARY) + sflags |= O_BINARY; +#endif + + switch (rwflags) + { + case CPFILE_FLAG_READ: + sflags |= O_RDONLY; + break; + case CPFILE_FLAG_WRITE: + sflags |= O_WRONLY; + break; + case CPFILE_FLAG_READWRITE: + sflags |= O_RDWR; + break; + } + + if (permissions == CPFILE_PERMISSION_NORMAL) + perms = (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + else + perms = 0; + + *fd = open (filename, sflags, perms); + + if (*fd < 0) + return errno; + + return CPNATIVE_OK; +} + +JNIEXPORT int cpio_closeFile (int fd) +{ + if (close (fd) < 0) + return errno; + + return CPNATIVE_OK; +} + +JNIEXPORT int cpio_availableBytes (int fd, jlong *bytes_available) +{ +#if defined (FIONREAD) + ssize_t n; + + if (ioctl (fd, FIONREAD, (char *)&n) != 0) + return errno; + + *bytes_available = n; + return CPNATIVE_OK; +#elif defined(HAVE_FSTAT) + struct stat statBuffer; + off_t n; + int result; + + *bytes_available = 0 + if ((fstat (fd, &statBuffer) == 0) && S_ISREG (statBuffer.st_mode)) + { + n = lseek (fd, 0, SEEK_CUR); + if (n != -1) + { + *bytes_available = statBuffer.st_size - n; + result = 0; + } + else + { + result = errno; + } + } + else + { + result = errno; + } + + return result; +#elif defined(HAVE_SELECT) + fd_set filedescriptset; + struct timeval tv; + int result; + + *bytes_available = 0; + + FD_ZERO (&filedescriptset); + FD_SET (fd,&filedescriptset); + memset (&tv, 0, sizeof(tv)); + + switch (select (fd+1, &filedescriptset, NULL, NULL, &timeval)) \ + { + case -1: + result=errno; + break; + case 0: + *bytes_available = 0; + result = CPNATIVE_OK; + break; + default: + *bytes_available = 1; + result = CPNATIVE_OK; + break; + } + return result; + +#else + *bytes_available = 0; + return ENOTSUP; +#endif +} + +JNIEXPORT int cpio_getFileSize (int fd, jlong *filesize) +{ + struct stat statBuffer; + + if (fstat(fd, &statBuffer) < 0) + return errno; + + *filesize = statBuffer.st_size; + return CPNATIVE_OK; +} + +JNIEXPORT int cpio_getFilePosition (int fd, jlong *offset) +{ + *offset = lseek (fd, 0, SEEK_CUR); + if (*offset < 0) + return errno; + + return CPNATIVE_OK; +} + +JNIEXPORT int cpio_setFilePosition (int fd, jlong position) +{ + if (lseek (fd, position, SEEK_SET) < 0) + return errno; + + return CPNATIVE_OK; +} + +JNIEXPORT int cpio_read (int fd, void *buffer, jint length, jint *bytes_read) +{ + *bytes_read = read (fd, buffer, length); + + if (*bytes_read < 0) + { + return errno; + } + + return CPNATIVE_OK; +} + +JNIEXPORT int cpio_write (int fd, const void *buffer, jint length, jint *bytes_written) +{ + *bytes_written = write (fd, buffer, length); + + if (*bytes_written < 0) + return errno; + + return CPNATIVE_OK; +} + +JNIEXPORT int cpio_fsync (int fd) +{ + if (fsync (fd) < 0) + return errno; + + return CPNATIVE_OK; +} + +JNIEXPORT int cpio_truncate (int fd, jlong size) +{ + if (ftruncate (fd, size) < 0) + return errno; + + return CPNATIVE_OK; +} + +JNIEXPORT int cpio_setFileSize (int native_fd, jlong new_size) +{ + jlong file_size; + jlong save_offset; + int result; + char data; + jint bytes_written; + + result = cpio_getFileSize (native_fd, &file_size); + if (result != CPNATIVE_OK) + return result; + + /* Save off current position */ + result = cpio_getFilePosition (native_fd, &save_offset); + if (result != CPNATIVE_OK) + return result; + + if (file_size < new_size) + { + /* File is too short -- seek to one byte short of where we want, + * then write a byte */ + + /* move to position n-1 */ + result = cpio_setFilePosition (native_fd, new_size-1); + if (result != CPNATIVE_OK) + return result; + + /* write a byte + Note: This will fail if we somehow get here in read only mode + * That shouldn't happen */ + data = '\0'; + result = cpio_write (native_fd, &data, 1, &bytes_written); + if (result != CPNATIVE_OK) + return result; + + /* Reposition file pointer to where we started if not beyond new len. */ + if (save_offset < new_size) + { + result = cpio_setFilePosition (native_fd, save_offset); + if (result != CPNATIVE_OK) + return result; + } + } + else if (new_size < file_size) + { + /* File is too long - use ftruncate if available */ + result = cpio_truncate (native_fd, new_size); + if (result != CPNATIVE_OK) + return result; + + /* Reposition file pointer when it now is beyond the end of file. */ + if (new_size < save_offset) + { + result = cpio_setFilePosition (native_fd, new_size); + if (result != CPNATIVE_OK) + return result; + } + } + + return CPNATIVE_OK; +} + +int cpio_setFileReadonly (const char *filename) +{ + struct stat statbuf; + + if (stat(filename, &statbuf) < 0) + return errno; + + if (chmod(filename, statbuf.st_mode & ~(S_IWRITE | S_IWGRP | S_IWOTH)) < 0) + return errno; + + return 0; +} + +int cpio_isFileExists (const char *filename) +{ + struct stat statbuf; + + if (stat(filename, &statbuf) < 0) + { + return errno; + } + + return 0; +} + +int cpio_checkType (const char *filename, jint *entryType) +{ + struct stat statbuf; + + if (stat(filename, &statbuf) < 0) + return errno; + + if (S_ISDIR(statbuf.st_mode)) + *entryType = CPFILE_DIRECTORY; + else + *entryType = CPFILE_FILE; + + return 0; +} + +int cpio_getModificationTime (const char *filename, jlong *mtime) +{ + struct stat statbuf; + + if (stat(filename, &statbuf) < 0) + return errno; + + *mtime = (jlong)statbuf.st_mtime * (jlong)1000; + + return 0; +} + +int cpio_setModificationTime (const char *filename, jlong mtime) +{ + struct stat statbuf; + struct utimbuf buf; + + if (stat(filename, &statbuf) < 0) + return errno; + + buf.actime = statbuf.st_atime; + buf.modtime = mtime / 1000; + + if (utime(filename, &buf) < 0) + return errno; + + return 0; +} + +int cpio_removeFile (const char *filename) +{ + if (unlink(filename) < 0 && rmdir(filename) < 0) + return errno; + + return 0; +} + +int cpio_mkdir (const char *path) +{ + if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) + return errno; + + return 0; +} + +int cpio_rename (const char *old_name, const char *new_name) +{ + if (rename(old_name, new_name) < 0) + return errno; + + return 0; +} + +int cpio_openDir (const char *dirname, void **handle) +{ + *handle = (void *)opendir(dirname); + if (*handle == NULL) + return errno; + + return 0; +} + +int cpio_closeDir (void *handle) +{ + closedir((DIR *)handle); + return 0; +} + + +int cpio_readDir (void *handle, const char **filename) +{ + struct dirent *dBuf; + + dBuf = readdir((DIR *)handle); + if (dBuf == NULL) + return errno; + + *filename = dBuf->d_name; + return 0; +} diff --git a/native/jni/native-lib/cpio.h b/native/jni/native-lib/cpio.h new file mode 100644 index 000000000..1776b199d --- /dev/null +++ b/native/jni/native-lib/cpio.h @@ -0,0 +1,84 @@ +/* cpio.h - + 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. */ + +#ifndef _CLASSPATH_IO_H_INCLUDED +#define _CLASSPATH_IO_H_INCLUDED + +#include <jni.h> + +#define CPFILE_FLAG_CREATE 0x0001 +#define CPFILE_FLAG_APPEND 0x0002 +#define CPFILE_FLAG_TRUNCATE 0x0004 +#define CPFILE_FLAG_SYNC 0x0008 +#define CPFILE_FLAG_DSYNC 0x0010 +#define CPFILE_FLAG_BINARY 0x0020 +#define CPFILE_FLAG_READ 0x0040 +#define CPFILE_FLAG_WRITE 0x0080 + +#define CPFILE_PERMISSION_NORMAL 1 + +#define CPFILE_FLAG_READWRITE (CPFILE_FLAG_READ|CPFILE_FLAG_WRITE) + +JNIEXPORT int cpio_openFile (const char *filename, int *fd, int flags, int permissions); +JNIEXPORT int cpio_closeFile (int fd); +JNIEXPORT int cpio_availableBytes (int fd, jlong *avail); +JNIEXPORT int cpio_getFileSize (int fd, jlong *filesize); +JNIEXPORT int cpio_setFileSize (int fd, jlong filesize); +JNIEXPORT int cpio_getFilePosition (int fd, jlong *position); +JNIEXPORT int cpio_setFilePosition (int fd, jlong position); +JNIEXPORT int cpio_read (int fd, void *data, jint len, jint *bytes_read); +JNIEXPORT int cpio_write (int fd, const void *data, jint len, jint *bytes_written); +JNIEXPORT int cpio_fsync (int fd); +JNIEXPORT int cpio_truncate (int fd, jlong size); + +#define CPFILE_FILE 0 +#define CPFILE_DIRECTORY 1 + +JNIEXPORT int cpio_setFileReadonly (const char *filename); +JNIEXPORT int cpio_isFileExists (const char *filename); +JNIEXPORT int cpio_checkType (const char *filename, jint *entryType); +JNIEXPORT int cpio_getModificationTime (const char *filename, jlong *mtime); +JNIEXPORT int cpio_setModificationTime (const char *filename, jlong mtime); +JNIEXPORT int cpio_removeFile (const char *filename); +JNIEXPORT int cpio_mkdir (const char *filename); +JNIEXPORT int cpio_rename (const char *old_name, const char *new_name); + +JNIEXPORT int cpio_openDir (const char *dirname, void **handle); +JNIEXPORT int cpio_closeDir (void *handle); +JNIEXPORT int cpio_readDir (void *handle, const char **filename); + +#endif diff --git a/native/jni/native-lib/cpnative.h b/native/jni/native-lib/cpnative.h new file mode 100644 index 000000000..4ba772603 --- /dev/null +++ b/native/jni/native-lib/cpnative.h @@ -0,0 +1,49 @@ +/* cpnative.h - + 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. */ + +#ifndef _CLASSPATH_NATIVE_H_INCLUDED +#define _CLASSPATH_NATIVE_H_INCLUDED + +#include <errno.h> +#include <string.h> + +#define CPNATIVE_OK 0 +#define CPNATIVE_EINTR EINTR + +#define cpnative_getErrorString strerror + +#endif diff --git a/native/jni/native-lib/cpnet.c b/native/jni/native-lib/cpnet.c new file mode 100644 index 000000000..c112ac2a7 --- /dev/null +++ b/native/jni/native-lib/cpnet.c @@ -0,0 +1,718 @@ +/* cpnet.c - + Copyright (C) 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. */ + +#include "config.h" +#include <jni.h> +#include <assert.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <netinet/in.h> +#include <netinet/tcp.h> +#include <netdb.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <unistd.h> + +#include "cpnet.h" + +#define SOCKET_DEFAULT_TIMEOUT -1 /* milliseconds */ + +#if defined (HAVE_MSG_NOSIGNAL) +#define SOCKET_NOSIGNAL MSG_NOSIGNAL +#elif defined (HAVE_SO_NOSIGPIPE) +#define SOCKET_NOSIGNAL SO_NOSIGPIPE +#else +#error "No suitable flag found to ommit a SIGPIPE on signal errors with send()." +#endif + +static int socketTimeouts[FD_SETSIZE]; + +static jint waitForWritable(jint fd) +{ + struct timeval tv; + fd_set writeset; + int ret; + + + FD_ZERO(&writeset); + FD_SET(fd, &writeset); + if (socketTimeouts[fd] > 0) + { + tv.tv_sec = socketTimeouts[fd] / 1000; + tv.tv_usec = (socketTimeouts[fd] % 1000) * 1000; + ret = select(fd+1, NULL, &writeset, NULL, &tv); + } + else + ret = select(fd+1, NULL, &writeset, NULL, NULL); + + return (ret <= 0) ? -1 : 0; +} + +static jint waitForReadable(jint fd) +{ + struct timeval tv; + fd_set readset; + int ret; + + + FD_ZERO(&readset); + FD_SET(fd, &readset); + if (socketTimeouts[fd] > 0) + { + tv.tv_sec = socketTimeouts[fd] / 1000; + tv.tv_usec = (socketTimeouts[fd] % 1000) * 1000; + ret = select(fd+1, &readset, NULL, NULL, &tv); + } + else + ret = select(fd+1, &readset, NULL, NULL, NULL); + + return (ret <= 0) ? -1 : 0; +} + +jint cpnet_openSocketStream(JNIEnv *env UNUSED, jint *fd, jint family) +{ + *fd = socket(family, SOCK_STREAM, 0); + if (*fd == -1) + return errno; + + fcntl(*fd, F_SETFD, FD_CLOEXEC); + assert(*fd < FD_SETSIZE); + socketTimeouts[*fd] = SOCKET_DEFAULT_TIMEOUT; + return 0; +} + +jint cpnet_openSocketDatagram(JNIEnv *env UNUSED, jint *fd, jint family) +{ + *fd = socket(family, SOCK_DGRAM, 0); + if (*fd == -1) + return errno; + + fcntl(*fd, F_SETFD, FD_CLOEXEC); + assert(*fd < FD_SETSIZE); + socketTimeouts[*fd] = SOCKET_DEFAULT_TIMEOUT; + return 0; +} + +jint cpnet_shutdown (JNIEnv *env UNUSED, jint fd, jbyte flag) +{ + int ret; + int shut_flag = 0; + + if (flag == CPNET_SHUTDOWN_READ) + shut_flag = SHUT_RD; + else if (flag == CPNET_SHUTDOWN_WRITE) + shut_flag = SHUT_WR; + + ret = shutdown (fd, shut_flag); + if (ret != 0) + return errno; + return 0; +} + +jint cpnet_close(JNIEnv *env UNUSED, jint fd) +{ + if (close (fd) != 0) + return errno; + return 0; +} + +jint cpnet_listen(JNIEnv *env UNUSED, jint fd, jint queuelen) +{ + if (listen (fd, queuelen) != 0) + return errno; + return 0; +} + +jint cpnet_accept(JNIEnv *env UNUSED, jint fd, jint *newfd) +{ + if (waitForReadable (fd) < 0) + return ETIMEDOUT; + + *newfd = accept(fd, NULL, 0); + if (*newfd != 0) + return errno; + + return 0; +} + +jint cpnet_bind(JNIEnv *env UNUSED, jint fd, cpnet_address *addr) +{ + int ret; + + ret = bind(fd, (struct sockaddr *)addr->data, addr->len); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_connect(JNIEnv *env UNUSED, jint fd, cpnet_address *addr) +{ + int ret; + + /* TODO: implement socket time out */ + ret = connect(fd, (struct sockaddr *)addr->data, addr->len); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_getLocalAddr(JNIEnv *env, jint fd, cpnet_address **addr) +{ + socklen_t slen = 1024; + int ret; + + *addr = JCL_malloc(env, slen); + + slen -= sizeof(jint); + ret = getsockname(fd, (struct sockaddr *)(*addr)->data, &slen ); + if (ret != 0) + { + int err = errno; + JCL_free(env, *addr); + return err; + } + + (*addr)->len = slen; + + return 0; +} + +jint cpnet_getRemoteAddr(JNIEnv *env, jint fd, cpnet_address **addr) +{ + socklen_t slen = 1024; + int ret; + + *addr = JCL_malloc(env, slen); + + slen -= sizeof(jint); + ret = getpeername(fd, (struct sockaddr *)(*addr)->data, &slen ); + if (ret != 0) + { + int err = errno; + JCL_free(env, *addr); + return err; + } + + (*addr)->len = slen; + + return 0; +} + +jint cpnet_setBroadcast(JNIEnv *env UNUSED, jint fd, jint flag) +{ + int ret; + + ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag)); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_send (JNIEnv *env UNUSED, jint fd, jbyte *data, jint len, jint *bytes_sent) +{ + ssize_t ret; + + if (waitForWritable(fd) < 0) + return ETIMEDOUT; + + ret = send(fd, data, len, SOCKET_NOSIGNAL); + if (ret < 0) + return errno; + + *bytes_sent = ret; + + return 0; +} + +jint cpnet_sendTo (JNIEnv *env UNUSED, jint fd, jbyte *data, jint len, cpnet_address *addr, jint *bytes_sent) +{ + ssize_t ret; + + if (waitForWritable(fd) < 0) + return ETIMEDOUT; + + ret = sendto(fd, data, len, SOCKET_NOSIGNAL, (struct sockaddr *)addr->data, + addr->len); + if (ret < 0) + return errno; + + *bytes_sent = ret; + return 0; +} + +jint cpnet_recv (JNIEnv *env UNUSED, jint fd, jbyte *data, jint len, jint *bytes_recv) +{ + ssize_t ret; + + if (waitForReadable(fd) < 0) + return ETIMEDOUT; + + ret = recv(fd, data, len, 0); + if (ret < 0) + return errno; + + *bytes_recv = ret; + + return 0; +} + +jint cpnet_recvFrom (JNIEnv *env, jint fd, jbyte *data, jint len, cpnet_address **addr, jint *bytes_recv) +{ + socklen_t slen = 1024; + ssize_t ret; + + if (waitForReadable(fd) < 0) + return ETIMEDOUT; + + *addr = JCL_malloc(env, slen); + + slen -= sizeof(jint); + ret = recvfrom(fd, data, len, 0, (struct sockaddr *) (*addr)->data, &slen); + if (ret < 0) + { + int err = errno; + JCL_free(env, *addr); + return err; + } + + (*addr)->len = slen; + *bytes_recv = ret; + + return 0; +} + +jint cpnet_setSocketTCPNoDelay (JNIEnv *env UNUSED, jint fd, jint nodelay) +{ + socklen_t len = sizeof(jint); + int ret; + + ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &nodelay, len); + if (ret < 0) + return errno; + + return 0; +} + +jint cpnet_getSocketTCPNoDelay (JNIEnv *env UNUSED, jint fd, jint *nodelay) +{ + socklen_t len = sizeof(jint); + int ret; + + ret = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, nodelay, &len); + if (ret < 0) + return errno; + + return 0; +} + +jint cpnet_setLinger (JNIEnv *env UNUSED, jint fd, jint flag, jint value) +{ + socklen_t len = sizeof(struct linger); + int ret; + struct linger __linger; + + if (flag) + { + __linger.l_onoff = 0; + } + else + { + __linger.l_linger = value; + __linger.l_onoff = 1; + } + + ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, &__linger, len); + if (ret < 0) + return errno; + + return 0; +} + +jint cpnet_getLinger (JNIEnv *env UNUSED, jint fd, jint *flag, jint *value) +{ + socklen_t slen = sizeof(struct linger); + struct linger __linger; + int ret; + + ret = getsockopt(fd, SOL_SOCKET, SO_LINGER, &__linger, &slen); + if (ret != 0) + return errno; + + *flag = __linger.l_onoff; + *value = __linger.l_linger; + + return ret; +} + +jint cpnet_setSocketTimeout (JNIEnv *env UNUSED, jint fd, jint value) +{ + socketTimeouts[fd] = value; + return 0; +} + +jint cpnet_getSocketTimeout (JNIEnv *env UNUSED, jint fd, jint *value) +{ + *value = socketTimeouts[fd]; + return 0; +} + +jint cpnet_setSendBuf (JNIEnv *env UNUSED, jint fd, jint value) +{ + int ret; + + ret = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof(value)); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_getSendBuf (JNIEnv *env UNUSED, jint fd, jint *value) +{ + int ret; + socklen_t slen = sizeof(*value); + + ret = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, value, &slen); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_setRecvBuf (JNIEnv *env UNUSED, jint fd, jint value) +{ + int ret; + + ret = setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof(value)); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_getRecvBuf (JNIEnv *env UNUSED, jint fd, jint *value) +{ + int ret; + socklen_t slen = sizeof(*value); + + ret = getsockopt(fd, SOL_SOCKET, SO_RCVBUF, value, &slen); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_setTTL (JNIEnv *env UNUSED, jint fd, jint value) +{ + int ret; + + ret = setsockopt(fd, IPPROTO_IP, IP_TTL, &value, sizeof(value)); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_getTTL (JNIEnv *env UNUSED, jint fd, jint *value) +{ + int ret; + socklen_t slen = sizeof(*value); + + ret = getsockopt(fd, IPPROTO_IP, IP_TTL, value, &slen); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_setMulticastIF (JNIEnv *env UNUSED, jint fd, cpnet_address *addr) +{ + int ret; + + ret = setsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (struct sockaddr *)addr->data, addr->len); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_getMulticastIF (JNIEnv *env, jint fd, cpnet_address **addr) +{ + socklen_t slen = 1024; + int ret; + + *addr = JCL_malloc(env, slen); + + slen -= sizeof(jint); + ret = getsockopt(fd, IPPROTO_IP, IP_MULTICAST_IF, (struct sockaddr *)(*addr)->data, &slen); + (*addr)->len = slen; + + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_setReuseAddress (JNIEnv *env UNUSED, jint fd, jint reuse) +{ + int ret; + + ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_getReuseAddress (JNIEnv *env UNUSED, jint fd, jint *reuse) +{ + int ret; + socklen_t slen = sizeof(*reuse); + + ret = getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reuse, &slen); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_setKeepAlive (JNIEnv *env UNUSED, jint fd, jint keep) +{ + int ret; + + ret = setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keep, sizeof(keep)); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_getKeepAlive (JNIEnv *env UNUSED, jint fd, jint *keep) +{ + int ret; + socklen_t slen = sizeof(*keep); + + ret = getsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, keep, &slen); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_addMembership (JNIEnv *env UNUSED, jint fd, cpnet_address *addr) +{ + struct ip_mreq req; + int ret; + + memset(&req, 0, sizeof(req)); + req.imr_multiaddr = ((struct sockaddr_in *)addr->data)->sin_addr; + req.imr_interface.s_addr = INADDR_ANY; + ret = setsockopt(fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &req, sizeof(req)); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_dropMembership (JNIEnv *env UNUSED, jint fd, cpnet_address *addr) +{ + struct ip_mreq req; + int ret; + + memset(&req, 0, sizeof(req)); + req.imr_multiaddr = ((struct sockaddr_in *)addr->data)->sin_addr; + req.imr_interface.s_addr = INADDR_ANY; + ret = setsockopt(fd, IPPROTO_IP, IP_DROP_MEMBERSHIP, &req, sizeof(req)); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_getAvailableBytes (JNIEnv *env UNUSED, jint fd, jint *availableBytes) +{ + int ret; + + ret = ioctl(fd, FIONREAD, availableBytes); + if (ret != 0) + return errno; + + return 0; +} + +jint cpnet_getHostname (JNIEnv *env UNUSED, char *hostname, jint hostname_len) +{ + int ret; + + ret = gethostname(hostname, hostname_len); + if (ret != 0) + return errno; + + hostname[hostname_len-1] = 0; + return 0; +} + +jint cpnet_getHostByName (JNIEnv *env, const char *hostname, cpnet_address ***addresses, jint *addresses_count) +{ + struct hostent hret; + struct hostent *result; + jint buflen = 1024; + int herr; + int ret; + int counter = 0; + cpnet_address **addr_arr; + int i; + char *buf; + + do + { + buf = (char *)JCL_malloc(env, buflen); +#ifdef HAVE_GETHOSTBYNAME_R + ret = gethostbyname_r (hostname, &hret, buf, buflen, &result, &herr); +#else + ret = gethostbyname (hostname); +#endif + if (ret != 0 || result == NULL) + { + if (herr == ERANGE) + { + buflen *= 2; + JCL_free(env, buf); + continue; + } + JCL_free(env, buf); + + return -herr; + } + + break; + } + while (1); + + while (hret.h_addr_list[counter] != NULL) + counter++; + + *addresses_count = counter; + addr_arr = *addresses = JCL_malloc(env, sizeof(cpnet_address *) * counter); + switch (hret.h_addrtype) + { + case AF_INET: + for (i = 0; i < counter; i++) + { + addr_arr[i] = cpnet_newIPV4Address(env); + cpnet_bytesToIPV4Address(addr_arr[i], (jbyte *)hret.h_addr_list[i]); + } + break; + case AF_INET6: + for (i = 0; i < counter; i++) + { + addr_arr[i] = cpnet_newIPV6Address(env); + cpnet_bytesToIPV6Address(addr_arr[i], (jbyte *)hret.h_addr_list[i]); + } + break; + default: + *addresses_count = 0; + JCL_free(env, addr_arr); + break; + } + + JCL_free(env, buf); + + return 0; +} + +jint cpnet_getHostByAddr (JNIEnv *env UNUSED, cpnet_address *addr, char *hostname, jint hostname_len) +{ + union + { + struct sockaddr_in *addr_v4; + struct sockaddr_in6 *addr_v6; + char *data; + } haddr; + void *raw_addr; + int addr_type; + struct hostent *ret; + int addr_len; + + haddr.data = addr->data; + + if (haddr.addr_v4->sin_family == AF_INET) + { + raw_addr = &haddr.addr_v4->sin_addr; + addr_len = sizeof(haddr.addr_v4->sin_addr); + addr_type = AF_INET; + } + else if (haddr.addr_v6->sin6_family == AF_INET6) + { + raw_addr = &haddr.addr_v6->sin6_addr; + addr_type = AF_INET6; + addr_len = sizeof(haddr.addr_v6->sin6_addr); + } + else + return EINVAL; + + /* Here we do not have any thread safe call. VM implementors will have to + * do a big lock. Or it should be put on the Classpath VM interface. + */ + ret = gethostbyaddr(raw_addr, addr_len, addr_type); + if (ret == NULL) + { + /* The trouble here is how to distinguish the two cases ? */ + if (h_errno != 0) + return h_errno; + else + return errno; + + } + strncpy(hostname, ret->h_name, hostname_len); + + return 0; +} + +void cpnet_freeAddresses(JNIEnv * env, cpnet_address **addr, jint addresses_count) +{ + jint i; + + for (i = 0; i < addresses_count; i++) + cpnet_freeAddress(env, addr[i]); +} diff --git a/native/jni/native-lib/cpnet.h b/native/jni/native-lib/cpnet.h new file mode 100644 index 000000000..0c7c215f8 --- /dev/null +++ b/native/jni/native-lib/cpnet.h @@ -0,0 +1,208 @@ +/* cpnet.h - + Copyright (C) 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. */ + +#ifndef _CLASSPATH_NET_H_INCLUDED +#define _CLASSPATH_NET_H_INCLUDED + +#include <jni.h> +#include <jcl.h> +#include <string.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/ip.h> + +typedef struct { + jint len; + char data[1]; +} cpnet_address; + +#define CPNET_SHUTDOWN_READ 1 +#define CPNET_SHUTDOWN_WRITE 2 + +JNIEXPORT jint cpnet_openSocketStream(JNIEnv *env, jint *fd, jint family); +JNIEXPORT jint cpnet_openSocketDatagram(JNIEnv *env, jint *fd, jint family); +JNIEXPORT jint cpnet_shutdown (JNIEnv *env, jint fd, jbyte flag); +JNIEXPORT jint cpnet_close(JNIEnv *env, jint fd); +JNIEXPORT jint cpnet_listen(JNIEnv *env, jint fd, jint queuelen); +JNIEXPORT jint cpnet_accept(JNIEnv *env, jint fd, jint *newfd); +JNIEXPORT jint cpnet_bind(JNIEnv *env, jint fd, cpnet_address *addr); +JNIEXPORT jint cpnet_connect(JNIEnv *env, jint fd, cpnet_address *addr); +JNIEXPORT jint cpnet_getLocalAddr(JNIEnv *env, jint fd, cpnet_address **addr); +JNIEXPORT jint cpnet_getRemoteAddr(JNIEnv *env, jint fd, cpnet_address **addr); +JNIEXPORT jint cpnet_setBroadcast(JNIEnv *env, jint fd, jint flag); +JNIEXPORT jint cpnet_send (JNIEnv *env, jint fd, jbyte *data, jint len, jint *bytes_sent); +JNIEXPORT jint cpnet_sendTo (JNIEnv *env, jint fd, jbyte *data, jint len, cpnet_address *addr, jint *bytes_sent); +JNIEXPORT jint cpnet_recv (JNIEnv *env, jint fd, jbyte *data, jint len, jint *bytes_recv); +JNIEXPORT jint cpnet_recvFrom (JNIEnv *env, jint fd, jbyte *data, jint len, cpnet_address **addr, jint *bytes_recv); +JNIEXPORT jint cpnet_setSocketTCPNoDelay (JNIEnv *env, jint fd, jint nodelay); +JNIEXPORT jint cpnet_getSocketTCPNoDelay (JNIEnv *env, jint fd, jint *nodelay); +JNIEXPORT jint cpnet_setLinger (JNIEnv *env, jint fd, jint flag, jint value); +JNIEXPORT jint cpnet_getLinger (JNIEnv *env, jint fd, jint *flag, jint *value); +JNIEXPORT jint cpnet_setSocketTimeout (JNIEnv *env, jint fd, jint value); +JNIEXPORT jint cpnet_getSocketTimeout (JNIEnv *env, jint fd, jint *value); +JNIEXPORT jint cpnet_setSendBuf (JNIEnv *env, jint fd, jint value); +JNIEXPORT jint cpnet_getSendBuf (JNIEnv *env, jint fd, jint *value); +JNIEXPORT jint cpnet_setRecvBuf (JNIEnv *env, jint fd, jint value); +JNIEXPORT jint cpnet_getRecvBuf (JNIEnv *env, jint fd, jint *value); +JNIEXPORT jint cpnet_setTTL (JNIEnv *env, jint fd, jint value); +JNIEXPORT jint cpnet_getTTL (JNIEnv *env, jint fd, jint *value); +JNIEXPORT jint cpnet_setMulticastIF (JNIEnv *env, jint fd, cpnet_address *addr); +JNIEXPORT jint cpnet_getMulticastIF (JNIEnv *env, jint fd, cpnet_address **addr); +JNIEXPORT jint cpnet_setReuseAddress (JNIEnv *env, jint fd, jint reuse); +JNIEXPORT jint cpnet_getReuseAddress (JNIEnv *env, jint fd, jint *reuse); +JNIEXPORT jint cpnet_setKeepAlive (JNIEnv *env, jint fd, jint keep); +JNIEXPORT jint cpnet_getKeepAlive (JNIEnv *env, jint fd, jint *keep); +JNIEXPORT jint cpnet_getBindAddress (JNIEnv *env, jint fd, cpnet_address **addr); +JNIEXPORT jint cpnet_addMembership (JNIEnv *env, jint fd, cpnet_address *addr); +JNIEXPORT jint cpnet_dropMembership (JNIEnv *env, jint fd, cpnet_address *addr); +JNIEXPORT jint cpnet_getAvailableBytes (JNIEnv *env, jint fd, jint *availableBytes); +JNIEXPORT jint cpnet_getHostname (JNIEnv *env, char *hostname, jint hostname_len); +JNIEXPORT jint cpnet_getHostByName (JNIEnv *env, const char *hostname, cpnet_address ***adresses, jint *addresses_count); +JNIEXPORT jint cpnet_getHostByAddr (JNIEnv *env, cpnet_address *addr, char *hostname, jint hostname_len); +JNIEXPORT void cpnet_freeAddresses(JNIEnv * env, cpnet_address **addr, jint addresses_count); + +static inline cpnet_address *cpnet_newIPV4Address(JNIEnv * env) +{ + cpnet_address *addr = (cpnet_address *)JCL_malloc(env, sizeof(cpnet_address) + sizeof(struct sockaddr_in)); + struct sockaddr_in *netaddr = (struct sockaddr_in *)&(addr->data[0]); + + addr->len = sizeof(struct sockaddr_in); + memset(netaddr, 0, addr->len); + netaddr->sin_family = AF_INET; + return addr; +} + +static inline void cpnet_setIPV4Any(cpnet_address *addr) +{ + struct sockaddr_in *netaddr = (struct sockaddr_in *)&(addr->data[0]); + + netaddr->sin_addr.s_addr = INADDR_ANY; +} + +static inline cpnet_address *cpnet_newIPV6Address(JNIEnv * env) +{ + cpnet_address * addr = (cpnet_address *)JCL_malloc(env, sizeof(cpnet_address) + sizeof(struct sockaddr_in6)); + struct sockaddr_in6 *netaddr = (struct sockaddr_in6 *)&(addr->data[0]); + + addr->len = sizeof(struct sockaddr_in6); + memset(netaddr, 0, addr->len); + netaddr->sin6_family = AF_INET6; + + return addr; +} + +static inline void cpnet_freeAddress(JNIEnv * env, cpnet_address *addr) +{ + JCL_free(env, addr); +} + +static inline void cpnet_addressSetPort(cpnet_address *addr, jint port) +{ + struct sockaddr_in *ipaddr = (struct sockaddr_in *)&(addr->data[0]); + + ipaddr->sin_port = htons(port); +} + +static inline jint cpnet_addressGetPort(cpnet_address *addr) +{ + struct sockaddr_in *ipaddr = (struct sockaddr_in *)&(addr->data[0]); + + return ntohs(ipaddr->sin_port); +} + +static inline jboolean cpnet_isAddressEqual(cpnet_address *addr1, cpnet_address *addr2) +{ + if (addr1->len != addr2->len) + return JNI_FALSE; + + return memcmp(addr1->data, addr2->data, addr1->len) == 0; +} + +static inline jboolean cpnet_isIPV6Address(cpnet_address *addr) +{ + struct sockaddr_in *ipaddr = (struct sockaddr_in *)&(addr->data[0]); + + return ipaddr->sin_family == AF_INET6; +} + +static inline jboolean cpnet_isIPV4Address(cpnet_address *addr) +{ + struct sockaddr_in *ipaddr = (struct sockaddr_in *)&(addr->data[0]); + + return ipaddr->sin_family == AF_INET; +} + +static inline void cpnet_IPV4AddressToBytes(cpnet_address *netaddr, jbyte *octets) +{ + struct sockaddr_in *ipaddr = (struct sockaddr_in *)&(netaddr->data[0]); + unsigned long sysaddr = ntohl(ipaddr->sin_addr.s_addr); + + octets[0] = ((sysaddr >> 24) & 0xff); + octets[1] = ((sysaddr >> 16) & 0xff); + octets[2] = ((sysaddr >> 8) & 0xff); + octets[3] = (sysaddr & 0xff); +} + +static inline void cpnet_bytesToIPV4Address(cpnet_address *netaddr, jbyte *octets) +{ + jint sysaddr; + struct sockaddr_in *ipaddr = (struct sockaddr_in *)&(netaddr->data[0]); + + sysaddr = ((jint)(unsigned char)octets[0]) << 24; + sysaddr |= ((jint)(unsigned char)octets[1]) << 16; + sysaddr |= ((jint)(unsigned char)octets[2]) << 8; + sysaddr |= ((jint)(unsigned char)octets[3]); + + ipaddr->sin_addr.s_addr = htonl(sysaddr); +} + +static inline void cpnet_IPV6AddressToBytes(cpnet_address *netaddr, jbyte *octets) +{ + struct sockaddr_in6 *ipaddr = (struct sockaddr_in6 *)&(netaddr->data[0]); + + memcpy(octets, &ipaddr->sin6_addr, 16); +} + +static inline void cpnet_bytesToIPV6Address(cpnet_address *netaddr, jbyte *octets) +{ + struct sockaddr_in6 *ipaddr = (struct sockaddr_in6 *)&(netaddr->data[0]); + + memcpy(&ipaddr->sin6_addr, octets, 16); +} + +#endif diff --git a/native/jni/native-lib/cpproc.c b/native/jni/native-lib/cpproc.c new file mode 100644 index 000000000..b6e9030b5 --- /dev/null +++ b/native/jni/native-lib/cpproc.c @@ -0,0 +1,136 @@ +/* cpproc.c - + Copyright (C) 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. */ + +#include "config.h" +#include <jni.h> +#include "cpproc.h" +#include <signal.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <unistd.h> +#include <errno.h> +#include <stdlib.h> + +static void close_all_fds(int *fds, int numFds) +{ + int i; + + for (i = 0; i < numFds; i++) + close(fds[i]); +} + +int cpproc_forkAndExec (char * const *commandLine, char * const * newEnviron, + int *fds, int pipe_count, pid_t *out_pid, const char *wd) +{ + int local_fds[6]; + int i; + pid_t pid; + + for (i = 0; i < (pipe_count * 2); i += 2) + { + if (pipe(&local_fds[i]) < 0) + { + int err = errno; + + close_all_fds(local_fds, i); + + return err; + } + } + + pid = fork(); + + switch (pid) + { + case 0: + dup2(local_fds[0], 0); + dup2(local_fds[3], 1); + if (pipe_count == 3) + dup2(local_fds[5], 2); + else + dup2(1, 2); + + close_all_fds(local_fds, pipe_count * 2); + + chdir(wd); + if (newEnviron == NULL) + execvp(commandLine[0], commandLine); + else + execve(commandLine[0], commandLine, newEnviron); + + abort(); + + break; + case -1: + { + int err = errno; + + close_all_fds(local_fds, pipe_count * 2); + return err; + } + default: + close(local_fds[0]); + close(local_fds[3]); + if (pipe_count == 3) + close(local_fds[5]); + + fds[0] = local_fds[1]; + fds[1] = local_fds[2]; + fds[2] = local_fds[4]; + *out_pid = pid; + return 0; + } +} + +int cpproc_waitpid (pid_t pid, int *status, pid_t *outpid, int options) +{ + pid_t wp = waitpid(pid, status, options); + + if (wp < 0) + return errno; + + *outpid = wp; + return 0; +} + +int cpproc_kill (pid_t pid, int signal) +{ + if (kill(pid, signal) < 0) + return errno; + + return 0; +} diff --git a/native/jni/native-lib/cpproc.h b/native/jni/native-lib/cpproc.h new file mode 100644 index 000000000..5e8db5800 --- /dev/null +++ b/native/jni/native-lib/cpproc.h @@ -0,0 +1,52 @@ +/* cpproc.h - + 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. */ +#ifndef _CLASSPATH_PROC_H_INCLUDED +#define _CLASSPATH_PROC_H_INCLUDED + +#include <sys/types.h> + +#define CPIO_EXEC_STDIN 0 +#define CPIO_EXEC_STDOUT 1 +#define CPIO_EXEC_STDERR 2 +#define CPIO_EXEC_NUM_PIPES 3 + +JNIEXPORT int cpproc_forkAndExec (char * const *commandLine, char * const * newEnviron, + int *fds, int pipe_count, pid_t *pid, const char *wd); +JNIEXPORT int cpproc_waitpid (pid_t pid, int *status, pid_t *outpid, int options); +JNIEXPORT int cpproc_kill (pid_t pid, int signal); + +#endif diff --git a/native/plugin/Makefile.am b/native/plugin/Makefile.am index 59c4cb3f3..db94bb411 100644 --- a/native/plugin/Makefile.am +++ b/native/plugin/Makefile.am @@ -3,7 +3,7 @@ nativeexeclib_LTLIBRARIES = libgcjwebplugin.la libgcjwebplugin_la_SOURCES = gcjwebplugin.cc libgcjwebplugin_la_CXXFLAGS = \ - -Wall -DAPPLETVIEWER_EXECUTABLE="\"$(bindir)/gappletviewer\"" \ + -Wall -DAPPLETVIEWER_EXECUTABLE="\"$(bindir)/`echo gappletviewer | sed '$(program_transform_name)'`\"" \ $(MOZILLA_CFLAGS) $(GLIB_CFLAGS) $(GTK_CFLAGS) libgcjwebplugin_la_LDFLAGS = -avoid-version \ diff --git a/scripts/check_jni_methods.sh b/scripts/check_jni_methods.sh index ffdc7b571..4cc00cc17 100755 --- a/scripts/check_jni_methods.sh +++ b/scripts/check_jni_methods.sh @@ -2,8 +2,6 @@ # Fail if any command fails set -e -# Don't override existing files -set -C TMPFILE=/tmp/check-jni-methods.$$.1 TMPFILE2=/tmp/check-jni-methods.$$.2 diff --git a/tools/Makefile.am b/tools/Makefile.am index 2c1ceacce..b6bc4fbe2 100755 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -22,62 +22,47 @@ bin_SCRIPTS = bin_PROGRAMS = gappletviewer gjarsigner gkeytool \ gjar gnative2ascii gserialver $(javah) -#if FOUND_GCJ -#LIBJVM = -lgcj -#else -if FOUND_CACAO -LIBJVM = -ljvm -else -LIBJVM = -endif -#endif AM_CPPFLAGS = -Wall \ -I$(top_srcdir)/include \ + -DLIBJVM="\"$(libdir)/libjvm\"" \ -DTOOLS_ZIP="\"$(TOOLSdir)/$(TOOLS_ZIP)\"" gappletviewer_SOURCES = toolwrapper.c gappletviewer_CFLAGS = \ -DTOOLPACKAGE="\"appletviewer\"" \ -DTOOLNAME="\"gappletviewer\"" -gappletviewer_LDFLAGS = -L$(libdir) $(LIBJVM) gjarsigner_SOURCES = toolwrapper.c gjarsigner_CFLAGS = \ -DTOOLPACKAGE="\"jarsigner\"" \ -DTOOLNAME="\"gjarsigner\"" -gjarsigner_LDFLAGS = -L$(libdir) $(LIBJVM) gkeytool_SOURCES = toolwrapper.c gkeytool_CFLAGS = \ -DTOOLPACKAGE="\"keytool\"" \ -DTOOLNAME="\"gkeytool\"" -gkeytool_LDFLAGS = -L$(libdir) $(LIBJVM) gjar_SOURCES = toolwrapper.c gjar_CFLAGS = \ -DTOOLPACKAGE="\"jar\"" \ -DTOOLNAME="\"gjar\"" -gjar_LDFLAGS = -L$(libdir) $(LIBJVM) gnative2ascii_SOURCES = toolwrapper.c gnative2ascii_CFLAGS = \ -DTOOLPACKAGE="\"native2ascii\"" \ -DTOOLNAME="\"gnative2ascii\"" -gnative2ascii_LDFLAGS = -L$(libdir) $(LIBJVM) gserialver_SOURCES = toolwrapper.c gserialver_CFLAGS = \ -DTOOLPACKAGE="\"serialver\"" \ -DTOOLNAME="\"gserialver\"" -gserialver_LDFLAGS = -L$(libdir) $(LIBJVM) if USE_ASM gjavah_SOURCES = toolwrapper.c gjavah_CFLAGS = \ -DTOOLPACKAGE="\"javah\"" \ -DTOOLNAME="\"gjavah\"" -gjavah_LDFLAGS = -L$(libdir) $(LIBJVM) endif else diff --git a/tools/toolwrapper.c b/tools/toolwrapper.c index 9ad14ac6d..9f4720ab9 100644 --- a/tools/toolwrapper.c +++ b/tools/toolwrapper.c @@ -37,6 +37,7 @@ obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include <jni.h> +#include <ltdl.h> #include <string.h> #include <stdlib.h> #include "config.h" @@ -51,6 +52,9 @@ union env_union JNIEnv *jni_env; }; +/* Typedef for JNI_CreateJavaVM dlopen call. */ +typedef jint createVM (JavaVM **, void **, void *); + int main (int argc, const char** argv) { @@ -68,6 +72,10 @@ main (int argc, const char** argv) int non_vm_argc; int i; int classpath_found = 0; + /* Variables for JNI_CreateJavaVM dlopen call. */ + lt_dlhandle libjvm_handle = NULL; + createVM* libjvm_create = NULL; + int libjvm_error = 0; env = NULL; jvm = NULL; @@ -152,7 +160,27 @@ main (int argc, const char** argv) vm_args.version = JNI_VERSION_1_2; vm_args.ignoreUnrecognized = JNI_TRUE; - result = JNI_CreateJavaVM (&jvm, &tmp.void_env, &vm_args); + /* dlopen libjvm.so */ + libjvm_error = lt_dlinit (); + if (libjvm_error) + { + fprintf (stderr, TOOLNAME ": lt_dlinit failed.\n"); + goto destroy; + } + + libjvm_handle = lt_dlopenext (LIBJVM); + if (!libjvm_handle) + { + fprintf (stderr, TOOLNAME ": failed to open " LIBJVM "\n"); + goto destroy; + } + libjvm_create = (createVM*) lt_dlsym (libjvm_handle, "JNI_CreateJavaVM"); + if (!libjvm_create) + { + fprintf (stderr, TOOLNAME ": failed to load JNI_CreateJavaVM symbol from " LIBJVM "\n"); + goto destroy; + } + result = (*libjvm_create) (&jvm, &tmp.void_env, &vm_args); if (result < 0) { @@ -216,5 +244,15 @@ main (int argc, const char** argv) (*jvm)->DestroyJavaVM (jvm); } + /* libltdl cleanup */ + if (libjvm_handle) + { + if (lt_dlclose (libjvm_handle) != 0) + fprintf (stderr, TOOLNAME ": failed to close " LIBJVM "\n"); + } + + if (lt_dlexit () != 0) + fprintf (stderr, TOOLNAME ": lt_dlexit failed.\n"); + return 1; } diff --git a/vm/reference/gnu/classpath/VMStackWalker.java b/vm/reference/gnu/classpath/VMStackWalker.java index e995c4634..2214e7571 100644 --- a/vm/reference/gnu/classpath/VMStackWalker.java +++ b/vm/reference/gnu/classpath/VMStackWalker.java @@ -1,5 +1,5 @@ /* VMStackWalker.java -- Reference implementation of VM hooks for stack access - Copyright (C) 2005 Free Software Foundation + Copyright (C) 2005, 2006 Free Software Foundation This file is part of GNU Classpath. @@ -112,5 +112,20 @@ public final class VMStackWalker * is here to work around access permissions. */ public static native ClassLoader getClassLoader(Class cl); -} + /** + * Walk up the stack and return the first non-null class loader. + * If there aren't any non-null class loaders on the stack, return null. + */ + public static ClassLoader firstNonNullClassLoader() + { + Class[] stack = getClassContext(); + for (int i = 0; i < stack.length; i++) + { + ClassLoader loader = getClassLoader(stack[i]); + if (loader != null) + return loader; + } + return null; + } +} diff --git a/vm/reference/java/io/VMFile.java b/vm/reference/java/io/VMFile.java index 2f48aad71..13d256d42 100644 --- a/vm/reference/java/io/VMFile.java +++ b/vm/reference/java/io/VMFile.java @@ -38,6 +38,9 @@ exception statement from your version. */ package java.io; +import java.net.MalformedURLException; +import java.net.URL; + import gnu.classpath.Configuration; import gnu.java.io.PlatformHelper; @@ -209,6 +212,108 @@ final class VMFile } /** + * Returns the path as an absolute path name. The value returned is the + * current directory plus the separatory string plus the path of the file. + * The current directory is determined from the <code>user.dir</code> system + * property. + * + * @param path the path to convert to absolute path + * + * @return the absolute path that corresponds to <code>path</code> + */ + static String getAbsolutePath(String path) + { + if (File.separatorChar == '\\' + && path.length() > 0 && path.charAt (0) == '\\') + { + // On Windows, even if the path starts with a '\\' it is not + // really absolute until we prefix the drive specifier from + // the current working directory to it. + return System.getProperty ("user.dir").substring (0, 2) + path; + } + else if (File.separatorChar == '\\' + && path.length() > 1 && path.charAt (1) == ':' + && ((path.charAt (0) >= 'a' && path.charAt (0) <= 'z') + || (path.charAt (0) >= 'A' && path.charAt (0) <= 'Z'))) + { + // On Windows, a process has a current working directory for + // each drive and a path like "G:foo\bar" would mean the + // absolute path "G:\wombat\foo\bar" if "\wombat" is the + // working directory on the G drive. + String drvDir = null; + try + { + drvDir = new File (path.substring (0, 2)).getCanonicalPath(); + } + catch (IOException e) + { + drvDir = path.substring (0, 2) + "\\"; + } + + // Note: this would return "C:\\." for the path "C:.", if "\" + // is the working folder on the C drive, but this is + // consistent with what Sun's JRE 1.4.1.01 actually returns! + if (path.length() > 2) + return drvDir + '\\' + path.substring (2, path.length()); + else + return drvDir; + } + else if (path.equals("")) + return System.getProperty ("user.dir"); + else + return System.getProperty ("user.dir") + File.separatorChar + path; + } + + /** + * This method returns true if the path 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 "/". + * + * @param path the path to check + * + * @return <code>true</code> if path represents an absolute file name, + * <code>false</code> otherwise. + */ + static boolean isAbsolute(String path) + { + if (File.separatorChar == '\\') + return path.startsWith(File.separator + File.separator) + || (path.length() > 2 + && ((path.charAt(0) >= 'a' && path.charAt(0) <= 'z') + || (path.charAt(0) >= 'A' && path.charAt(0) <= 'Z')) + && path.charAt(1) == ':' + && path.charAt(2) == '\\'); + else + return path.startsWith(File.separator); + } + + /** + * Returns a <code>URL</code> with the <code>file:</code> + * protocol that represents this file. The exact form of this URL is + * system dependent. + * + * @param file the file to convert to URL + * + * @return a <code>URL</code> for this object. + * + * @throws MalformedURLException if the URL cannot be created + * successfully. + */ + static URL toURL(File file) + throws MalformedURLException + { + // On Win32, Sun's JDK returns URLs of the form "file:/c:/foo/bar.txt", + // while on UNIX, it returns URLs of the form "file:/foo/bar.txt". + if (File.separatorChar == '\\') + return new URL ("file:/" + file.getAbsolutePath().replace ('\\', '/') + + (file.isDirectory() ? "/" : "")); + else + return new URL ("file:" + file.getAbsolutePath() + + (file.isDirectory() ? "/" : "")); + } + + /** * 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 diff --git a/vm/reference/java/io/VMObjectInputStream.java b/vm/reference/java/io/VMObjectInputStream.java index 5fb56fcd4..be0f8eb52 100644 --- a/vm/reference/java/io/VMObjectInputStream.java +++ b/vm/reference/java/io/VMObjectInputStream.java @@ -40,10 +40,7 @@ exception statement from your version. */ package java.io; import gnu.classpath.Configuration; -import gnu.classpath.VMStackWalker; import java.lang.reflect.Constructor; -import java.security.AccessController; -import java.security.PrivilegedAction; final class VMObjectInputStream { @@ -56,42 +53,6 @@ final class VMObjectInputStream } /** - * PrivilegedAction needed for Class.getClassLoader() - */ - private static PrivilegedAction loaderAction = new PrivilegedAction() - { - /** - * Returns the first user defined class loader on the call stack, or the - * context class loader of the current thread, when no non-null class loader - * was found. - */ - public Object run() - { - Class[] ctx = VMStackWalker.getClassContext(); - - for (int i = 0; i < ctx.length; i++) - { - ClassLoader cl = ctx[i].getClassLoader(); - if (cl != null) - return cl; - } - return Thread.currentThread().getContextClassLoader(); - } - }; - - /** - * Returns the first user defined class loader on the call stack, or the - * context class loader of the current thread, when no non-null class loader - * was found. - * - * @return the class loader - */ - static ClassLoader currentClassLoader() - { - return (ClassLoader) AccessController.doPrivileged(loaderAction); - } - - /** * Allocates a new Object of type clazz but without running the * default constructor on it. It then calls the given constructor on * it. The given constructor method comes from the constr_clazz |