From 237bdd93058f891ae4567cf4e051c5831bd4646b Mon Sep 17 00:00:00 2001 From: Andrew John Hughes Date: Sat, 4 Nov 2006 18:55:49 +0000 Subject: 2006-11-04 Andrew John Hughes * Merge of HEAD-->generics-branch for 2006/10/29 to 2006/11/04. --- ChangeLog | 293 ++++++ gnu/java/awt/peer/ClasspathFontPeer.java | 14 - gnu/java/awt/peer/gtk/CairoGraphics2D.java | 2 +- gnu/java/awt/peer/gtk/FreetypeGlyphVector.java | 21 +- gnu/java/awt/peer/gtk/GdkFontMetrics.java | 16 +- gnu/java/awt/peer/gtk/GdkFontPeer.java | 12 +- gnu/java/awt/peer/gtk/GtkWindowPeer.java | 3 + gnu/java/awt/peer/qt/QtFontPeer.java | 11 - gnu/java/awt/peer/x/XFontPeer.java | 7 - gnu/java/awt/peer/x/XFontPeer2.java | 6 - java/awt/Font.java | 25 +- java/awt/GridBagLayout.java | 10 + java/awt/ScrollPaneAdjustable.java | 21 +- java/awt/TextArea.java | 68 +- java/awt/datatransfer/DataFlavor.java | 280 +++--- java/awt/datatransfer/MimeType.java | 281 ++++++ java/awt/dnd/DragGestureRecognizer.java | 9 +- java/awt/dnd/DragSourceContext.java | 46 +- java/awt/event/ComponentEvent.java | 39 +- java/awt/geom/GeneralPath.java | 39 +- javax/swing/JEditorPane.java | 7 + javax/swing/JTextField.java | 3 +- javax/swing/text/FlowView.java | 136 ++- javax/swing/text/GlyphView.java | 93 +- javax/swing/text/Utilities.java | 5 +- javax/swing/text/html/FormView.java | 166 +++- javax/swing/text/html/HTMLDocument.java | 225 ++++- javax/swing/text/html/HTMLEditorKit.java | 181 +++- javax/swing/text/html/HTMLWriter.java | 1126 ++++++++++++++++++++++++ javax/swing/text/html/ImageView.java | 25 +- javax/swing/text/html/StyleSheet.java | 21 +- native/plugin/gcjwebplugin.cc | 9 +- 32 files changed, 2716 insertions(+), 484 deletions(-) create mode 100644 java/awt/datatransfer/MimeType.java create mode 100644 javax/swing/text/html/HTMLWriter.java diff --git a/ChangeLog b/ChangeLog index 97fd1ba36..e19e1bb74 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,296 @@ +2006-11-03 Tania Bento + + * java/awt/TextArea.java + (getMinimumSize): Changed documentation. + (getPreferredSize): Changed documentation. + (getMinimumSize(int,int)): Changed documenation. + (getPreferredSize(int,int)): Changed documenation. + (minimumSize): Changed documentation. + (preferredSize): Changed documenation. + (minimumSize(int,int)): Changed documentation. Checked if + minimum size had been previously set and changed values of + Dimension returned if peer == null. + (preferredSize(int, int)): Checked if preferred size had been + previously set and changed values of Dimension returned if + peer = null. + +2006-11-03 Tania Bento + + * java/awt/event/ComponentEvent.java + (paramString): Changed format of string representation returned. + +2006-11-03 Roman Kennke + + * javax/swing/text/html/HTMLDocument.java + (HTMLReader.FormAction.start): Added support for textarea. + (HTMLReader.FormAction.end): Added support for textarea. + (HTMLReader.HeadAction.end): Call super to actually close the + block. + (HTMLReader.inTextArea): New field. + (HTMLReader.textAreaDocument): New field. + (HTMLReader.handleText): Call textAreaContent when inside + a textarea tag. + (HTMLReader.textAreaContent): Implemented to initialize + the text area's model. + * javax/swing/text/html/FormView.java + (createComponent): Added support for textarea tag. + +2006-11-03 Roman Kennke + + * javax/swing/text/html/HTMLDocument.java + (HTMLReader.IsindexAction.start): Implemented. + +2006-11-03 Roman Kennke + + * javax/swing/text/html/StyleSheet.java + (addRule): Implemented. + * javax/swing/text/html/HTMLDocument.java + (HTMLReader.inStyleTag): New field. + (HTMLReader.styles): New field. + (HTMLReader.HeadAction.end): Implemented to read all stylesheets, + if any. + (HTMLReader.StyleAction.start): Set inStyleTag flag. + (HTMLReader.StyleAction.end): Set inStyleTag flag. + (HTMLReader.handleText): When inside a style tag, add + content to the styles array. + +2006-11-02 Roman Kennke + + * javax/swing/text/html/FormView.java + (maxIsPreferred): New field. + (createComponent): Initialize components correctly. + (getMaximumSpan): Return the preferred span for components + that need this. The maxIsPreferred flag is set accordingly + in createComponent. + * javax/swing/text/html/HTMLDocument.java + (HTMLReader.FormAction.start): Implemented to set the + correct model as attribute. + (HTMLReader.FormAction.setModel): New helper method. + (HTMLReader.FormAction.end): Call super to finish the element. + Added TODO about things left to do. + (HTMLReader.handleComment): Use SimpleAttributeSet rather + than htmlAttributeSet. + * javax/swing/text/html/HTMLEditorKit.java + (HTMLFactory.create): Create BlockView for FORM tags. + Create FormView for INPUT, TEXTAREA and SELECT tags. + +2006-11-02 David Gilbert + + * java/awt/geom/GeneralPath.java: API doc fixes. + +2006-11-02 Roman Kennke + + * javax/swing/text/html/ImageView.java + (getImageURL): Fetch attribute from element. Consider the + base URL for relative image locations. + +2006-11-02 Roman Kennke + + * javax/swing/JEditorPane.java + (setContentType): Strip off attributes. + * javax/swing/text/html/HTMLEditorKit.java + (LinkController.activateLink(int,JEditorPane,int,int): New + method. Implements activation of a hyperlink. + (LinkController.activateLinke(int,JEditorPane)): Delegate + to the other activateLink() method. + (LinkController.createHyperlinkEvent): New helper method. + (LinkController.mouseClicked): Implemented to activate the link. + (LinkController.mouseDragged): Added comment that this + method does nothing. + (LinkController.mouseMoved): Update cursor for hyperlinks. + (mouseHandler): Renamed field to linkController. + (HTMLEditorKit): Create a link controller. + (clone): Give the clone a new link controller. + (deinstall): De-install link controller as mouseMotionListener too. + (install): Install link controller as mouseMotionListener too. + +2006-11-02 Roman Kennke + + PR 29644 + * javax/swing/text/FlowView.java + (FlowStrategy.changedUpdate): Reversed condition. This caused + wrong layout and bad performance. + (FlowStrategy.insertUpdate): Reversed condition. This caused + wrong layout and bad performance. + (FlowStrategy.removeUpdate): Reversed condition. This caused + wrong layout and bad performance. + (LogicalView): Changed to be a subclass of CompositeView. + (LogicalView()): Only take one Element argument. + (LogicalView.childAllocation): New method for implementing + the abstract CompositeView method. + (LogicalView.forwardUpdateToView): Overridden for correct + reparenting. + (getMinimumSpan): Overridden to handle line breaking correctly. + (getPreferredSpan): Implemented to handle line breaking correctly. + (getViewAtPoint): New method for implementing + the abstract CompositeView method. + (getViewIndexAtPosition): Overridden to handle leaf elements + correctly. + (isAfter): New method for implementing + the abstract CompositeView method. + (isBefore): New method for implementing + the abstract CompositeView method. + (loadChildren): Overridden to handle leaf elements + correctly. + (paint): New method for implementing + the abstract CompositeView method. + (calculateMinorAxisRequirements): Use preferredSpan in calculation. + (loadChildren): Initialize flow layout by sending a synthetic + insertUpdate() to the layout strategy. + * javax/swing/text/GlyphView.java + (DefaultGlyphPainter.getBoundedPosition): Fall back to Toolkit's + font metrics if component is not available. Add initial offset + to result. + (breakView): Be more clever when breaking the view. + (getBreakLocation): New helper method to determine a good + break location. + (getBreakWeight): Be more clever when breaking the view. + (getTabbedSpan): Make sure we have a painter. Use view's + start and end offset rather than the element's. + * javax/swing/text/Utilities.java + (drawTabbedText): Avoid useless add and sub with the y offset. + +2006-11-02 Roman Kennke + + PR 29644 + * gnu/java/awt/peer/ClasspathFontPeer.java + (getStringBounds): Removed abstract method. This is replaced + in java.awt.Font to use a TextLayout. + * gnu/java/awt/peer/gtk/GtkWindowPeer.java + (GtkWindowPeer): Set a font on the window object. + * gnu/java/awt/peer/gtk/FreetypeGlyphVector.java + (FreetypeGlyphVector(Font,String,FontRenderContext,int)): + Changed to take char,int,int instead of String. Filter + control characters. + (FreetypeGlyphVector(Font,String,FontRenderContext)): + Create char array out of string. + (getLogicalBounds): Don't translate bounds. They already are + translated. + * gnu/java/awt/peer/gtk/GdkFontMetrics.java + (stringWidth): Filter out control characters. + * gnu/java/awt/peer/gtk/GdkFontPeer.java + (getStringBounds): Removed unneeded method. + (layoutGlyphVector): Pass char array directly to FreetypeGlyphVector + constructor. + * gnu/java/awt/peer/qt/QtFontPeer.java + (getStringBounds): Removed unneeded method. + * gnu/java/awt/peer/x/XFontPeer.java + (getStringBounds): Removed unneeded method. + * gnu/java/awt/peer/x/XFontPeer2.java + (getStringBounds): Removed unneeded method. + * java/awt/Font.java + (getStringBounds(char[],int,int,FontRenderContext)): + Use TextLayout to determine the bounds. + (getStringBounds(CharacterIterator,int,int,FontRenderContext)): + Delegate to the char[] version of this method. + (getStringBounds(String,FontRenderContext)): + Delegate to the char[] version of this method. + (getStringBounds(String,int,int,FontRenderContext)): + Delegate to the String version of this method. + +2006-11-01 Tania Bento + + * java/awt/ScrollPaneAdjustable.java + (paramString): Changed format of string representation returned. + (paramStringHelper): New private method. + +2006-11-01 Tania Bento + + * java/awt/GridBagLayout.java + (toString): Implemented method. + +2006-10-30 Thomas Fitzsimmons + + * native/plugin/gcjwebplugin.cc (GCJ_New): Move GLib threading + initialization to NP_Initialize. + (NP_Initialize): Initialize GLib threading. + +2006-10-31 Tania Bento + + * javax/swing/JTextField.java + (fireActionPerformed): When creating the new event, if + actionCommand == null, then getText() is used. + +2006-10-31 Francis Kung + + * gnu/java/awt/peer/gtk/CairoGraphics2D.java + (fillArc): Corrected arc type to Arc2D.PIE. + +2006-10-31 Roman Kennke + + * javax/swing/text/html/HTMLDocument.java + (HTMLReader.PreAction.end): Implemented. + (HTMLReader.PreAction.start): Implemented. + (HTMLReader.inPreTag): New field. + (HTMLReader.handleTag): When inside a pre tag, call preContent(). + (HTMLReader.preContent): Implemented. + +2006-10-31 Tania Bento + + * javax/swing/JTextField.java + (fireActionPerformed): When creating the new event, + actionCommand should be used as the command, not + getText(). + +2006-10-31 Roman Kennke + + * javax/swing/text/html/HTMLEditorKit.java + (write): Use HTMLWriter or MinimalHTMLWriter for writing + HTML or Styled documents. + +2006-10-31 David Fu + + * javax/swing/text/html/HTMLWriter.java: New class. + +2006-10-30 Roman Kennke + + * java/awt/dnd/DragSourceContext.java + (dragExit): Use constant fields instead of 0. + (updateCurrentCursor): Completed implementation. + +2006-10-30 Roman Kennke + + * java/awt/dnd/DragGestureRecognizer.java + (resetRecognizer): Added API docs. Do not replace the events object + but rather clear() it. Removed not implemented tag. + +2006-10-30 Roman Kennke + + * java/awt/datatransfer/DataFlavor.java + (writeExternal): Remove not implemented tag. + +2006-10-30 Roman Kennke + + * java/awt/datatransfer/DataFlavor.java + (javaFileListFlavor): Don't explicitly specify class. + (plainTextFlavor): Don't explicitly specify class. + (mimeType): Changed to type MimeType. Remove final. + (representationClass): Remove final. + (DataFlavor): Don't do anything here. + (DataFlavor(Class,String,String)): Removed. + (DataFlavor(Class,String)): Initialize here. + (DataFlavor(String,String,ClassLoader)): Initialize in init(). + (DataFlavor(String,String)): Initialize in init(). + (DataFlavor(String)): Initialize in init(). + (init): New initialization method. + (getMimeType): Delegate to MimeType.toString(). + (getParameter(String,String)): Removed. Is now done in MimeType. + (getParameter(String)): Delegate to MimeType. + (getPrimaryType): Delegate to MimeType. + (getRepresentationClassFromMime): Removed. + (getRepresentationClassFromMimeThrows): Removed. + (getSubType): Delegate to MimeType. + (hashCode): Take MimeType.toString() for the hashCode. + (isFlavorRemoveObjectType): Return true only when representation + class is remove and serializable and the mime type is remote. + (isFlavorSerializedObjectType): Return true only when representation + class is serializable and the mime type is serialized. + (isMimeTypeEqual): Rewritten to delegate to MimeType.matches(). + (isMimeTypeSerializedObject): Delegate to isMimeTypeEqual(). + (readExternal): Implemented stub method. + (writeExternal): Implemented stub method. + * java/awt/datatransfer/MimeType.java: New helper class. + 2006-10-28 Roman Kennke * javax/swing/TransferHandler.java diff --git a/gnu/java/awt/peer/ClasspathFontPeer.java b/gnu/java/awt/peer/ClasspathFontPeer.java index dad7bb0b0..2176f34a5 100644 --- a/gnu/java/awt/peer/ClasspathFontPeer.java +++ b/gnu/java/awt/peer/ClasspathFontPeer.java @@ -832,18 +832,4 @@ public abstract class ClasspathFontPeer public abstract Rectangle2D getMaxCharBounds (Font font, FontRenderContext rc); - /** - * Implementation of {@link Font#getStringBounds(CharacterIterator, int, - * int, FontRenderContext)} - * - * @param font the font this peer is being called from. This may be - * useful if you are sharing peers between Font objects. Otherwise it may - * be ignored. - */ - - public abstract Rectangle2D getStringBounds (Font font, - CharacterIterator ci, - int begin, int limit, - FontRenderContext frc); - } diff --git a/gnu/java/awt/peer/gtk/CairoGraphics2D.java b/gnu/java/awt/peer/gtk/CairoGraphics2D.java index 4c60336a3..7f55747c6 100644 --- a/gnu/java/awt/peer/gtk/CairoGraphics2D.java +++ b/gnu/java/awt/peer/gtk/CairoGraphics2D.java @@ -1154,7 +1154,7 @@ public abstract class CairoGraphics2D extends Graphics2D { fill(new Arc2D.Double((double) x, (double) y, (double) width, (double) height, (double) startAngle, - (double) arcAngle, Arc2D.OPEN)); + (double) arcAngle, Arc2D.PIE)); } public void fillRect(int x, int y, int width, int height) diff --git a/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java b/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java index 1449cdf61..98f1bd69b 100644 --- a/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java +++ b/gnu/java/awt/peer/gtk/FreetypeGlyphVector.java @@ -92,16 +92,23 @@ public class FreetypeGlyphVector extends GlyphVector */ public FreetypeGlyphVector(Font f, String s, FontRenderContext frc) { - this(f, s, frc, Font.LAYOUT_LEFT_TO_RIGHT); + this(f, s.toCharArray(), 0, s.length(), frc, Font.LAYOUT_LEFT_TO_RIGHT); } /** * Create a glyphvector from a given (Freetype) font and a String. */ - public FreetypeGlyphVector(Font f, String s, FontRenderContext frc, - int flags) + public FreetypeGlyphVector(Font f, char[] chars, int start, int len, + FontRenderContext frc, int flags) { - this.s = s; + // We need to filter out control characters (and possibly other + // non-renderable characters here). + StringBuilder b = new StringBuilder(chars.length); + for (int i = start; i < start + len; i++) + if (!Character.isISOControl(chars[i])) + b.append(chars[i]); + this.s = b.toString(); + this.font = f; this.frc = frc; if( !(font.getPeer() instanceof GdkFontPeer ) ) @@ -429,12 +436,6 @@ public class FreetypeGlyphVector extends GlyphVector { Rectangle2D r2 = (Rectangle2D)getGlyphLogicalBounds( i ); - // Translate to the glyph's position - tx.setToTranslation(glyphPositions[i*2], glyphPositions[i*2+1]); - Point2D pt = new Point2D.Double(r2.getMinX(), r2.getMinY()); - tx.transform(pt, pt); - r2.setRect(pt.getX(), pt.getY(), r2.getWidth(), r2.getHeight()); - rect = rect.createUnion( r2 ); } diff --git a/gnu/java/awt/peer/gtk/GdkFontMetrics.java b/gnu/java/awt/peer/gtk/GdkFontMetrics.java index b2ffed10e..30b1ff15e 100644 --- a/gnu/java/awt/peer/gtk/GdkFontMetrics.java +++ b/gnu/java/awt/peer/gtk/GdkFontMetrics.java @@ -95,8 +95,22 @@ public class GdkFontMetrics extends FontMetrics public int stringWidth (String str) { + +// TextLayout layout = new TextLayout(str, getFont(), null); +// return (int) layout.getAdvance(); + // We need to filter control chars, because they are never displayed + // in Java and thus also have no metrics. + int len = str.length(); + StringBuilder b = new StringBuilder(len); + for (int i = 0; i < len; i++) + { + char ch = str.charAt(i); + if (! Character.isISOControl(ch)) + b.append(ch); + } double [] hires = new double[6]; - peer.getTextMetrics(str, hires); + String string = b.toString(); + peer.getTextMetrics(string, hires); return (int) hires [TEXT_METRICS_WIDTH]; } diff --git a/gnu/java/awt/peer/gtk/GdkFontPeer.java b/gnu/java/awt/peer/gtk/GdkFontPeer.java index 11635c354..40b234984 100644 --- a/gnu/java/awt/peer/gtk/GdkFontPeer.java +++ b/gnu/java/awt/peer/gtk/GdkFontPeer.java @@ -345,15 +345,6 @@ public class GdkFontPeer extends ClasspathFontPeer return buf.getShort(4); } - public Rectangle2D getStringBounds (Font font, CharacterIterator ci, - int begin, int limit, FontRenderContext frc) - { - GlyphVector gv = new FreetypeGlyphVector( font, - buildString(ci, begin, limit), - frc); - return gv.getVisualBounds(); - } - public boolean hasUniformLineMetrics (Font font) { return true; @@ -363,8 +354,7 @@ public class GdkFontPeer extends ClasspathFontPeer char[] chars, int start, int limit, int flags) { - return new FreetypeGlyphVector( font, new String( chars, start, - limit - start), + return new FreetypeGlyphVector( font, chars, start, limit - start, frc, flags); } diff --git a/gnu/java/awt/peer/gtk/GtkWindowPeer.java b/gnu/java/awt/peer/gtk/GtkWindowPeer.java index 1abc5ca84..8d49719b1 100644 --- a/gnu/java/awt/peer/gtk/GtkWindowPeer.java +++ b/gnu/java/awt/peer/gtk/GtkWindowPeer.java @@ -41,6 +41,7 @@ package gnu.java.awt.peer.gtk; import gnu.java.awt.ComponentReshapeEvent; import java.awt.Component; +import java.awt.Font; import java.awt.Frame; import java.awt.Graphics; import java.awt.KeyboardFocusManager; @@ -151,6 +152,8 @@ public class GtkWindowPeer extends GtkContainerPeer public GtkWindowPeer (Window window) { super (window); + // Set reasonable font for the window. + window.setFont(new Font("Dialog", Font.PLAIN, 12)); } public native void toBack(); diff --git a/gnu/java/awt/peer/qt/QtFontPeer.java b/gnu/java/awt/peer/qt/QtFontPeer.java index d847a8053..6ffe3f691 100644 --- a/gnu/java/awt/peer/qt/QtFontPeer.java +++ b/gnu/java/awt/peer/qt/QtFontPeer.java @@ -194,15 +194,4 @@ public class QtFontPeer extends ClasspathFontPeer throw new UnsupportedOperationException(); } - public Rectangle2D getStringBounds (Font font, - CharacterIterator ci, - int begin, int limit, - FontRenderContext frc) - { - int index = begin; - String s = "" + ci.setIndex( index ); - while( index++ <= limit ) - s = s + ci.next(); - return metrics.getStringBounds(s); - } } diff --git a/gnu/java/awt/peer/x/XFontPeer.java b/gnu/java/awt/peer/x/XFontPeer.java index fd293d8dd..8183fed0c 100644 --- a/gnu/java/awt/peer/x/XFontPeer.java +++ b/gnu/java/awt/peer/x/XFontPeer.java @@ -608,13 +608,6 @@ public class XFontPeer throw new UnsupportedOperationException("Not yet implemented."); } - public Rectangle2D getStringBounds(Font font, CharacterIterator ci, - int begin, int limit, FontRenderContext frc) - { - // TODO: Implement this. - throw new UnsupportedOperationException("Not yet implemented."); - } - /** * Encodes a font name + style + size specification into a X logical font * description (XLFD) as described here: diff --git a/gnu/java/awt/peer/x/XFontPeer2.java b/gnu/java/awt/peer/x/XFontPeer2.java index 25371de1a..ef9507f30 100644 --- a/gnu/java/awt/peer/x/XFontPeer2.java +++ b/gnu/java/awt/peer/x/XFontPeer2.java @@ -326,10 +326,4 @@ public class XFontPeer2 throw new UnsupportedOperationException("Not yet implemented"); } - public Rectangle2D getStringBounds(Font font, CharacterIterator ci, int begin, int limit, FontRenderContext frc) - { - // FIXME: Implement this. - throw new UnsupportedOperationException("Not yet implemented"); - } - } diff --git a/java/awt/Font.java b/java/awt/Font.java index e630207d5..29b87d6af 100644 --- a/java/awt/Font.java +++ b/java/awt/Font.java @@ -1087,7 +1087,8 @@ public class Font implements Serializable */ public Rectangle2D getStringBounds(String str, FontRenderContext frc) { - return getStringBounds(str, 0, str.length() - 1, frc); + char[] chars = str.toCharArray(); + return getStringBounds(chars, 0, chars.length, frc); } /** @@ -1115,8 +1116,8 @@ public class Font implements Serializable public Rectangle2D getStringBounds(String str, int begin, int limit, FontRenderContext frc) { - return peer.getStringBounds(this, new StringCharacterIterator(str), begin, - limit, frc); + String sub = str.substring(begin, limit); + return getStringBounds(sub, frc); } /** @@ -1144,7 +1145,16 @@ public class Font implements Serializable public Rectangle2D getStringBounds(CharacterIterator ci, int begin, int limit, FontRenderContext frc) { - return peer.getStringBounds(this, ci, begin, limit, frc); + int start = ci.getBeginIndex(); + int end = ci.getEndIndex(); + char[] chars = new char[limit - start]; + ci.setIndex(start); + for (int index = 0; index < chars.length; index++) + { + chars[index] = ci.current(); + ci.next(); + } + return getStringBounds(chars, 0, chars.length, frc); } /** @@ -1172,9 +1182,10 @@ public class Font implements Serializable public Rectangle2D getStringBounds(char[] chars, int begin, int limit, FontRenderContext frc) { - return peer.getStringBounds(this, - new StringCharacterIterator(new String(chars)), - begin, limit, frc); + String str = new String(chars, begin, limit - begin); + TextLayout layout = new TextLayout(str, this, frc); + return new Rectangle2D.Float(0, -layout.getAscent(), layout.getAdvance(), + layout.getDescent() + layout.getLeading()); } /** diff --git a/java/awt/GridBagLayout.java b/java/awt/GridBagLayout.java index 4853ba908..0415c7bd3 100644 --- a/java/awt/GridBagLayout.java +++ b/java/awt/GridBagLayout.java @@ -320,6 +320,16 @@ public class GridBagLayout return new Point (col, row); } + /** + * Return a string representation of this GridBagLayout. + * + * @return a string representation + */ + public String toString() + { + return getClass().getName(); + } + /** * Move and resize a rectangle according to a set of grid bag * constraints. The x, y, width and height fields of the diff --git a/java/awt/ScrollPaneAdjustable.java b/java/awt/ScrollPaneAdjustable.java index 6ce79b1f6..ca61801bf 100644 --- a/java/awt/ScrollPaneAdjustable.java +++ b/java/awt/ScrollPaneAdjustable.java @@ -196,13 +196,23 @@ public class ScrollPaneAdjustable public String paramString () { - return ("scrollpane=" + sp + ", orientation=" + orientation - + ", value=" + value + ", minimum=" + minimum - + ", maximum=" + maximum + ", visibleAmount=" + visibleAmount - + ", unitIncrement=" + unitIncrement - + ", blockIncrement=" + blockIncrement); + return paramStringHelper() + + ",[" + getMinimum() + ".." + getMaximum() + + "],val=" + getValue() + + ",vis=" + getVisibleAmount() + + ",unit=" + getUnitIncrement() + + ",block=" + getBlockIncrement() + + ",isAdjusting=" + valueIsAdjusting; } + private String paramStringHelper() + { + if (getOrientation() == HORIZONTAL) + return "horizontal"; + else + return "vertical"; + } + public String toString() { return getClass().getName() + "[" + paramString() + "]"; @@ -227,5 +237,6 @@ public class ScrollPaneAdjustable { this.valueIsAdjusting = valueIsAdjusting; } + } // class ScrollPaneAdjustable diff --git a/java/awt/TextArea.java b/java/awt/TextArea.java index 7e3463ab8..30b278d0c 100644 --- a/java/awt/TextArea.java +++ b/java/awt/TextArea.java @@ -284,11 +284,7 @@ public class TextArea extends TextComponent implements java.io.Serializable } /** - * Retrieve the minimum size for this text area, considering the - * text area's current row and column values. A text area's minimum - * size depends on the number of rows and columns of text it would - * prefer to display, and on the size of the font in which the text - * would be displayed. + * Retrieve the minimum size for this text area. * * @return The minimum size for this text field. */ @@ -298,11 +294,8 @@ public class TextArea extends TextComponent implements java.io.Serializable } /** - * Retrieve the minimum size that this text area would have if its - * row and column values were equal to those specified. A text - * area's minimum size depends on the number of rows and columns of - * text it would prefer to display, and on the size of the font in - * which the text would be displayed. + * Retrieve the minimum size for this text area. If the minimum + * size has been set, then rows and columns are used in the calculation. * * @param rows The number of rows to use in the minimum size * calculation. @@ -317,12 +310,8 @@ public class TextArea extends TextComponent implements java.io.Serializable } /** - * Retrieve the minimum size for this text area, considering the - * text area's current row and column values. A text area's minimum - * size depends on the number of rows and columns of text it would - * prefer to display, and on the size of the font in which the text - * would be displayed. - * + * Retrieve the minimum size for this text area. + * * @return The minimum size for this text area. * * @deprecated This method is deprecated in favor of @@ -334,11 +323,8 @@ public class TextArea extends TextComponent implements java.io.Serializable } /** - * Retrieve the minimum size that this text area would have if its - * row and column values were equal to those specified. A text - * area's minimum size depends on the number of rows and columns of - * text it would prefer to display, and on the size of the font in - * which the text would be displayed. + * Retrieve the minimum size for this text area. If the minimum + * size has been set, then rows and columns are used in the calculation. * * @param rows The number of rows to use in the minimum size * calculation. @@ -352,21 +338,18 @@ public class TextArea extends TextComponent implements java.io.Serializable */ public Dimension minimumSize (int rows, int columns) { + if (isMinimumSizeSet()) + return new Dimension(minSize); + TextAreaPeer peer = (TextAreaPeer) getPeer (); - - // Sun returns Dimension (0,0) in this case. if (peer == null) - return new Dimension (0, 0); + return new Dimension (getWidth(), getHeight()); return peer.getMinimumSize (rows, columns); } /** - * Retrieve the preferred size for this text area, considering the - * text area's current row and column values. A text area's preferred - * size depends on the number of rows and columns of text it would - * prefer to display, and on the size of the font in which the text - * would be displayed. + * Retrieve the preferred size for this text area. * * @return The preferred size for this text field. */ @@ -376,11 +359,8 @@ public class TextArea extends TextComponent implements java.io.Serializable } /** - * Retrieve the preferred size that this text area would have if its - * row and column values were equal to those specified. A text - * area's preferred size depends on the number of rows and columns - * of text it would prefer to display, and on the size of the font - * in which the text would be displayed. + * Retrieve the preferred size for this text area. If the preferred + * size has been set, then rows and columns are used in the calculation. * * @param rows The number of rows to use in the preferred size * calculation. @@ -395,11 +375,7 @@ public class TextArea extends TextComponent implements java.io.Serializable } /** - * Retrieve the preferred size for this text area, considering the - * text area's current row and column values. A text area's preferred - * size depends on the number of rows and columns of text it would - * prefer to display, and on the size of the font in which the text - * would be displayed. + * Retrieve the preferred size for this text area. * * @return The preferred size for this text field. * @@ -412,11 +388,8 @@ public class TextArea extends TextComponent implements java.io.Serializable } /** - * Retrieve the preferred size that this text area would have if its - * row and column values were equal to those specified. A text - * area's preferred size depends on the number of rows and columns - * of text it would prefer to display, and on the size of the font - * in which the text would be displayed. + * Retrieve the preferred size for this text area. If the preferred + * size has been set, then rows and columns are used in the calculation. * * @param rows The number of rows to use in the preferred size * calculation. @@ -430,11 +403,12 @@ public class TextArea extends TextComponent implements java.io.Serializable */ public Dimension preferredSize (int rows, int columns) { + if (isPreferredSizeSet()) + return new Dimension(prefSize); + TextAreaPeer peer = (TextAreaPeer) getPeer (); - - // Sun returns Dimension (0,0) in this case. if (peer == null) - return new Dimension (0, 0); + return new Dimension (getWidth(), getHeight()); return peer.getPreferredSize (rows, columns); } diff --git a/java/awt/datatransfer/DataFlavor.java b/java/awt/datatransfer/DataFlavor.java index d31c7065f..baaf43d85 100644 --- a/java/awt/datatransfer/DataFlavor.java +++ b/java/awt/datatransfer/DataFlavor.java @@ -38,14 +38,13 @@ exception statement from your version. */ package java.awt.datatransfer; -import gnu.classpath.NotImplementedException; - import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.ObjectInput; import java.io.ObjectOutput; +import java.io.OptionalDataException; import java.io.Reader; import java.io.Serializable; import java.io.StringReader; @@ -76,8 +75,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable * deals with bytes not chars. Use getRederForText(). */ public static final DataFlavor plainTextFlavor = - new DataFlavor(java.io.InputStream.class, - "text/plain; charset=unicode", + new DataFlavor("text/plain; charset=unicode; class=java.io.InputStream", "plain unicode text"); /** @@ -94,8 +92,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable * element of the list being a java.io.File. */ public static final DataFlavor javaFileListFlavor = - new DataFlavor(java.util.List.class, - "application/x-java-file-list; class=java.util.List", + new DataFlavor("application/x-java-file-list; class=java.util.List", "Java File List"); /** @@ -132,10 +129,10 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ // The MIME type for this flavor - private final String mimeType; + private MimeType mimeType; // The representation class for this flavor - private final Class representationClass; + private Class representationClass; // The human readable name of this flavor private String humanPresentableName; @@ -198,62 +195,6 @@ public class DataFlavor implements java.io.Externalizable, Cloneable throw new ClassNotFoundException(className); } - private static Class getRepresentationClassFromMimeThrows(String mimeString, - ClassLoader classLoader) - throws ClassNotFoundException - { - String classname = getParameter("class", mimeString); - if (classname != null) - return tryToLoadClass(classname, classLoader); - else - return java.io.InputStream.class; - } - - // Same as above, but wraps any ClassNotFoundExceptions - private static Class getRepresentationClassFromMime(String mimeString, - ClassLoader classLoader) - { - try - { - return getRepresentationClassFromMimeThrows(mimeString, classLoader); - } - catch(ClassNotFoundException cnfe) - { - IllegalArgumentException iae; - iae = new IllegalArgumentException("mimeString: " - + mimeString - + " classLoader: " - + classLoader); - iae.initCause(cnfe); - throw iae; - } - } - - /** - * Returns the value of the named MIME type parameter, or null - * if the parameter does not exist. Given the parameter name and the mime - * string. - * - * @param paramName The name of the parameter. - * @param mimeString The mime string from where the name should be found. - * - * @return The value of the parameter or null. - */ - private static String getParameter(String paramName, String mimeString) - { - int idx = mimeString.indexOf(paramName + "="); - if (idx == -1) - return(null); - - String value = mimeString.substring(idx + paramName.length() + 1); - - idx = value.indexOf(";"); - if (idx == -1) - return(value); - else - return(value.substring(0, idx)); - } - /** * XXX - Currently returns plainTextFlavor. */ @@ -321,31 +262,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public DataFlavor() { - mimeType = null; - representationClass = null; - humanPresentableName = null; - } - - /** - * Private constructor. - */ - private DataFlavor(Class representationClass, - String mimeType, - String humanPresentableName) - { - this.representationClass = representationClass; - this.mimeType = mimeType; - - // Do some simple validity checks - String type = getPrimaryType() + "/" + getSubType(); - if (type.indexOf('=') != -1 - || type.indexOf(';') != -1) - throw new IllegalArgumentException(mimeType); - - if (humanPresentableName != null) - this.humanPresentableName = humanPresentableName; - else - this.humanPresentableName = mimeType; + // Used for deserialization only, nothing to do here. } /** @@ -360,11 +277,21 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public DataFlavor(Class representationClass, String humanPresentableName) { - this(representationClass, - "application/x-java-serialized-object" - + "; class=" - + representationClass.getName(), - humanPresentableName); + if (representationClass == null) + throw new NullPointerException("representationClass must not be null"); + try + { + mimeType = new MimeType(javaSerializedObjectMimeType); + } + catch (MimeTypeParseException ex) + { + // Must not happen as we use a constant string. + assert false; + } + if (humanPresentableName == null) + humanPresentableName = javaSerializedObjectMimeType; + this.humanPresentableName = humanPresentableName; + this.representationClass = representationClass; } /** @@ -389,8 +316,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable ClassLoader classLoader) throws ClassNotFoundException { - this(getRepresentationClassFromMimeThrows(mimeType, classLoader), - mimeType, humanPresentableName); + init(mimeType, humanPresentableName, classLoader); } /** @@ -411,8 +337,17 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public DataFlavor(String mimeType, String humanPresentableName) { - this(getRepresentationClassFromMime (mimeType, null), - mimeType, humanPresentableName); + try + { + init(mimeType, humanPresentableName, getClass().getClassLoader()); + } + catch (ClassNotFoundException ex) + { + IllegalArgumentException iae = + new IllegalArgumentException("Class not found: " + ex.getMessage()); + iae.initCause(ex); + throw iae; + } } /** @@ -431,8 +366,54 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public DataFlavor(String mimeType) throws ClassNotFoundException { - this(getRepresentationClassFromMimeThrows(mimeType, null), - mimeType, null); + init(mimeType, null, getClass().getClassLoader()); + } + + /** + * Called by various constructors to initialize this object. + * + * @param mime the mime string + * @param humanPresentableName the human presentable name + * @param loader the class loader to use for loading the representation + * class + */ + private void init(String mime, String humanPresentableName, + ClassLoader loader) + throws ClassNotFoundException + { + if (mime == null) + throw new NullPointerException("The mime type must not be null"); + try + { + mimeType = new MimeType(mime); + } + catch (MimeTypeParseException ex) + { + IllegalArgumentException iae = + new IllegalArgumentException("Invalid mime type"); + iae.initCause(ex); + throw iae; + } + String className = mimeType.getParameter("class"); + if (className == null) + { + if (mimeType.getBaseType().equals(javaSerializedObjectMimeType)) + throw new IllegalArgumentException("Serialized object type must have" + + " a representation class parameter"); + else + representationClass = java.io.InputStream.class; + } + else + representationClass = tryToLoadClass(className, loader); + mimeType.addParameter("class", representationClass.getName()); + + if (humanPresentableName == null) + { + humanPresentableName = mimeType.getParameter("humanPresentableName"); + if (humanPresentableName == null) + humanPresentableName = mimeType.getBaseType(); + } + this.humanPresentableName = humanPresentableName; } /** @@ -442,7 +423,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public String getMimeType() { - return(mimeType); + return(mimeType.toString()); } /** @@ -472,11 +453,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public String getPrimaryType() { - int idx = mimeType.indexOf("/"); - if (idx == -1) - return(mimeType); - - return(mimeType.substring(0, idx)); + return(mimeType.getPrimaryType()); } /** @@ -486,15 +463,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public String getSubType() { - int start = mimeType.indexOf("/"); - if (start == -1) - return ""; - - int end = mimeType.indexOf(";", start + 1); - if (end == -1) - return mimeType.substring(start + 1); - else - return mimeType.substring(start + 1, end); + return mimeType.getSubType(); } /** @@ -510,7 +479,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable if ("humanPresentableName".equals(paramName)) return getHumanPresentableName(); - return getParameter(paramName, mimeType); + return mimeType.getParameter(paramName); } /** @@ -536,16 +505,22 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public boolean isMimeTypeEqual(String mimeType) { - String mime = getMimeType(); - int i = mime.indexOf(";"); - if (i != -1) - mime = mime.substring(0, i); - - i = mimeType.indexOf(";"); - if (i != -1) - mimeType = mimeType.substring(0, i); - - return mime.equals(mimeType); + if (mimeType == null) + throw new NullPointerException("mimeType must not be null"); + boolean equal = false; + try + { + if (this.mimeType != null) + { + MimeType other = new MimeType(mimeType); + equal = this.mimeType.matches(other); + } + } + catch (MimeTypeParseException ex) + { + // Return false in this case. + } + return equal; } /** @@ -570,7 +545,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public boolean isMimeTypeSerializedObject() { - return mimeType.startsWith(javaSerializedObjectMimeType); + return isMimeTypeEqual(javaSerializedObjectMimeType); } /** @@ -616,8 +591,8 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public boolean isFlavorSerializedObjectType() { - // FIXME: What is the diff between this and isMimeTypeSerializedObject? - return(mimeType.startsWith(javaSerializedObjectMimeType)); + return isRepresentationClassSerializable() + && isMimeTypeEqual(javaSerializedObjectMimeType); } /** @@ -628,7 +603,9 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public boolean isFlavorRemoteObjectType() { - return(mimeType.startsWith(javaRemoteObjectMimeType)); + return isRepresentationClassRemote() + && isRepresentationClassSerializable() + && isMimeTypeEqual(javaRemoteObjectMimeType); } /** @@ -769,7 +746,7 @@ public class DataFlavor implements java.io.Externalizable, Cloneable */ public int hashCode() { - return mimeType.toLowerCase().hashCode() ^ representationClass.hashCode(); + return mimeType.toString().hashCode() ^ representationClass.hashCode(); } /** @@ -821,9 +798,17 @@ public class DataFlavor implements java.io.Externalizable, Cloneable * @exception IOException If an error occurs. */ public void writeExternal(ObjectOutput stream) - throws IOException, NotImplementedException + throws IOException { - // FIXME: Implement me + if (mimeType != null) + { + mimeType.addParameter("humanPresentableName", humanPresentableName); + stream.writeObject(mimeType); + mimeType.removeParameter("humanPresentableName"); + } + else + stream.writeObject(null); + stream.writeObject(representationClass); } @@ -837,9 +822,34 @@ public class DataFlavor implements java.io.Externalizable, Cloneable * cannot be found. */ public void readExternal(ObjectInput stream) - throws IOException, ClassNotFoundException, NotImplementedException + throws IOException, ClassNotFoundException { - // FIXME: Implement me + mimeType = (MimeType) stream.readObject(); + String className = null; + if (mimeType != null) + { + humanPresentableName = + mimeType.getParameter("humanPresentableName"); + mimeType.removeParameter("humanPresentableName"); + className = mimeType.getParameter("class"); + if (className == null) + throw new IOException("No class in mime type"); + } + try + { + representationClass = (Class) stream.readObject(); + } + catch (OptionalDataException ex) + { + if (ex.eof && ex.length == 0) + { + if (className != null) + representationClass = tryToLoadClass(className, + getClass().getClassLoader()); + } + else + throw ex; + } } /** diff --git a/java/awt/datatransfer/MimeType.java b/java/awt/datatransfer/MimeType.java new file mode 100644 index 000000000..438d78e9e --- /dev/null +++ b/java/awt/datatransfer/MimeType.java @@ -0,0 +1,281 @@ +/* MimeType.java -- A helper class for mime handling in DataFlavor + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.awt.datatransfer; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.StringTokenizer; + +/** + * A helper class for mime handling in DataFlavor. + * + * A Mauve test for DataFlavor.writeExternal() shows that a non-public + * class java.awt.datatransfer.MimeType gets serialized. This class + * is mainly here for serialization compatibility. Of course, + * now that we have it here, we can just as well implement some + * mime handling facility here. + */ +class MimeType + implements Externalizable +{ + + /** + * The primary type. + */ + private String primaryType; + + /** + * The subtype. + */ + private String subType; + + /** + * Additional parameters to be appended to the mime string. + */ + private HashMap parameters; + + /** + * This is only here for deserialization. + */ + public MimeType() + { + parameters = new HashMap(); + } + + /** + * Creates a new MimeType object. + * + * @param mime the mime type + */ + MimeType(String mime) + throws MimeTypeParseException + { + this(); + parse(mime); + } + + /** + * Adds a mime parameter. + * + * @param param the parameter key + * @param value the parameter value + */ + void addParameter(String param, String value) + { + parameters.put(param, value); + } + + /** + * Removes the parameter with the specified key. + * + * @param param the parameter to remove + */ + void removeParameter(String param) + { + parameters.remove(param); + } + + /** + * Returns the parameter for the key. + * + * @param key the parameter key + * + * @return the parameter for the key + */ + String getParameter(String key) + { + return (String) parameters.get(key); + } + + /** + * Returns the primary type. + * + * @return the primary type + */ + String getPrimaryType() + { + return primaryType; + } + + String getSubType() + { + return subType; + } + + /** + * Returns the base type of this mime type. This is the primary + * type plus the subtype, separated by '/'. + * + * @return the base type of this mime type + */ + String getBaseType() + { + return primaryType + '/' + subType; + } + + /** + * Returns true if this mime type and another mime type + * match. This will be true when their primary types are equal, and their + * subtypes are equal (or when either subtype is * ). + * + * @param other the other mime type + * + * @return true if the mime types match, false + * otherwise + */ + boolean matches(MimeType other) + { + boolean match = false; + if (other != null) + { + match = primaryType.equals(other.primaryType) + && (subType.equals("*") || other.subType.equals("*") + || subType.equals(other.subType)); + } + return match; + } + + /** + * Serializes the mime type. + * + * @param in the input stream to read from + * + * @throws ClassNotFoundException not thrown here + * @throws IOException when something goes wrong on the input stream, + * or when the mime type can't be parsed + */ + public void readExternal(ObjectInput in) + throws ClassNotFoundException, IOException + { + String mime = in.readUTF(); + parameters.clear(); + try + { + parse(mime); + } + catch (MimeTypeParseException ex) + { + IOException ioEx = new IOException(); + ioEx.initCause(ex); + throw ioEx; + } + } + + /** + * Serializes this mime type. + * + * @param out the output stream + * + * @throws IOException when something goes wrong on the output stream + */ + public void writeExternal(ObjectOutput out) + throws IOException + { + out.writeUTF(toString()); + } + + /** + * Creates a string representation of this mime type. + * + * @return a string representation of this mime type + */ + public String toString() + { + StringBuilder s = new StringBuilder(); + s.append(primaryType); + s.append('/'); + s.append(subType); + if (parameters.size() > 0) + { + Set entries = parameters.entrySet(); + for (Iterator i = entries.iterator(); i.hasNext();) + { + s.append("; "); + Map.Entry entry = (Map.Entry) i.next(); + s.append(entry.getKey()); + s.append('='); + s.append(entry.getValue()); + } + } + return s.toString(); + } + + /** + * Parses the specified mime type string and initializes the fields + * of this object. + * + * @param mime the mime type string + */ + private void parse(String mime) + throws MimeTypeParseException + { + // FIXME: Maybe implement more sophisticated mime string parsing according + // to RFC 2045 and 2046. + StringTokenizer tokenizer = new StringTokenizer(mime); + try + { + primaryType = tokenizer.nextToken("/"); + subType = tokenizer.nextToken("/;"); + } + catch (NoSuchElementException ex) + { + throw new MimeTypeParseException("Expected / separator"); + } + + // Add any parameters. + while (tokenizer.hasMoreTokens()) + { + String keyValuePair = tokenizer.nextToken(";"); + int i = keyValuePair.indexOf('='); + if (i == -1) + throw new MimeTypeParseException("Expected = as parameter separator"); + String key = keyValuePair.substring(0, i).trim(); + String value = keyValuePair.substring(i + 1).trim(); + parameters.put(key, value); + } + } + +} diff --git a/java/awt/dnd/DragGestureRecognizer.java b/java/awt/dnd/DragGestureRecognizer.java index 6a00347b2..3973e5284 100644 --- a/java/awt/dnd/DragGestureRecognizer.java +++ b/java/awt/dnd/DragGestureRecognizer.java @@ -38,8 +38,6 @@ exception statement from your version. */ package java.awt.dnd; -import gnu.classpath.NotImplementedException; - import java.awt.Component; import java.awt.Point; import java.awt.event.InputEvent; @@ -129,11 +127,12 @@ public abstract class DragGestureRecognizer implements Serializable return events.size() > 0 ? (InputEvent) events.get(0) : null; } + /** + * Resets the recognizer. If a gesture is currently recognize, discard it. + */ public void resetRecognizer() - throws NotImplementedException { - events = new ArrayList(); - // FIXME: Not implemented fully. + events.clear(); } /** diff --git a/java/awt/dnd/DragSourceContext.java b/java/awt/dnd/DragSourceContext.java index 1fee5c0c3..ed1cbaa44 100644 --- a/java/awt/dnd/DragSourceContext.java +++ b/java/awt/dnd/DragSourceContext.java @@ -38,8 +38,6 @@ exception statement from your version. */ package java.awt.dnd; -import gnu.classpath.NotImplementedException; - import java.awt.Component; import java.awt.Cursor; import java.awt.Image; @@ -268,7 +266,8 @@ public class DragSourceContext for (int i = 0; i < dsl.length; i++) dsl[i].dragExit(e); - updateCurrentCursor(0, 0, DEFAULT); + updateCurrentCursor(DnDConstants.ACTION_NONE, DnDConstants.ACTION_NONE, + DEFAULT); } /** @@ -340,26 +339,45 @@ public class DragSourceContext * @param status - the status of the cursor (constant). */ protected void updateCurrentCursor(int dropOp, int targetAct, int status) - throws NotImplementedException { - // FIXME: Not implemented fully - if (!useCustomCursor) + if (! useCustomCursor) { - Cursor cursor = null; + Cursor newCursor = null; switch (status) { + default: + targetAct = DnDConstants.ACTION_NONE; case ENTER: - break; case CHANGED: - break; case OVER: - break; - default: - break; + int action = dropOp & targetAct; + if (action == DnDConstants.ACTION_NONE) + { + if ((dropOp & DnDConstants.ACTION_LINK) != 0) + newCursor = DragSource.DefaultLinkNoDrop; + else if ((dropOp & DnDConstants.ACTION_MOVE) != 0) + newCursor = DragSource.DefaultMoveNoDrop; + else + newCursor = DragSource.DefaultCopyNoDrop; + } + else + { + if ((dropOp & DnDConstants.ACTION_LINK) != 0) + newCursor = DragSource.DefaultLinkDrop; + else if ((dropOp & DnDConstants.ACTION_MOVE) != 0) + newCursor = DragSource.DefaultMoveDrop; + else + newCursor = DragSource.DefaultCopyDrop; + } } - this.cursor = cursor; - peer.setCursor(cursor); + if (cursor == null || ! cursor.equals(newCursor)) + { + cursor = newCursor; + DragSourceContextPeer p = peer; + if (p != null) + p.setCursor(cursor); + } } } } // class DragSourceContext diff --git a/java/awt/event/ComponentEvent.java b/java/awt/event/ComponentEvent.java index ba9c2a5b3..6d478055a 100644 --- a/java/awt/event/ComponentEvent.java +++ b/java/awt/event/ComponentEvent.java @@ -1,5 +1,5 @@ /* ComponentEvent.java -- notification of events for components - Copyright (C) 1999, 2002, 2005 Free Software Foundation, Inc. + Copyright (C) 1999, 2002, 2005, 2006 Free Software Foundation, Inc. This file is part of GNU Classpath. @@ -114,24 +114,27 @@ public class ComponentEvent extends AWTEvent */ public String paramString() { + StringBuffer s = new StringBuffer(); + // Unlike Sun, we don't throw NullPointerException or ClassCastException // when source was illegally changed. - switch (id) - { - case COMPONENT_MOVED: - return "COMPONENT_MOVED " - + (source instanceof Component - ? ((Component) source).getBounds() : (Object) ""); - case COMPONENT_RESIZED: - return "COMPONENT_RESIZED " - + (source instanceof Component - ? ((Component) source).getBounds() : (Object) ""); - case COMPONENT_SHOWN: - return "COMPONENT_SHOWN"; - case COMPONENT_HIDDEN: - return "COMPONENT_HIDDEN"; - default: - return "unknown type"; - } + if (id == COMPONENT_MOVED) + s.append("COMPONENT_MOVED "); + else if (id == COMPONENT_RESIZED) + s.append("COMPONENT_RESIZED "); + else if (id == COMPONENT_SHOWN) + s.append("COMPONENT_SHOWN "); + else if (id == COMPONENT_HIDDEN) + s.append("COMPONENT_HIDDEN "); + else + return "unknown type"; + + s.append("(").append(getComponent().getX()).append(",") + .append(getComponent().getY()).append(" ") + .append(getComponent().getWidth()).append("x") + .append(getComponent().getHeight()).append(")"); + + return s.toString(); } + } // class ComponentEvent diff --git a/java/awt/geom/GeneralPath.java b/java/awt/geom/GeneralPath.java index e0ca8e183..1e9ede5ee 100644 --- a/java/awt/geom/GeneralPath.java +++ b/java/awt/geom/GeneralPath.java @@ -86,7 +86,7 @@ public final class GeneralPath implements Shape, Cloneable public static final int WIND_EVEN_ODD = java.awt.geom.PathIterator.WIND_EVEN_ODD; - /** Same constant as {@link PathIterator.WIND_NON_ZERO}. */ + /** Same constant as {@link PathIterator#WIND_NON_ZERO}. */ public static final int WIND_NON_ZERO = java.awt.geom.PathIterator.WIND_NON_ZERO; @@ -140,7 +140,11 @@ public final class GeneralPath implements Shape, Cloneable /** * Constructs a GeneralPath with a specific winding rule * and the default initial capacity (20). - * @param rule the winding rule (WIND_NON_ZERO or WIND_EVEN_ODD) + * @param rule the winding rule ({@link #WIND_NON_ZERO} or + * {@link #WIND_EVEN_ODD}) + * + * @throws IllegalArgumentException if rule is not one of the + * listed values. */ public GeneralPath(int rule) { @@ -151,8 +155,12 @@ public final class GeneralPath implements Shape, Cloneable * Constructs a GeneralPath with a specific winding rule * and the initial capacity. The initial capacity should be * the approximate number of path segments to be used. - * @param rule the winding rule (WIND_NON_ZERO or WIND_EVEN_ODD) + * @param rule the winding rule ({@link #WIND_NON_ZERO} or + * {@link #WIND_EVEN_ODD}) * @param capacity the inital capacity, in path segments + * + * @throws IllegalArgumentException if rule is not one of the + * listed values. */ public GeneralPath(int rule, int capacity) { @@ -169,7 +177,10 @@ public final class GeneralPath implements Shape, Cloneable /** * Constructs a GeneralPath from an arbitrary shape object. * The Shapes PathIterator path and winding rule will be used. - * @param s the shape + * + * @param s the shape (null not permitted). + * + * @throws NullPointerException if shape is null. */ public GeneralPath(Shape s) { @@ -183,6 +194,9 @@ public final class GeneralPath implements Shape, Cloneable /** * Adds a new point to a path. + * + * @param x the x-coordinate. + * @param y the y-coordinate. */ public void moveTo(float x, float y) { @@ -263,6 +277,11 @@ public final class GeneralPath implements Shape, Cloneable * Appends the segments of a Shape to the path. If connect is * true, the new path segments are connected to the existing one with a line. * The winding rule of the Shape is ignored. + * + * @param s the shape (null not permitted). + * @param connect whether to connect the new shape to the existing path. + * + * @throws NullPointerException if s is null. */ public void append(Shape s, boolean connect) { @@ -276,7 +295,7 @@ public final class GeneralPath implements Shape, Cloneable * PathIterator#SEG_LINETO} segment. * * @param iter the PathIterator specifying which segments shall be - * appended. + * appended (null not permitted). * * @param connect true for substituting the initial * {@link PathIterator#SEG_MOVETO} segment by a {@link @@ -327,6 +346,8 @@ public final class GeneralPath implements Shape, Cloneable /** * Returns the path’s current winding rule. + * + * @return {@link #WIND_EVEN_ODD} or {@link #WIND_NON_ZERO}. */ public int getWindingRule() { @@ -338,6 +359,8 @@ public final class GeneralPath implements Shape, Cloneable * considered ’inside’ or ’outside’ the path * on drawing. Valid rules are WIND_EVEN_ODD for an even-odd winding rule, * or WIND_NON_ZERO for a non-zero winding rule. + * + * @param rule the rule ({@link #WIND_EVEN_ODD} or {@link #WIND_NON_ZERO}). */ public void setWindingRule(int rule) { @@ -348,6 +371,8 @@ public final class GeneralPath implements Shape, Cloneable /** * Returns the current appending point of the path. + * + * @return The point. */ public Point2D getCurrentPoint() { @@ -367,6 +392,8 @@ public final class GeneralPath implements Shape, Cloneable /** * Applies a transform to the path. + * + * @param xform the transform (null not permitted). */ public void transform(AffineTransform xform) { @@ -706,6 +733,8 @@ public final class GeneralPath implements Shape, Cloneable /** * Helper method - ensure the size of the data arrays, * otherwise, reallocate new ones twice the size + * + * @param size the minimum array size. */ private void ensureSize(int size) { diff --git a/javax/swing/JEditorPane.java b/javax/swing/JEditorPane.java index 06844355a..a503bb6e8 100644 --- a/javax/swing/JEditorPane.java +++ b/javax/swing/JEditorPane.java @@ -869,6 +869,13 @@ public class JEditorPane extends JTextComponent public final void setContentType(String type) { + // Strip off content type parameters. + int paramIndex = type.indexOf(';'); + if (paramIndex > -1) + { + // TODO: Handle character encoding. + type = type.substring(0, paramIndex).trim(); + } if (editorKit != null && editorKit.getContentType().equals(type)) return; diff --git a/javax/swing/JTextField.java b/javax/swing/JTextField.java index 367503b73..ace358f89 100644 --- a/javax/swing/JTextField.java +++ b/javax/swing/JTextField.java @@ -270,7 +270,8 @@ public class JTextField extends JTextComponent */ protected void fireActionPerformed() { - ActionEvent event = new ActionEvent(this, 0, getText()); + ActionEvent event = new ActionEvent(this, 0, + actionCommand == null ? getText() : actionCommand); ActionListener[] listeners = getActionListeners(); for (int index = 0; index < listeners.length; ++index) diff --git a/javax/swing/text/FlowView.java b/javax/swing/text/FlowView.java index c4625fc62..cee5bb126 100644 --- a/javax/swing/text/FlowView.java +++ b/javax/swing/text/FlowView.java @@ -39,6 +39,7 @@ exception statement from your version. */ package javax.swing.text; import java.awt.Component; +import java.awt.Graphics; import java.awt.Rectangle; import java.awt.Shape; @@ -86,7 +87,7 @@ public abstract class FlowView extends BoxView */ public void insertUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) { - if (alloc != null) + if (alloc == null) { fv.layoutChanged(X_AXIS); fv.layoutChanged(Y_AXIS); @@ -95,7 +96,7 @@ public abstract class FlowView extends BoxView { Component host = fv.getContainer(); if (host != null) - host.repaint(); + host.repaint(alloc.x, alloc.y, alloc.width, alloc.height); } } @@ -112,7 +113,7 @@ public abstract class FlowView extends BoxView */ public void removeUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) { - if (alloc != null) + if (alloc == null) { fv.layoutChanged(X_AXIS); fv.layoutChanged(Y_AXIS); @@ -121,7 +122,7 @@ public abstract class FlowView extends BoxView { Component host = fv.getContainer(); if (host != null) - host.repaint(); + host.repaint(alloc.x, alloc.y, alloc.width, alloc.height); } } @@ -138,7 +139,7 @@ public abstract class FlowView extends BoxView */ public void changedUpdate(FlowView fv, DocumentEvent e, Rectangle alloc) { - if (alloc != null) + if (alloc == null) { fv.layoutChanged(X_AXIS); fv.layoutChanged(Y_AXIS); @@ -147,7 +148,7 @@ public abstract class FlowView extends BoxView { Component host = fv.getContainer(); if (host != null) - host.repaint(); + host.repaint(alloc.x, alloc.y, alloc.width, alloc.height); } } @@ -432,14 +433,14 @@ public abstract class FlowView extends BoxView * visual representation, this is handled by the physical view implemented * in the FlowView. */ - class LogicalView extends BoxView + class LogicalView extends CompositeView { /** * Creates a new LogicalView instance. */ - LogicalView(Element el, int axis) + LogicalView(Element el) { - super(el, axis); + super(el); } /** @@ -451,6 +452,117 @@ public abstract class FlowView extends BoxView View p = getParent(); return p != null ? p.getAttributes() : null; } + + protected void childAllocation(int index, Rectangle a) + { + // Nothing to do here (not visual). + } + + protected View getViewAtPoint(int x, int y, Rectangle r) + { + // Nothing to do here (not visual). + return null; + } + + protected boolean isAfter(int x, int y, Rectangle r) + { + // Nothing to do here (not visual). + return false; + } + + protected boolean isBefore(int x, int y, Rectangle r) + { + // Nothing to do here (not visual). + return false; + } + + public float getPreferredSpan(int axis) + { + float max = 0; + float pref = 0; + int n = getViewCount(); + for (int i = 0; i < n; i++) + { + View v = getView(i); + pref += v.getPreferredSpan(axis); + if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) + >= ForcedBreakWeight) + { + max = Math.max(pref, pref); + pref = 0; + } + } + max = Math.max(max, pref); + return max; + } + + public float getMinimumSpan(int axis) + { + float max = 0; + float min = 0; + boolean wrap = true; + int n = getViewCount(); + for (int i = 0; i < n; i++) + { + View v = getView(i); + if (v.getBreakWeight(axis, 0, Integer.MAX_VALUE) + == BadBreakWeight) + { + min += v.getPreferredSpan(axis); + wrap = false; + } + else if (! wrap) + { + max = Math.max(min, max); + wrap = true; + min = 0; + } + } + max = Math.max(max, min); + return max; + } + + public void paint(Graphics g, Shape s) + { + // Nothing to do here (not visual). + } + + /** + * Overridden to handle possible leaf elements. + */ + protected void loadChildren(ViewFactory f) + { + Element el = getElement(); + if (el.isLeaf()) + { + View v = new LabelView(el); + append(v); + } + else + super.loadChildren(f); + } + + /** + * Overridden to reparent the children to this logical view, in case + * they have been parented by a row. + */ + protected void forwardUpdateToView(View v, DocumentEvent e, Shape a, + ViewFactory f) + { + v.setParent(this); + super.forwardUpdateToView(v, e, a, f); + } + + /** + * Overridden to handle possible leaf element. + */ + protected int getViewIndexAtPosition(int pos) + { + int index = 0; + if (! getElement().isLeaf()) + index = super.getViewIndexAtPosition(pos); + return index; + } } /** @@ -565,9 +677,11 @@ public abstract class FlowView extends BoxView { if (layoutPool == null) { - layoutPool = new LogicalView(getElement(), getAxis()); + layoutPool = new LogicalView(getElement()); layoutPool.setParent(this); } + // Initialize the flow strategy. + strategy.insertUpdate(this, null, null); } /** @@ -722,7 +836,7 @@ public abstract class FlowView extends BoxView res = new SizeRequirements(); res.minimum = (int) layoutPool.getMinimumSpan(axis); res.preferred = Math.max(res.minimum, - (int) layoutPool.getMinimumSpan(axis)); + (int) layoutPool.getPreferredSpan(axis)); res.maximum = Integer.MAX_VALUE; res.alignment = 0.5F; return res; diff --git a/javax/swing/text/GlyphView.java b/javax/swing/text/GlyphView.java index e177d927d..cb7f8f05d 100644 --- a/javax/swing/text/GlyphView.java +++ b/javax/swing/text/GlyphView.java @@ -433,10 +433,15 @@ public class GlyphView extends View implements TabableView, Cloneable TabExpander te = v.getTabExpander(); Segment txt = v.getText(p0, v.getEndOffset()); Font font = v.getFont(); - FontMetrics fm = v.getContainer().getFontMetrics(font); + Container c = v.getContainer(); + FontMetrics fm; + if (c != null) + fm = c.getFontMetrics(font); + else + fm = Toolkit.getDefaultToolkit().getFontMetrics(font); int pos = Utilities.getTabbedTextOffset(txt, fm, (int) x, (int) (x + len), te, p0, false); - return pos; + return pos + p0; } /** @@ -655,9 +660,9 @@ public class GlyphView extends View implements TabableView, Cloneable */ public float getTabbedSpan(float x, TabExpander te) { - Element el = getElement(); - return getGlyphPainter().getSpan(this, el.getStartOffset(), - el.getEndOffset(), te, x); + checkPainter(); + return getGlyphPainter().getSpan(this, getStartOffset(), + getEndOffset(), te, x); } /** @@ -905,31 +910,19 @@ public class GlyphView extends View implements TabableView, Cloneable */ public View breakView(int axis, int p0, float pos, float len) { - if (axis == Y_AXIS) - return this; - - checkPainter(); - - // Try to find a suitable line break. - Segment txt = new Segment(); - try + View brokenView = this; + if (axis == X_AXIS) { - int start = getStartOffset(); - int length = getEndOffset() - start; - getDocument().getText(start, length, txt); - } - catch (BadLocationException ex) - { - AssertionError err = new AssertionError("BadLocationException must not " - + "be thrown here."); - err.initCause(ex); - throw err; + checkPainter(); + int end = glyphPainter.getBoundedPosition(this, p0, pos, len); + int breakLoc = getBreakLocation(p0, end); + if (breakLoc != -1) + end = breakLoc; + if (p0 != getStartOffset() || end != getEndOffset()) + { + brokenView = createFragment(p0, end); + } } - int breakLocation = - Utilities.getBreakLocation(txt, getContainer().getFontMetrics(getFont()), - (int) pos, (int) (pos + len), - getTabExpander(), p0); - View brokenView = createFragment(p0, breakLocation); return brokenView; } @@ -955,28 +948,36 @@ public class GlyphView extends View implements TabableView, Cloneable weight = super.getBreakWeight(axis, pos, len); else { - // FIXME: Commented out because the Utilities.getBreakLocation method - // is still buggy. The GoodBreakWeight is a reasonable workaround for - // now. -// int startOffset = getStartOffset(); -// int endOffset = getEndOffset() - 1; -// Segment s = getText(startOffset, endOffset); -// Container c = getContainer(); -// FontMetrics fm = c.getFontMetrics(c.getFont()); -// int x0 = (int) pos; -// int x = (int) (pos + len); -// int breakLoc = Utilities.getBreakLocation(s, fm, x0, x, -// getTabExpander(), -// startOffset); -// if (breakLoc == startOffset || breakLoc == endOffset) -// weight = GoodBreakWeight; -// else -// weight = ExcellentBreakWeight; - weight = GoodBreakWeight; + checkPainter(); + int start = getStartOffset(); + int end = glyphPainter.getBoundedPosition(this, start, pos, len); + if (end == 0) + weight = BadBreakWeight; + else + { + if (getBreakLocation(start, end) != -1) + weight = ExcellentBreakWeight; + else + weight = GoodBreakWeight; + } } return weight; } + private int getBreakLocation(int start, int end) + { + int loc = -1; + Segment s = getText(start, end); + for (char c = s.last(); c != Segment.DONE && loc == -1; c = s.previous()) + { + if (Character.isWhitespace(c)) + { + loc = s.getIndex() - s.getBeginIndex() + 1 + start; + } + } + return loc; + } + /** * Receives notification that some text attributes have changed within the * text fragment that this view is responsible for. This calls diff --git a/javax/swing/text/Utilities.java b/javax/swing/text/Utilities.java index 8ddf97a12..fa2d1ab52 100644 --- a/javax/swing/text/Utilities.java +++ b/javax/swing/text/Utilities.java @@ -93,7 +93,6 @@ public class Utilities // The current x and y pixel coordinates. int pixelX = x; - int pixelY = y - ascent; int pixelWidth = 0; int pos = s.offset; @@ -107,7 +106,7 @@ public class Utilities if (c == '\t') { if (len > 0) { - g.drawChars(buffer, pos, len, pixelX, pixelY + ascent); + g.drawChars(buffer, pos, len, pixelX, y); pixelX += pixelWidth; pixelWidth = 0; } @@ -134,7 +133,7 @@ public class Utilities } if (len > 0) - g.drawChars(buffer, pos, len, pixelX, pixelY + ascent); + g.drawChars(buffer, pos, len, pixelX, y); return pixelX + pixelWidth; } diff --git a/javax/swing/text/html/FormView.java b/javax/swing/text/html/FormView.java index d54021066..031a8b7a4 100644 --- a/javax/swing/text/html/FormView.java +++ b/javax/swing/text/html/FormView.java @@ -44,15 +44,23 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.net.MalformedURLException; +import java.net.URL; +import javax.swing.ButtonModel; +import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JPasswordField; import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; import javax.swing.JTextField; +import javax.swing.JToggleButton; import javax.swing.UIManager; import javax.swing.text.AttributeSet; import javax.swing.text.ComponentView; +import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.StyleConstants; @@ -124,6 +132,11 @@ public class FormView public static final String RESET = UIManager.getString("FormView.resetButtonText"); + /** + * If this is true, the maximum size is set to the preferred size. + */ + private boolean maxIsPreferred; + /** * Creates a new FormView. * @@ -141,37 +154,147 @@ public class FormView { Component comp = null; Element el = getElement(); - Object tag = el.getAttributes().getAttribute(StyleConstants.NameAttribute); + AttributeSet atts = el.getAttributes(); + Object tag = atts.getAttribute(StyleConstants.NameAttribute); + Object model = atts.getAttribute(StyleConstants.ModelAttribute); if (tag.equals(HTML.Tag.INPUT)) { - AttributeSet atts = el.getAttributes(); String type = (String) atts.getAttribute(HTML.Attribute.TYPE); - String value = (String) atts.getAttribute(HTML.Attribute.VALUE); if (type.equals("button")) - comp = new JButton(value); + { + String value = (String) atts.getAttribute(HTML.Attribute.VALUE); + JButton b = new JButton(value); + if (model != null) + { + b.setModel((ButtonModel) model); + b.addActionListener(this); + } + comp = b; + maxIsPreferred = true; + } else if (type.equals("checkbox")) - comp = new JCheckBox(value); + { + JCheckBox c = new JCheckBox(); + if (model != null) + { + boolean sel = atts.getAttribute(HTML.Attribute.CHECKED) != null; + ((JToggleButton.ToggleButtonModel) model).setSelected(sel); + c.setModel((ButtonModel) model); + } + comp = c; + maxIsPreferred = true; + } else if (type.equals("image")) - comp = new JButton(value); // FIXME: Find out how to fetch the image. + { + String src = (String) atts.getAttribute(HTML.Attribute.SRC); + JButton b; + try + { + URL base = ((HTMLDocument) el.getDocument()).getBase(); + URL srcURL = new URL(base, src); + ImageIcon icon = new ImageIcon(srcURL); + b = new JButton(icon); + } + catch (MalformedURLException ex) + { + b = new JButton(src); + } + if (model != null) + { + b.setModel((ButtonModel) model); + b.addActionListener(this); + } + comp = b; + maxIsPreferred = true; + } else if (type.equals("password")) - comp = new JPasswordField(value); + { + int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE, + -1); + JTextField tf = new JPasswordField(); + if (size > 0) + tf.setColumns(size); + else + tf.setColumns(20); + if (model != null) + tf.setDocument((Document) model); + String value = (String) atts.getAttribute(HTML.Attribute.VALUE); + if (value != null) + tf.setText(value); + tf.addActionListener(this); + comp = tf; + maxIsPreferred = true; + } else if (type.equals("radio")) - comp = new JRadioButton(value); + { + JRadioButton c = new JRadioButton(); + if (model != null) + { + boolean sel = atts.getAttribute(HTML.Attribute.CHECKED) != null; + ((JToggleButton.ToggleButtonModel) model).setSelected(sel); + c.setModel((ButtonModel) model); + } + comp = c; + maxIsPreferred = true; + } else if (type.equals("reset")) { - if (value == null || value.equals("")) - value = RESET; - comp = new JButton(value); + String value = (String) atts.getAttribute(HTML.Attribute.VALUE); + if (value == null) + value = UIManager.getString("FormView.resetButtonText"); + JButton b = new JButton(value); + if (model != null) + { + b.setModel((ButtonModel) model); + b.addActionListener(this); + } + comp = b; + maxIsPreferred = true; } else if (type.equals("submit")) { - if (value == null || value.equals("")) - value = SUBMIT; - comp = new JButton(value); + String value = (String) atts.getAttribute(HTML.Attribute.VALUE); + if (value == null) + value = UIManager.getString("FormView.submitButtonText"); + JButton b = new JButton(value); + if (model != null) + { + b.setModel((ButtonModel) model); + b.addActionListener(this); + } + comp = b; + maxIsPreferred = true; } else if (type.equals("text")) - comp = new JTextField(value); - + { + int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE, + -1); + JTextField tf = new JTextField(); + if (size > 0) + tf.setColumns(size); + else + tf.setColumns(20); + if (model != null) + tf.setDocument((Document) model); + String value = (String) atts.getAttribute(HTML.Attribute.VALUE); + if (value != null) + tf.setText(value); + tf.addActionListener(this); + comp = tf; + maxIsPreferred = true; + } + } + else if (tag == HTML.Tag.TEXTAREA) + { + JTextArea textArea = new JTextArea((Document) model); + int rows = HTML.getIntegerAttributeValue(atts, HTML.Attribute.ROWS, 1); + textArea.setRows(rows); + int cols = HTML.getIntegerAttributeValue(atts, HTML.Attribute.COLS, 20); + textArea.setColumns(cols); + maxIsPreferred = true; + comp = new JScrollPane(textArea, + JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, + JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); } // FIXME: Implement the remaining components. return comp; @@ -188,16 +311,11 @@ public class FormView */ public float getMaximumSpan(int axis) { - // FIXME: The specs say that for some components the maximum span == the - // preferred span of the component. This should be figured out and - // implemented accordingly. float span; - if (axis == X_AXIS) - span = getComponent().getMaximumSize().width; - else if (axis == Y_AXIS) - span = getComponent().getMaximumSize().height; + if (maxIsPreferred) + span = getPreferredSpan(axis); else - throw new IllegalArgumentException("Invalid axis parameter"); + span = super.getMaximumSpan(axis); return span; } diff --git a/javax/swing/text/html/HTMLDocument.java b/javax/swing/text/html/HTMLDocument.java index 00372cd36..2e2eb3abc 100644 --- a/javax/swing/text/html/HTMLDocument.java +++ b/javax/swing/text/html/HTMLDocument.java @@ -44,19 +44,24 @@ import gnu.javax.swing.text.html.parser.htmlAttributeSet; import java.io.IOException; import java.io.StringReader; import java.net.URL; +import java.util.ArrayList; import java.util.HashMap; import java.util.Stack; import java.util.Vector; +import javax.swing.DefaultButtonModel; import javax.swing.JEditorPane; +import javax.swing.JToggleButton; import javax.swing.text.AbstractDocument; import javax.swing.text.AttributeSet; import javax.swing.text.BadLocationException; import javax.swing.text.DefaultStyledDocument; +import javax.swing.text.Document; import javax.swing.text.Element; import javax.swing.text.ElementIterator; import javax.swing.text.GapContent; import javax.swing.text.MutableAttributeSet; +import javax.swing.text.PlainDocument; import javax.swing.text.SimpleAttributeSet; import javax.swing.text.StyleConstants; import javax.swing.text.html.HTML.Tag; @@ -559,7 +564,42 @@ public class HTMLDocument extends DefaultStyledDocument /** A temporary variable that helps with the printing out of debug information **/ boolean debug = false; - + + /** + * This is true when we are inside a pre tag. + */ + boolean inPreTag = false; + + /** + * This is true when we are inside a style tag. This will add text + * content inside this style tag beeing parsed as CSS. + * + * This is package private to avoid accessor methods. + */ + boolean inStyleTag = false; + + /** + * This is true when we are inside a <textarea> tag. Any text + * content will then be added to the text area. + * + * This is package private to avoid accessor methods. + */ + boolean inTextArea = false; + + /** + * This contains all stylesheets that are somehow read, either + * via embedded style tags, or via linked stylesheets. The + * elements will be String objects containing a stylesheet each. + */ + ArrayList styles; + + /** + * The document model for a textarea. + * + * This is package private to avoid accessor methods. + */ + Document textAreaDocument; + void print (String line) { if (debug) @@ -634,7 +674,11 @@ public class HTMLDocument extends DefaultStyledDocument popCharacterStyle(); } } - + + /** + * Processes elements that make up forms: <input>, <textarea>, + * <select> and <option>. + */ public class FormAction extends SpecialAction { /** @@ -642,10 +686,27 @@ public class HTMLDocument extends DefaultStyledDocument * of tags associated with this Action. */ public void start(HTML.Tag t, MutableAttributeSet a) - throws NotImplementedException { - // FIXME: Implement. - print ("FormAction.start not implemented"); + if (t == HTML.Tag.INPUT) + { + String type = (String) a.getAttribute(HTML.Attribute.TYPE); + if (type == null) + { + type = "text"; // Default to 'text' when nothing was specified. + a.addAttribute(HTML.Attribute.TYPE, type); + } + setModel(type, a); + } + else if (t == HTML.Tag.TEXTAREA) + { + inTextArea = true; + textAreaDocument = new PlainDocument(); + a.addAttribute(StyleConstants.ModelAttribute, textAreaDocument); + } + // TODO: Handle select and option tags. + + // Build the element. + super.start(t, a); } /** @@ -653,11 +714,46 @@ public class HTMLDocument extends DefaultStyledDocument * with this Action. */ public void end(HTML.Tag t) - throws NotImplementedException { - // FIXME: Implement. - print ("FormAction.end not implemented"); - } + if (t == HTML.Tag.TEXTAREA) + { + inTextArea = false; + } + + // TODO: Handle select and option tags. + + // Finish the element. + super.end(t); + } + + private void setModel(String type, MutableAttributeSet attrs) + { + if (type.equals("submit") || type.equals("reset") + || type.equals("image")) + { + // Create button. + attrs.addAttribute(StyleConstants.ModelAttribute, + new DefaultButtonModel()); + } + else if (type.equals("text") || type.equals("password")) + { + // TODO: Handle fixed length input fields. + attrs.addAttribute(StyleConstants.ModelAttribute, + new PlainDocument()); + } + else if (type.equals("file")) + { + attrs.addAttribute(StyleConstants.ModelAttribute, + new PlainDocument()); + } + else if (type.equals("checkbox") || type.equals("radio")) + { + JToggleButton.ToggleButtonModel model = + new JToggleButton.ToggleButtonModel(); + // TODO: Handle radio button via ButtonGroups. + attrs.addAttribute(StyleConstants.ModelAttribute, model); + } + } } /** @@ -690,7 +786,10 @@ public class HTMLDocument extends DefaultStyledDocument blockClose(t); } } - + + /** + * Handles <isindex> tags. + */ public class IsindexAction extends TagAction { /** @@ -698,10 +797,10 @@ public class HTMLDocument extends DefaultStyledDocument * of tags associated with this Action. */ public void start(HTML.Tag t, MutableAttributeSet a) - throws NotImplementedException { - // FIXME: Implement. - print ("IsindexAction.start not implemented"); + blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet()); + addSpecialElement(t, a); + blockClose(HTML.Tag.IMPLIED); } } @@ -725,7 +824,10 @@ public class HTMLDocument extends DefaultStyledDocument blockClose(t); } } - + + /** + * This action is performed when a <pre> tag is parsed. + */ public class PreAction extends BlockAction { /** @@ -733,11 +835,11 @@ public class HTMLDocument extends DefaultStyledDocument * of tags associated with this Action. */ public void start(HTML.Tag t, MutableAttributeSet a) - throws NotImplementedException { - // FIXME: Implement. - print ("PreAction.start not implemented"); - super.start(t, a); + inPreTag = true; + blockOpen(t, a); + a.addAttribute(CSS.Attribute.WHITE_SPACE, "pre"); + blockOpen(HTML.Tag.IMPLIED, a); } /** @@ -745,11 +847,10 @@ public class HTMLDocument extends DefaultStyledDocument * with this Action. */ public void end(HTML.Tag t) - throws NotImplementedException { - // FIXME: Implement. - print ("PreAction.end not implemented"); - super.end(t); + blockClose(HTML.Tag.IMPLIED); + inPreTag = false; + blockClose(t); } } @@ -875,12 +976,20 @@ public class HTMLDocument extends DefaultStyledDocument * with this Action. */ public void end(HTML.Tag t) - throws NotImplementedException { - // FIXME: Implement. - print ("HeadAction.end not implemented: "+t); + // We read in all the stylesheets that are embedded or referenced + // inside the header. + if (styles != null) + { + int numStyles = styles.size(); + for (int i = 0; i < numStyles; i++) + { + String style = (String) styles.get(i); + getStyleSheet().addRule(style); + } + } super.end(t); - } + } } class LinkAction extends TagAction @@ -957,7 +1066,7 @@ public class HTMLDocument extends DefaultStyledDocument print ("MetaAction.end not implemented"); } } - + class StyleAction extends TagAction { /** @@ -965,10 +1074,8 @@ public class HTMLDocument extends DefaultStyledDocument * of tags associated with this Action. */ public void start(HTML.Tag t, MutableAttributeSet a) - throws NotImplementedException { - // FIXME: Implement. - print ("StyleAction.start not implemented"); + inStyleTag = true; } /** @@ -976,10 +1083,8 @@ public class HTMLDocument extends DefaultStyledDocument * with this Action. */ public void end(HTML.Tag t) - throws NotImplementedException { - // FIXME: Implement. - print ("StyleAction.end not implemented"); + inStyleTag = false; } } @@ -1182,7 +1287,21 @@ public class HTMLDocument extends DefaultStyledDocument public void handleText(char[] data, int pos) { if (shouldInsert() && data != null && data.length > 0) - addContent(data, 0, data.length); + { + if (inTextArea) + textAreaContent(data); + else if (inPreTag) + preContent(data); + else if (inStyleTag) + { + if (styles == null) + styles = new ArrayList(); + styles.add(new String(data)); + } + else + addContent(data, 0, data.length); + + } } /** @@ -1305,22 +1424,46 @@ public class HTMLDocument extends DefaultStyledDocument * @param data the text to add to the textarea */ protected void textAreaContent(char[] data) - throws NotImplementedException { - // FIXME: Implement. - print ("HTMLReader.textAreaContent not implemented yet"); + try + { + int offset = textAreaDocument.getLength(); + textAreaDocument.insertString(offset, new String(data), null); + } + catch (BadLocationException ex) + { + // Must not happen as we insert at a model location that we + // got from the document itself. + assert false; + } } /** * Adds the given text that was encountered in a
 element.
-     * 
+     * This adds synthesized lines to hold the text runs.
+     *
      * @param data the text
      */
     protected void preContent(char[] data)
-      throws NotImplementedException
     {
-      // FIXME: Implement
-      print ("HTMLReader.preContent not implemented yet");
+      int start = 0;
+      for (int i = 0; i < data.length; i++)
+        {
+          if (data[i] == '\n')
+            {
+              addContent(data, start, i - start + 1);
+              blockClose(HTML.Tag.IMPLIED);
+              MutableAttributeSet atts = new SimpleAttributeSet();
+              atts.addAttribute(CSS.Attribute.WHITE_SPACE, "pre");
+              blockOpen(HTML.Tag.IMPLIED, atts);
+              start = i + 1;
+            }
+        }
+      if (start < data.length)
+        {
+          // Add remaining last line.
+          addContent(data, start, data.length - start);
+        }
     }
     
     /**
diff --git a/javax/swing/text/html/HTMLEditorKit.java b/javax/swing/text/html/HTMLEditorKit.java
index 523c2aca0..c541a4d34 100644
--- a/javax/swing/text/html/HTMLEditorKit.java
+++ b/javax/swing/text/html/HTMLEditorKit.java
@@ -46,6 +46,7 @@ import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
 import java.awt.event.MouseMotionListener;
 import java.awt.Cursor;
+import java.awt.Point;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -54,18 +55,24 @@ import java.io.Reader;
 import java.io.Serializable;
 import java.io.StringReader;
 import java.io.Writer;
+import java.net.MalformedURLException;
+import java.net.URL;
 
 import javax.accessibility.Accessible;
 import javax.accessibility.AccessibleContext;
 
 import javax.swing.Action;
 import javax.swing.JEditorPane;
+import javax.swing.SwingUtilities;
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.text.AttributeSet;
 import javax.swing.text.BadLocationException;
 import javax.swing.text.Document;
 import javax.swing.text.EditorKit;
 import javax.swing.text.Element;
 import javax.swing.text.MutableAttributeSet;
 import javax.swing.text.StyleConstants;
+import javax.swing.text.StyledDocument;
 import javax.swing.text.StyledEditorKit;
 import javax.swing.text.TextAction;
 import javax.swing.text.View;
@@ -111,11 +118,14 @@ public class HTMLEditorKit
        */
       public void mouseClicked(MouseEvent e)
       {
-        /*
-         These MouseInputAdapter methods generate mouse appropriate events around
-         hyperlinks (entering, exiting, and activating).
-         */
-        // FIXME: Not implemented.
+        JEditorPane editor = (JEditorPane) e.getSource();
+        if (! editor.isEditable() && SwingUtilities.isLeftMouseButton(e))
+          {
+            Point loc = e.getPoint();
+            int pos = editor.viewToModel(loc);
+            if (pos >= 0)
+              activateLink(pos, editor, e.getX(), e.getY());
+          }
       }
       
       /**
@@ -125,11 +135,7 @@ public class HTMLEditorKit
        */
       public void mouseDragged(MouseEvent e)
       {
-        /*
-        These MouseInputAdapter methods generate mouse appropriate events around
-        hyperlinks (entering, exiting, and activating).
-        */
-        // FIXME: Not implemented.     
+        // Nothing to do here.
       }
       
       /**
@@ -139,29 +145,111 @@ public class HTMLEditorKit
        */
       public void mouseMoved(MouseEvent e)
       {
-        /*
-        These MouseInputAdapter methods generate mouse appropriate events around
-        hyperlinks (entering, exiting, and activating).
-        */
-        // FIXME: Not implemented.
+        JEditorPane editor = (JEditorPane) e.getSource();
+        HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit();
+        if (! editor.isEditable())
+          {
+            Document doc = editor.getDocument();
+            if (doc instanceof HTMLDocument)
+              {
+                Cursor newCursor = kit.getDefaultCursor();
+                HTMLDocument htmlDoc = (HTMLDocument) doc;
+                Point loc = e.getPoint();
+                int pos = editor.viewToModel(loc);
+                Element el = htmlDoc.getCharacterElement(pos);
+                if (pos < el.getStartOffset() || pos >= el.getEndOffset())
+                  el = null;
+                if (el != null)
+                  {
+                    AttributeSet aAtts = (AttributeSet)
+                                   el.getAttributes().getAttribute(HTML.Tag.A);
+                    if (aAtts != null)
+                      newCursor = kit.getLinkCursor();
+                  }
+                if (editor.getCursor() != newCursor)
+                  editor.setCursor(newCursor);
+              }
+          }
       }
-      
+
       /**
        * If the given position represents a link, then linkActivated is called
-       * on the JEditorPane. Implemented to forward to the method with the same
-       * name, but pos == editor == -1.
-       * 
-       * @param pos - the position
-       * @param editor - the editor pane
+       * on the JEditorPane.
+       *
+       * @param pos the position
+       * @param editor the editor pane
        */
-      protected void activateLink(int pos,
-                                  JEditorPane editor)
+      protected void activateLink(int pos, JEditorPane editor)
       {
-        /*
-          This method creates and fires a HyperlinkEvent if the document is an
-          instance of HTMLDocument and the href tag of the link is not null.
-         */
-        // FIXME: Not implemented.
+        activateLink(pos, editor);
+      }
+
+      private void activateLink(int pos, JEditorPane editor, int x, int y)
+      {
+        // TODO: This is here for future extension for mapped links support.
+        // For the time beeing we implement simple hyperlinks.
+        Document doc = editor.getDocument();
+        if (doc instanceof HTMLDocument)
+          {
+            HTMLDocument htmlDoc = (HTMLDocument) doc;
+            Element el = htmlDoc.getCharacterElement(pos);
+            AttributeSet atts = el.getAttributes();
+            AttributeSet anchorAtts =
+              (AttributeSet) atts.getAttribute(HTML.Tag.A);
+            String href = null;
+            if (anchorAtts != null)
+              {
+                href = (String) anchorAtts.getAttribute(HTML.Attribute.HREF);
+              }
+            else
+              {
+                // TODO: Implement link maps here.
+              }
+            HyperlinkEvent event = null;
+            if (href != null)
+              event = createHyperlinkEvent(editor, htmlDoc, href,
+                                           anchorAtts, el);
+            if (event != null)
+              editor.fireHyperlinkUpdate(event);
+          }
+        
+      }
+
+      /**
+       * Creates a HyperlinkEvent for the specified href and anchor if
+       * possible. If for some reason this won't work, return null.
+       *
+       * @param editor the editor
+       * @param doc the document
+       * @param href the href link
+       * @param anchor the anchor
+       * @param el the element
+       *
+       * @return the hyperlink event, or null if we couldn't
+       *         create one
+       */
+      private HyperlinkEvent createHyperlinkEvent(JEditorPane editor,
+                                                  HTMLDocument doc,
+                                                  String href,
+                                                  AttributeSet anchor,
+                                                  Element el)
+      {
+        URL url;
+        try
+          {
+            URL base = doc.getBase();
+            url = new URL(base, href);
+            
+          }
+        catch (MalformedURLException ex)
+          {
+            url = null;
+          }
+        // TODO: Handle frame documents and target here.
+        HyperlinkEvent ev =
+          new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED, url,
+                             href, el);
+        return ev;
       }
     }
   
@@ -552,7 +640,8 @@ public class HTMLEditorKit
                    || tag.equals(HTML.Tag.HTML) || tag.equals(HTML.Tag.CENTER)
                    || tag.equals(HTML.Tag.DIV)
                    || tag.equals(HTML.Tag.BLOCKQUOTE)
-                   || tag.equals(HTML.Tag.PRE))
+                   || tag.equals(HTML.Tag.PRE)
+                   || tag.equals(HTML.Tag.FORM))
             view = new BlockView(element, View.Y_AXIS);
           else if (tag.equals(HTML.Tag.IMG))
             view = new ImageView(element);
@@ -570,14 +659,14 @@ public class HTMLEditorKit
             view = new HRuleView(element);
           else if (tag.equals(HTML.Tag.BR))
             view = new BRView(element);
+          else if (tag.equals(HTML.Tag.INPUT) || tag.equals(HTML.Tag.SELECT)
+                   || tag.equals(HTML.Tag.TEXTAREA))
+            view = new FormView(element);
 
           /*
           else if (tag.equals(HTML.Tag.MENU) || tag.equals(HTML.Tag.DIR)
                    || tag.equals(HTML.Tag.UL) || tag.equals(HTML.Tag.OL))
             view = new ListView(element);
-          else if (tag.equals(HTML.Tag.INPUT) || tag.equals(HTML.Tag.SELECT)
-                   || tag.equals(HTML.Tag.TEXTAREA))
-            view = new FormView(element);
           else if (tag.equals(HTML.Tag.OBJECT))
             view = new ObjectView(element);
           else if (tag.equals(HTML.Tag.FRAMESET))
@@ -830,7 +919,7 @@ public class HTMLEditorKit
   /**
    * The mouse listener used for links.
    */
-  LinkController mouseListener;
+  private LinkController linkController;
   
   /** The content type */
   String contentType = "text/html";
@@ -846,7 +935,7 @@ public class HTMLEditorKit
    */
   public HTMLEditorKit()
   {
-    // Nothing to do here.
+    linkController = new LinkController();
   }
   
   /**
@@ -982,8 +1071,15 @@ public class HTMLEditorKit
   {
     if (doc instanceof HTMLDocument)
       {
-        // FIXME: Not implemented. Use HTMLWriter.
-        out.write(doc.getText(pos, len));
+        HTMLWriter writer = new HTMLWriter(out, (HTMLDocument) doc, pos, len);
+        writer.write();
+      }
+    else if (doc instanceof StyledDocument)
+      {
+        MinimalHTMLWriter writer = new MinimalHTMLWriter(out,
+                                                         (StyledDocument) doc,
+                                                         pos, len);
+        writer.write();
       }
     else
       super.write(out, doc, pos, len);
@@ -1008,7 +1104,9 @@ public class HTMLEditorKit
   public Object clone()
   {
     // FIXME: Need to clone all fields
-    return (HTMLEditorKit) super.clone();
+    HTMLEditorKit copy = (HTMLEditorKit) super.clone();
+    copy.linkController = new LinkController();
+    return copy;
   }
   
   /**
@@ -1035,10 +1133,9 @@ public class HTMLEditorKit
   public void install(JEditorPane c)
   {
     super.install(c);
-    mouseListener = new LinkController();
-    c.addMouseListener(mouseListener);
+    c.addMouseListener(linkController);
+    c.addMouseMotionListener(linkController);
     editorPane = c;
-    // FIXME: need to set up hyperlinklistener object
   }
   
   /**
@@ -1050,8 +1147,8 @@ public class HTMLEditorKit
   public void deinstall(JEditorPane c)
   {
     super.deinstall(c);
-    c.removeMouseListener(mouseListener);
-    mouseListener = null;
+    c.removeMouseListener(linkController);
+    c.removeMouseMotionListener(linkController);
     editorPane = null;
   }
   
diff --git a/javax/swing/text/html/HTMLWriter.java b/javax/swing/text/html/HTMLWriter.java
new file mode 100644
index 000000000..16e9a7163
--- /dev/null
+++ b/javax/swing/text/html/HTMLWriter.java
@@ -0,0 +1,1126 @@
+/* HTMLWriter.java -- 
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+package javax.swing.text.html;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import java.util.Enumeration;
+import java.util.HashSet;
+
+import javax.swing.ComboBoxModel;
+
+import javax.swing.text.AbstractWriter;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
+
+import javax.swing.text.html.HTML;
+import javax.swing.text.html.HTMLDocument;
+import javax.swing.text.html.Option;
+
+/**
+ * HTMLWriter,
+ * A Writer for HTMLDocuments.
+ *
+ * @author David Fu (fchoong at netbeans.jp)
+ */
+
+public class HTMLWriter
+  extends AbstractWriter
+{
+  /**
+   * We keep a reference of the writer passed by the construct.
+   */
+  private Writer outWriter = null;
+
+  /**
+   * We keep a reference of the HTMLDocument passed by the construct.
+   */
+  private HTMLDocument htmlDoc = null; 
+
+  /**
+   * Used to keep track of which embeded has been written out.
+   */
+  private HashSet openEmbededTagHashSet = null;
+
+  private String new_line_str = "" + NEWLINE;
+    
+  private char[] html_entity_char_arr = {'<',    '>',    '&',     '"'};
+
+  private String[] html_entity_escape_str_arr = {"<", ">", "&", 
+                                                 """};
+
+  // variables used to output Html Fragment
+  private int doc_pos = -1;
+  private int doc_len = -1;
+  private int doc_offset_remaining = -1;
+  private int doc_len_remaining = -1;
+  private HashSet htmlFragmentParentHashSet = null;
+  private Element startElem = null;
+  private Element endElem = null;
+  private boolean fg_pass_start_elem = false;
+  private boolean fg_pass_end_elem = false;
+
+  /**
+   * Constructs a HTMLWriter.
+   *
+   * @param writer writer to write output to
+   * @param doc the HTMLDocument to output
+   */
+  public HTMLWriter(Writer writer, HTMLDocument doc)
+  {
+    super(writer, doc);
+    outWriter = writer;
+    htmlDoc = doc;
+    openEmbededTagHashSet = new HashSet();
+  } // public HTMLWriter(Writer writer, HTMLDocument doc)
+
+  /**
+   * Constructs a HTMLWriter which outputs a Html Fragment.
+   *
+   * @param writer Writer to write output to
+   * @param doc the javax.swing.text.html.HTMLDocument
+   *        to output
+   * @param pos position to start outputing the document
+   * @param len amount to output the document
+   */
+  public HTMLWriter(Writer writer, HTMLDocument doc, int pos, int len)
+  {
+    super(writer, doc, pos, len);
+    outWriter = writer;
+    htmlDoc = doc;
+    openEmbededTagHashSet = new HashSet();
+
+    doc_pos = pos;
+    doc_offset_remaining = pos;
+    doc_len = len;
+    doc_len_remaining = len;
+    htmlFragmentParentHashSet = new HashSet();
+  } // public HTMLWriter(Writer writer, HTMLDocument doc, int pos, int len)
+    
+  /**
+   * Call this method to start outputing HTML.
+   *
+   * @throws IOException on any I/O exceptions
+   * @throws BadLocationException if a pos is not a valid position in the
+   *                              html doc element
+   */
+  public void write()
+    throws IOException, BadLocationException
+  {
+    Element rootElem = htmlDoc.getDefaultRootElement();
+
+    if (doc_pos == -1 && doc_len == -1)
+      {
+        // Normal traversal.
+        traverse(rootElem);
+      } // if(doc_pos == -1 && doc_len == -1)
+    else    
+      {
+        // Html fragment traversal.
+        if (doc_pos == -1 || doc_len == -1)
+          throw new BadLocationException("Bad Location("
+          + doc_pos + ", " + doc_len + ")", doc_pos);
+
+        startElem = htmlDoc.getCharacterElement(doc_pos);
+
+        int start_offset = startElem.getStartOffset(); 
+
+        // Positions before start_offset will not be traversed, and thus
+        // will not be counted.
+        if (start_offset > 0)
+          doc_offset_remaining = doc_offset_remaining - start_offset;
+
+        Element tempParentElem = startElem;
+
+        while ((tempParentElem = tempParentElem.getParentElement()) != null)
+          {
+            if (!htmlFragmentParentHashSet.contains(tempParentElem))
+              htmlFragmentParentHashSet.add(tempParentElem);
+          } // while((tempParentElem = tempParentElem.getParentElement())
+            //   != null)
+
+        // NOTE: 20061030 - fchoong - the last index should not be included.
+        endElem = htmlDoc.getCharacterElement(doc_pos + doc_len - 1);
+
+        tempParentElem = endElem;
+
+        while ((tempParentElem = tempParentElem.getParentElement()) != null)
+          {
+            if (!htmlFragmentParentHashSet.contains(tempParentElem))
+              htmlFragmentParentHashSet.add(tempParentElem);
+          } // while((tempParentElem = tempParentElem.getParentElement())
+            //   != null)
+
+        traverseHtmlFragment(rootElem);
+
+      } // else
+
+    // NOTE: close out remaining open embeded tags.
+    Object[] tag_arr = openEmbededTagHashSet.toArray();
+
+    for (int i = 0; i < tag_arr.length; i++)
+      {
+        writeRaw("");
+      } // for(int i = 0; i < tag_arr.length; i++)
+
+  } // public void write() throws IOException, BadLocationException
+  
+  /**
+   * Writes all the attributes in the attrSet, except for attrbutes with
+   * keys of javax.swing.text.html.HTML.Tag,
+   * javax.swing.text.StyleConstants or
+   * javax.swing.text.html.HTML.Attribute.ENDTAG.
+   *
+   * @param attrSet attrSet to write out
+   *
+   * @throws IOException on any I/O exceptions
+   */
+  protected void writeAttributes(AttributeSet attrSet)
+    throws IOException
+  {
+    Enumeration attrNameEnum = attrSet.getAttributeNames();
+        
+    while (attrNameEnum.hasMoreElements())
+      {
+        Object key = attrNameEnum.nextElement();
+        Object value = attrSet.getAttribute(key);
+            
+        // HTML.Attribute.ENDTAG is an instance, not a class.
+        if (!((key instanceof HTML.Tag) || (key instanceof StyleConstants)
+          || (key == HTML.Attribute.ENDTAG)))
+          {
+            if (key == HTML.Attribute.SELECTED)
+              writeRaw(" selected");
+            else if (key == HTML.Attribute.CHECKED)
+              writeRaw(" checked");
+            else
+              writeRaw(" " + key + "=\"" + value + "\"");
+          } // if(!((key instanceof HTML.Tag) || (key instanceof
+            //   StyleConstants) || (key == HTML.Attribute.ENDTAG)))
+      } // while(attrNameEnum.hasMoreElements())
+        
+  } // protected void writeAttributes(AttributeSet attrSet) throws IOException
+
+  /**
+   * Writes out an empty tag. i.e. a tag without any child elements.
+   *
+   * @param paramElem the element to output as an empty tag
+   *
+   * @throws IOException on any I/O exceptions
+   * @throws BadLocationException if a pos is not a valid position in the
+   *                              html doc element
+   */
+  protected void emptyTag(Element paramElem)
+    throws IOException, BadLocationException
+  {
+    String elem_name = paramElem.getName();
+    AttributeSet attrSet = paramElem.getAttributes();
+
+    writeRaw("<" + elem_name);
+    writeAttributes(attrSet);
+    writeRaw(">");
+
+    if (isBlockTag(attrSet))
+      {
+        writeRaw("");
+      } // if(isBlockTag(attrSet))
+        
+  } // protected void emptyTag(Element paramElem)
+    //   throws IOException, BadLocationException
+    
+  /**
+   * Determines if it is a block tag or not.
+   *
+   * @param attrSet the attrSet of the element
+   *
+   * @return true if it is a block tag
+   *         false if it is a not block tag
+   */
+  protected boolean isBlockTag(AttributeSet attrSet)
+  {
+    return ((HTML.Tag)
+      attrSet.getAttribute(StyleConstants.NameAttribute)).isBlock();
+  } // protected boolean isBlockTag(AttributeSet attrSet)
+
+  /**
+   * Writes out a start tag. Synthesized elements are skipped.
+   *
+   * @param paramElem the element to output as a start tag
+   * @throws IOException on any I/O exceptions
+   * @throws BadLocationException if a pos is not a valid position in the
+   *                              html doc element
+   */
+  protected void startTag(Element paramElem)
+    throws IOException, BadLocationException
+  {
+    // NOTE: Sysnthesized elements do no call this method at all.
+    String elem_name = paramElem.getName();
+    AttributeSet attrSet = paramElem.getAttributes();
+
+    indent();
+    writeRaw("<" + elem_name);
+    writeAttributes(attrSet);
+    writeRaw(">");
+    writeLineSeparator(); // Extra formatting to look more like the RI.
+    incrIndent();
+
+  } // protected void startTag(Element paramElem)
+    //   throws IOException, BadLocationException
+
+  /**
+   * Writes out the contents of a textarea.
+   *
+   * @param attrSet the attrSet of the element to output as a text area
+   * @throws IOException on any I/O exceptions
+   * @throws BadLocationException if a pos is not a valid position in the
+   *                              html doc element
+   */
+  protected void textAreaContent(AttributeSet attrSet)
+    throws IOException, BadLocationException
+  {
+    writeLineSeparator(); // Extra formatting to look more like the RI.
+    indent();
+    writeRaw("");
+
+    Document tempDocument = 
+      (Document) attrSet.getAttribute(StyleConstants.ModelAttribute);
+
+    writeRaw(tempDocument.getText(0, tempDocument.getLength()));
+    indent();
+    writeRaw("");
+
+  } // protected void textAreaContent(AttributeSet attrSet)
+    //   throws IOException, BadLocationException
+
+  /**
+   * Writes out text, within the appropriate range if it is specified.
+   *
+   * @param paramElem the element to output as a text
+   * @throws IOException on any I/O exceptions
+   * @throws BadLocationException if a pos is not a valid position in the
+   *                              html doc element
+   */
+  protected void text(Element paramElem)
+    throws IOException, BadLocationException
+  {
+    int offset =  paramElem.getStartOffset();
+    int len =  paramElem.getEndOffset() -  paramElem.getStartOffset();
+    String txt_value = htmlDoc.getText(offset, len);
+
+    writeContent(txt_value);
+
+  } // protected void text(Element paramElem)
+    //   throws IOException, BadLocationException
+
+  /**
+   * Writes out the contents of a select element.
+   *
+   * @param attrSet the attrSet of the element to output as a select box
+   *
+   * @throws IOException on any I/O exceptions
+   */
+  protected void selectContent(AttributeSet attrSet)
+    throws IOException
+  {
+    writeLineSeparator(); // Extra formatting to look more like the RI.
+    indent();
+    writeRaw("");
+    incrIndent();
+    writeLineSeparator(); // extra formatting to look more like the RI.
+
+    ComboBoxModel comboBoxModel =
+      (ComboBoxModel) attrSet.getAttribute(StyleConstants.ModelAttribute);
+
+    for (int i = 0; i < comboBoxModel.getSize(); i++)
+      {
+        writeOption((Option) comboBoxModel.getElementAt(i));
+      } // for(int i = 0; i < comboBoxModel.getSize(); i++)
+
+    decrIndent();
+    indent();
+    writeRaw("");
+
+  } // protected void selectContent(AttributeSet attrSet) throws IOException
+
+  /**
+   * Writes out the contents of an option element.
+   *
+   * @param option the option object to output as a select option
+   *
+   * @throws IOException on any I/O exceptions
+   */
+  protected void writeOption(Option option)
+    throws IOException
+  {
+    indent();
+    writeRaw("");
+
+    writeContent(option.getLabel());
+
+    writeRaw("");
+    writeLineSeparator(); // extra formatting to look more like the RI.
+
+  } // protected void writeOption(Option option) throws IOException
+
+  /**
+   * Writes out an end tag.
+   *
+   * @param paramElem the element to output as an end tag
+   *
+   * @throws IOException on any I/O exceptions
+   */
+  protected void endTag(Element paramElem)
+    throws IOException
+  {
+    String elem_name = paramElem.getName();
+
+    //writeLineSeparator(); // Extra formatting to look more like the RI.
+    decrIndent();
+    indent();
+    writeRaw("");
+    writeLineSeparator(); // Extra formatting to look more like the RI.
+
+  } // protected void endTag(Element paramElem) throws IOException
+
+  /**
+   * Writes out the comment.
+   *
+   * @param paramElem the element to output as a comment
+   */
+  protected void comment(Element paramElem)
+    throws IOException, BadLocationException
+  {
+    AttributeSet attrSet = paramElem.getAttributes();
+
+    String comment_str = (String) attrSet.getAttribute(HTML.Attribute.COMMENT);
+
+    writeRaw("");
+
+  } // protected void comment(Element paramElem)
+    //   throws IOException, BadLocationException
+
+  /**
+   * Determines if element is a synthesized
+   * javax.swing.text.Element or not.
+   *
+   * @param element the element to test
+   *
+   * @return true if it is a synthesized element,
+   *         false if it is a not synthesized element
+   */
+  protected boolean synthesizedElement(Element element)
+  {
+    AttributeSet attrSet = element.getAttributes();
+    Object tagType = attrSet.getAttribute(StyleConstants.NameAttribute);
+
+    if (tagType == HTML.Tag.CONTENT || tagType == HTML.Tag.COMMENT
+        || tagType == HTML.Tag.IMPLIED)
+      return true;
+    else
+      return false;
+  } // protected boolean synthesizedElement(Element element)
+
+  /**
+   * Determines if
+   * javax.swing.text.StyleConstants.NameAttribute
+   * matches tag or not.
+   *
+   * @param attrSet the javax.swing.text.AttributeSet of
+   *        element to be matched
+   * @param tag the HTML.Tag to match
+   *
+   * @return true if it matches,
+   *         false if it does not match
+   */
+  protected boolean matchNameAttribute(AttributeSet attrSet, HTML.Tag tag)
+  {
+    Object tagType = attrSet.getAttribute(StyleConstants.NameAttribute);
+
+    if (tagType == tag)
+      return true;
+    else
+      return false;
+  } // protected boolean matchNameAttribute(AttributeSet attrSet,
+    //   HTML.Tag tag)
+
+  /**
+   * Writes out an embedded tag. The tags not already in
+   * openEmbededTagHashSet will written out.
+   *
+   * @param attrSet the javax.swing.text.AttributeSet of
+   *        the element to write out
+   *
+   * @throws IOException on any I/O exceptions
+   */
+  protected void writeEmbeddedTags(AttributeSet attrSet)
+    throws IOException
+  {
+    Enumeration attrNameEnum = attrSet.getAttributeNames();
+
+    while (attrNameEnum.hasMoreElements())
+      {
+        Object key = attrNameEnum.nextElement();
+        Object value = attrSet.getAttribute(key);
+
+        if (key instanceof HTML.Tag)
+          {
+            if (!openEmbededTagHashSet.contains(key))
+              {
+                writeRaw("<" + key);
+                writeAttributes((AttributeSet) value);
+                writeRaw(">");
+                openEmbededTagHashSet.add(key);
+              } // if(!openEmbededTagHashSet.contains(key))
+          } // if(key instanceof HTML.Tag)
+      } // while(attrNameEnum.hasMoreElements())
+
+  } // protected void writeEmbeddedTags(AttributeSet attrSet)
+    //   throws IOException
+
+  /**
+   * Closes out an unwanted embedded tag. The tags from the
+   *  openEmbededTagHashSet not found in attrSet will be written out.
+   * 
+   *  @param attrSet the AttributeSet of the element to write out
+   * 
+   *  @throws IOException on any I/O exceptions
+   */
+  protected void closeOutUnwantedEmbeddedTags(AttributeSet attrSet)
+    throws IOException
+  {
+    Object[] tag_arr = openEmbededTagHashSet.toArray();
+
+    for (int i = 0; i < tag_arr.length; i++)
+      {
+        HTML.Tag key = (HTML.Tag) tag_arr[i];
+            
+        if (!attrSet.isDefined(key))
+          {
+            writeRaw("");
+            openEmbededTagHashSet.remove(key);
+          } // if(!attrSet.isDefined(key))
+      } // for(int i = 0; i < tag_arr.length; i++)
+
+  } // protected void closeOutUnwantedEmbeddedTags(AttributeSet attrSet)
+    //   throws IOException
+
+  /**
+   * Writes out a line separator. Overwrites the parent to write out a new
+   * line.
+   *
+   * @throws IOException on any I/O exceptions.
+   */
+  protected void writeLineSeparator()
+    throws IOException
+  {
+    writeRaw(new_line_str);
+  } // protected void writeLineSeparator() throws IOException
+
+  /**
+   * Write to the writer. Character entites such as <, >
+   * are escaped appropriately.
+   *
+   * @param chars char array to write out
+   * @param off offset
+   * @param len length
+   *
+   * @throws IOException on any I/O exceptions
+   */
+  protected void output(char[] chars, int off, int len)
+   throws IOException
+  {
+    StringBuffer strBuffer = new StringBuffer();
+
+    for (int i = 0; i < chars.length; i++)
+      {
+        if (isCharHtmlEntity(chars[i]))
+          strBuffer.append(escapeCharHtmlEntity(chars[i]));
+        else
+          strBuffer.append(chars[i]);
+      } // for(int i = 0; i < chars.length; i++)
+
+    writeRaw(strBuffer.toString());
+
+  } // protected void output(char[] chars, int off, int len)
+    //   throws IOException
+ 
+  //-------------------------------------------------------------------------
+  // private methods
+  
+  /**
+   * The main method used to traverse through the elements.
+   *
+   * @param paramElem element to traverse
+   *
+   * @throws IOException on any I/O exceptions
+   */
+  private void traverse(Element paramElem)
+    throws IOException, BadLocationException
+  {
+    Element currElem = paramElem;
+
+    AttributeSet attrSet = currElem.getAttributes();
+
+    closeOutUnwantedEmbeddedTags(attrSet);
+
+    // NOTE: 20061030 - fchoong - GNU CP uses a different implimentation of
+    // the IMPLIED tag.
+    boolean fg_gnu_cp_implied_tag = false;
+
+    if (matchNameAttribute(attrSet, HTML.Tag.P))
+      {
+        //writeAllAttributes(attrSet);
+
+        Enumeration attrNameEnum = attrSet.getAttributeNames();
+
+        while (attrNameEnum.hasMoreElements())
+          {
+            Object key = attrNameEnum.nextElement();
+            Object value = attrSet.getAttribute(key);
+
+            if (key.equals("_implied_") && value.toString().equals("true"))
+              fg_gnu_cp_implied_tag = true;
+          } // while(attrNameEnum.hasMoreElements())
+      } // if(matchNameAttribute(attrSet, HTML.Tag.P))
+
+    // handle the tag
+    if (synthesizedElement(paramElem) || fg_gnu_cp_implied_tag)
+      {
+        if (matchNameAttribute(attrSet, HTML.Tag.CONTENT))
+          {
+            writeEmbeddedTags(attrSet);
+            text(currElem);
+          } // if(matchNameAttribute(attrSet, HTML.Tag.CONTENT))
+        else if (matchNameAttribute(attrSet, HTML.Tag.COMMENT))
+          {
+            comment(currElem);
+          } // else if(matchNameAttribute(attrSet, HTML.Tag.COMMENT))
+        else if (matchNameAttribute(attrSet, HTML.Tag.IMPLIED)
+                 || fg_gnu_cp_implied_tag) // NOTE: GNU CP specific
+          {
+            int child_elem_count = currElem.getElementCount();
+                
+            if (child_elem_count > 0)
+              {
+                for (int i = 0; i < child_elem_count; i++)
+                  {
+                    Element childElem = paramElem.getElement(i);
+
+                    traverse(childElem);
+
+                  } // for(int i = 0; i < child_elem_count; i++)
+              } // if(child_elem_count > 0)
+          } // else if(matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
+      } // if(synthesizedElement(paramElem))
+    else
+      {
+        // NOTE: 20061030 - fchoong - title is treated specially here.
+        // based on RI behavior.
+        if (matchNameAttribute(attrSet, HTML.Tag.TITLE))
+          {
+            boolean fg_is_end_tag = false;
+            Enumeration attrNameEnum = attrSet.getAttributeNames();
+
+            while (attrNameEnum.hasMoreElements())
+              {
+                Object key = attrNameEnum.nextElement();
+                Object value = attrSet.getAttribute(key);
+
+                if (key == HTML.Attribute.ENDTAG && value.equals("true"))
+                  fg_is_end_tag = true;
+              } // while(attrNameEnum.hasMoreElements())
+
+            if (fg_is_end_tag)
+              writeRaw("");
+            else
+              {
+                indent();
+                writeRaw("");
+
+                String title_str = 
+                  (String) htmlDoc.getProperty(HTMLDocument.TitleProperty);
+
+                if (title_str != null)
+                  writeContent(title_str);
+
+              } // else
+          } // if(matchNameAttribute(attrSet, HTML.Tag.TITLE))
+        else if (matchNameAttribute(attrSet, HTML.Tag.PRE))
+          {
+            // We pursue more stringent formating here.
+            attrSet = paramElem.getAttributes();
+
+            indent();
+            writeRaw("<pre");
+            writeAttributes(attrSet);
+            writeRaw(">");
+
+            int child_elem_count = currElem.getElementCount();
+
+            for (int i = 0; i < child_elem_count; i++)
+              {
+                Element childElem = paramElem.getElement(i);
+
+                traverse(childElem);
+
+              } // for(int i = 0; i < child_elem_count; i++)
+
+            writeRaw("</pre>");
+
+          } // else if(matchNameAttribute(attrSet, HTML.Tag.PRE))
+        else if (matchNameAttribute(attrSet, HTML.Tag.SELECT))
+          {
+            selectContent(attrSet);
+          } // else if(matchNameAttribute(attrSet, HTML.Tag.SELECT))
+        else if (matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
+          {
+            textAreaContent(attrSet);
+          } // else if(matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
+        else
+          {
+            int child_elem_count = currElem.getElementCount();
+
+            if (child_elem_count > 0)
+              {
+                startTag(currElem);
+
+                for (int i = 0; i < child_elem_count; i++)
+                  {
+                    Element childElem = paramElem.getElement(i);
+
+                    traverse(childElem);
+
+                  } // for(int i = 0; i < child_elem_count; i++)
+
+                  endTag(currElem);
+
+              } // if(child_elem_count > 0)
+            else
+              {
+                emptyTag(currElem);
+              } // else 
+            } // else
+          } // else
+
+  } // private void traverse(Element paramElem)
+    //   throws IOException, BadLocationException
+
+  /**
+   * The method used to traverse through a html fragment.
+   *
+   * @param paramElem element to traverse
+   *
+   * @throws IOException on any I/O exceptions
+   */
+  private void traverseHtmlFragment(Element paramElem)
+    throws IOException, BadLocationException
+  {
+    // NOTE: This method is similar to traverse(Element paramElem)
+    Element currElem = paramElem;
+
+    boolean fg_is_fragment_parent_elem = false;
+    boolean fg_is_start_and_end_elem = false;
+
+    if (htmlFragmentParentHashSet.contains(paramElem))
+      fg_is_fragment_parent_elem = true;
+
+    if (paramElem == startElem)
+      fg_pass_start_elem = true;
+
+    if (paramElem == startElem && paramElem == endElem)
+      fg_is_start_and_end_elem = true;
+
+    AttributeSet attrSet = currElem.getAttributes();
+
+    closeOutUnwantedEmbeddedTags(attrSet);
+
+    if (fg_is_fragment_parent_elem || (fg_pass_start_elem
+        && fg_pass_end_elem == false) || fg_is_start_and_end_elem)
+    {
+      // NOTE: 20061030 - fchoong - GNU CP uses a different implimentation of
+      // the IMPLIED tag.
+      boolean fg_gnu_cp_implied_tag = false;
+
+      if (matchNameAttribute(attrSet, HTML.Tag.P))
+        {
+          //writeAllAttributes(attrSet);
+
+          Enumeration attrNameEnum = attrSet.getAttributeNames();
+
+          while (attrNameEnum.hasMoreElements())
+            {
+              Object key = attrNameEnum.nextElement();
+              Object value = attrSet.getAttribute(key);
+
+              if (key.equals("_implied_") && value.toString().equals("true"))
+                fg_gnu_cp_implied_tag = true;
+            } // while(attrNameEnum.hasMoreElements())
+        } // if(matchNameAttribute(attrSet, HTML.Tag.P))
+
+      // handle the tag
+      if (synthesizedElement(paramElem) || fg_gnu_cp_implied_tag)
+        {
+          if (matchNameAttribute(attrSet, HTML.Tag.CONTENT))
+            {
+              writeEmbeddedTags(attrSet);
+
+              int content_offset =  paramElem.getStartOffset();
+              int content_length = currElem.getEndOffset() - content_offset;
+
+              if (doc_offset_remaining > 0)
+                {
+                  if (content_length > doc_offset_remaining)
+                    {
+                      int split_len = content_length;
+
+                      split_len = split_len - doc_offset_remaining;
+
+                      if (split_len > doc_len_remaining)
+                        split_len = doc_len_remaining;
+
+                      // we need to split it.
+                      String txt_value = htmlDoc.getText(content_offset
+                        + doc_offset_remaining, split_len);
+
+                      writeContent(txt_value);
+
+                      doc_offset_remaining = 0; // the offset is used up.
+                      doc_len_remaining = doc_len_remaining - split_len;
+                    } // if(content_length > doc_offset_remaining)
+                  else
+                    {
+                      // doc_offset_remaining is greater than the entire
+                      //   length of content
+                      doc_offset_remaining = doc_offset_remaining
+                        - content_length;
+                    }  // else
+                } // if(doc_offset_remaining > 0)
+              else if (content_length <= doc_len_remaining)
+                {
+                  // we can fit the entire content.
+                  text(currElem);
+                  doc_len_remaining = doc_len_remaining - content_length;
+                } // else if(content_length <= doc_len_remaining)
+              else
+                {
+                  // we need to split it.
+                  String txt_value = htmlDoc.getText(content_offset,
+                    doc_len_remaining);
+
+                  writeContent(txt_value);
+
+                  doc_len_remaining = 0;
+                } // else
+
+            } // if(matchNameAttribute(attrSet, HTML.Tag.CONTENT))
+          else if (matchNameAttribute(attrSet, HTML.Tag.COMMENT))
+            {
+              comment(currElem);
+            } // else if(matchNameAttribute(attrSet, HTML.Tag.COMMENT))
+          else if (matchNameAttribute(attrSet, HTML.Tag.IMPLIED)
+            || fg_gnu_cp_implied_tag) // NOTE: GNU CP specific
+            {
+              int child_elem_count = currElem.getElementCount();
+
+              if (child_elem_count > 0)
+                {
+                  for (int i = 0; i < child_elem_count; i++)
+                    {
+                      Element childElem = paramElem.getElement(i);
+
+                      traverseHtmlFragment(childElem);
+
+                    } // for(int i = 0; i < child_elem_count; i++)
+                } // if(child_elem_count > 0)
+            } // else if(matchNameAttribute(attrSet, HTML.Tag.IMPLIED))
+        } // if(synthesizedElement(paramElem))
+      else
+        { 
+            // NOTE: 20061030 - fchoong - the isLeaf() condition seems to
+            // generate the closest behavior to the RI.
+            if (paramElem.isLeaf())
+              {
+                if (doc_offset_remaining > 0)
+                  {
+                    doc_offset_remaining--;
+                  } // if(doc_offset_remaining > 0)
+                else if (doc_len_remaining > 0)
+                  {
+                    doc_len_remaining--;
+                  } // else if(doc_len_remaining > 0)
+              } // if(paramElem.isLeaf())
+
+          // NOTE: 20061030 - fchoong - title is treated specially here.
+          // based on RI behavior.
+          if (matchNameAttribute(attrSet, HTML.Tag.TITLE))
+            {
+              boolean fg_is_end_tag = false;
+              Enumeration attrNameEnum = attrSet.getAttributeNames();
+
+              while (attrNameEnum.hasMoreElements())
+                {
+                  Object key = attrNameEnum.nextElement();
+                  Object value = attrSet.getAttribute(key);
+
+                  if (key == HTML.Attribute.ENDTAG && value.equals("true"))
+                    fg_is_end_tag = true;
+                } // while(attrNameEnum.hasMoreElements())
+
+              if (fg_is_end_tag)
+                writeRaw("");
+              else
+                {
+                  indent();
+                  writeRaw("");
+
+                  String title_str = 
+                    (String) htmlDoc.getProperty(HTMLDocument.TitleProperty);
+
+                  if (title_str != null)
+                    writeContent(title_str);
+
+                } // else
+            } // if(matchNameAttribute(attrSet, HTML.Tag.TITLE))
+          else if (matchNameAttribute(attrSet, HTML.Tag.PRE))
+            {
+              // We pursue more stringent formating here.
+              attrSet = paramElem.getAttributes();
+
+              indent();
+              writeRaw("<pre");
+              writeAttributes(attrSet);
+              writeRaw(">");
+
+              int child_elem_count = currElem.getElementCount();
+
+              for (int i = 0; i < child_elem_count; i++)
+                {
+                  Element childElem = paramElem.getElement(i);
+
+                  traverseHtmlFragment(childElem);
+
+                } // for(int i = 0; i < child_elem_count; i++)
+
+              writeRaw("</pre>");
+
+            } // else if(matchNameAttribute(attrSet, HTML.Tag.PRE))
+          else if (matchNameAttribute(attrSet, HTML.Tag.SELECT))
+            {
+              selectContent(attrSet);
+            } // else if(matchNameAttribute(attrSet, HTML.Tag.SELECT))
+          else if (matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
+            {
+              textAreaContent(attrSet);
+            } // else if(matchNameAttribute(attrSet, HTML.Tag.TEXTAREA))
+          else
+            {
+              int child_elem_count = currElem.getElementCount();
+
+              if (child_elem_count > 0)
+                {
+                  startTag(currElem);
+
+                  for (int i = 0; i < child_elem_count; i++)
+                    {
+                      Element childElem = paramElem.getElement(i);
+
+                      traverseHtmlFragment(childElem);
+
+                    } // for(int i = 0; i < child_elem_count; i++)
+
+                    endTag(currElem);
+
+                } // if(child_elem_count > 0)
+              else
+                {
+                  emptyTag(currElem);
+                } // else 
+            } // else
+        } // else
+
+    } // if(fg_is_fragment_parent_elem || (fg_pass_start_elem
+      //   && fg_pass_end_elem == false) || fg_is_start_and_end_elem)
+
+    if (paramElem == endElem)
+      fg_pass_end_elem = true;
+
+  } // private void traverseHtmlFragment(Element paramElem)
+    //   throws IOException, BadLocationException
+
+  /**
+   * Write to the writer without any modifications.
+   *
+   * @param param_str the str to write out
+   *
+   * @throws IOException on any I/O exceptions
+   */
+  private void writeRaw(String param_str)
+    throws IOException
+  {
+    super.output(param_str.toCharArray(), 0, param_str.length());
+  } // private void writeRaw(char[] chars, int off, int len)
+    //   throws IOException
+
+  /**
+   * Write to the writer, escaping HTML character entitie where neccessary.
+   *
+   * @param param_str the str to write out
+   *
+   * @throws IOException on any I/O exceptions
+   */
+  private void writeContent(String param_str)
+    throws IOException
+  {
+    char[] str_char_arr = param_str.toCharArray();
+
+    if (hasHtmlEntity(param_str))
+      output(str_char_arr, 0, str_char_arr.length);
+    else
+      super.output(str_char_arr, 0, str_char_arr.length);
+
+  } // private void writeContent(String param_str) throws IOException
+
+  /**
+   * Use this for debugging. Writes out all attributes regardless of type.
+   *
+   * @param attrSet the <code>javax.swing.text.AttributeSet</code> to
+   *        write out
+   *
+   * @throws IOException on any I/O exceptions
+   */
+  private void writeAllAttributes(AttributeSet attrSet)
+    throws IOException
+  {
+    Enumeration attrNameEnum = attrSet.getAttributeNames();
+
+    while (attrNameEnum.hasMoreElements())
+      {
+        Object key = attrNameEnum.nextElement();
+        Object value = attrSet.getAttribute(key);
+
+        writeRaw(" " + key + "=\"" + value + "\"");
+        writeRaw(" " + key.getClass().toString() + "=\""
+          + value.getClass().toString() + "\"");
+      } // while(attrNameEnum.hasMoreElements())
+
+  } // private void writeAllAttributes(AttributeSet attrSet)
+    //   throws IOException
+
+  /**
+   * Tests if the str contains any html entities.
+   *
+   * @param param_str the str to test
+   *
+   * @return <code>true</code> if it has a html entity
+   *         <code>false</code> if it does not have a html entity
+   */
+  private boolean hasHtmlEntity(String param_str)
+  {
+    boolean ret_bool = false;
+
+    for (int i = 0; i < html_entity_char_arr.length; i++)
+      {
+        if (param_str.indexOf(html_entity_char_arr[i]) != -1)
+          {
+            ret_bool = true;
+            break;
+          } // if(param_str.indexOf(html_entity_char_arr[i]) != -1)
+      } // for(int i = 0; i < html_entity_char_arr.length; i++)
+
+    return ret_bool;
+  } // private boolean hasHtmlEntity(String param_str)
+
+  /**
+   * Tests if the char is a html entities.
+   *
+   * @param param_char the char to test
+   *
+   * @return <code>true</code> if it is a html entity
+   *         <code>false</code> if it is not a html entity.
+   */
+  private boolean isCharHtmlEntity(char param_char)
+  {
+    boolean ret_bool = false;
+
+    for (int i = 0; i < html_entity_char_arr.length; i++)
+      {
+        if (param_char == html_entity_char_arr[i])
+          {
+            ret_bool = true;
+            break;
+          } // if(param_char == html_entity_char_arr[i])
+      } // for(int i = 0; i < html_entity_char_arr.length; i++)
+
+      return ret_bool;
+  } // private boolean hasHtmlEntity(String param_str)
+
+  /**
+   * Escape html entities.
+   *
+   * @param param_char the char to escape
+   *
+   * @return escaped html entity. Original char is returned as a str if is
+   *         is not a html entity
+   */
+  private String escapeCharHtmlEntity(char param_char)
+  {
+    String ret_str = "" + param_char;
+
+    for (int i = 0; i < html_entity_char_arr.length; i++)
+      {
+        if (param_char == html_entity_char_arr[i])
+          {
+            ret_str = html_entity_escape_str_arr[i];
+            break;
+          } // if(param_char == html_entity_char_arr[i])
+      } // for(int i = 0; i < html_entity_char_arr.length; i++)
+
+      return ret_str;
+  } // private String escapeCharHtmlEntity(char param_char)
+
+} // public class HTMLWriter extends AbstractWriter
\ No newline at end of file
diff --git a/javax/swing/text/html/ImageView.java b/javax/swing/text/html/ImageView.java
index 84b021070..ff0d3ea40 100644
--- a/javax/swing/text/html/ImageView.java
+++ b/javax/swing/text/html/ImageView.java
@@ -175,19 +175,22 @@ public class ImageView extends View
    */
   public URL getImageURL()
   {
-    Object url = getAttributes().getAttribute(Attribute.SRC);
-    if (url == null)
-      return null;
-
-    try
-      {
-        return new URL(url.toString());
-      }
-    catch (MalformedURLException e)
+    Element el = getElement();
+    String src = (String) el.getAttributes().getAttribute(Attribute.SRC);
+    URL url = null;
+    if (src != null)
       {
-        // The URL is malformed - no image.
-        return null;
+        URL base = ((HTMLDocument) getDocument()).getBase();
+        try
+          {
+            url = new URL(base, src);
+          }
+        catch (MalformedURLException ex)
+          {
+            // Return null.
+          }
       }
+    return url;
   }
 
   /**
diff --git a/javax/swing/text/html/StyleSheet.java b/javax/swing/text/html/StyleSheet.java
index 520076652..703a3864a 100644
--- a/javax/swing/text/html/StyleSheet.java
+++ b/javax/swing/text/html/StyleSheet.java
@@ -38,7 +38,6 @@ exception statement from your version. */
 
 package javax.swing.text.html;
 
-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;
@@ -53,6 +52,7 @@ import java.awt.Graphics;
 import java.io.IOException;
 import java.io.Reader;
 import java.io.Serializable;
+import java.io.StringReader;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Enumeration;
@@ -94,8 +94,10 @@ public class StyleSheet extends StyleContext
 
   /**
    * Parses CSS stylesheets using the parser in gnu/javax/swing/html/css.
+   *
+   * This is package private to avoid accessor methods.
    */
-  private class CSSStyleSheetParserCallback
+  class CSSStyleSheetParserCallback
     implements CSSParserCallback
   {
     /**
@@ -405,9 +407,20 @@ public class StyleSheet extends StyleContext
    * @param rule - the rule to add to the sheet
    */
   public void addRule(String rule)
-    throws NotImplementedException
   {
-    // FIXME: Implement.
+    CSSStyleSheetParserCallback cb = new CSSStyleSheetParserCallback();
+    // FIXME: Handle ref.
+    StringReader in = new StringReader(rule);
+    CSSParser parser = new CSSParser(in, cb);
+    try
+      {
+        parser.parse();
+      }
+    catch (IOException ex)
+      {
+        // Shouldn't happen. And if, then we
+        assert false;
+      }
   }
   
   /**
diff --git a/native/plugin/gcjwebplugin.cc b/native/plugin/gcjwebplugin.cc
index 1202c4719..bb2bfa223 100644
--- a/native/plugin/gcjwebplugin.cc
+++ b/native/plugin/gcjwebplugin.cc
@@ -283,10 +283,6 @@ GCJ_New (NPMIMEType pluginType, NPP instance, uint16 mode,
       goto cleanup_done;
     }
 
-  // Initialize threads (needed for mutexes).
-  if (!g_thread_supported ())
-    g_thread_init (NULL);
-
   // data
   plugin_data_new (&data);
   if (data == NULL)
@@ -1686,6 +1682,11 @@ NP_Initialize (NPNetscapeFuncs* browserTable, NPPluginFuncs* pluginTable)
   pluginTable->getvalue = NewNPP_GetValueProc (GCJ_GetValue);
   
   initialized = true;
+
+  // Initialize threads (needed for mutexes).
+  if (!g_thread_supported ())
+    g_thread_init (NULL);
+
   plugin_instance_mutex = g_mutex_new ();
 
   PLUGIN_DEBUG ("NP_Initialize: using " APPLETVIEWER_EXECUTABLE ".");
-- 
cgit v1.2.1