From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- .../classpath/javax/swing/text/html/BRView.java | 70 + .../classpath/javax/swing/text/html/BlockView.java | 721 ++++++ libjava/classpath/javax/swing/text/html/CSS.java | 736 +++++++ .../classpath/javax/swing/text/html/CSSBorder.java | 421 ++++ .../classpath/javax/swing/text/html/CSSParser.java | 561 +++++ .../javax/swing/text/html/FormSubmitEvent.java | 123 ++ .../classpath/javax/swing/text/html/FormView.java | 870 ++++++++ .../javax/swing/text/html/FrameSetView.java | 274 +++ .../classpath/javax/swing/text/html/FrameView.java | 233 ++ .../classpath/javax/swing/text/html/HRuleView.java | 189 ++ libjava/classpath/javax/swing/text/html/HTML.java | 1253 +++++++++++ .../javax/swing/text/html/HTMLDocument.java | 2298 ++++++++++++++++++++ .../javax/swing/text/html/HTMLEditorKit.java | 1520 +++++++++++++ .../swing/text/html/HTMLFrameHyperlinkEvent.java | 130 ++ .../javax/swing/text/html/HTMLWriter.java | 1088 +++++++++ .../classpath/javax/swing/text/html/ImageView.java | 591 +++++ .../javax/swing/text/html/InlineView.java | 307 +++ .../classpath/javax/swing/text/html/ListView.java | 128 ++ .../javax/swing/text/html/MinimalHTMLWriter.java | 453 ++++ .../javax/swing/text/html/MultiAttributeSet.java | 213 ++ .../javax/swing/text/html/MultiStyle.java | 136 ++ .../classpath/javax/swing/text/html/NullView.java | 102 + .../javax/swing/text/html/ObjectView.java | 110 + .../classpath/javax/swing/text/html/Option.java | 159 ++ .../javax/swing/text/html/ParagraphView.java | 322 +++ .../javax/swing/text/html/ResetableModel.java | 50 + .../swing/text/html/ResetablePlainDocument.java | 82 + .../text/html/ResetableToggleButtonModel.java | 70 + .../javax/swing/text/html/SelectComboBoxModel.java | 84 + .../javax/swing/text/html/SelectListModel.java | 106 + .../javax/swing/text/html/StyleSheet.java | 1455 +++++++++++++ .../classpath/javax/swing/text/html/TableView.java | 974 +++++++++ .../javax/swing/text/html/ViewAttributeSet.java | 163 ++ .../classpath/javax/swing/text/html/package.html | 50 + .../swing/text/html/parser/AttributeList.java | 294 +++ .../javax/swing/text/html/parser/ContentModel.java | 223 ++ .../javax/swing/text/html/parser/DTD.java | 609 ++++++ .../javax/swing/text/html/parser/DTDConstants.java | 292 +++ .../swing/text/html/parser/DocumentParser.java | 268 +++ .../javax/swing/text/html/parser/Element.java | 317 +++ .../javax/swing/text/html/parser/Entity.java | 183 ++ .../javax/swing/text/html/parser/Parser.java | 446 ++++ .../swing/text/html/parser/ParserDelegator.java | 207 ++ .../javax/swing/text/html/parser/TagElement.java | 142 ++ .../javax/swing/text/html/parser/package.html | 50 + 45 files changed, 19073 insertions(+) create mode 100644 libjava/classpath/javax/swing/text/html/BRView.java create mode 100644 libjava/classpath/javax/swing/text/html/BlockView.java create mode 100644 libjava/classpath/javax/swing/text/html/CSS.java create mode 100644 libjava/classpath/javax/swing/text/html/CSSBorder.java create mode 100644 libjava/classpath/javax/swing/text/html/CSSParser.java create mode 100644 libjava/classpath/javax/swing/text/html/FormSubmitEvent.java create mode 100644 libjava/classpath/javax/swing/text/html/FormView.java create mode 100644 libjava/classpath/javax/swing/text/html/FrameSetView.java create mode 100644 libjava/classpath/javax/swing/text/html/FrameView.java create mode 100644 libjava/classpath/javax/swing/text/html/HRuleView.java create mode 100644 libjava/classpath/javax/swing/text/html/HTML.java create mode 100644 libjava/classpath/javax/swing/text/html/HTMLDocument.java create mode 100644 libjava/classpath/javax/swing/text/html/HTMLEditorKit.java create mode 100644 libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java create mode 100644 libjava/classpath/javax/swing/text/html/HTMLWriter.java create mode 100644 libjava/classpath/javax/swing/text/html/ImageView.java create mode 100644 libjava/classpath/javax/swing/text/html/InlineView.java create mode 100644 libjava/classpath/javax/swing/text/html/ListView.java create mode 100644 libjava/classpath/javax/swing/text/html/MinimalHTMLWriter.java create mode 100644 libjava/classpath/javax/swing/text/html/MultiAttributeSet.java create mode 100644 libjava/classpath/javax/swing/text/html/MultiStyle.java create mode 100644 libjava/classpath/javax/swing/text/html/NullView.java create mode 100644 libjava/classpath/javax/swing/text/html/ObjectView.java create mode 100644 libjava/classpath/javax/swing/text/html/Option.java create mode 100644 libjava/classpath/javax/swing/text/html/ParagraphView.java create mode 100644 libjava/classpath/javax/swing/text/html/ResetableModel.java create mode 100644 libjava/classpath/javax/swing/text/html/ResetablePlainDocument.java create mode 100644 libjava/classpath/javax/swing/text/html/ResetableToggleButtonModel.java create mode 100644 libjava/classpath/javax/swing/text/html/SelectComboBoxModel.java create mode 100644 libjava/classpath/javax/swing/text/html/SelectListModel.java create mode 100644 libjava/classpath/javax/swing/text/html/StyleSheet.java create mode 100644 libjava/classpath/javax/swing/text/html/TableView.java create mode 100644 libjava/classpath/javax/swing/text/html/ViewAttributeSet.java create mode 100644 libjava/classpath/javax/swing/text/html/package.html create mode 100644 libjava/classpath/javax/swing/text/html/parser/AttributeList.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/ContentModel.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/DTD.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/DTDConstants.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/DocumentParser.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/Element.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/Entity.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/Parser.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/TagElement.java create mode 100644 libjava/classpath/javax/swing/text/html/parser/package.html (limited to 'libjava/classpath/javax/swing/text/html') diff --git a/libjava/classpath/javax/swing/text/html/BRView.java b/libjava/classpath/javax/swing/text/html/BRView.java new file mode 100644 index 000000000..6f465c9d1 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/BRView.java @@ -0,0 +1,70 @@ +/* BRView.java -- HTML BR tag view + 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 javax.swing.text.Element; + +/** + * Handled the HTML BR tag. + */ +class BRView + extends InlineView +{ + /** + * Creates the new BR view. + * + * @param elem the HTML element, representing the view. + */ + public BRView(Element elem) + { + super(elem); + } + + /** + * Always return ForcedBreakWeight for the X_AXIS, BadBreakWeight for the + * Y_AXIS. + */ + public int getBreakWeight(int axis, float pos, float len) + { + if (axis == X_AXIS) + return ForcedBreakWeight; + else + return super.getBreakWeight(axis, pos, len); + } +} diff --git a/libjava/classpath/javax/swing/text/html/BlockView.java b/libjava/classpath/javax/swing/text/html/BlockView.java new file mode 100644 index 000000000..1c3397126 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/BlockView.java @@ -0,0 +1,721 @@ +/* BlockView.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import gnu.javax.swing.text.html.css.Length; + +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; +import java.util.HashMap; + +import javax.swing.SizeRequirements; +import javax.swing.event.DocumentEvent; +import javax.swing.text.AttributeSet; +import javax.swing.text.BoxView; +import javax.swing.text.Element; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; + +/** + * @author Lillian Angel + */ +public class BlockView extends BoxView +{ + + /** + * Stores information about child positioning according to the + * CSS attributes position, left, right, top and bottom. + */ + private static class PositionInfo + { + // TODO: Use enums when available. + + /** + * Static positioning. This is the default and is thus rarely really + * used. + */ + static final int STATIC = 0; + + /** + * Relative positioning. The box is teaked relative to its static + * computed bounds. + */ + static final int RELATIVE = 1; + + /** + * Absolute positioning. The box is moved relative to the parent's box. + */ + static final int ABSOLUTE = 2; + + /** + * Like ABSOLUTE, with some fixation against the viewport (not yet + * implemented). + */ + static final int FIXED = 3; + + /** + * The type according to the constants of this class. + */ + int type; + + /** + * The left constraint, null if not set. + */ + Length left; + + /** + * The right constraint, null if not set. + */ + Length right; + + /** + * The top constraint, null if not set. + */ + Length top; + + /** + * The bottom constraint, null if not set. + */ + Length bottom; + + /** + * Creates a new PositionInfo object. + * + * @param typ the type to set + * @param l the left constraint + * @param r the right constraint + * @param t the top constraint + * @param b the bottom constraint + */ + PositionInfo(int typ, Length l, Length r, Length t, Length b) + { + type = typ; + left = l; + right = r; + top = t; + bottom = b; + } + } + + /** + * The attributes for this view. + */ + private AttributeSet attributes; + + /** + * The box painter for this view. + * + * This is package private because the TableView needs access to it. + */ + StyleSheet.BoxPainter painter; + + /** + * The width and height as specified in the stylesheet, null if not + * specified. The first value is the X_AXIS, the second the Y_AXIS. You + * can index this directly by the X_AXIS and Y_AXIS constants. + */ + private Length[] cssSpans; + + /** + * Stores additional CSS layout information. + */ + private HashMap positionInfo; + + /** + * Creates a new view that represents an html box. + * This can be used for a number of elements. + * + * @param elem - the element to create a view for + * @param axis - either View.X_AXIS or View.Y_AXIS + */ + public BlockView(Element elem, int axis) + { + super(elem, axis); + cssSpans = new Length[2]; + positionInfo = new HashMap(); + } + + /** + * Creates the parent view for this. It is called before + * any other methods, if the parent view is working properly. + * Implemented to forward to the superclass and call + * setPropertiesFromAttributes to set the paragraph + * properties. + * + * @param parent - the new parent, or null if the view + * is being removed from a parent it was added to. + */ + public void setParent(View parent) + { + super.setParent(parent); + + if (parent != null) + setPropertiesFromAttributes(); + } + + /** + * Calculates the requirements along the major axis. + * This is implemented to call the superclass and then + * adjust it if the CSS width or height attribute is specified + * and applicable. + * + * @param axis - the axis to check the requirements for. + * @param r - the SizeRequirements. If null, one is created. + * @return the new SizeRequirements object. + */ + protected SizeRequirements calculateMajorAxisRequirements(int axis, + SizeRequirements r) + { + if (r == null) + r = new SizeRequirements(); + + if (setCSSSpan(r, axis)) + { + // If we have set the span from CSS, then we need to adjust + // the margins. + SizeRequirements parent = super.calculateMajorAxisRequirements(axis, + null); + int margin = axis == X_AXIS ? getLeftInset() + getRightInset() + : getTopInset() + getBottomInset(); + r.minimum -= margin; + r.preferred -= margin; + r.maximum -= margin; + constrainSize(axis, r, parent); + } + else + r = super.calculateMajorAxisRequirements(axis, r); + return r; + } + + /** + * Calculates the requirements along the minor axis. + * This is implemented to call the superclass and then + * adjust it if the CSS width or height attribute is specified + * and applicable. + * + * @param axis - the axis to check the requirements for. + * @param r - the SizeRequirements. If null, one is created. + * @return the new SizeRequirements object. + */ + protected SizeRequirements calculateMinorAxisRequirements(int axis, + SizeRequirements r) + { + if (r == null) + r = new SizeRequirements(); + + if (setCSSSpan(r, axis)) + { + // If we have set the span from CSS, then we need to adjust + // the margins. + SizeRequirements parent = super.calculateMinorAxisRequirements(axis, + null); + int margin = axis == X_AXIS ? getLeftInset() + getRightInset() + : getTopInset() + getBottomInset(); + r.minimum -= margin; + r.preferred -= margin; + r.maximum -= margin; + constrainSize(axis, r, parent); + } + else + r = super.calculateMinorAxisRequirements(axis, r); + + // Apply text alignment if appropriate. + if (axis == X_AXIS) + { + Object o = getAttributes().getAttribute(CSS.Attribute.TEXT_ALIGN); + if (o != null) + { + String al = o.toString().trim(); + if (al.equals("center")) + r.alignment = 0.5f; + else if (al.equals("right")) + r.alignment = 1.0f; + else + r.alignment = 0.0f; + } + } + return r; + } + + /** + * Sets the span on the SizeRequirements object according to the + * according CSS span value, when it is set. + * + * @param r the size requirements + * @param axis the axis + * + * @return true when the CSS span has been set, + * false otherwise + */ + private boolean setCSSSpan(SizeRequirements r, int axis) + { + boolean ret = false; + Length span = cssSpans[axis]; + // We can't set relative CSS spans here because we don't know + // yet about the allocated span. Instead we use the view's + // normal requirements. + if (span != null && ! span.isPercentage()) + { + r.minimum = (int) span.getValue(); + r.preferred = (int) span.getValue(); + r.maximum = (int) span.getValue(); + ret = true; + } + return ret; + } + + /** + * Constrains the r requirements according to + * min. + * + * @param axis the axis + * @param r the requirements to constrain + * @param min the constraining requirements + */ + private void constrainSize(int axis, SizeRequirements r, + SizeRequirements min) + { + if (min.minimum > r.minimum) + { + r.minimum = min.minimum; + r.preferred = min.minimum; + r.maximum = Math.max(r.maximum, min.maximum); + } + } + + /** + * Lays out the box along the minor axis (the axis that is + * perpendicular to the axis that it represents). The results + * of the layout are placed in the given arrays which are + * the allocations to the children along the minor axis. + * + * @param targetSpan - the total span given to the view, also + * used to layout the children. + * @param axis - the minor axis + * @param offsets - the offsets from the origin of the view for + * all the child views. This is a return value and is filled in by this + * function. + * @param spans - the span of each child view. This is a return value and is + * filled in by this function. + */ + protected void layoutMinorAxis(int targetSpan, int axis, + int[] offsets, int[] spans) + { + int viewCount = getViewCount(); + for (int i = 0; i < viewCount; i++) + { + View view = getView(i); + int min = (int) view.getMinimumSpan(axis); + int max; + // Handle CSS span value of child. + Length length = cssSpans[axis]; + if (length != null) + { + min = Math.max((int) length.getValue(targetSpan), min); + max = min; + } + else + max = (int) view.getMaximumSpan(axis); + + if (max < targetSpan) + { + // Align child. + float align = view.getAlignment(axis); + offsets[i] = (int) ((targetSpan - max) * align); + spans[i] = max; + } + else + { + offsets[i] = 0; + spans[i] = Math.max(min, targetSpan); + } + + // Adjust according to CSS position info. + positionView(targetSpan, axis, i, offsets, spans); + } + } + + /** + * Overridden to perform additional CSS layout (absolute/relative + * positioning). + */ + protected void layoutMajorAxis(int targetSpan, int axis, + int[] offsets, int[] spans) + { + super.layoutMajorAxis(targetSpan, axis, offsets, spans); + + // Adjust according to CSS position info. + int viewCount = getViewCount(); + for (int i = 0; i < viewCount; i++) + { + positionView(targetSpan, axis, i, offsets, spans); + } + } + + /** + * Positions a view according to any additional CSS constraints. + * + * @param targetSpan the target span + * @param axis the axis + * @param i the index of the view + * @param offsets the offsets get placed here + * @param spans the spans get placed here + */ + private void positionView(int targetSpan, int axis, int i, int[] offsets, + int[] spans) + { + View view = getView(i); + PositionInfo pos = (PositionInfo) positionInfo.get(view); + if (pos != null) + { + int p0 = -1; + int p1 = -1; + if (axis == X_AXIS) + { + Length l = pos.left; + if (l != null) + p0 = (int) l.getValue(targetSpan); + l = pos.right; + if (l != null) + p1 = (int) l.getValue(targetSpan); + } + else + { + Length l = pos.top; + if (l != null) + p0 = (int) l.getValue(targetSpan); + l = pos.bottom; + if (l != null) + p1 = (int) l.getValue(targetSpan); + } + if (pos.type == PositionInfo.ABSOLUTE + || pos.type == PositionInfo.FIXED) + { + if (p0 != -1) + { + offsets[i] = p0; + if (p1 != -1) + { + // Overrides computed width. (Possibly overconstrained + // when the width attribute was set too.) + spans[i] = targetSpan - p1 - offsets[i]; + } + } + else if (p1 != -1) + { + // Preserve any computed width. + offsets[i] = targetSpan - p1 - spans[i]; + } + } + else if (pos.type == PositionInfo.RELATIVE) + { + if (p0 != -1) + { + offsets[i] += p0; + if (p1 != -1) + { + // Overrides computed width. (Possibly overconstrained + // when the width attribute was set too.) + spans[i] = spans[i] - p0 - p1 - offsets[i]; + } + } + else if (p1 != -1) + { + // Preserve any computed width. + offsets[i] -= p1; + } + } + } + } + + /** + * Paints using the given graphics configuration and shape. + * This delegates to the css box painter to paint the + * border and background prior to the interior. + * + * @param g - Graphics configuration + * @param a - the Shape to render into. + */ + public void paint(Graphics g, Shape a) + { + Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds(); + + // Debug output. Shows blocks in green rectangles. + // g.setColor(Color.GREEN); + // g.drawRect(rect.x, rect.y, rect.width, rect.height); + + painter.paint(g, rect.x, rect.y, rect.width, rect.height, this); + super.paint(g, a); + } + + /** + * Fetches the attributes to use when painting. + * + * @return the attributes of this model. + */ + public AttributeSet getAttributes() + { + if (attributes == null) + attributes = getStyleSheet().getViewAttributes(this); + return attributes; + } + + /** + * Gets the resize weight. + * + * @param axis - the axis to get the resize weight for. + * @return the resize weight. + * @throws IllegalArgumentException - for an invalid axis + */ + public int getResizeWeight(int axis) throws IllegalArgumentException + { + // Can't resize the Y_AXIS + if (axis == Y_AXIS) + return 0; + if (axis == X_AXIS) + return 1; + throw new IllegalArgumentException("Invalid Axis"); + } + + /** + * Gets the alignment. + * + * @param axis - the axis to get the alignment for. + * @return the alignment. + */ + public float getAlignment(int axis) + { + if (axis == X_AXIS) + return super.getAlignment(axis); + if (axis == Y_AXIS) + { + if (getViewCount() == 0) + return 0.0F; + float prefHeight = getPreferredSpan(Y_AXIS); + View first = getView(0); + float firstRowHeight = first.getPreferredSpan(Y_AXIS); + return prefHeight != 0 ? (firstRowHeight * first.getAlignment(Y_AXIS)) + / prefHeight + : 0; + } + throw new IllegalArgumentException("Invalid Axis"); + } + + /** + * Gives notification from the document that attributes were + * changed in a location that this view is responsible for. + * + * @param ev - the change information + * @param a - the current shape of the view + * @param f - the factory to use to rebuild if the view has children. + */ + public void changedUpdate(DocumentEvent ev, + Shape a, ViewFactory f) + { + super.changedUpdate(ev, a, f); + + // If more elements were added, then need to set the properties for them + int currPos = ev.getOffset(); + if (currPos <= getStartOffset() + && (currPos + ev.getLength()) >= getEndOffset()) + setPropertiesFromAttributes(); + } + + /** + * Determines the preferred span along the axis. + * + * @param axis - the view to get the preferred span for. + * @return the span the view would like to be painted into >=0/ + * The view is usually told to paint into the span that is returned, + * although the parent may choose to resize or break the view. + * @throws IllegalArgumentException - for an invalid axis + */ + public float getPreferredSpan(int axis) throws IllegalArgumentException + { + if (axis == X_AXIS || axis == Y_AXIS) + return super.getPreferredSpan(axis); + throw new IllegalArgumentException("Invalid Axis"); + } + + /** + * Determines the minimum span along the axis. + * + * @param axis - the axis to get the minimum span for. + * @return the span the view would like to be painted into >=0/ + * The view is usually told to paint into the span that is returned, + * although the parent may choose to resize or break the view. + * @throws IllegalArgumentException - for an invalid axis + */ + public float getMinimumSpan(int axis) throws IllegalArgumentException + { + if (axis == X_AXIS || axis == Y_AXIS) + return super.getMinimumSpan(axis); + throw new IllegalArgumentException("Invalid Axis"); + } + + /** + * Determines the maximum span along the axis. + * + * @param axis - the axis to get the maximum span for. + * @return the span the view would like to be painted into >=0/ + * The view is usually told to paint into the span that is returned, + * although the parent may choose to resize or break the view. + * @throws IllegalArgumentException - for an invalid axis + */ + public float getMaximumSpan(int axis) throws IllegalArgumentException + { + if (axis == X_AXIS || axis == Y_AXIS) + return super.getMaximumSpan(axis); + throw new IllegalArgumentException("Invalid Axis"); + } + + /** + * Updates any cached values that come from attributes. + */ + protected void setPropertiesFromAttributes() + { + // Fetch attributes. + StyleSheet ss = getStyleSheet(); + attributes = ss.getViewAttributes(this); + + // Fetch painter. + painter = ss.getBoxPainter(attributes); + + // Update insets. + if (attributes != null) + { + setInsets((short) painter.getInset(TOP, this), + (short) painter.getInset(LEFT, this), + (short) painter.getInset(BOTTOM, this), + (short) painter.getInset(RIGHT, this)); + } + + // Fetch width and height. + float emBase = ss.getEMBase(attributes); + float exBase = ss.getEXBase(attributes); + cssSpans[X_AXIS] = (Length) attributes.getAttribute(CSS.Attribute.WIDTH); + if (cssSpans[X_AXIS] != null) + cssSpans[X_AXIS].setFontBases(emBase, exBase); + cssSpans[Y_AXIS] = (Length) attributes.getAttribute(CSS.Attribute.HEIGHT); + if (cssSpans[Y_AXIS] != null) + cssSpans[Y_AXIS].setFontBases(emBase, exBase); + } + + /** + * Gets the default style sheet. + * + * @return the style sheet + */ + protected StyleSheet getStyleSheet() + { + HTMLDocument doc = (HTMLDocument) getDocument(); + return doc.getStyleSheet(); + } + + /** + * Overridden to fetch additional CSS layout information. + */ + public void replace(int offset, int length, View[] views) + { + // First remove unneeded stuff. + for (int i = 0; i < length; i++) + { + View child = getView(i + offset); + positionInfo.remove(child); + } + + // Call super to actually replace the views. + super.replace(offset, length, views); + + // Now fetch the position infos for the new views. + for (int i = 0; i < views.length; i++) + { + fetchLayoutInfo(views[i]); + } + } + + /** + * Fetches and stores the layout info for the specified view. + * + * @param view the view for which the layout info is stored + */ + private void fetchLayoutInfo(View view) + { + AttributeSet atts = view.getAttributes(); + Object o = atts.getAttribute(CSS.Attribute.POSITION); + if (o != null && o instanceof String && ! o.equals("static")) + { + int type; + if (o.equals("relative")) + type = PositionInfo.RELATIVE; + else if (o.equals("absolute")) + type = PositionInfo.ABSOLUTE; + else if (o.equals("fixed")) + type = PositionInfo.FIXED; + else + type = PositionInfo.STATIC; + + if (type != PositionInfo.STATIC) + { + StyleSheet ss = getStyleSheet(); + float emBase = ss.getEMBase(atts); + float exBase = ss.getEXBase(atts); + Length left = (Length) atts.getAttribute(CSS.Attribute.LEFT); + if (left != null) + left.setFontBases(emBase, exBase); + Length right = (Length) atts.getAttribute(CSS.Attribute.RIGHT); + if (right != null) + right.setFontBases(emBase, exBase); + Length top = (Length) atts.getAttribute(CSS.Attribute.TOP); + if (top != null) + top.setFontBases(emBase, exBase); + Length bottom = (Length) atts.getAttribute(CSS.Attribute.BOTTOM); + if (bottom != null) + bottom.setFontBases(emBase, exBase); + if (left != null || right != null || top != null || bottom != null) + { + PositionInfo pos = new PositionInfo(type, left, right, top, + bottom); + positionInfo.put(view, pos); + } + } + } + } +} diff --git a/libjava/classpath/javax/swing/text/html/CSS.java b/libjava/classpath/javax/swing/text/html/CSS.java new file mode 100644 index 000000000..0a77bdff9 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/CSS.java @@ -0,0 +1,736 @@ +/* CSS.java -- Provides CSS attributes + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing.text.html; + +import gnu.javax.swing.text.html.css.BorderStyle; +import gnu.javax.swing.text.html.css.BorderWidth; +import gnu.javax.swing.text.html.css.CSSColor; +import gnu.javax.swing.text.html.css.FontSize; +import gnu.javax.swing.text.html.css.FontStyle; +import gnu.javax.swing.text.html.css.FontWeight; +import gnu.javax.swing.text.html.css.Length; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.StringTokenizer; + +import javax.swing.text.MutableAttributeSet; + +/** + * Provides CSS attributes to be used by the HTML view classes. The constants + * defined here are used as keys for text attributes for use in + * {@link javax.swing.text.AttributeSet}s of {@link javax.swing.text.Element}s. + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class CSS implements Serializable +{ + /** + * Returns an array of all CSS attributes. + * + * @return All available CSS.Attribute objects. + */ + public static CSS.Attribute[] getAllAttributeKeys() + { + Object[] src = Attribute.attributeMap.values().toArray(); + CSS.Attribute[] dst = new CSS.Attribute[ src.length ]; + System.arraycopy(src, 0, dst, 0, src.length); + return dst; + } + + /** + * Returns an a given CSS attribute. + * + * @param name - The name of the attribute. + * @return The CSS attribute with the given name, or null if + * no attribute with that name exists. + */ + public static CSS.Attribute getAttribute(String name) + { + return (CSS.Attribute)Attribute.attributeMap.get( name ); + } + + public static final class Attribute + { + /** + * The CSS attribute 'background'. + */ + public static final Attribute BACKGROUND = + new Attribute("background", false, null); + + /** + * The CSS attribute 'background-attachment'. + */ + public static final Attribute BACKGROUND_ATTACHMENT = + new Attribute("background-attachment", false, "scroll"); + + /** + * The CSS attribute 'background-color'. + */ + public static final Attribute BACKGROUND_COLOR = + new Attribute("background-color", false, "transparent"); + + /** + * The CSS attribute 'background-image'. + */ + public static final Attribute BACKGROUND_IMAGE = + new Attribute("background-image", false, "none"); + + /** + * The CSS attribute 'background-position'. + */ + public static final Attribute BACKGROUND_POSITION = + new Attribute("background-position", false, null); + + /** + * The CSS attribute 'background-repeat'. + */ + public static final Attribute BACKGROUND_REPEAT = + new Attribute("background-repeat", false, "repeat"); + + /** + * The CSS attribute 'border'. + */ + public static final Attribute BORDER = new Attribute("border", false, null); + + /** + * The CSS attribute 'border-bottom'. + */ + public static final Attribute BORDER_BOTTOM = + new Attribute("border-bottom", false, null); + + /** + * The CSS attribute 'border-bottom-width'. + */ + public static final Attribute BORDER_BOTTOM_WIDTH = + new Attribute("border-bottom-width", false, "medium"); + + /** + * The CSS attribute 'border-color'. + */ + public static final Attribute BORDER_COLOR = + new Attribute("border-color", false, "black"); + + /** + * The CSS attribute 'border-left'. + */ + public static final Attribute BORDER_LEFT = + new Attribute("border-left", false, null); + + /** + * The CSS attribute 'border-left-width'. + */ + public static final Attribute BORDER_LEFT_WIDTH = + new Attribute("border-left-width", false, "medium"); + + /** + * The CSS attribute 'border-right'. + */ + public static final Attribute BORDER_RIGHT = + new Attribute("border-right", false, null); + + /** + * The CSS attribute 'border-right-width'. + */ + public static final Attribute BORDER_RIGHT_WIDTH = + new Attribute("border-right-width", false, "medium"); + + /** + * The CSS attribute 'border-style'. + */ + public static final Attribute BORDER_STYLE = + new Attribute("border-style", false, "none"); + + /** + * The CSS attribute 'border-top'. + */ + public static final Attribute BORDER_TOP = + new Attribute("border-top", false, null); + + /** + * The CSS attribute 'border-top-width'. + */ + public static final Attribute BORDER_TOP_WIDTH = + new Attribute("border-top-width", false, "medium"); + + /** + * The CSS attribute 'border-width'. + */ + public static final Attribute BORDER_WIDTH = + new Attribute("border-width", false, "medium"); + + /** + * The CSS attribute 'clear'. + */ + public static final Attribute CLEAR = new Attribute("clear", false, "none"); + + /** + * The CSS attribute 'color'. + */ + public static final Attribute COLOR = new Attribute("color", true, "black"); + + /** + * The CSS attribute 'display'. + */ + public static final Attribute DISPLAY = + new Attribute("display", false, "block"); + + /** + * The CSS attribute 'float'. + */ + public static final Attribute FLOAT = new Attribute("float", false, "none"); + + /** + * The CSS attribute 'font'. + */ + public static final Attribute FONT = new Attribute("font", true, null); + + /** + * The CSS attribute 'font-family'. + */ + public static final Attribute FONT_FAMILY = + new Attribute("font-family", true, null); + + /** + * The CSS attribute 'font-size'. + */ + public static final Attribute FONT_SIZE = + new Attribute("font-size", true, "medium"); + + /** + * The CSS attribute 'font-style'. + */ + public static final Attribute FONT_STYLE = + new Attribute("font-style", true, "normal"); + + /** + * The CSS attribute 'font-variant'. + */ + public static final Attribute FONT_VARIANT = + new Attribute("font-variant", true, "normal"); + + /** + * The CSS attribute 'font-weight'. + */ + public static final Attribute FONT_WEIGHT = + new Attribute("font-weight", true, "normal"); + + /** + * The CSS attribute 'height'. + */ + public static final Attribute HEIGHT = + new Attribute("height", false, "auto"); + + /** + * The CSS attribute 'letter-spacing'. + */ + public static final Attribute LETTER_SPACING = + new Attribute("letter-spacing", true, "normal"); + + /** + * The CSS attribute 'line-height'. + */ + public static final Attribute LINE_HEIGHT = + new Attribute("line-height", true, "normal"); + + /** + * The CSS attribute 'list-style'. + */ + public static final Attribute LIST_STYLE = + new Attribute("list-style", true, null); + + /** + * The CSS attribute 'list-style-image'. + */ + public static final Attribute LIST_STYLE_IMAGE = + new Attribute("list-style-image", true, "none"); + + /** + * The CSS attribute 'list-style-position'. + */ + public static final Attribute LIST_STYLE_POSITION = + new Attribute("list-style-position", true, "outside"); + + /** + * The CSS attribute 'list-style-type'. + */ + public static final Attribute LIST_STYLE_TYPE = + new Attribute("list-style-type", true, "disc"); + + /** + * The CSS attribute 'margin'. + */ + public static final Attribute MARGIN = new Attribute("margin", false, null); + + /** + * The CSS attribute 'margin-bottom'. + */ + public static final Attribute MARGIN_BOTTOM = + new Attribute("margin-bottom", false, "0"); + + /** + * The CSS attribute 'margin-left'. + */ + public static final Attribute MARGIN_LEFT = + new Attribute("margin-left", false, "0"); + + /** + * The CSS attribute 'margin-right'. + */ + public static final Attribute MARGIN_RIGHT = + new Attribute("margin-right", false, "0"); + + /** + * The CSS attribute 'margin-top'. + */ + public static final Attribute MARGIN_TOP = + new Attribute("margin-top", false, "0"); + + /** + * The CSS attribute 'padding'. + */ + public static final Attribute PADDING = + new Attribute("padding", false, null); + + /** + * The CSS attribute 'padding-bottom'. + */ + public static final Attribute PADDING_BOTTOM = + new Attribute("padding-bottom", false, "0"); + + /** + * The CSS attribute 'padding-left'. + */ + public static final Attribute PADDING_LEFT = + new Attribute("padding-left", false, "0"); + + /** + * The CSS attribute 'padding-right'. + */ + public static final Attribute PADDING_RIGHT = + new Attribute("padding-right", false, "0"); + + /** + * The CSS attribute 'padding-top'. + */ + public static final Attribute PADDING_TOP = + new Attribute("padding-top", false, "0"); + + /** + * The CSS attribute 'text-align'. + */ + public static final Attribute TEXT_ALIGN = + new Attribute("text-align", true, null); + + /** + * The CSS attribute 'text-decoration'. + */ + public static final Attribute TEXT_DECORATION = + new Attribute("text-decoration", true, "none"); + + /** + * The CSS attribute 'text-indent'. + */ + public static final Attribute TEXT_INDENT = + new Attribute("text-indent", true, "0"); + + /** + * The CSS attribute 'text-transform'. + */ + public static final Attribute TEXT_TRANSFORM = + new Attribute("text-transform", true, "none"); + + /** + * The CSS attribute 'vertical-align'. + */ + public static final Attribute VERTICAL_ALIGN = + new Attribute("vertical-align", false, "baseline"); + + /** + * The CSS attribute 'white-space'. + */ + public static final Attribute WHITE_SPACE = + new Attribute("white-space", true, "normal"); + + /** + * The CSS attribute 'width'. + */ + public static final Attribute WIDTH = + new Attribute("width", false, "auto"); + + /** + * The CSS attribute 'word-spacing'. + */ + public static final Attribute WORD_SPACING = + new Attribute("word-spacing", true, "normal"); + + // Some GNU Classpath specific extensions. + static final Attribute BORDER_TOP_STYLE = + new Attribute("border-top-style", false, null); + static final Attribute BORDER_BOTTOM_STYLE = + new Attribute("border-bottom-style", false, null); + static final Attribute BORDER_LEFT_STYLE = + new Attribute("border-left-style", false, null); + static final Attribute BORDER_RIGHT_STYLE = + new Attribute("border-right-style", false, null); + static final Attribute BORDER_TOP_COLOR = + new Attribute("border-top-color", false, null); + static final Attribute BORDER_BOTTOM_COLOR = + new Attribute("border-bottom-color", false, null); + static final Attribute BORDER_LEFT_COLOR = + new Attribute("border-left-color", false, null); + static final Attribute BORDER_RIGHT_COLOR = + new Attribute("border-right-color", false, null); + static final Attribute BORDER_SPACING = + new Attribute("border-spacing", false, null); + static final Attribute POSITION = + new Attribute("position", false, null); + static final Attribute LEFT = + new Attribute("left", false, null); + static final Attribute RIGHT = + new Attribute("right", false, null); + static final Attribute TOP = + new Attribute("top", false, null); + static final Attribute BOTTOM = + new Attribute("bottom", false, null); + + /** + * The attribute string. + */ + String attStr; + + /** + * Indicates if this attribute should be inherited from it's parent or + * not. + */ + boolean isInherited; + + /** + * A default value for this attribute if one exists, otherwise null. + */ + String defaultValue; + + /** + * A HashMap of all attributes. + */ + static HashMap attributeMap; + + /** + * Creates a new Attribute instance with the specified values. + * + * @param attr the attribute string + * @param inherited if the attribute should be inherited or not + * @param def a default value; may be null + */ + Attribute(String attr, boolean inherited, String def) + { + attStr = attr; + isInherited = inherited; + defaultValue = def; + if( attributeMap == null) + attributeMap = new HashMap(); + attributeMap.put( attr, this ); + } + + /** + * Returns the string representation of this attribute as specified + * in the CSS specification. + */ + public String toString() + { + return attStr; + } + + /** + * Returns true if the attribute should be inherited from + * the parent, false otherwise. + * + * @return true if the attribute should be inherited from + * the parent, false otherwise + */ + public boolean isInherited() + { + return isInherited; + } + + /** + * Returns the default value of this attribute if one exists, + * null otherwise. + * + * @return the default value of this attribute if one exists, + * null otherwise + */ + public String getDefaultValue() + { + return defaultValue; + } + } + + /** + * Maps attribute values (String) to some converter class, based on the + * key. + * + * @param att the key + * @param v the value + * + * @return the wrapped value + */ + static Object getValue(Attribute att, String v) + { + Object o; + if (att == Attribute.FONT_SIZE) + o = new FontSize(v); + else if (att == Attribute.FONT_WEIGHT) + o = new FontWeight(v); + else if (att == Attribute.FONT_STYLE) + o = new FontStyle(v); + else if (att == Attribute.COLOR || att == Attribute.BACKGROUND_COLOR + || att == Attribute.BORDER_COLOR + || att == Attribute.BORDER_TOP_COLOR + || att == Attribute.BORDER_BOTTOM_COLOR + || att == Attribute.BORDER_LEFT_COLOR + || att == Attribute.BORDER_RIGHT_COLOR) + o = new CSSColor(v); + else if (att == Attribute.MARGIN || att == Attribute.MARGIN_BOTTOM + || att == Attribute.MARGIN_LEFT || att == Attribute.MARGIN_RIGHT + || att == Attribute.MARGIN_TOP || att == Attribute.WIDTH + || att == Attribute.HEIGHT + || att == Attribute.PADDING || att == Attribute.PADDING_BOTTOM + || att == Attribute.PADDING_LEFT || att == Attribute.PADDING_RIGHT + || att == Attribute.PADDING_TOP + || att == Attribute.LEFT || att == Attribute.RIGHT + || att == Attribute.TOP || att == Attribute.BOTTOM) + o = new Length(v); + else if (att == Attribute.BORDER_WIDTH || att == Attribute.BORDER_TOP_WIDTH + || att == Attribute.BORDER_LEFT_WIDTH + || att == Attribute.BORDER_RIGHT_WIDTH + || att == Attribute.BORDER_BOTTOM_WIDTH) + o = new BorderWidth(v); + else + o = v; + return o; + } + + static void addInternal(MutableAttributeSet atts, Attribute a, String v) + { + if (a == Attribute.BACKGROUND) + parseBackgroundShorthand(atts, v); + else if (a == Attribute.PADDING) + parsePaddingShorthand(atts, v); + else if (a == Attribute.MARGIN) + parseMarginShorthand(atts, v); + else if (a == Attribute.BORDER || a == Attribute.BORDER_LEFT + || a == Attribute.BORDER_RIGHT || a == Attribute.BORDER_TOP + || a == Attribute.BORDER_BOTTOM) + parseBorderShorthand(atts, v, a); + } + + /** + * Parses the background shorthand and translates it to more specific + * background attributes. + * + * @param atts the attributes + * @param v the value + */ + private static void parseBackgroundShorthand(MutableAttributeSet atts, + String v) + { + StringTokenizer tokens = new StringTokenizer(v, " "); + while (tokens.hasMoreElements()) + { + String token = tokens.nextToken(); + if (CSSColor.isValidColor(token)) + atts.addAttribute(Attribute.BACKGROUND_COLOR, + new CSSColor(token)); + } + } + + /** + * Parses the padding shorthand and translates to the specific padding + * values. + * + * @param atts the attributes + * @param v the actual value + */ + private static void parsePaddingShorthand(MutableAttributeSet atts, String v) + { + StringTokenizer tokens = new StringTokenizer(v, " "); + int numTokens = tokens.countTokens(); + if (numTokens == 1) + { + Length l = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.PADDING_BOTTOM, l); + atts.addAttribute(Attribute.PADDING_LEFT, l); + atts.addAttribute(Attribute.PADDING_RIGHT, l); + atts.addAttribute(Attribute.PADDING_TOP, l); + } + else if (numTokens == 2) + { + Length l1 = new Length(tokens.nextToken()); + Length l2 = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.PADDING_BOTTOM, l1); + atts.addAttribute(Attribute.PADDING_TOP, l1); + atts.addAttribute(Attribute.PADDING_LEFT, l2); + atts.addAttribute(Attribute.PADDING_RIGHT, l2); + } + else if (numTokens == 3) + { + Length l1 = new Length(tokens.nextToken()); + Length l2 = new Length(tokens.nextToken()); + Length l3 = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.PADDING_TOP, l1); + atts.addAttribute(Attribute.PADDING_LEFT, l2); + atts.addAttribute(Attribute.PADDING_RIGHT, l2); + atts.addAttribute(Attribute.PADDING_BOTTOM, l3); + } + else + { + Length l1 = new Length(tokens.nextToken()); + Length l2 = new Length(tokens.nextToken()); + Length l3 = new Length(tokens.nextToken()); + Length l4 = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.PADDING_TOP, l1); + atts.addAttribute(Attribute.PADDING_RIGHT, l2); + atts.addAttribute(Attribute.PADDING_BOTTOM, l3); + atts.addAttribute(Attribute.PADDING_LEFT, l4); + } + } + + /** + * Parses the margin shorthand and translates to the specific margin + * values. + * + * @param atts the attributes + * @param v the actual value + */ + private static void parseMarginShorthand(MutableAttributeSet atts, String v) + { + StringTokenizer tokens = new StringTokenizer(v, " "); + int numTokens = tokens.countTokens(); + if (numTokens == 1) + { + Length l = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.MARGIN_BOTTOM, l); + atts.addAttribute(Attribute.MARGIN_LEFT, l); + atts.addAttribute(Attribute.MARGIN_RIGHT, l); + atts.addAttribute(Attribute.MARGIN_TOP, l); + } + else if (numTokens == 2) + { + Length l1 = new Length(tokens.nextToken()); + Length l2 = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.MARGIN_BOTTOM, l1); + atts.addAttribute(Attribute.MARGIN_TOP, l1); + atts.addAttribute(Attribute.MARGIN_LEFT, l2); + atts.addAttribute(Attribute.MARGIN_RIGHT, l2); + } + else if (numTokens == 3) + { + Length l1 = new Length(tokens.nextToken()); + Length l2 = new Length(tokens.nextToken()); + Length l3 = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.MARGIN_TOP, l1); + atts.addAttribute(Attribute.MARGIN_LEFT, l2); + atts.addAttribute(Attribute.MARGIN_RIGHT, l2); + atts.addAttribute(Attribute.MARGIN_BOTTOM, l3); + } + else + { + Length l1 = new Length(tokens.nextToken()); + Length l2 = new Length(tokens.nextToken()); + Length l3 = new Length(tokens.nextToken()); + Length l4 = new Length(tokens.nextToken()); + atts.addAttribute(Attribute.MARGIN_TOP, l1); + atts.addAttribute(Attribute.MARGIN_RIGHT, l2); + atts.addAttribute(Attribute.MARGIN_BOTTOM, l3); + atts.addAttribute(Attribute.MARGIN_LEFT, l4); + } + } + + /** + * Parses the CSS border shorthand attribute and translates it to the + * more specific border attributes. + * + * @param atts the attribute + * @param value the value + */ + private static void parseBorderShorthand(MutableAttributeSet atts, + String value, Attribute cssAtt) + { + StringTokenizer tokens = new StringTokenizer(value, " "); + while (tokens.hasMoreTokens()) + { + String token = tokens.nextToken(); + if (BorderStyle.isValidStyle(token)) + { + if (cssAtt == Attribute.BORDER_LEFT || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_LEFT_STYLE, token); + if (cssAtt == Attribute.BORDER_RIGHT || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_RIGHT_STYLE, token); + if (cssAtt == Attribute.BORDER_BOTTOM || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_BOTTOM_STYLE, token); + if (cssAtt == Attribute.BORDER_TOP || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_TOP_STYLE, token); + } + else if (BorderWidth.isValid(token)) + { + BorderWidth w = new BorderWidth(token); + if (cssAtt == Attribute.BORDER_LEFT || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_LEFT_WIDTH, w); + if (cssAtt == Attribute.BORDER_RIGHT || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_RIGHT_WIDTH, w); + if (cssAtt == Attribute.BORDER_BOTTOM || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_BOTTOM_WIDTH, w); + if (cssAtt == Attribute.BORDER_TOP || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_TOP_WIDTH, w); + } + else if (CSSColor.isValidColor(token)) + { + CSSColor c = new CSSColor(token); + if (cssAtt == Attribute.BORDER_LEFT || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_LEFT_COLOR, c); + if (cssAtt == Attribute.BORDER_RIGHT || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_RIGHT_COLOR, c); + if (cssAtt == Attribute.BORDER_BOTTOM || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_BOTTOM_COLOR, c); + if (cssAtt == Attribute.BORDER_TOP || cssAtt == Attribute.BORDER) + atts.addAttribute(Attribute.BORDER_TOP_COLOR, c); + } + } + } +} diff --git a/libjava/classpath/javax/swing/text/html/CSSBorder.java b/libjava/classpath/javax/swing/text/html/CSSBorder.java new file mode 100644 index 000000000..23fcdccc6 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/CSSBorder.java @@ -0,0 +1,421 @@ +/* CSSBorder.java -- A border for rendering CSS border styles + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import gnu.javax.swing.text.html.css.BorderWidth; +import gnu.javax.swing.text.html.css.CSSColor; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Insets; + +import javax.swing.border.Border; +import javax.swing.text.AttributeSet; + +/** + * A border implementation to render CSS border styles. + */ +class CSSBorder + implements Border +{ + + /** + * The CSS border styles. + */ + + private static final int STYLE_NOT_SET = -1; + private static final int STYLE_NONE = 0; + private static final int STYLE_HIDDEN = 1; + private static final int STYLE_DOTTED = 2; + private static final int STYLE_DASHED = 3; + private static final int STYLE_SOLID = 4; + private static final int STYLE_DOUBLE = 5; + private static final int STYLE_GROOVE = 6; + private static final int STYLE_RIDGE = 7; + private static final int STYLE_INSET = 8; + private static final int STYLE_OUTSET = 9; + + /** + * The left insets. + */ + private int left; + + /** + * The right insets. + */ + private int right; + + /** + * The top insets. + */ + private int top; + + /** + * The bottom insets. + */ + private int bottom; + + /** + * The border style on the left. + */ + private int leftStyle; + + /** + * The border style on the right. + */ + private int rightStyle; + + /** + * The border style on the top. + */ + private int topStyle; + + /** + * The color for the top border. + */ + private Color topColor; + + /** + * The color for the bottom border. + */ + private Color bottomColor; + + /** + * The color for the left border. + */ + private Color leftColor; + + /** + * The color for the right border. + */ + private Color rightColor; + + /** + * The border style on the bottom. + */ + private int bottomStyle; + + /** + * Creates a new CSS border and fetches its attributes from the specified + * attribute set. + * + * @param atts the attribute set that contains the border spec + */ + CSSBorder(AttributeSet atts, StyleSheet ss) + { + // Determine the border styles. + int style = getBorderStyle(atts, CSS.Attribute.BORDER_STYLE); + if (style == STYLE_NOT_SET) + style = STYLE_NONE; // Default to none. + topStyle = bottomStyle = leftStyle = rightStyle = style; + style = getBorderStyle(atts, CSS.Attribute.BORDER_TOP_STYLE); + if (style != STYLE_NOT_SET) + topStyle = style; + style = getBorderStyle(atts, CSS.Attribute.BORDER_BOTTOM_STYLE); + if (style != STYLE_NOT_SET) + bottomStyle = style; + style = getBorderStyle(atts, CSS.Attribute.BORDER_LEFT_STYLE); + if (style != STYLE_NOT_SET) + leftStyle = style; + style = getBorderStyle(atts, CSS.Attribute.BORDER_RIGHT_STYLE); + if (style != STYLE_NOT_SET) + rightStyle = style; + + // Determine the border colors. + Color color = getBorderColor(atts, CSS.Attribute.BORDER_COLOR); + if (color == null) + color = Color.BLACK; + topColor = bottomColor = leftColor = rightColor = color; + color = getBorderColor(atts, CSS.Attribute.BORDER_TOP_COLOR); + if (color != null) + topColor = color; + color = getBorderColor(atts, CSS.Attribute.BORDER_BOTTOM_COLOR); + if (color != null) + bottomColor = color; + color = getBorderColor(atts, CSS.Attribute.BORDER_LEFT_COLOR); + if (color != null) + leftColor = color; + color = getBorderColor(atts, CSS.Attribute.BORDER_RIGHT_COLOR); + if (color != null) + rightColor = color; + + // Determine the border widths. + int width = getBorderWidth(atts, CSS.Attribute.BORDER_WIDTH, ss); + if (width == -1) + width = 0; + top = bottom = left = right = width; + width = getBorderWidth(atts, CSS.Attribute.BORDER_TOP_WIDTH, ss); + if (width >= 0) + top = width; + width = getBorderWidth(atts, CSS.Attribute.BORDER_BOTTOM_WIDTH, ss); + if (width >= 0) + bottom = width; + width = getBorderWidth(atts, CSS.Attribute.BORDER_LEFT_WIDTH, ss); + if (width >= 0) + left = width; + width = getBorderWidth(atts, CSS.Attribute.BORDER_RIGHT_WIDTH, ss); + if (width >= 0) + right = width; + } + + /** + * Determines the border style for a given CSS attribute. + * + * @param atts the attribute set + * @param key the CSS key + * + * @return the border style according to the constants defined in this class + */ + private int getBorderStyle(AttributeSet atts, CSS.Attribute key) + { + int style = STYLE_NOT_SET; + Object o = atts.getAttribute(key); + if (o != null) + { + String cssStyle = o.toString(); + if (cssStyle.equals("none")) + style = STYLE_NONE; + else if (cssStyle.equals("hidden")) + style = STYLE_HIDDEN; + else if (cssStyle.equals("dotted")) + style = STYLE_DOTTED; + else if (cssStyle.equals("dashed")) + style = STYLE_DASHED; + else if (cssStyle.equals("solid")) + style = STYLE_SOLID; + else if (cssStyle.equals("double")) + style = STYLE_DOUBLE; + else if (cssStyle.equals("groove")) + style = STYLE_GROOVE; + else if (cssStyle.equals("ridge")) + style = STYLE_RIDGE; + else if (cssStyle.equals("inset")) + style = STYLE_INSET; + else if (cssStyle.equals("outset")) + style = STYLE_OUTSET; + } + return style; + } + + /** + * Determines the border color for the specified key. + * + * @param atts the attribute set from which to fetch the color + * @param key the CSS key + * + * @return the border color + */ + private Color getBorderColor(AttributeSet atts, CSS.Attribute key) + { + Object o = atts.getAttribute(key); + Color color = null; + if (o instanceof CSSColor) + { + CSSColor cssColor = (CSSColor) o; + color = cssColor.getValue(); + } + return color; + } + + /** + * Returns the width for the specified key. + * + * @param atts the attributes to fetch the width from + * @param key the CSS key + * + * @return the width, or -1 of none has been set + */ + private int getBorderWidth(AttributeSet atts, CSS.Attribute key, + StyleSheet ss) + { + int width = -1; + Object o = atts.getAttribute(key); + if (o instanceof BorderWidth) + { + BorderWidth w = (BorderWidth) o; + w.setFontBases(ss.getEMBase(atts), ss.getEXBase(atts)); + width = (int) ((BorderWidth) o).getValue(); + } + return width; + } + + /** + * Returns the border insets. + */ + public Insets getBorderInsets(Component c) + { + return new Insets(top, left, bottom, right); + } + + /** + * CSS borders are generally opaque so return true here. + */ + public boolean isBorderOpaque() + { + return true; + } + + public void paintBorder(Component c, Graphics g, int x, int y, int width, + int height) + { + // Top border. + paintBorderLine(g, x, y + top / 2, x + width, y + top / 2, topStyle, top, + topColor, false); + // Left border. + paintBorderLine(g, x + left / 2, y, x + left / 2, y + height, leftStyle, + left, leftColor, true); + // Bottom border. + paintBorderLine(g, x, y + height - bottom / 2, x + width, + y + height - bottom / 2, topStyle, bottom, bottomColor, + false); + // Right border. + paintBorderLine(g, x + width - right / 2, y, x + width - right / 2, + y + height, topStyle, right, rightColor, true); + + } + + private void paintBorderLine(Graphics g, int x1, int y1, int x2, int y2, + int style, int width, Color color, + boolean vertical) + { + switch (style) + { + case STYLE_DOTTED: + paintDottedLine(g, x1, y1, x2, y2, width, color, vertical); + break; + case STYLE_DASHED: + paintDashedLine(g, x1, y1, x2, y2, width, color, vertical); + break; + case STYLE_SOLID: + paintSolidLine(g, x1, y1, x2, y2, width, color, vertical); + break; + case STYLE_DOUBLE: + paintDoubleLine(g, x1, y1, x2, y2, width, color, vertical); + break; + case STYLE_GROOVE: + paintGrooveLine(g, x1, y1, x2, y2, width, color, vertical); + break; + case STYLE_RIDGE: + paintRidgeLine(g, x1, y1, x2, y2, width, color, vertical); + break; + case STYLE_OUTSET: + paintOutsetLine(g, x1, y1, x2, y2, width, color, vertical); + break; + case STYLE_INSET: + paintInsetLine(g, x1, y1, x2, y2, width, color, vertical); + break; + case STYLE_NONE: + case STYLE_HIDDEN: + default: + // Nothing to do in these cases. + } + } + + private void paintDottedLine(Graphics g, int x1, int y1, int x2, int y2, + int width, Color color, boolean vertical) + { + // FIXME: Implement this. + paintSolidLine(g, x1, y1, x2, y2, width, color, vertical); + } + + private void paintDashedLine(Graphics g, int x1, int y1, int x2, int y2, + int width, Color color, boolean vertical) + { + // FIXME: Implement this. + paintSolidLine(g, x1, y1, x2, y2, width, color, vertical); + } + + private void paintSolidLine(Graphics g, int x1, int y1, int x2, int y2, + int width, Color color, boolean vertical) + { + int x = Math.min(x1, x2); + int y = Math.min(y1, y1); + int w = Math.abs(x2 - x1); + int h = Math.abs(y2 - y1); + if (vertical) + { + w = width; + x -= width / 2; + } + else + { + h = width; + y -= width / 2; + } + g.setColor(color); + g.fillRect(x, y, w, h); + } + + private void paintDoubleLine(Graphics g, int x1, int y1, int x2, int y2, + int width, Color color, boolean vertical) + { + // FIXME: Implement this. + paintSolidLine(g, x1, y1, x2, y2, width, color, vertical); + } + + private void paintGrooveLine(Graphics g, int x1, int y1, int x2, int y2, + int width, Color color, boolean vertical) + { + // FIXME: Implement this. + paintSolidLine(g, x1, y1, x2, y2, width, color, vertical); + } + + private void paintRidgeLine(Graphics g, int x1, int y1, int x2, int y2, + int width, Color color, boolean vertical) + { + // FIXME: Implement this. + paintSolidLine(g, x1, y1, x2, y2, width, color, vertical); + } + + private void paintOutsetLine(Graphics g, int x1, int y1, int x2, int y2, + int width, Color color, boolean vertical) + { + // FIXME: Implement this. + paintSolidLine(g, x1, y1, x2, y2, width, color, vertical); + } + + private void paintInsetLine(Graphics g, int x1, int y1, int x2, int y2, + int width, Color color, boolean vertical) + { + // FIXME: Implement this. + paintSolidLine(g, x1, y1, x2, y2, width, color, vertical); + } + +} diff --git a/libjava/classpath/javax/swing/text/html/CSSParser.java b/libjava/classpath/javax/swing/text/html/CSSParser.java new file mode 100644 index 000000000..5024c7b59 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/CSSParser.java @@ -0,0 +1,561 @@ +/* CSSParser.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import java.io.*; + +/** + * Parses a CSS document. This works by way of a delegate that implements the + * CSSParserCallback interface. The delegate is notified of the following + * events: + * - Import statement: handleImport + * - Selectors handleSelector. This is invoked for each string. For example if + * the Reader contained p, bar , a {}, the delegate would be notified 4 times, + * for 'p,' 'bar' ',' and 'a'. + * - When a rule starts, startRule + * - Properties in the rule via the handleProperty. This + * is invoked one per property/value key, eg font size: foo;, would cause the + * delegate to be notified once with a value of 'font size'. + * - Values in the rule via the handleValue, this is notified for the total value. + * - When a rule ends, endRule + * + * @author Lillian Angel (langel@redhat.com) + */ +class CSSParser +{ + + /** + * Receives all information about the CSS document structure while parsing it. + * The methods are invoked by parser. + */ + static interface CSSParserCallback + { + /** + * Handles the import statment in the document. + * + * @param imp - the import string + */ + public abstract void handleImport(String imp); + + /** + * Called when the start of a rule is encountered. + */ + public abstract void startRule(); + + /** + * Called when the end of a rule is encountered. + */ + public abstract void endRule(); + + /** + * Handles the selector of a rule. + * + * @param selector - the selector in the rule + */ + public abstract void handleSelector(String selector); + + /** + * Handles the properties in the document. + * + * @param property - the property in the document. + */ + public abstract void handleProperty(String property); + + /** + * Handles the values in the document. + * + * @param value - the value to handle. + */ + public abstract void handleValue(String value); + + } + + /** + * The identifier of the rule. + */ + private static final int IDENTIFIER = 1; + + /** + * The open bracket. + */ + private static final int BRACKET_OPEN = 2; + + /** + * The close bracket. + */ + private static final int BRACKET_CLOSE = 3; + + /** + * The open brace. + */ + private static final int BRACE_OPEN = 4; + + /** + * The close brace. + */ + private static final int BRACE_CLOSE = 5; + + /** + * The open parentheses. + */ + private static final int PAREN_OPEN = 6; + + /** + * The close parentheses. + */ + private static final int PAREN_CLOSE = 7; + + /** + * The end of the document. + */ + private static final int END = -1; + + /** + * The character mapping in the document. + */ + // FIXME: What is this used for? + private static final char[] charMapping = null; + + /** + * Set to true if one character has been read ahead. + */ + private boolean didPushChar; + + /** + * The read ahead character. + */ + private int pushedChar; + + /** + * Used to indicate blocks. + */ + private int[] unitStack; + + /** + * Number of valid blocks. + */ + private int stackCount; + + /** + * Holds the incoming CSS rules. + */ + private Reader reader; + + /** + * Set to true when the first non @ rule is encountered. + */ + private boolean encounteredRuleSet; + + /** + * The call back used to parse. + */ + private CSSParser.CSSParserCallback callback; + + /** + * nextToken() inserts the string here. + */ + private char[] tokenBuffer; + + /** + * Current number of chars in tokenBufferLength. + */ + private int tokenBufferLength; + + /** + * Set to true if any whitespace is read. + */ + private boolean readWS; + + /** + * Constructor + */ + CSSParser() + { + tokenBuffer = new char[10]; + } + + /** + * Appends a character to the token buffer. + * + * @param c - the character to append + */ + private void append(char c) + { + if (tokenBuffer.length >= tokenBufferLength) + { + char[] temp = new char[tokenBufferLength * 2]; + if (tokenBuffer != null) + System.arraycopy(tokenBuffer, 0, temp, 0, tokenBufferLength); + + temp[tokenBufferLength] = c; + tokenBuffer = temp; + } + else + tokenBuffer[tokenBufferLength] = c; + tokenBufferLength++; + } + + /** + * Fetches the next token. + * + * @param c - the character to fetch. + * @return the location + * @throws IOException - any i/o error encountered while reading + */ + private int nextToken(char c) throws IOException + { + readWS = false; + int next = readWS(); + + switch (next) + { + case '\"': + if (tokenBufferLength > 0) + tokenBufferLength--; + return IDENTIFIER; + case '\'': + if (tokenBufferLength > 0) + tokenBufferLength--; + return IDENTIFIER; + case '(': + return PAREN_OPEN; + case ')': + return PAREN_CLOSE; + case '{': + return BRACE_OPEN; + case '}': + return BRACE_CLOSE; + case '[': + return BRACKET_OPEN; + case ']': + return BRACKET_CLOSE; + case -1: + return END; + default: + pushChar(next); + getIdentifier(c); + return IDENTIFIER; + } + } + + /** + * Reads a character from the stream. + * + * @return the number of characters read or -1 if end of stream is reached. + * @throws IOException - any i/o encountered while reading + */ + private int readChar() throws IOException + { + if (didPushChar) + { + didPushChar = false; + return pushedChar; + } + return reader.read(); + } + + /** + * Parses the the contents of the reader using the + * callback. + * + * @param reader - the reader to read from + * @param callback - the callback instance + * @param parsingDeclaration - true if parsing a declaration + * @throws IOException - any i/o error from the reader + */ + void parse(Reader reader, CSSParser.CSSParserCallback callback, + boolean parsingDeclaration) + throws IOException + { + this.reader = reader; + this.callback = callback; + + try + { + if (!parsingDeclaration) + while(getNextStatement()) + ; + else + parseDeclarationBlock(); + } + catch (IOException ioe) + { + // Nothing to do here. + } + } + + /** + * Skips any white space, returning the character after the white space. + * + * @return the character after the whitespace + * @throws IOException - any i/o error from the reader + */ + private int readWS() throws IOException + { + int next = readChar(); + while (Character.isWhitespace((char) next)) + { + readWS = true; + int tempNext = readChar(); + if (tempNext == END) + return next; + next = tempNext; + } + + // Its all whitespace + return END; + } + + /** + * Gets the next statement, returning false if the end is reached. + * A statement is either an At-rule, or a ruleset. + * + * @return false if the end is reached + * @throws IOException - any i/o error from the reader + */ + private boolean getNextStatement() throws IOException + { + int c = nextToken((char) 0); + switch (c) + { + case PAREN_OPEN: + case BRACE_OPEN: + case BRACKET_OPEN: + parseTillClosed(c); + break; + case BRACKET_CLOSE: + case BRACE_CLOSE: + case PAREN_CLOSE: + throw new IOException("Not a proper statement."); + case IDENTIFIER: + if (tokenBuffer[0] == ('@')) + parseAtRule(); + else + parseRuleSet(); + break; + case END: + return false; + } + return true; + } + + /** + * Parses an @ rule, stopping at a matching brace pair, or ;. + * + * @throws IOException - any i/o error from the reader + */ + private void parseAtRule() throws IOException + { + // An At-Rule begins with the "@" character followed immediately by a keyword. + // Following the keyword separated by a space is an At-rule statement appropriate + // to the At-keyword used. If the At-Rule is a simple declarative statement + // (charset, import, fontdef), it is terminated by a semi-colon (";".) + // If the At-Rule is a conditional or informative statement (media, page, font-face), + // it is followed by optional arguments and then a style declaration block inside matching + // curly braces ("{", "}".) At-Rules are sometimes nestable, depending on the context. + // If any part of an At-Rule is not understood, it should be ignored. + + // FIXME: Not Implemented + // call handleimport + } + + /** + * Parses the next rule set, which is a selector followed by a declaration + * block. + * + * @throws IOException - any i/o error from the reader + */ + private void parseRuleSet() throws IOException + { + // call parseDeclarationBlock + // call parse selectors + // call parse identifiers + // call startrule/endrule + // FIXME: Not Implemented + } + + /** + * Parses a set of selectors, returning false if the end of the stream is + * reached. + * + * @return false if the end of stream is reached + * @throws IOException - any i/o error from the reader + */ + private boolean parseSelectors() throws IOException + { + // FIXME: Not Implemented + // call handleselector + return false; + } + + /** + * Parses a declaration block. Which a number of declarations followed by a + * })]. + * + * @throws IOException - any i/o error from the reader + */ + private void parseDeclarationBlock() throws IOException + { + // call parseDeclaration + // FIXME: Not Implemented + } + + /** + * Parses a single declaration, which is an identifier a : and another identifier. + * This returns the last token seen. + * + * @returns the last token + * @throws IOException - any i/o error from the reader + */ + private int parseDeclaration() throws IOException + { + // call handleValue + // FIXME: Not Implemented + return 0; + } + + /** + * Parses identifiers until c is encountered, returning the ending token, + * which will be IDENTIFIER if c is found. + * + * @param c - the stop character + * @param wantsBlocks - true if blocks are wanted + * @return the ending token + * @throws IOException - any i/o error from the reader + */ + private int parseIdentifiers(char c, boolean wantsBlocks) throws IOException + { + // FIXME: Not implemented + // call handleproperty? + return 0; + } + + /** + * Parses till a matching block close is encountered. This is only appropriate + * to be called at the top level (no nesting). + * + * @param i - FIXME + * @throws IOException - any i/o error from the reader + */ + private void parseTillClosed(int i) throws IOException + { + // FIXME: Not Implemented + } + + /** + * Gets an identifier, returning true if the length of the string is greater + * than 0, stopping when c, whitespace, or one of {}()[] is hit. + * + * @param c - the stop character + * @return returns true if the length of the string > 0 + * @throws IOException - any i/o error from the reader + */ + private boolean getIdentifier(char c) throws IOException + { + // FIXME: Not Implemented + return false; + } + + /** + * Reads till c is encountered, escaping characters as necessary. + * + * @param c - the stop character + * @throws IOException - any i/o error from the reader + */ + private void readTill(char c) throws IOException + { + // FIXME: Not Implemented + } + + /** + * Parses a comment block. + * + * @throws IOException - any i/o error from the reader + */ + private void readComment() throws IOException + { + // Should ignore comments. Read until end of comment. + // FIXME: Not implemented + } + + /** + * Called when a block start is encountered ({[. + * + * @param start of block + */ + private void startBlock(int start) + { + // FIXME: Not Implemented + } + + /** + * Called when an end block is encountered )]} + * + * @param end of block + */ + private void endBlock(int end) + { + // FIXME: Not Implemented + } + + /** + * Checks if currently in a block. + * + * @return true if currently in a block. + */ + private boolean inBlock() + { + // FIXME: Not Implemented + return false; + } + + /** + * Supports one character look ahead, this will throw if called twice in a row. + * + * @param c - the character to push. + * @throws IOException - if called twice in a row + */ + private void pushChar(int c) throws IOException + { + if (didPushChar) + throw new IOException("pushChar called twice."); + didPushChar = true; + pushedChar = c; + } +} diff --git a/libjava/classpath/javax/swing/text/html/FormSubmitEvent.java b/libjava/classpath/javax/swing/text/html/FormSubmitEvent.java new file mode 100644 index 000000000..bc7c36f4b --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/FormSubmitEvent.java @@ -0,0 +1,123 @@ +/* FormSubmitEvent.java -- Event fired on form submit + 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.net.URL; + +import javax.swing.text.Element; + +/** + * The event fired on form submit. + * + * @since 1.5 + */ +public class FormSubmitEvent + extends HTMLFrameHyperlinkEvent +{ + + // FIXME: Use enums when available. + /** + * The submit method. + */ + public static class MethodType + { + /** + * Indicates a form submit with HTTP method POST. + */ + public static final MethodType POST = new MethodType(); + + /** + * Indicates a form submit with HTTP method GET. + */ + public static final MethodType GET = new MethodType(); + + private MethodType() + { + } + } + + /** + * The submit method. + */ + private MethodType method; + + /** + * The actual submit data. + */ + private String data; + + /** + * Creates a new FormSubmitEvent. + * + * @param source the source + * @param type the type of hyperlink update + * @param url the action url + * @param el the associated element + * @param target the target attribute + * @param m the submit method + * @param d the submit data + */ + FormSubmitEvent(Object source, EventType type, URL url, Element el, + String target, MethodType m, String d) + { + super(source, type, url, el, target); + method = m; + data = d; + } + + /** + * Returns the submit data. + * + * @return the submit data + */ + public String getData() + { + return data; + } + + /** + * Returns the HTTP submit method. + * + * @return the HTTP submit method + */ + public MethodType getMethod() + { + return method; + } +} diff --git a/libjava/classpath/javax/swing/text/html/FormView.java b/libjava/classpath/javax/swing/text/html/FormView.java new file mode 100644 index 000000000..61c568f02 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/FormView.java @@ -0,0 +1,870 @@ +/* FormView.java -- A view for a variety of HTML form elements + 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.awt.Component; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; + +import javax.swing.ButtonModel; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JEditorPane; +import javax.swing.JList; +import javax.swing.JPasswordField; +import javax.swing.JRadioButton; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.ListSelectionModel; +import javax.swing.SwingUtilities; +import javax.swing.UIManager; +import javax.swing.event.HyperlinkEvent; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.ComponentView; +import javax.swing.text.Document; +import javax.swing.text.Element; +import javax.swing.text.ElementIterator; +import javax.swing.text.StyleConstants; + +/** + * A View that renders HTML form elements like buttons and input fields. + * This is implemented as a {@link ComponentView} that creates different Swing + * component depending on the type and setting of the different form elements. + * + * Namely, this view creates the following components: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Element typeSwing component
input, buttonJButton
input, checkboxJButton
input, imageJButton
input, passwordJButton
input, radioJButton
input, resetJButton
input, submitJButton
input, textJButton
select, size > 1 or with multiple attributeJList in JScrollPane
select, size unspecified or == 1JComboBox
textarea, textJTextArea in JScrollPane
input, fileJTextField
+ * + * @author Roman Kennke (kennke@aicas.com) + */ +public class FormView + extends ComponentView + implements ActionListener +{ + + protected class MouseEventListener + extends MouseAdapter + { + /** + * Creates a new MouseEventListener. + */ + protected MouseEventListener() + { + // Nothing to do here. + } + + public void mouseReleased(MouseEvent ev) + { + String data = getImageData(ev.getPoint()); + imageSubmit(data); + } + } + + /** + * Actually submits the form data. + */ + private class SubmitThread + extends Thread + { + /** + * The submit data. + */ + private String data; + + /** + * Creates a new SubmitThread. + * + * @param d the submit data + */ + SubmitThread(String d) + { + data = d; + } + + /** + * Actually performs the submit. + */ + public void run() + { + if (data.length() > 0) + { + final String method = getMethod(); + final URL actionURL = getActionURL(); + final String target = getTarget(); + URLConnection conn; + final JEditorPane editor = (JEditorPane) getContainer(); + final HTMLDocument doc = (HTMLDocument) editor.getDocument(); + HTMLEditorKit kit = (HTMLEditorKit) editor.getEditorKit(); + if (kit.isAutoFormSubmission()) + { + try + { + final URL url; + if (method != null && method.equals("post")) + { + // Perform POST. + url = actionURL; + conn = url.openConnection(); + postData(conn, data); + } + else + { + // Default to GET. + url = new URL(actionURL + "?" + data); + } + Runnable loadDoc = new Runnable() + { + public void run() + { + if (doc.isFrameDocument()) + { + editor.fireHyperlinkUpdate(createSubmitEvent(method, + actionURL, + target)); + } + else + { + try + { + editor.setPage(url); + } + catch (IOException ex) + { + // Oh well. + ex.printStackTrace(); + } + } + } + }; + SwingUtilities.invokeLater(loadDoc); + } + catch (MalformedURLException ex) + { + ex.printStackTrace(); + } + catch (IOException ex) + { + ex.printStackTrace(); + } + } + else + { + editor.fireHyperlinkUpdate(createSubmitEvent(method,actionURL, + target)); + } + } + } + + /** + * Determines the submit method. + * + * @return the submit method + */ + private String getMethod() + { + AttributeSet formAtts = getFormAttributes(); + String method = null; + if (formAtts != null) + { + method = (String) formAtts.getAttribute(HTML.Attribute.METHOD); + } + return method; + } + + /** + * Determines the action URL. + * + * @return the action URL + */ + private URL getActionURL() + { + AttributeSet formAtts = getFormAttributes(); + HTMLDocument doc = (HTMLDocument) getElement().getDocument(); + URL url = doc.getBase(); + if (formAtts != null) + { + String action = + (String) formAtts.getAttribute(HTML.Attribute.ACTION); + if (action != null) + { + try + { + url = new URL(url, action); + } + catch (MalformedURLException ex) + { + url = null; + } + } + } + return url; + } + + /** + * Fetches the target attribute. + * + * @return the target attribute or _self if none is present + */ + private String getTarget() + { + AttributeSet formAtts = getFormAttributes(); + String target = null; + if (formAtts != null) + { + target = (String) formAtts.getAttribute(HTML.Attribute.TARGET); + if (target != null) + target = target.toLowerCase(); + } + if (target == null) + target = "_self"; + return target; + } + + /** + * Posts the form data over the specified connection. + * + * @param conn the connection + */ + private void postData(URLConnection conn, String data) + { + conn.setDoOutput(true); + PrintWriter out = null; + try + { + out = new PrintWriter(new OutputStreamWriter(conn.getOutputStream())); + out.print(data); + out.flush(); + } + catch (IOException ex) + { + // Deal with this! + ex.printStackTrace(); + } + finally + { + if (out != null) + out.close(); + } + } + + /** + * Determines the attributes from the relevant form tag. + * + * @return the attributes from the relevant form tag, null + * when there is no form tag + */ + private AttributeSet getFormAttributes() + { + AttributeSet atts = null; + Element form = getFormElement(); + if (form != null) + atts = form.getAttributes(); + return atts; + } + + /** + * Creates the submit event that should be fired. + * + * This is package private to avoid accessor methods. + * + * @param method the submit method + * @param actionURL the action URL + * @param target the target + * + * @return the submit event + */ + FormSubmitEvent createSubmitEvent(String method, URL actionURL, + String target) + { + FormSubmitEvent.MethodType m = "post".equals(method) + ? FormSubmitEvent.MethodType.POST + : FormSubmitEvent.MethodType.GET; + return new FormSubmitEvent(FormView.this, + HyperlinkEvent.EventType.ACTIVATED, + actionURL, getElement(), target, m, data); + } + } + + /** + * If the value attribute of an <input type="submit">> + * tag is not specified, then this string is used. + * + * @deprecated As of JDK1.3 the value is fetched from the UIManager property + * FormView.submitButtonText. + */ + public static final String SUBMIT = + UIManager.getString("FormView.submitButtonText"); + + /** + * If the value attribute of an <input type="reset">> + * tag is not specified, then this string is used. + * + * @deprecated As of JDK1.3 the value is fetched from the UIManager property + * FormView.resetButtonText. + */ + 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. + * + * @param el the element that is displayed by this view. + */ + public FormView(Element el) + { + super(el); + } + + /** + * Creates the correct AWT component for rendering the form element. + */ + protected Component createComponent() + { + Component comp = null; + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + Object tag = atts.getAttribute(StyleConstants.NameAttribute); + Object model = atts.getAttribute(StyleConstants.ModelAttribute); + if (tag.equals(HTML.Tag.INPUT)) + { + String type = (String) atts.getAttribute(HTML.Attribute.TYPE); + if (type.equals("button")) + { + 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")) + { + if (model instanceof ResetableToggleButtonModel) + { + ResetableToggleButtonModel m = + (ResetableToggleButtonModel) model; + JCheckBox c = new JCheckBox(); + c.setModel(m); + comp = c; + maxIsPreferred = true; + } + } + else if (type.equals("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")) + { + 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); + tf.addActionListener(this); + comp = tf; + maxIsPreferred = true; + } + else if (type.equals("radio")) + { + if (model instanceof ResetableToggleButtonModel) + { + ResetableToggleButtonModel m = + (ResetableToggleButtonModel) model; + JRadioButton c = new JRadioButton(); + c.setModel(m); + comp = c; + maxIsPreferred = true; + } + } + else if (type.equals("reset")) + { + 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")) + { + 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")) + { + 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); + 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); + } + else if (tag == HTML.Tag.SELECT) + { + if (model instanceof SelectListModel) + { + SelectListModel slModel = (SelectListModel) model; + JList list = new JList(slModel); + int size = HTML.getIntegerAttributeValue(atts, HTML.Attribute.SIZE, + 1); + list.setVisibleRowCount(size); + list.setSelectionModel(slModel.getSelectionModel()); + comp = new JScrollPane(list); + } + else if (model instanceof SelectComboBoxModel) + { + SelectComboBoxModel scbModel = (SelectComboBoxModel) model; + comp = new JComboBox(scbModel); + } + maxIsPreferred = true; + } + return comp; + } + + /** + * Determines the maximum span for this view on the specified axis. + * + * @param axis the axis along which to determine the span + * + * @return the maximum span for this view on the specified axis + * + * @throws IllegalArgumentException if the axis is invalid + */ + public float getMaximumSpan(int axis) + { + float span; + if (maxIsPreferred) + span = getPreferredSpan(axis); + else + span = super.getMaximumSpan(axis); + return span; + } + + /** + * Processes an action from the Swing component. + * + * If the action comes from a submit button, the form is submitted by calling + * {@link #submitData}. In the case of a reset button, the form is reset to + * the original state. If the action comes from a password or text field, + * then the input focus is transferred to the next input element in the form, + * unless this text/password field is the last one, in which case the form + * is submitted. + * + * @param ev the action event + */ + public void actionPerformed(ActionEvent ev) + { + Element el = getElement(); + Object tag = el.getAttributes().getAttribute(StyleConstants.NameAttribute); + if (tag.equals(HTML.Tag.INPUT)) + { + AttributeSet atts = el.getAttributes(); + String type = (String) atts.getAttribute(HTML.Attribute.TYPE); + if (type.equals("submit")) + submitData(getFormData()); + else if (type.equals("reset")) + resetForm(); + } + // FIXME: Implement the remaining actions. + } + + /** + * Submits the form data. A separate thread is created to do the + * transmission. + * + * @param data the form data + */ + protected void submitData(String data) + { + SubmitThread submitThread = new SubmitThread(data); + submitThread.start(); + } + + /** + * Submits the form data in response to a click on a + * <input type="image"> element. + * + * @param imageData the mouse click coordinates + */ + protected void imageSubmit(String imageData) + { + // FIXME: Implement this. + } + + /** + * Determines the image data that should be submitted in response to a + * mouse click on a image. This is either 'x=&y=' if the name + * attribute of the element is null or '' or + * .x=&.y=' when the name attribute is not empty. + * + * @param p the coordinates of the mouseclick + */ + String getImageData(Point p) + { + String name = (String) getElement().getAttributes() + .getAttribute(HTML.Attribute.NAME); + String data; + if (name == null || name.equals("")) + { + data = "x=" + p.x + "&y=" + p.y; + } + else + { + data = name + ".x=" + p.x + "&" + name + ".y=" + p.y; + } + return data; + } + + /** + * Determines and returns the enclosing form element if there is any. + * + * This is package private to avoid accessor methods. + * + * @return the enclosing form element, or null if there is no + * enclosing form element + */ + Element getFormElement() + { + Element form = null; + Element el = getElement(); + while (el != null && form == null) + { + AttributeSet atts = el.getAttributes(); + if (atts.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.FORM) + form = el; + else + el = el.getParentElement(); + } + return form; + } + + /** + * Determines the form data that is about to be submitted. + * + * @return the form data + */ + private String getFormData() + { + Element form = getFormElement(); + StringBuilder b = new StringBuilder(); + if (form != null) + { + ElementIterator i = new ElementIterator(form); + Element next; + while ((next = i.next()) != null) + { + if (next.isLeaf()) + { + AttributeSet atts = next.getAttributes(); + String type = (String) atts.getAttribute(HTML.Attribute.TYPE); + if (type != null && type.equals("submit") + && next != getElement()) + { + // Skip this. This is not the actual submit trigger. + } + else if (type == null || ! type.equals("image")) + { + getElementFormData(next, b); + } + } + } + } + return b.toString(); + } + + /** + * Fetches the form data from the specified element and appends it to + * the data string. + * + * @param el the element from which to fetch form data + * @param b the data string + */ + private void getElementFormData(Element el, StringBuilder b) + { + AttributeSet atts = el.getAttributes(); + String name = (String) atts.getAttribute(HTML.Attribute.NAME); + if (name != null) + { + String value = null; + HTML.Tag tag = (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute); + if (tag == HTML.Tag.SELECT) + { + getSelectData(atts, b); + } + else + { + if (tag == HTML.Tag.INPUT) + value = getInputFormData(atts); + else if (tag == HTML.Tag.TEXTAREA) + value = getTextAreaData(atts); + if (name != null && value != null) + { + addData(b, name, value); + } + } + } + } + + /** + * Fetches form data from select boxes. + * + * @param atts the attributes of the element + * + * @param b the form data string to append to + */ + private void getSelectData(AttributeSet atts, StringBuilder b) + { + String name = (String) atts.getAttribute(HTML.Attribute.NAME); + if (name != null) + { + Object m = atts.getAttribute(StyleConstants.ModelAttribute); + if (m instanceof SelectListModel) + { + SelectListModel sl = (SelectListModel) m; + ListSelectionModel lsm = sl.getSelectionModel(); + for (int i = 0; i < sl.getSize(); i++) + { + if (lsm.isSelectedIndex(i)) + { + Option o = (Option) sl.getElementAt(i); + addData(b, name, o.getValue()); + } + } + } + else if (m instanceof SelectComboBoxModel) + { + SelectComboBoxModel scb = (SelectComboBoxModel) m; + Option o = (Option) scb.getSelectedItem(); + if (o != null) + addData(b, name, o.getValue()); + } + } + } + + /** + * Fetches form data from a textarea. + * + * @param atts the attributes + * + * @return the form data + */ + private String getTextAreaData(AttributeSet atts) + { + Document doc = (Document) atts.getAttribute(StyleConstants.ModelAttribute); + String data; + try + { + data = doc.getText(0, doc.getLength()); + } + catch (BadLocationException ex) + { + data = null; + } + return data; + } + + /** + * Fetches form data from an input tag. + * + * @param atts the attributes from which to fetch the data + * + * @return the field value + */ + private String getInputFormData(AttributeSet atts) + { + String type = (String) atts.getAttribute(HTML.Attribute.TYPE); + Object model = atts.getAttribute(StyleConstants.ModelAttribute); + String value = null; + if (type.equals("text") || type.equals("password")) + { + Document doc = (Document) model; + try + { + value = doc.getText(0, doc.getLength()); + } + catch (BadLocationException ex) + { + // Sigh. + assert false; + } + } + else if (type.equals("hidden") || type.equals("submit")) + { + value = (String) atts.getAttribute(HTML.Attribute.VALUE); + if (value == null) + value = ""; + } + // TODO: Implement the others. radio, checkbox and file. + return value; + } + + /** + * Actually adds the specified data to the string. It URL encodes + * the name and value and handles separation of the fields. + * + * @param b the string at which the form data to be added + * @param name the name of the field + * @param value the value + */ + private void addData(StringBuilder b, String name, String value) + { + if (b.length() > 0) + b.append('&'); + String encName = URLEncoder.encode(name); + b.append(encName); + b.append('='); + String encValue = URLEncoder.encode(value); + b.append(encValue); + } + + /** + * Resets the form data to their initial state. + */ + private void resetForm() + { + Element form = getFormElement(); + if (form != null) + { + ElementIterator iter = new ElementIterator(form); + Element next; + while ((next = iter.next()) != null) + { + if (next.isLeaf()) + { + AttributeSet atts = next.getAttributes(); + Object m = atts.getAttribute(StyleConstants.ModelAttribute); + if (m instanceof ResetableModel) + ((ResetableModel) m).reset(); + } + } + } + } +} diff --git a/libjava/classpath/javax/swing/text/html/FrameSetView.java b/libjava/classpath/javax/swing/text/html/FrameSetView.java new file mode 100644 index 000000000..e3252d79c --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/FrameSetView.java @@ -0,0 +1,274 @@ +/* FrameSetView.java -- Implements HTML frameset + Copyright (C) 2006 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import java.util.StringTokenizer; + +import javax.swing.text.AttributeSet; +import javax.swing.text.BoxView; +import javax.swing.text.Element; +import javax.swing.text.View; +import javax.swing.text.ViewFactory; + +/** + * Implements HTML framesets. This is implemented as a vertical box that + * holds the rows of the frameset. Each row is again a horizontal box that + * holds the actual columns. + */ +public class FrameSetView + extends BoxView +{ + + /** + * A row of a frameset. + */ + private class FrameSetRow + extends BoxView + { + private int row; + FrameSetRow(Element el, int r) + { + super(el, X_AXIS); + row = r; + } + + protected void loadChildren(ViewFactory f) + { + // Load the columns here. + Element el = getElement(); + View[] columns = new View[numViews[X_AXIS]]; + int offset = row * numViews[X_AXIS]; + for (int c = 0; c < numViews[X_AXIS]; c++) + { + Element child = el.getElement(offset + c); + columns[c] = f.create(child); + } + replace(0, 0, columns); + } + + protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, + int[] spans) + { + int numRows = numViews[X_AXIS]; + int[] abs = absolute[X_AXIS]; + int[] rel = relative[X_AXIS]; + int[] perc = percent[X_AXIS]; + layoutViews(targetSpan, axis, offsets, spans, numRows, abs, rel, perc); + } + } + + /** + * Holds the absolute layout information for the views along one axis. The + * indices are absolute[axis][index], where axis is either X_AXIS (columns) + * or Y_AXIS (rows). Rows or columns that don't have absolute layout have + * a -1 in this array. + */ + int[][] absolute; + + /** + * Holds the relative (*) layout information for the views along one axis. + * The indices are relative[axis][index], where axis is either X_AXIS + * (columns) or Y_AXIS (rows). Rows or columns that don't have relative + * layout have a Float.NaN in this array. + */ + int[][] relative; + + /** + * Holds the relative (%) layout information for the views along one axis. + * The indices are relative[axis][index], where axis is either X_AXIS + * (columns) or Y_AXIS (rows). Rows or columns that don't have relative + * layout have a Float.NaN in this array. + * + * The percentage is divided by 100 so that we hold the actual fraction here. + */ + int[][] percent; + + /** + * The number of children in each direction. + */ + int[] numViews; + + FrameSetView(Element el) + { + super(el, Y_AXIS); + numViews = new int[2]; + absolute = new int[2][]; + relative = new int[2][]; + percent = new int[2][]; + } + + /** + * Loads the children and places them inside the grid. + */ + protected void loadChildren(ViewFactory f) + { + parseRowsCols(); + // Set up the rows. + View[] rows = new View[numViews[Y_AXIS]]; + for (int r = 0; r < numViews[Y_AXIS]; r++) + { + rows[r] = new FrameSetRow(getElement(), r); + } + replace(0, 0, rows); + } + + /** + * Parses the rows and cols attributes and sets up the layout info. + */ + private void parseRowsCols() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + String cols = (String) atts.getAttribute(HTML.Attribute.COLS); + if (cols == null) // Defaults to '100%' when not specified. + cols = "100%"; + parseLayout(cols, X_AXIS); + String rows = (String) atts.getAttribute(HTML.Attribute.ROWS); + if (rows == null) // Defaults to '100%' when not specified. + rows = "100%"; + parseLayout(rows, Y_AXIS); + } + + /** + * Parses the cols or rows attribute and places the layout info in the + * appropriate arrays. + * + * @param att the attributes to parse + * @param axis the axis + */ + private void parseLayout(String att, int axis) + { + StringTokenizer tokens = new StringTokenizer(att, ","); + numViews[axis] = tokens.countTokens(); + absolute[axis] = new int[numViews[axis]]; + relative[axis] = new int[numViews[axis]]; + percent[axis] = new int[numViews[axis]]; + for (int index = 0; tokens.hasMoreTokens(); index++) + { + String token = tokens.nextToken(); + int p = token.indexOf('%'); + int s = token.indexOf('*'); + if (p != -1) + { + // Percent value. + String number = token.substring(0, p); + try + { + percent[axis][index] = Integer.parseInt(number); + } + catch (NumberFormatException ex) + { + // Leave value as 0 then. + } + } + else if (s != -1) + { + // Star relative value. + String number = token.substring(0, s); + try + { + relative[axis][index] = Integer.parseInt(number); + } + catch (NumberFormatException ex) + { + // Leave value as 0 then. + } + } + else + { + // Absolute value. + try + { + absolute[axis][index] = Integer.parseInt(token); + } + catch (NumberFormatException ex) + { + // Leave value as 0 then. + } + } + } + } + + protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets, + int[] spans) + { + int numRows = numViews[Y_AXIS]; + int[] abs = absolute[Y_AXIS]; + int[] rel = relative[Y_AXIS]; + int[] perc = percent[Y_AXIS]; + layoutViews(targetSpan, axis, offsets, spans, numRows, abs, rel, perc); + } + + void layoutViews(int targetSpan, int axis, int[] offsets, int[] spans, + int numViews, int[] abs, int[] rel, int[] perc) + { + // We need two passes. In the first pass we layout the absolute and + // percent values and accumulate the needed space. In the second pass + // the relative values are distributed and the offsets are set. + int total = 0; + int relTotal = 0; + for (int i = 0; i < numViews; i++) + { + if (abs[i] > 0) + { + spans[i] = abs[i]; + total += spans[i]; + } + else if (perc[i] > 0) + { + spans[i] = (targetSpan * perc[i]) / 100; + total += spans[i]; + } + else if (rel[i] > 0) + { + relTotal += rel[i]; + } + } + int offs = 0; + for (int i = 0; i < numViews; i++) + { + if (relTotal > 0 && rel[i] > 0) + { + spans[i] = targetSpan * (rel[i] / relTotal); + } + offsets[i] = offs; + offs += spans[i]; + } + } +} diff --git a/libjava/classpath/javax/swing/text/html/FrameView.java b/libjava/classpath/javax/swing/text/html/FrameView.java new file mode 100644 index 000000000..cd4e44a98 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/FrameView.java @@ -0,0 +1,233 @@ +/* FrameView.java -- Renders HTML frame tags + 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.awt.Component; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.swing.JEditorPane; +import javax.swing.event.HyperlinkEvent; +import javax.swing.event.HyperlinkListener; +import javax.swing.text.AttributeSet; +import javax.swing.text.ComponentView; +import javax.swing.text.Element; +import javax.swing.text.View; + +/** + * A view that is responsible for rendering HTML frame tags. + * This is accomplished by a specialized {@link ComponentView} + * that embeds a JEditorPane with an own document. + */ +class FrameView + extends ComponentView + implements HyperlinkListener +{ + + /** + * Creates a new FrameView for the specified element. + * + * @param el the element for the view + */ + FrameView(Element el) + { + super(el); + } + + /** + * Creates the element that will be embedded in the view. + * This will be a JEditorPane with the appropriate content set. + * + * @return the element that will be embedded in the view + */ + protected Component createComponent() + { + Element el = getElement(); + AttributeSet atts = el.getAttributes(); + JEditorPane html = new JEditorPane(); + html.addHyperlinkListener(this); + URL base = ((HTMLDocument) el.getDocument()).getBase(); + String srcAtt = (String) atts.getAttribute(HTML.Attribute.SRC); + if (srcAtt != null && ! srcAtt.equals("")) + { + try + { + URL page = new URL(base, srcAtt); + html.setPage(page); + ((HTMLDocument) html.getDocument()).setFrameDocument(true); + } + catch (MalformedURLException ex) + { + // Leave page empty. + } + catch (IOException ex) + { + // Leave page empty. + } + } + return html; + } + + /** + * Catches hyperlink events on that frame's editor and forwards it to + * the outermost editorpane. + */ + public void hyperlinkUpdate(HyperlinkEvent event) + { + JEditorPane outer = getTopEditorPane(); + if (outer != null) + { + if (event instanceof HTMLFrameHyperlinkEvent) + { + HTMLFrameHyperlinkEvent hfhe = (HTMLFrameHyperlinkEvent) event; + if (hfhe.getEventType() == HyperlinkEvent.EventType.ACTIVATED) + { + String target = hfhe.getTarget(); + if (event instanceof FormSubmitEvent) + { + handleFormSubmitEvent(hfhe, outer, target); + } + else // No FormSubmitEvent. + { + handleHyperlinkEvent(hfhe, outer, target); + } + } + } + else + { + // Simply forward this event. + outer.fireHyperlinkUpdate(event); + } + } + } + + /** + * Handles normal hyperlink events. + * + * @param event the event + * @param outer the top editor + * @param target the target + */ + private void handleHyperlinkEvent(HyperlinkEvent event, + JEditorPane outer, String target) + { + if (target.equals("_top")) + { + try + { + outer.setPage(event.getURL()); + } + catch (IOException ex) + { + // Well... + ex.printStackTrace(); + } + } + if (! outer.isEditable()) + { + outer.fireHyperlinkUpdate + (new HTMLFrameHyperlinkEvent(outer, + event.getEventType(), + event.getURL(), + event.getDescription(), + getElement(), + target)); + } + } + + /** + * Handles form submit events. + * + * @param event the event + * @param outer the top editor + * @param target the target + */ + private void handleFormSubmitEvent(HTMLFrameHyperlinkEvent event, + JEditorPane outer, + String target) + { + HTMLEditorKit kit = (HTMLEditorKit) outer.getEditorKit(); + if (kit != null && kit.isAutoFormSubmission()) + { + if (target.equals("_top")) + { + try + { + outer.setPage(event.getURL()); + } + catch (IOException ex) + { + // Well... + ex.printStackTrace(); + } + } + else + { + HTMLDocument doc = + (HTMLDocument) outer.getDocument(); + doc.processHTMLFrameHyperlinkEvent(event); + } + } + else + { + outer.fireHyperlinkUpdate(event); + } + } + + /** + * Determines the topmost editor in a nested frameset. + * + * @return the topmost editor in a nested frameset + */ + private JEditorPane getTopEditorPane() + { + View parent = getParent(); + View top = null; + while (parent != null) + { + if (parent instanceof FrameSetView) + top = parent; + } + JEditorPane editor = null; + if (top != null) + editor = (JEditorPane) top.getContainer(); + return editor; + } +} diff --git a/libjava/classpath/javax/swing/text/html/HRuleView.java b/libjava/classpath/javax/swing/text/html/HRuleView.java new file mode 100644 index 000000000..9be1efff3 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/HRuleView.java @@ -0,0 +1,189 @@ +/* HRuleView.java -- Horizontal dash in HTML documents. + 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.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Rectangle; +import java.awt.Shape; + +import javax.swing.text.Element; +import javax.swing.text.View; + +/** + * Represents the long horizontal separating dash that can be inserted into the + * HTML documents with HR tag. + */ +class HRuleView extends InlineView +{ + /** + * The null view, indicating, that nothing should be painted ahead the + * breaking point. + */ + View nullView; + + /** + * The height of the horizontal dash area. + */ + static int HEIGHT = 4; + + /** + * The imaginary invisible view that stays after end of line after the + * breaking procedure. It occupies on character. + */ + class Beginning extends NullView + { + /** + * The break offset that becomes the views start offset. + */ + int breakOffset; + + /** + * Return the end offset that is always one char after the break offset. + */ + public int getEndOffset() + { + return breakOffset + 1; + } + + /** + * Return the start offset that has been passed in a constructor. + */ + public int getStartOffset() + { + return breakOffset; + } + + /** + * Create the new instance of this view. + * + * @param element the element (inherited from the HR view) + * @param offset the position where the HR view has been broken + */ + public Beginning(Element element, int offset) + { + super(element); + breakOffset = offset; + } + } + + /** + * Creates the new HR view. + */ + public HRuleView(Element element) + { + super(element); + } + + /** + * Returns the ForcedBreakWeight for the vertical axis, indicating, the the + * view must be broken to be displayed correctly. The horizontal dash is + * not breakeable along the Y axis. + */ + public int getBreakWeight(int axis, float pos, float len) + { + if (axis == X_AXIS && ((getEndOffset() - getStartOffset()) > 1)) + return ForcedBreakWeight; + else + return BadBreakWeight; + } + + /** + * Draws the double line, upped black and the lower light gray. + */ + public void paint(Graphics g, Shape a) + { + Rectangle bounds = a.getBounds(); + + int x = bounds.x; + int y = bounds.y; + + int w = bounds.x + bounds.width; + + // We move "half pixel up" from the actual horizontal position - + // this will be rounded to the closest actual int co-ordinate. + int h = bounds.y + (int) Math.round(bounds.height * 0.5 - 0.5); + + g.setColor(Color.black); + g.drawLine(x, y++, w, h++); + g.setColor(Color.lightGray); + g.drawLine(x, y, w, h); + } + + /** + * Break the view into this view and the invisible imaginary view that + * stays on the end of line that is broken by HR dash. The view is broken + * only if its length is longer than one (the two characters are expected + * in the initial length). + */ + public View breakView(int axis, int offset, float pos, float len) + { + if (getEndOffset() - getStartOffset() > 1) + return new Beginning(getElement(), offset); + else + return this; + } + + /** + * Returns the width of the container for the horizontal axis and the + * thickness of the dash area for the vertical axis. + */ + public float getMaximumSpan(int axis) + { + if (axis == X_AXIS) + { + Component container = getContainer(); + if (container != null) + return getContainer().getWidth(); + else + return 640; + } + else + return HEIGHT; + } + + /** + * Returns the same values as {@link #getMaximumSpan(int)} + */ + public float getPreferredSpan(int axis) + { + return getMaximumSpan(axis); + } +} diff --git a/libjava/classpath/javax/swing/text/html/HTML.java b/libjava/classpath/javax/swing/text/html/HTML.java new file mode 100644 index 000000000..93c05daa2 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/HTML.java @@ -0,0 +1,1253 @@ +/* HTML.java -- HTML document tag constants + Copyright (C) 2002 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import java.io.Serializable; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import java.util.Map; +import java.util.TreeMap; + +import javax.swing.text.AttributeSet; + +/** + * HTML attribute and tag definitions. + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ +public class HTML +{ + /** + * Represents a HTML attribute. + */ + public static final class Attribute + { + /** + * The action attribute + */ + public static final Attribute ACTION = new Attribute("action"); + + /** + * The align attribute + */ + public static final Attribute ALIGN = new Attribute("align"); + + /** + * The alink attribute + */ + public static final Attribute ALINK = new Attribute("alink"); + + /** + * The alt attribute + */ + public static final Attribute ALT = new Attribute("alt"); + + /** + * The archive attribute + */ + public static final Attribute ARCHIVE = new Attribute("archive"); + + /** + * The background attribute + */ + public static final Attribute BACKGROUND = new Attribute("background"); + + /** + * The bgcolor attribute + */ + public static final Attribute BGCOLOR = new Attribute("bgcolor"); + + /** + * The border attribute + */ + public static final Attribute BORDER = new Attribute("border"); + + /** + * The cellpadding attribute + */ + public static final Attribute CELLPADDING = new Attribute("cellpadding"); + + /** + * The cellspacing attribute + */ + public static final Attribute CELLSPACING = new Attribute("cellspacing"); + + /** + * The checked attribute + */ + public static final Attribute CHECKED = new Attribute("checked"); + + /** + * The class attribute + */ + public static final Attribute CLASS = new Attribute("class"); + + /** + * The classid attribute + */ + public static final Attribute CLASSID = new Attribute("classid"); + + /** + * The clear attribute + */ + public static final Attribute CLEAR = new Attribute("clear"); + + /** + * The code attribute + */ + public static final Attribute CODE = new Attribute("code"); + + /** + * The codebase attribute + */ + public static final Attribute CODEBASE = new Attribute("codebase"); + + /** + * The codetype attribute + */ + public static final Attribute CODETYPE = new Attribute("codetype"); + + /** + * The color attribute + */ + public static final Attribute COLOR = new Attribute("color"); + + /** + * The cols attribute + */ + public static final Attribute COLS = new Attribute("cols"); + + /** + * The colspan attribute + */ + public static final Attribute COLSPAN = new Attribute("colspan"); + + /** + * The comment attribute + */ + public static final Attribute COMMENT = new Attribute("comment"); + + /** + * The compact attribute + */ + public static final Attribute COMPACT = new Attribute("compact"); + + /** + * The content attribute + */ + public static final Attribute CONTENT = new Attribute("content"); + + /** + * The coords attribute + */ + public static final Attribute COORDS = new Attribute("coords"); + + /** + * The data attribute + */ + public static final Attribute DATA = new Attribute("data"); + + /** + * The declare attribute + */ + public static final Attribute DECLARE = new Attribute("declare"); + + /** + * The dir attribute + */ + public static final Attribute DIR = new Attribute("dir"); + + /** + * The dummy attribute + */ + public static final Attribute DUMMY = new Attribute("dummy"); + + /** + * The enctype attribute + */ + public static final Attribute ENCTYPE = new Attribute("enctype"); + + /** + * The endtag attribute + */ + public static final Attribute ENDTAG = new Attribute("endtag"); + + /** + * The face attribute + */ + public static final Attribute FACE = new Attribute("face"); + + /** + * The frameborder attribute + */ + public static final Attribute FRAMEBORDER = new Attribute("frameborder"); + + /** + * The halign attribute + */ + public static final Attribute HALIGN = new Attribute("halign"); + + /** + * The height attribute + */ + public static final Attribute HEIGHT = new Attribute("height"); + + /** + * The href attribute + */ + public static final Attribute HREF = new Attribute("href"); + + /** + * The hspace attribute + */ + public static final Attribute HSPACE = new Attribute("hspace"); + + /** + * The http-equiv attribute + */ + public static final Attribute HTTPEQUIV = new Attribute("http-equiv"); + + /** + * The id attribute + */ + public static final Attribute ID = new Attribute("id"); + + /** + * The ismap attribute + */ + public static final Attribute ISMAP = new Attribute("ismap"); + + /** + * The lang attribute + */ + public static final Attribute LANG = new Attribute("lang"); + + /** + * The language attribute + */ + public static final Attribute LANGUAGE = new Attribute("language"); + + /** + * The link attribute + */ + public static final Attribute LINK = new Attribute("link"); + + /** + * The lowsrc attribute + */ + public static final Attribute LOWSRC = new Attribute("lowsrc"); + + /** + * The marginheight attribute + */ + public static final Attribute MARGINHEIGHT = new Attribute("marginheight"); + + /** + * The marginwidth attribute + */ + public static final Attribute MARGINWIDTH = new Attribute("marginwidth"); + + /** + * The maxlength attribute + */ + public static final Attribute MAXLENGTH = new Attribute("maxlength"); + + /** + * The media attribute + */ + static final Attribute MEDIA = new Attribute("media"); + + /** + * The method attribute + */ + public static final Attribute METHOD = new Attribute("method"); + + /** + * The multiple attribute + */ + public static final Attribute MULTIPLE = new Attribute("multiple"); + + /** + * The n attribute + */ + public static final Attribute N = new Attribute("n"); + + /** + * The name attribute + */ + public static final Attribute NAME = new Attribute("name"); + + /** + * The nohref attribute + */ + public static final Attribute NOHREF = new Attribute("nohref"); + + /** + * The noresize attribute + */ + public static final Attribute NORESIZE = new Attribute("noresize"); + + /** + * The noshade attribute + */ + public static final Attribute NOSHADE = new Attribute("noshade"); + + /** + * The nowrap attribute + */ + public static final Attribute NOWRAP = new Attribute("nowrap"); + + /** + * The prompt attribute + */ + public static final Attribute PROMPT = new Attribute("prompt"); + + /** + * The rel attribute + */ + public static final Attribute REL = new Attribute("rel"); + + /** + * The rev attribute + */ + public static final Attribute REV = new Attribute("rev"); + + /** + * The rows attribute + */ + public static final Attribute ROWS = new Attribute("rows"); + + /** + * The rowspan attribute + */ + public static final Attribute ROWSPAN = new Attribute("rowspan"); + + /** + * The scrolling attribute + */ + public static final Attribute SCROLLING = new Attribute("scrolling"); + + /** + * The selected attribute + */ + public static final Attribute SELECTED = new Attribute("selected"); + + /** + * The shape attribute + */ + public static final Attribute SHAPE = new Attribute("shape"); + + /** + * The shapes attribute + */ + public static final Attribute SHAPES = new Attribute("shapes"); + + /** + * The size attribute + */ + public static final Attribute SIZE = new Attribute("size"); + + /** + * The src attribute + */ + public static final Attribute SRC = new Attribute("src"); + + /** + * The standby attribute + */ + public static final Attribute STANDBY = new Attribute("standby"); + + /** + * The start attribute + */ + public static final Attribute START = new Attribute("start"); + + /** + * The style attribute + */ + public static final Attribute STYLE = new Attribute("style"); + + /** + * The target attribute + */ + public static final Attribute TARGET = new Attribute("target"); + + /** + * The text attribute + */ + public static final Attribute TEXT = new Attribute("text"); + + /** + * The title attribute + */ + public static final Attribute TITLE = new Attribute("title"); + + /** + * The type attribute + */ + public static final Attribute TYPE = new Attribute("type"); + + /** + * The usemap attribute + */ + public static final Attribute USEMAP = new Attribute("usemap"); + + /** + * The valign attribute + */ + public static final Attribute VALIGN = new Attribute("valign"); + + /** + * The value attribute + */ + public static final Attribute VALUE = new Attribute("value"); + + /** + * The valuetype attribute + */ + public static final Attribute VALUETYPE = new Attribute("valuetype"); + + /** + * The version attribute + */ + public static final Attribute VERSION = new Attribute("version"); + + /** + * The vlink attribute + */ + public static final Attribute VLINK = new Attribute("vlink"); + + /** + * The vspace attribute + */ + public static final Attribute VSPACE = new Attribute("vspace"); + + /** + * The width attribute + */ + public static final Attribute WIDTH = new Attribute("width"); + + /** + * This is used to reflect the pseudo class for the a tag. + */ + static final Attribute PSEUDO_CLASS = new Attribute("_pseudo"); + + /** + * This is used to reflect the dynamic class for the a tag. + */ + static final Attribute DYNAMIC_CLASS = new Attribute("_dynamic"); + + /** + * The attribute name. + */ + private final String name; + + /** + * Creates the attribute with the given name. + */ + private Attribute(String a_name) + { + name = a_name; + } + + /** + * Returns the attribute name. The names of the built-in attributes + * are always returned in lowercase. + */ + public String toString() + { + return name; + } + + /** + * Return an array of all attributes, declared in the HTML.Attribute + * class. WARNING: attributes are the only public fields, + * expected in this class. + */ + static Attribute[] getAllAttributes() + { + Field[] f = Attribute.class.getFields(); + Attribute[] attrs = new Attribute[ f.length ]; + Field x; + int p = 0; + Attribute a; + + for (int i = 0; i < f.length; i++) + { + x = f [ i ]; + + if ((x.getModifiers() & Modifier.STATIC) != 0) + { + if (x.getType().equals(Attribute.class)) + { + try + { + a = (Attribute) x.get(null); + attrs [ p++ ] = a; + } + catch (Exception ex) + { + ex.printStackTrace(System.err); + throw new Error("This should never happen, report a bug"); + } + } + } + } + + return attrs; + } + } + + /** + * Represents a HTML tag. + */ + public static class Tag + { + /** + * The <a> tag + */ + public static final Tag A = new Tag("a"); + + /** + * The <address> tag + */ + public static final Tag ADDRESS = new Tag("address"); + + /** + * The <applet> tag + */ + public static final Tag APPLET = new Tag("applet"); + + /** + * The <area> tag + */ + public static final Tag AREA = new Tag("area"); + + /** + * The <b> tag + */ + public static final Tag B = new Tag("b"); + + /** + * The <base> tag + */ + public static final Tag BASE = new Tag("base"); + + /** + * The <basefont> tag + */ + public static final Tag BASEFONT = new Tag("basefont"); + + /** + * The <big> tag + */ + public static final Tag BIG = new Tag("big"); + + /** + * The <blockquote> tag , breaks flow, block tag. + */ + public static final Tag BLOCKQUOTE = new Tag("blockquote", BREAKS | BLOCK); + + /** + * The <body> tag , breaks flow, block tag. + */ + public static final Tag BODY = new Tag("body", BREAKS | BLOCK); + + /** + * The <br> tag , breaks flow. + */ + public static final Tag BR = new Tag("br", BREAKS); + + /** + * The <caption> tag + */ + public static final Tag CAPTION = new Tag("caption"); + + /** + * The <center> tag , breaks flow. + */ + public static final Tag CENTER = new Tag("center", BREAKS); + + /** + * The <cite> tag + */ + public static final Tag CITE = new Tag("cite"); + + /** + * The <code> tag + */ + public static final Tag CODE = new Tag("code"); + + /** + * The <dd> tag , breaks flow, block tag. + */ + public static final Tag DD = new Tag("dd", BREAKS | BLOCK); + + /** + * The <dfn> tag + */ + public static final Tag DFN = new Tag("dfn"); + + /** + * The <dir> tag , breaks flow, block tag. + */ + public static final Tag DIR = new Tag("dir", BREAKS | BLOCK); + + /** + * The <div> tag , breaks flow, block tag. + */ + public static final Tag DIV = new Tag("div", BREAKS | BLOCK); + + /** + * The <dl> tag , breaks flow, block tag. + */ + public static final Tag DL = new Tag("dl", BREAKS | BLOCK); + + /** + * The <dt> tag , breaks flow, block tag. + */ + public static final Tag DT = new Tag("dt", BREAKS | BLOCK); + + /** + * The <em> tag + */ + public static final Tag EM = new Tag("em"); + + /** + * The <font> tag + */ + public static final Tag FONT = new Tag("font"); + + /** + * The <form> tag , breaks flow. + */ + public static final Tag FORM = new Tag("form", BREAKS); + + /** + * The <frame> tag + */ + public static final Tag FRAME = new Tag("frame"); + + /** + * The <frameset> tag + */ + public static final Tag FRAMESET = new Tag("frameset"); + + /** + * The <h1> tag , breaks flow, block tag. + */ + public static final Tag H1 = new Tag("h1", BREAKS | BLOCK); + + /** + * The <h2> tag , breaks flow, block tag. + */ + public static final Tag H2 = new Tag("h2", BREAKS | BLOCK); + + /** + * The <h3> tag , breaks flow, block tag. + */ + public static final Tag H3 = new Tag("h3", BREAKS | BLOCK); + + /** + * The <h4> tag , breaks flow, block tag. + */ + public static final Tag H4 = new Tag("h4", BREAKS | BLOCK); + + /** + * The <h5> tag , breaks flow, block tag. + */ + public static final Tag H5 = new Tag("h5", BREAKS | BLOCK); + + /** + * The <h6> tag , breaks flow, block tag. + */ + public static final Tag H6 = new Tag("h6", BREAKS | BLOCK); + + /** + * The <head> tag , breaks flow, block tag. + */ + public static final Tag HEAD = new Tag("head", BREAKS | BLOCK); + + /** + * The <hr> tag , breaks flow. + */ + public static final Tag HR = new Tag("hr", BREAKS); + + /** + * The <html> tag , breaks flow. + */ + public static final Tag HTML = new Tag("html", BREAKS); + + /** + * The <i> tag + */ + public static final Tag I = new Tag("i"); + + /** + * The <img> tag + */ + public static final Tag IMG = new Tag("img"); + + /** + * The <input> tag + */ + public static final Tag INPUT = new Tag("input"); + + /** + * The <isindex> tag , breaks flow. + */ + public static final Tag ISINDEX = new Tag("isindex", BREAKS); + + /** + * The <kbd> tag + */ + public static final Tag KBD = new Tag("kbd"); + + /** + * The <li> tag , breaks flow, block tag. + */ + public static final Tag LI = new Tag("li", BREAKS | BLOCK); + + /** + * The <link> tag + */ + public static final Tag LINK = new Tag("link"); + + /** + * The <map> tag + */ + public static final Tag MAP = new Tag("map"); + + /** + * The <menu> tag , breaks flow, block tag. + */ + public static final Tag MENU = new Tag("menu", BREAKS | BLOCK); + + /** + * The <meta> tag + */ + public static final Tag META = new Tag("meta"); + + /** + * The <nobr> tag + */ + static final Tag NOBR = new Tag("nobr"); + + /** + * The <noframes> tag , breaks flow, block tag. + */ + public static final Tag NOFRAMES = new Tag("noframes", BREAKS | BLOCK); + + /** + * The <object> tag + */ + public static final Tag OBJECT = new Tag("object"); + + /** + * The <ol> tag , breaks flow, block tag. + */ + public static final Tag OL = new Tag("ol", BREAKS | BLOCK); + + /** + * The <option> tag + */ + public static final Tag OPTION = new Tag("option"); + + /** + * The <p> tag , breaks flow, block tag. + */ + public static final Tag P = new Tag("p", BREAKS | BLOCK); + + /** + * The <param> tag + */ + public static final Tag PARAM = new Tag("param"); + + /** + * The <pre> tag , breaks flow, block tag, preformatted. + */ + public static final Tag PRE = new Tag("pre", BREAKS | BLOCK | PREFORMATTED); + + /** + * The <s> tag + */ + public static final Tag S = new Tag("s"); + + /** + * The <samp> tag + */ + public static final Tag SAMP = new Tag("samp"); + + /** + * The <script> tag + */ + public static final Tag SCRIPT = new Tag("script"); + + /** + * The <select> tag + */ + public static final Tag SELECT = new Tag("select"); + + /** + * The <small> tag + */ + public static final Tag SMALL = new Tag("small"); + + /** + * The <span> tag + */ + public static final Tag SPAN = new Tag("span"); + + /** + * The <strike> tag + */ + public static final Tag STRIKE = new Tag("strike"); + + /** + * The <strong> tag + */ + public static final Tag STRONG = new Tag("strong"); + + /** + * The <style> tag + */ + public static final Tag STYLE = new Tag("style"); + + /** + * The <sub> tag + */ + public static final Tag SUB = new Tag("sub"); + + /** + * The <sup> tag + */ + public static final Tag SUP = new Tag("sup"); + + /** + * The <table> tag , block tag. + */ + public static final Tag TABLE = new Tag("table", BLOCK); + + /** + * The <td> tag , breaks flow, block tag. + */ + public static final Tag TD = new Tag("td", BREAKS | BLOCK); + + /** + * The <textarea> tag , preformatted. + */ + public static final Tag TEXTAREA = new Tag("textarea", PREFORMATTED); + + /** + * The <th> tag , breaks flow, block tag. + */ + public static final Tag TH = new Tag("th", BREAKS | BLOCK); + + /** + * The <title> tag , breaks flow, block tag. + */ + public static final Tag TITLE = new Tag("title", BREAKS | BLOCK); + + /** + * The <tr> tag , block tag. + */ + public static final Tag TR = new Tag("tr", BLOCK); + + /** + * The <tt> tag + */ + public static final Tag TT = new Tag("tt"); + + /** + * The <u> tag + */ + public static final Tag U = new Tag("u"); + + /** + * The <ul> tag , breaks flow, block tag. + */ + public static final Tag UL = new Tag("ul", BREAKS | BLOCK); + + /** + * The <var> tag + */ + public static final Tag VAR = new Tag("var"); + + /* Special tags */ + + /** + * Total number of syntetic tags, delared in the Tag class. + * This must be adjusted if the new synthetic tags are declared. + * Otherwise the HTML.getAllTags() will not work as expected. + */ + private static final int TOTAL_SYNTHETIC_TAGS = 3; + + /** + * All comments are labeled with this tag. + * This tag is not included into the array, returned by getAllTags(). + * toString() returns 'comment'. HTML reader synthesizes this tag. + */ + public static final Tag COMMENT = new Tag("comment", SYNTHETIC); + + /** + * All text content is labeled with this tag. + * This tag is not included into the array, returned by getAllTags(). + * toString() returns 'content'. HTML reader synthesizes this tag. + */ + public static final Tag CONTENT = new Tag("content", SYNTHETIC); + + /** + * All text content must be in a paragraph element. + * If a paragraph didn't exist when content was encountered, + * a paragraph is manufactured. + * toString() returns 'p-implied'. HTML reader synthesizes this tag. + */ + public static final Tag IMPLIED = new Tag("p-implied", SYNTHETIC); + final String name; + final int flags; + + /** + * Create the unitialised instance of HTML.Tag. + * + * The {@link #breaksFlow()}, {@link #isBlock()} + * and {@link #isPreformatted()} will always return false. + * The {@link #toString()} will return null. + * + * @since 1.3 + */ + public Tag() + { + name = null; + flags = 0; + } + + /** + * Creates a new Tag with the specified id, and with causesBreak + * and isBlock set to false. + */ + protected Tag(String id) + { + name = id; + flags = 0; + } + + /** + * Creates a new Tag with the specified tag name and + * causesBreak and isBlock properties. + */ + protected Tag(String id, boolean causesBreak, boolean isBlock) + { + int f = 0; + + if (causesBreak) + { + f |= BREAKS; + } + + if (isBlock) + { + f |= BLOCK; + } + + flags = f; + name = id; + } + + /** + * Create a tag taking flags. + */ + Tag(String id, int a_flags) + { + name = id; + flags = a_flags; + } + + /** + * Returns true if this tag is a block tag, which is a tag used to + * add structure to a document. + */ + public boolean isBlock() + { + return (flags & BLOCK) != 0; + } + + /** + * Returns true if this tag is pre-formatted, which is true if + * the tag is either PRE or TEXTAREA + */ + public boolean isPreformatted() + { + return (flags & PREFORMATTED) != 0; + } + + /** + * Returns true if this tag causes a line break to the flow of text + */ + public boolean breaksFlow() + { + return (flags & BREAKS) != 0; + } + + /** + * Returns the tag name. The names of the built-in tags are always + * returned in lowercase. + */ + public String toString() + { + return name; + } + + /** + * Return an array of HTML tags, declared in HTML.Tag class. + * WARNING: This method expects that the Tags are the only + * public fields declared in the Tag class. + */ + static Tag[] getAllTags() + { + Field[] f = Tag.class.getFields(); + Field x; + + // The syntetic tags are not included. + Tag[] tags = new Tag[ f.length - TOTAL_SYNTHETIC_TAGS ]; + int p = 0; + Tag t; + + for (int i = 0; i < f.length; i++) + { + x = f [ i ]; + + if ((x.getModifiers() & Modifier.STATIC) != 0) + { + if (x.getType().equals(Tag.class)) + { + try + { + t = (Tag) x.get(null); + + if (!t.isSyntetic()) + { + tags [ p++ ] = t; + } + } + catch (IllegalAccessException ex) + { + unexpected(ex); + } + catch (IllegalArgumentException ex) + { + unexpected(ex); + } + } + } + } + + return tags; + } + + /** + * Returns true for tags, generated by the html reader + * (COMMENT, CONTENT and IMPLIED). + */ + boolean isSyntetic() + { + return (flags & SYNTHETIC) != 0; + } + + private static void unexpected(Exception ex) + throws Error + { + throw new Error("This should never happen, report a bug", ex); + } + } + + /** + * Represents an unknown HTML tag. + * @author Mark Wielaard (mark@klomp.org) + */ + public static class UnknownTag + extends Tag + implements Serializable + { + private static final long serialVersionUID = -1534369342247250625L; + + /** + * Creates a new UnknownTag with the specified name + * @param name The tag name. + * + */ + public UnknownTag(String name) + { + super(name); + } + } + + /** + * This value is returned for attributes without value that have no + * default value defined in the DTD. + */ + public static final String NULL_ATTRIBUTE_VALUE = "#DEFAULT"; + + /* Package level html tag flags */ + static final int BREAKS = 1; + static final int BLOCK = 2; + static final int PREFORMATTED = 4; + static final int SYNTHETIC = 8; + private static Map tagMap; + private static Map attrMap; + + /** + * The public constructor (does nothing). It it seldom required to have + * an instance of this class, because all public fields and methods + * are static. + */ + public HTML() + { + // Nothing to do here. + } + + /** + * Returns the set of the recognized HTML attributes. + */ + public static HTML.Attribute[] getAllAttributeKeys() + { + return Attribute.getAllAttributes(); + } + + /** + * Returns the set of actual HTML tags that are recognized by + * the default HTML reader. The returned array does not include the + * COMMENT, CONTENT and IMPLIED tags. + */ + public static HTML.Tag[] getAllTags() + { + return Tag.getAllTags(); + } + + /** + * Returns an htl attribute constant for the given attribute name. + * @param attName the attribute name, case insensitive + */ + public static Attribute getAttributeKey(String attName) + { + if (attrMap == null) + { + // Create the map on demand. + attrMap = new TreeMap(); + + Attribute[] attrs = getAllAttributeKeys(); + + for (int i = 0; i < attrs.length; i++) + { + attrMap.put(attrs [ i ].toString(), attrs [ i ]); + } + } + + return attrMap.get(attName.toLowerCase()); + } + + /** + * Searches the value of given attribute in the provided set. + * If the value is found (String type expected), tries to parse it as + * an integer value. If succeded, returns the obtained integer value. + * + * For example:

+ * SimpleAttributeSet ase = new SimpleAttributeSet(); + * ase.addAttribute(HTML.getAttributeKey("size"),"222"); + * System.out.println( + * HTML.getIntegerAttributeValue + * (ase, HTML.getAttributeKey("size"), 333)); // prints "222" + * System.out.println( + * HTML.getIntegerAttributeValue + * (ase, HTML.getAttributeKey("width"), 333)); // prints "333". + *

+ * + * + * @param set The attribute set to search in. If the set contains the + * given attribute, it must by a type of String. + * @param attribute The html attribute to search in + * @param defaultValue The value that is returned if the attribute is not + * found in the given set or if the NumberFormatException was thrown + * during the parsing. + */ + public static int getIntegerAttributeValue(AttributeSet set, + HTML.Attribute attribute, + int defaultValue + ) + { + Object v = set.getAttribute(attribute); + + if (v == null) + { + return defaultValue; + } + + try + { + return Integer.parseInt(v.toString().trim()); + } + catch (Exception ex) + { + return defaultValue; + } + } + + /** + * Returns a HTML tag constant for the given HTML attribute name. + * If the tag is unknown, the null is returned. + * @param tagName the tag name, case insensitive + */ + public static Tag getTag(String tagName) + { + if (tagMap == null) + { + // Create the mao on demand. + tagMap = new TreeMap(); + + Tag[] tags = getAllTags(); + + for (int i = 0; i < tags.length; i++) + { + tagMap.put(tags [ i ].toString(), tags [ i ]); + } + } + + return tagMap.get(tagName.toLowerCase()); + } +} diff --git a/libjava/classpath/javax/swing/text/html/HTMLDocument.java b/libjava/classpath/javax/swing/text/html/HTMLDocument.java new file mode 100644 index 000000000..9545be4e8 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/HTMLDocument.java @@ -0,0 +1,2298 @@ +/* HTMLDocument.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html; + +import gnu.classpath.NotImplementedException; + +import java.io.IOException; +import java.io.StringReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Stack; +import java.util.Vector; + +import javax.swing.ButtonGroup; +import javax.swing.DefaultButtonModel; +import javax.swing.JEditorPane; +import javax.swing.ListSelectionModel; +import javax.swing.event.DocumentEvent; +import javax.swing.event.UndoableEditEvent; +import javax.swing.text.AbstractDocument; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.DefaultStyledDocument; +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; + +/** + * Represents the HTML document that is constructed by defining the text and + * other components (images, buttons, etc) in HTML language. This class can + * becomes the default document for {@link JEditorPane} after setting its + * content type to "text/html". HTML document also serves as an intermediate + * data structure when it is needed to parse HTML and then obtain the content of + * the certain types of tags. This class also has methods for modifying the HTML + * content. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + * @author Anthony Balkissoon (abalkiss@redhat.com) + * @author Lillian Angel (langel@redhat.com) + */ +public class HTMLDocument extends DefaultStyledDocument +{ + /** A key for document properies. The value for the key is + * a Vector of Strings of comments not found in the body. + */ + public static final String AdditionalComments = "AdditionalComments"; + URL baseURL = null; + boolean preservesUnknownTags = true; + int tokenThreshold = Integer.MAX_VALUE; + HTMLEditorKit.Parser parser; + + /** + * Indicates whether this document is inside a frame or not. + */ + private boolean frameDocument; + + /** + * Package private to avoid accessor methods. + */ + String baseTarget; + + /** + * Constructs an HTML document using the default buffer size and a default + * StyleSheet. + */ + public HTMLDocument() + { + this(new GapContent(BUFFER_SIZE_DEFAULT), new StyleSheet()); + } + + /** + * Constructs an HTML document with the default content storage + * implementation and the specified style/attribute storage mechanism. + * + * @param styles - the style sheet + */ + public HTMLDocument(StyleSheet styles) + { + this(new GapContent(BUFFER_SIZE_DEFAULT), styles); + } + + /** + * Constructs an HTML document with the given content storage implementation + * and the given style/attribute storage mechanism. + * + * @param c - the document's content + * @param styles - the style sheet + */ + public HTMLDocument(AbstractDocument.Content c, StyleSheet styles) + { + super(c, styles); + } + + /** + * Gets the style sheet with the document display rules (CSS) that were specified + * in the HTML document. + * + * @return - the style sheet + */ + public StyleSheet getStyleSheet() + { + return (StyleSheet) getAttributeContext(); + } + + /** + * This method creates a root element for the new document. + * + * @return the new default root + */ + protected AbstractElement createDefaultRoot() + { + AbstractDocument.AttributeContext ctx = getAttributeContext(); + + // Create html element. + AttributeSet atts = ctx.getEmptySet(); + atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.HTML); + BranchElement html = (BranchElement) createBranchElement(null, atts); + + // Create body element. + atts = ctx.getEmptySet(); + atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.BODY); + BranchElement body = (BranchElement) createBranchElement(html, atts); + html.replace(0, 0, new Element[] { body }); + + // Create p element. + atts = ctx.getEmptySet(); + atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, HTML.Tag.P); + BranchElement p = (BranchElement) createBranchElement(body, atts); + body.replace(0, 0, new Element[] { p }); + + // Create an empty leaf element. + atts = ctx.getEmptySet(); + atts = ctx.addAttribute(atts, StyleConstants.NameAttribute, + HTML.Tag.CONTENT); + Element leaf = createLeafElement(p, atts, 0, 1); + p.replace(0, 0, new Element[]{ leaf }); + + return html; + } + + /** + * This method returns an HTMLDocument.RunElement object attached to + * parent representing a run of text from p0 to p1. The run has + * attributes described by a. + * + * @param parent - the parent element + * @param a - the attributes for the element + * @param p0 - the beginning of the range >= 0 + * @param p1 - the end of the range >= p0 + * + * @return the new element + */ + protected Element createLeafElement(Element parent, AttributeSet a, int p0, + int p1) + { + return new RunElement(parent, a, p0, p1); + } + + /** + * This method returns an HTMLDocument.BlockElement object representing the + * attribute set a and attached to parent. + * + * @param parent - the parent element + * @param a - the attributes for the element + * + * @return the new element + */ + protected Element createBranchElement(Element parent, AttributeSet a) + { + return new BlockElement(parent, a); + } + + /** + * Returns the parser used by this HTMLDocument to insert HTML. + * + * @return the parser used by this HTMLDocument to insert HTML. + */ + public HTMLEditorKit.Parser getParser() + { + return parser; + } + + /** + * Sets the parser used by this HTMLDocument to insert HTML. + * + * @param p the parser to use + */ + public void setParser (HTMLEditorKit.Parser p) + { + parser = p; + } + /** + * Sets the number of tokens to buffer before trying to display the + * Document. + * + * @param n the number of tokens to buffer + */ + public void setTokenThreshold (int n) + { + tokenThreshold = n; + } + + /** + * Returns the number of tokens that are buffered before the document + * is rendered. + * + * @return the number of tokens buffered + */ + public int getTokenThreshold () + { + return tokenThreshold; + } + + /** + * Returns the location against which to resolve relative URLs. + * This is the document's URL if the document was loaded from a URL. + * If a base tag is found, it will be used. + * @return the base URL + */ + public URL getBase() + { + return baseURL; + } + + /** + * Sets the location against which to resolve relative URLs. + * @param u the new base URL + */ + public void setBase(URL u) + { + baseURL = u; + getStyleSheet().setBase(u); + } + + /** + * Returns whether or not the parser preserves unknown HTML tags. + * @return true if the parser preserves unknown tags + */ + public boolean getPreservesUnknownTags() + { + return preservesUnknownTags; + } + + /** + * Sets the behaviour of the parser when it encounters unknown HTML tags. + * @param preservesTags true if the parser should preserve unknown tags. + */ + public void setPreservesUnknownTags(boolean preservesTags) + { + preservesUnknownTags = preservesTags; + } + + /** + * An iterator to iterate through LeafElements in the document. + */ + class LeafIterator extends Iterator + { + HTML.Tag tag; + HTMLDocument doc; + ElementIterator it; + + public LeafIterator (HTML.Tag t, HTMLDocument d) + { + doc = d; + tag = t; + it = new ElementIterator(doc); + } + + /** + * Return the attributes for the tag associated with this iteartor + * @return the AttributeSet + */ + public AttributeSet getAttributes() + { + if (it.current() != null) + return it.current().getAttributes(); + return null; + } + + /** + * Get the end of the range for the current occurrence of the tag + * being defined and having the same attributes. + * @return the end of the range + */ + public int getEndOffset() + { + if (it.current() != null) + return it.current().getEndOffset(); + return -1; + } + + /** + * Get the start of the range for the current occurrence of the tag + * being defined and having the same attributes. + * @return the start of the range (-1 if it can't be found). + */ + + public int getStartOffset() + { + if (it.current() != null) + return it.current().getStartOffset(); + return -1; + } + + /** + * Advance the iterator to the next LeafElement . + */ + public void next() + { + it.next(); + while (it.current()!= null && !it.current().isLeaf()) + it.next(); + } + + /** + * Indicates whether or not the iterator currently represents an occurrence + * of the tag. + * @return true if the iterator currently represents an occurrence of the + * tag. + */ + public boolean isValid() + { + return it.current() != null; + } + + /** + * Type of tag for this iterator. + */ + public Tag getTag() + { + return tag; + } + + } + + public void processHTMLFrameHyperlinkEvent(HTMLFrameHyperlinkEvent event) + { + String target = event.getTarget(); + Element el = event.getSourceElement(); + URL url = event.getURL(); + if (target.equals("_self")) + { + updateFrame(el, url); + } + else if (target.equals("_parent")) + { + updateFrameSet(el.getParentElement(), url); + } + else + { + Element targetFrame = findFrame(target); + if (targetFrame != null) + updateFrame(targetFrame, url); + } + } + + /** + * Finds the named frame inside this document. + * + * @param target the name to look for + * + * @return the frame if there is a matching frame, null + * otherwise + */ + private Element findFrame(String target) + { + ElementIterator i = new ElementIterator(this); + Element next = null; + while ((next = i.next()) != null) + { + AttributeSet atts = next.getAttributes(); + if (atts.getAttribute(StyleConstants.NameAttribute) == HTML.Tag.FRAME) + { + String name = (String) atts.getAttribute(HTML.Attribute.NAME); + if (name != null && name.equals(target)) + break; + } + } + return next; + } + + /** + * Updates the frame that is represented by the specified element to + * refer to the specified URL. + * + * @param el the element + * @param url the new url + */ + private void updateFrame(Element el, URL url) + { + try + { + writeLock(); + DefaultDocumentEvent ev = + new DefaultDocumentEvent(el.getStartOffset(), 1, + DocumentEvent.EventType.CHANGE); + AttributeSet elAtts = el.getAttributes(); + AttributeSet copy = elAtts.copyAttributes(); + MutableAttributeSet matts = (MutableAttributeSet) elAtts; + ev.addEdit(new AttributeUndoableEdit(el, copy, false)); + matts.removeAttribute(HTML.Attribute.SRC); + matts.addAttribute(HTML.Attribute.SRC, url.toString()); + ev.end(); + fireChangedUpdate(ev); + fireUndoableEditUpdate(new UndoableEditEvent(this, ev)); + } + finally + { + writeUnlock(); + } + } + + /** + * Updates the frameset that is represented by the specified element + * to create a frame that refers to the specified URL. + * + * @param el the element + * @param url the url + */ + private void updateFrameSet(Element el, URL url) + { + int start = el.getStartOffset(); + int end = el.getEndOffset(); + + StringBuilder html = new StringBuilder(); + html.append("'); + if (getParser() == null) + setParser(new HTMLEditorKit().getParser()); + try + { + setOuterHTML(el, html.toString()); + } + catch (BadLocationException ex) + { + ex.printStackTrace(); + } + catch (IOException ex) + { + ex.printStackTrace(); + } + } + + /** + * Gets an iterator for the given HTML.Tag. + * @param t the requested HTML.Tag + * @return the Iterator + */ + public HTMLDocument.Iterator getIterator (HTML.Tag t) + { + return new HTMLDocument.LeafIterator(t, this); + } + + /** + * An iterator over a particular type of tag. + */ + public abstract static class Iterator + { + /** + * Return the attribute set for this tag. + * @return the AttributeSet (null if none found). + */ + public abstract AttributeSet getAttributes(); + + /** + * Get the end of the range for the current occurrence of the tag + * being defined and having the same attributes. + * @return the end of the range + */ + public abstract int getEndOffset(); + + /** + * Get the start of the range for the current occurrence of the tag + * being defined and having the same attributes. + * @return the start of the range (-1 if it can't be found). + */ + public abstract int getStartOffset(); + + /** + * Move the iterator forward. + */ + public abstract void next(); + + /** + * Indicates whether or not the iterator currently represents an occurrence + * of the tag. + * @return true if the iterator currently represents an occurrence of the + * tag. + */ + public abstract boolean isValid(); + + /** + * Type of tag this iterator represents. + * @return the tag. + */ + public abstract HTML.Tag getTag(); + } + + public class BlockElement extends AbstractDocument.BranchElement + { + public BlockElement (Element parent, AttributeSet a) + { + super(parent, a); + } + + /** + * Gets the resolving parent. Since HTML attributes are not + * inherited at the model level, this returns null. + */ + public AttributeSet getResolveParent() + { + return null; + } + + /** + * Gets the name of the element. + * + * @return the name of the element if it exists, null otherwise. + */ + public String getName() + { + Object tag = getAttribute(StyleConstants.NameAttribute); + String name = null; + if (tag != null) + name = tag.toString(); + if (name == null) + name = super.getName(); + return name; + } + } + + /** + * RunElement represents a section of text that has a set of + * HTML character level attributes assigned to it. + */ + public class RunElement extends AbstractDocument.LeafElement + { + + /** + * Constructs an element that has no children. It represents content + * within the document. + * + * @param parent - parent of this + * @param a - elements attributes + * @param start - the start offset >= 0 + * @param end - the end offset + */ + public RunElement(Element parent, AttributeSet a, int start, int end) + { + super(parent, a, start, end); + } + + /** + * Gets the name of the element. + * + * @return the name of the element if it exists, null otherwise. + */ + public String getName() + { + Object tag = getAttribute(StyleConstants.NameAttribute); + String name = null; + if (tag != null) + name = tag.toString(); + if (name == null) + name = super.getName(); + return name; + } + + /** + * Gets the resolving parent. HTML attributes do not inherit at the + * model level, so this method returns null. + * + * @return null + */ + public AttributeSet getResolveParent() + { + return null; + } + } + + /** + * A reader to load an HTMLDocument with HTML structure. + * + * @author Anthony Balkissoon abalkiss at redhat dot com + */ + public class HTMLReader extends HTMLEditorKit.ParserCallback + { + /** + * The maximum token threshold. We don't grow it larger than this. + */ + private static final int MAX_THRESHOLD = 10000; + + /** + * The threshold growth factor. + */ + private static final int GROW_THRESHOLD = 5; + + /** + * Holds the current character attribute set * + */ + protected MutableAttributeSet charAttr = new SimpleAttributeSet(); + + protected Vector parseBuffer = new Vector(); + + /** + * The parse stack. It holds the current element tree path. + */ + private Stack parseStack = new Stack(); + + /** + * A stack for character attribute sets * + */ + Stack charAttrStack = new Stack(); + + /** A mapping between HTML.Tag objects and the actions that handle them **/ + HashMap tagToAction; + + /** Tells us whether we've received the '' tag yet **/ + boolean endHTMLEncountered = false; + + /** + * Related to the constructor with explicit insertTag + */ + int popDepth; + + /** + * Related to the constructor with explicit insertTag + */ + int pushDepth; + + /** + * Related to the constructor with explicit insertTag + */ + int offset; + + /** + * The tag (inclusve), after that the insertion should start. + */ + HTML.Tag insertTag; + + /** + * This variable becomes true after the insert tag has been encountered. + */ + boolean insertTagEncountered; + + + /** 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. + */ + ResetablePlainDocument textAreaDocument; + + /** + * The current model of a select tag. Can be a ComboBoxModel or a + * ListModel depending on the type of the select box. + */ + Object selectModel; + + /** + * The current option beeing read. + */ + Option option; + + /** + * The current number of options in the current select model. + */ + int numOptions; + + /** + * The current button groups mappings. + */ + HashMap buttonGroups; + + /** + * The token threshold. This gets increased while loading. + */ + private int threshold; + + public class TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. By default this does nothing. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // Nothing to do here. + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. By default does nothing. + */ + public void end(HTML.Tag t) + { + // Nothing to do here. + } + } + + public class BlockAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // Tell the parse buffer to open a new block for this tag. + blockOpen(t, a); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + // Tell the parse buffer to close this block. + blockClose(t); + } + } + + public class CharacterAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + // Put the old attribute set on the stack. + pushCharacterStyle(); + + // Initialize with link pseudo class. + if (t == HTML.Tag.A) + a.addAttribute(HTML.Attribute.PSEUDO_CLASS, "link"); + + // Just add the attributes in a. + charAttr.addAttribute(t, a.copyAttributes()); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + popCharacterStyle(); + } + } + + /** + * Processes elements that make up forms: <input>, <textarea>, + * <select> and <option>. + */ + public class FormAction extends SpecialAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + 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 ResetablePlainDocument(); + a.addAttribute(StyleConstants.ModelAttribute, textAreaDocument); + } + else if (t == HTML.Tag.SELECT) + { + int size = HTML.getIntegerAttributeValue(a, HTML.Attribute.SIZE, + 1); + boolean multi = a.getAttribute(HTML.Attribute.MULTIPLE) != null; + if (size > 1 || multi) + { + SelectListModel m = new SelectListModel(); + if (multi) + m.getSelectionModel().setSelectionMode(ListSelectionModel + .MULTIPLE_INTERVAL_SELECTION); + selectModel = m; + } + else + { + selectModel = new SelectComboBoxModel(); + } + a.addAttribute(StyleConstants.ModelAttribute, selectModel); + } + if (t == HTML.Tag.OPTION) + { + option = new Option(a); + if (selectModel instanceof SelectListModel) + { + SelectListModel m = (SelectListModel) selectModel; + m.addElement(option); + if (option.isSelected()) + { + m.getSelectionModel().addSelectionInterval(numOptions, + numOptions); + m.addInitialSelection(numOptions); + } + } + else if (selectModel instanceof SelectComboBoxModel) + { + SelectComboBoxModel m = (SelectComboBoxModel) selectModel; + m.addElement(option); + if (option.isSelected()) + { + m.setSelectedItem(option); + m.setInitialSelection(option); + } + } + numOptions++; + } + else + { + // Build the element. + super.start(t, a); + } + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + if (t == HTML.Tag.OPTION) + { + option = null; + } + else + { + if (t == HTML.Tag.TEXTAREA) + { + inTextArea = false; + } + else if (t == HTML.Tag.SELECT) + { + selectModel = null; + numOptions = 0; + } + // 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")) + { + String text = (String) attrs.getAttribute(HTML.Attribute.VALUE); + ResetablePlainDocument doc = new ResetablePlainDocument(); + if (text != null) + { + doc.setInitialText(text); + try + { + doc.insertString(0, text, null); + } + catch (BadLocationException ex) + { + // Shouldn't happen. + assert false; + } + } + attrs.addAttribute(StyleConstants.ModelAttribute, doc); + } + else if (type.equals("file")) + { + attrs.addAttribute(StyleConstants.ModelAttribute, + new PlainDocument()); + } + else if (type.equals("checkbox") || type.equals("radio")) + { + ResetableToggleButtonModel model = + new ResetableToggleButtonModel(); + if (attrs.getAttribute(HTML.Attribute.SELECTED) != null) + { + model.setSelected(true); + model.setInitial(true); + } + if (type.equals("radio")) + { + String name = (String) attrs.getAttribute(HTML.Attribute.NAME); + if (name != null) + { + if (buttonGroups == null) + buttonGroups = new HashMap(); + ButtonGroup group = (ButtonGroup) buttonGroups.get(name); + if (group == null) + { + group = new ButtonGroup(); + buttonGroups.put(name, group); + } + model.setGroup(group); + } + } + attrs.addAttribute(StyleConstants.ModelAttribute, model); + } + } + } + + /** + * Called for form tags. + */ + class FormTagAction + extends BlockAction + { + /** + * Clears the button group mapping. + */ + public void end(HTML.Tag t) + { + super.end(t); + buttonGroups = null; + } + } + + /** + * This action indicates that the content between starting and closing HTML + * elements (like script - /script) should not be visible. The content is + * still inserted and can be accessed when iterating the HTML document. The + * parser will only fire + * {@link javax.swing.text.html.HTMLEditorKit.ParserCallback#handleText} for + * the hidden tags, regardless from that html tags the hidden section may + * contain. + */ + public class HiddenAction + extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + blockOpen(t, a); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + blockClose(t); + } + } + + /** + * Handles <isindex> tags. + */ + public class IsindexAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet()); + addSpecialElement(t, a); + blockClose(HTML.Tag.IMPLIED); + } + } + + public class ParagraphAction extends BlockAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + super.start(t, a); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + super.end(t); + } + } + + /** + * This action is performed when a <pre> tag is parsed. + */ + public class PreAction extends BlockAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + inPreTag = true; + blockOpen(t, a); + a.addAttribute(CSS.Attribute.WHITE_SPACE, "pre"); + blockOpen(HTML.Tag.IMPLIED, a); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + blockClose(HTML.Tag.IMPLIED); + inPreTag = false; + blockClose(t); + } + } + + /** + * Inserts the elements that are represented by ths single tag with + * attributes (only). The closing tag, even if present, mut follow + * immediately after the starting tag without providing any additional + * information. Hence the {@link TagAction#end} method need not be + * overridden and still does nothing. + */ + public class SpecialAction extends TagAction + { + /** + * The functionality is delegated to {@link HTMLReader#addSpecialElement} + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + addSpecialElement(t, a); + } + } + + class AreaAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + throws NotImplementedException + { + // FIXME: Implement. + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + throws NotImplementedException + { + // FIXME: Implement. + } + } + + /** + * Converts HTML tags to CSS attributes. + */ + class ConvertAction + extends TagAction + { + + public void start(HTML.Tag tag, MutableAttributeSet atts) + { + pushCharacterStyle(); + charAttr.addAttribute(tag, atts.copyAttributes()); + StyleSheet styleSheet = getStyleSheet(); + // TODO: Add other tags here. + if (tag == HTML.Tag.FONT) + { + String color = (String) atts.getAttribute(HTML.Attribute.COLOR); + if (color != null) + styleSheet.addCSSAttribute(charAttr, CSS.Attribute.COLOR, color); + String face = (String) atts.getAttribute(HTML.Attribute.FACE); + if (face != null) + styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_FAMILY, + face); + String size = (String) atts.getAttribute(HTML.Attribute.SIZE); + if (size != null) + styleSheet.addCSSAttribute(charAttr, CSS.Attribute.FONT_SIZE, + size); + } + } + + public void end(HTML.Tag tag) + { + popCharacterStyle(); + } + } + + class BaseAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + baseTarget = (String) a.getAttribute(HTML.Attribute.TARGET); + } + } + + class HeadAction extends BlockAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + throws NotImplementedException + { + // FIXME: Implement. + super.start(t, a); + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag 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 HiddenAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + super.start(t, a); + String type = (String) a.getAttribute(HTML.Attribute.TYPE); + if (type == null) + type = "text/css"; + if (type.equals("text/css")) + { + String rel = (String) a.getAttribute(HTML.Attribute.REL); + String media = (String) a.getAttribute(HTML.Attribute.MEDIA); + String title = (String) a.getAttribute(HTML.Attribute.TITLE); + if (media == null) + media = "all"; + else + media = media.toLowerCase(); + if (rel != null) + { + rel = rel.toLowerCase(); + if ((media.indexOf("all") != -1 + || media.indexOf("screen") != -1) + && (rel.equals("stylesheet"))) + { + String href = (String) a.getAttribute(HTML.Attribute.HREF); + URL url = null; + try + { + url = new URL(baseURL, href); + } + catch (MalformedURLException ex) + { + try + { + url = new URL(href); + } + catch (MalformedURLException ex2) + { + url = null; + } + } + if (url != null) + { + try + { + getStyleSheet().importStyleSheet(url); + } + catch (Exception ex) + { + // Don't let exceptions and runtime exceptions + // in CSS parsing disprupt the HTML parsing + // process. But inform the user/developer + // on the console about it. + ex.printStackTrace(); + } + } + } + } + } + } + + } + + class MapAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + throws NotImplementedException + { + // FIXME: Implement. + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + throws NotImplementedException + { + // FIXME: Implement. + } + } + + class MetaAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + throws NotImplementedException + { + // FIXME: Implement. + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + throws NotImplementedException + { + // FIXME: Implement. + } + } + + class StyleAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + { + inStyleTag = true; + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + { + inStyleTag = false; + } + } + + class TitleAction extends TagAction + { + /** + * This method is called when a start tag is seen for one of the types + * of tags associated with this Action. + */ + public void start(HTML.Tag t, MutableAttributeSet a) + throws NotImplementedException + { + // FIXME: Implement. + } + + /** + * Called when an end tag is seen for one of the types of tags associated + * with this Action. + */ + public void end(HTML.Tag t) + throws NotImplementedException + { + // FIXME: Implement. + } + } + + public HTMLReader(int offset) + { + this (offset, 0, 0, null); + } + + public HTMLReader(int offset, int popDepth, int pushDepth, + HTML.Tag insertTag) + { + this.insertTag = insertTag; + this.offset = offset; + this.popDepth = popDepth; + this.pushDepth = pushDepth; + threshold = getTokenThreshold(); + initTags(); + } + + void initTags() + { + tagToAction = new HashMap(72); + CharacterAction characterAction = new CharacterAction(); + HiddenAction hiddenAction = new HiddenAction(); + AreaAction areaAction = new AreaAction(); + BaseAction baseAction = new BaseAction(); + BlockAction blockAction = new BlockAction(); + SpecialAction specialAction = new SpecialAction(); + ParagraphAction paragraphAction = new ParagraphAction(); + HeadAction headAction = new HeadAction(); + FormAction formAction = new FormAction(); + IsindexAction isindexAction = new IsindexAction(); + LinkAction linkAction = new LinkAction(); + MapAction mapAction = new MapAction(); + PreAction preAction = new PreAction(); + MetaAction metaAction = new MetaAction(); + StyleAction styleAction = new StyleAction(); + TitleAction titleAction = new TitleAction(); + + ConvertAction convertAction = new ConvertAction(); + tagToAction.put(HTML.Tag.A, characterAction); + tagToAction.put(HTML.Tag.ADDRESS, characterAction); + tagToAction.put(HTML.Tag.APPLET, hiddenAction); + tagToAction.put(HTML.Tag.AREA, areaAction); + tagToAction.put(HTML.Tag.B, characterAction); + tagToAction.put(HTML.Tag.BASE, baseAction); + tagToAction.put(HTML.Tag.BASEFONT, characterAction); + tagToAction.put(HTML.Tag.BIG, characterAction); + tagToAction.put(HTML.Tag.BLOCKQUOTE, blockAction); + tagToAction.put(HTML.Tag.BODY, blockAction); + tagToAction.put(HTML.Tag.BR, specialAction); + tagToAction.put(HTML.Tag.CAPTION, blockAction); + tagToAction.put(HTML.Tag.CENTER, blockAction); + tagToAction.put(HTML.Tag.CITE, characterAction); + tagToAction.put(HTML.Tag.CODE, characterAction); + tagToAction.put(HTML.Tag.DD, blockAction); + tagToAction.put(HTML.Tag.DFN, characterAction); + tagToAction.put(HTML.Tag.DIR, blockAction); + tagToAction.put(HTML.Tag.DIV, blockAction); + tagToAction.put(HTML.Tag.DL, blockAction); + tagToAction.put(HTML.Tag.DT, paragraphAction); + tagToAction.put(HTML.Tag.EM, characterAction); + tagToAction.put(HTML.Tag.FONT, convertAction); + tagToAction.put(HTML.Tag.FORM, new FormTagAction()); + tagToAction.put(HTML.Tag.FRAME, specialAction); + tagToAction.put(HTML.Tag.FRAMESET, blockAction); + tagToAction.put(HTML.Tag.H1, paragraphAction); + tagToAction.put(HTML.Tag.H2, paragraphAction); + tagToAction.put(HTML.Tag.H3, paragraphAction); + tagToAction.put(HTML.Tag.H4, paragraphAction); + tagToAction.put(HTML.Tag.H5, paragraphAction); + tagToAction.put(HTML.Tag.H6, paragraphAction); + tagToAction.put(HTML.Tag.HEAD, headAction); + tagToAction.put(HTML.Tag.HR, specialAction); + tagToAction.put(HTML.Tag.HTML, blockAction); + tagToAction.put(HTML.Tag.I, characterAction); + tagToAction.put(HTML.Tag.IMG, specialAction); + tagToAction.put(HTML.Tag.INPUT, formAction); + tagToAction.put(HTML.Tag.ISINDEX, isindexAction); + tagToAction.put(HTML.Tag.KBD, characterAction); + tagToAction.put(HTML.Tag.LI, blockAction); + tagToAction.put(HTML.Tag.LINK, linkAction); + tagToAction.put(HTML.Tag.MAP, mapAction); + tagToAction.put(HTML.Tag.MENU, blockAction); + tagToAction.put(HTML.Tag.META, metaAction); + tagToAction.put(HTML.Tag.NOFRAMES, blockAction); + tagToAction.put(HTML.Tag.OBJECT, specialAction); + tagToAction.put(HTML.Tag.OL, blockAction); + tagToAction.put(HTML.Tag.OPTION, formAction); + tagToAction.put(HTML.Tag.P, paragraphAction); + tagToAction.put(HTML.Tag.PARAM, hiddenAction); + tagToAction.put(HTML.Tag.PRE, preAction); + tagToAction.put(HTML.Tag.SAMP, characterAction); + tagToAction.put(HTML.Tag.SCRIPT, hiddenAction); + tagToAction.put(HTML.Tag.SELECT, formAction); + tagToAction.put(HTML.Tag.SMALL, characterAction); + tagToAction.put(HTML.Tag.STRIKE, characterAction); + tagToAction.put(HTML.Tag.S, characterAction); + tagToAction.put(HTML.Tag.STRONG, characterAction); + tagToAction.put(HTML.Tag.STYLE, styleAction); + tagToAction.put(HTML.Tag.SUB, characterAction); + tagToAction.put(HTML.Tag.SUP, characterAction); + tagToAction.put(HTML.Tag.TABLE, blockAction); + tagToAction.put(HTML.Tag.TD, blockAction); + tagToAction.put(HTML.Tag.TEXTAREA, formAction); + tagToAction.put(HTML.Tag.TH, blockAction); + tagToAction.put(HTML.Tag.TITLE, titleAction); + tagToAction.put(HTML.Tag.TR, blockAction); + tagToAction.put(HTML.Tag.TT, characterAction); + tagToAction.put(HTML.Tag.U, characterAction); + tagToAction.put(HTML.Tag.UL, blockAction); + tagToAction.put(HTML.Tag.VAR, characterAction); + } + + /** + * Pushes the current character style onto the stack. + * + */ + protected void pushCharacterStyle() + { + charAttrStack.push(charAttr.copyAttributes()); + } + + /** + * Pops a character style off of the stack and uses it as the + * current character style. + * + */ + protected void popCharacterStyle() + { + if (!charAttrStack.isEmpty()) + charAttr = (MutableAttributeSet) charAttrStack.pop(); + } + + /** + * Registers a given tag with a given Action. All of the well-known tags + * are registered by default, but this method can change their behaviour + * or add support for custom or currently unsupported tags. + * + * @param t the Tag to register + * @param a the Action for the Tag + */ + protected void registerTag(HTML.Tag t, HTMLDocument.HTMLReader.TagAction a) + { + tagToAction.put (t, a); + } + + /** + * This is the last method called on the HTMLReader, allowing any pending + * changes to be flushed to the HTMLDocument. + */ + public void flush() throws BadLocationException + { + flushImpl(); + } + + /** + * Flushes the buffer and handle partial inserts. + * + */ + private void flushImpl() + throws BadLocationException + { + int oldLen = getLength(); + int size = parseBuffer.size(); + ElementSpec[] elems = new ElementSpec[size]; + parseBuffer.copyInto(elems); + if (oldLen == 0) + create(elems); + else + insert(offset, elems); + parseBuffer.removeAllElements(); + offset += getLength() - oldLen; + } + + /** + * This method is called by the parser to indicate a block of + * text was encountered. Should insert the text appropriately. + * + * @param data the text that was inserted + * @param pos the position at which the text was inserted + */ + public void handleText(char[] data, int pos) + { + if (shouldInsert() && data != null && data.length > 0) + { + if (inTextArea) + textAreaContent(data); + else if (inPreTag) + preContent(data); + else if (option != null) + option.setLabel(new String(data)); + else if (inStyleTag) + { + if (styles == null) + styles = new ArrayList(); + styles.add(new String(data)); + } + else + addContent(data, 0, data.length); + + } + } + + /** + * Checks if the HTML tag should be inserted. The tags before insert tag (if + * specified) are not inserted. Also, the tags after the end of the html are + * not inserted. + * + * @return true if the tag should be inserted, false otherwise. + */ + private boolean shouldInsert() + { + return ! endHTMLEncountered + && (insertTagEncountered || insertTag == null); + } + + /** + * This method is called by the parser and should route the call to the + * proper handler for the tag. + * + * @param t the HTML.Tag + * @param a the attribute set + * @param pos the position at which the tag was encountered + */ + public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos) + { + if (t == insertTag) + insertTagEncountered = true; + + if (shouldInsert()) + { + TagAction action = (TagAction) tagToAction.get(t); + if (action != null) + action.start(t, a); + } + } + + /** + * This method called by parser to handle a comment block. + * + * @param data the comment + * @param pos the position at which the comment was encountered + */ + public void handleComment(char[] data, int pos) + { + if (shouldInsert()) + { + TagAction action = (TagAction) tagToAction.get(HTML.Tag.COMMENT); + if (action != null) + { + action.start(HTML.Tag.COMMENT, new SimpleAttributeSet()); + action.end(HTML.Tag.COMMENT); + } + } + } + + /** + * This method is called by the parser and should route the call to the + * proper handler for the tag. + * + * @param t the HTML.Tag + * @param pos the position at which the tag was encountered + */ + public void handleEndTag(HTML.Tag t, int pos) + { + if (shouldInsert()) + { + // If this is the tag we need to stop calling the Actions + if (t == HTML.Tag.HTML) + endHTMLEncountered = true; + + TagAction action = (TagAction) tagToAction.get(t); + if (action != null) + action.end(t); + } + } + + /** + * This is a callback from the parser that should be routed to the + * appropriate handler for the tag. + * + * @param t the HTML.Tag that was encountered + * @param a the attribute set + * @param pos the position at which the tag was encountered + */ + public void handleSimpleTag(HTML.Tag t, MutableAttributeSet a, int pos) + { + if (t == insertTag) + insertTagEncountered = true; + + if (shouldInsert()) + { + TagAction action = (TagAction) tagToAction.get(t); + if (action != null) + { + action.start(t, a); + action.end(t); + } + } + } + + /** + * This is invoked after the stream has been parsed but before it has been + * flushed. + * + * @param eol one of \n, \r, or \r\n, whichever was encountered the most in + * parsing the stream + * @since 1.3 + */ + public void handleEndOfLineString(String eol) + { + // FIXME: Implement. + } + + /** + * Adds the given text to the textarea document. Called only when we are + * within a textarea. + * + * @param data the text to add to the textarea + */ + protected void textAreaContent(char[] data) + { + try + { + int offset = textAreaDocument.getLength(); + String text = new String(data); + textAreaDocument.setInitialText(text); + textAreaDocument.insertString(offset, text, 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)
+    {
+      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);
+        }
+    }
+
+    /**
+     * Instructs the parse buffer to create a block element with the given
+     * attributes.
+     *
+     * @param t the tag that requires opening a new block
+     * @param attr the attribute set for the new block
+     */
+    protected void blockOpen(HTML.Tag t, MutableAttributeSet attr)
+    {
+      if (inImpliedParagraph())
+        blockClose(HTML.Tag.IMPLIED);
+
+      // Push the new tag on top of the stack.
+      parseStack.push(t);
+
+      DefaultStyledDocument.ElementSpec element;
+
+      AbstractDocument.AttributeContext ctx = getAttributeContext();
+      AttributeSet copy = attr.copyAttributes();
+      copy = ctx.addAttribute(copy, StyleConstants.NameAttribute, t);
+      element = new DefaultStyledDocument.ElementSpec(copy,
+                               DefaultStyledDocument.ElementSpec.StartTagType);
+      parseBuffer.addElement(element);
+    }
+
+    /**
+     * Returns true when we are currently inside a paragraph, either
+     * a real one or an implied, false otherwise.
+     *
+     * @return
+     */
+    private boolean inParagraph()
+    {
+      boolean inParagraph = false;
+      if (! parseStack.isEmpty())
+        {
+          HTML.Tag top = parseStack.peek();
+          inParagraph = top == HTML.Tag.P || top == HTML.Tag.IMPLIED;
+        }
+      return inParagraph;
+    }
+
+    private boolean inImpliedParagraph()
+    {
+      boolean inParagraph = false;
+      if (! parseStack.isEmpty())
+        {
+          HTML.Tag top = parseStack.peek();
+          inParagraph = top == HTML.Tag.IMPLIED;
+        }
+      return inParagraph;
+    }
+
+    /**
+     * Instructs the parse buffer to close the block element associated with
+     * the given HTML.Tag
+     *
+     * @param t the HTML.Tag that is closing its block
+     */
+    protected void blockClose(HTML.Tag t)
+    {
+      DefaultStyledDocument.ElementSpec element;
+
+      if (inImpliedParagraph() && t != HTML.Tag.IMPLIED)
+        blockClose(HTML.Tag.IMPLIED);
+
+      // Pull the token from the stack.
+      if (! parseStack.isEmpty()) // Just to be sure.
+        parseStack.pop();
+
+      // If the previous tag is a start tag then we insert a synthetic
+      // content tag.
+      DefaultStyledDocument.ElementSpec prev;
+      prev = parseBuffer.size() > 0 ? (DefaultStyledDocument.ElementSpec)
+                                parseBuffer.get(parseBuffer.size() - 1) : null;
+      if (prev != null &&
+          prev.getType() == DefaultStyledDocument.ElementSpec.StartTagType)
+        {
+          addContent(new char[]{' '}, 0, 1);
+        }
+
+      element = new DefaultStyledDocument.ElementSpec(null,
+                                DefaultStyledDocument.ElementSpec.EndTagType);
+      parseBuffer.addElement(element);
+    }
+
+    /**
+     * Adds text to the appropriate context using the current character
+     * attribute set.
+     *
+     * @param data the text to add
+     * @param offs the offset at which to add it
+     * @param length the length of the text to add
+     */
+    protected void addContent(char[] data, int offs, int length)
+    {
+      addContent(data, offs, length, true);
+    }
+
+    /**
+     * Adds text to the appropriate context using the current character
+     * attribute set, and possibly generating an IMPLIED Tag if necessary.
+     *
+     * @param data the text to add
+     * @param offs the offset at which to add it
+     * @param length the length of the text to add
+     * @param generateImpliedPIfNecessary whether or not we should generate
+     * an HTML.Tag.IMPLIED tag if necessary
+     */
+    protected void addContent(char[] data, int offs, int length,
+                              boolean generateImpliedPIfNecessary)
+    {
+      if (generateImpliedPIfNecessary && ! inParagraph())
+        {
+          blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet());
+        }
+
+      AbstractDocument.AttributeContext ctx = getAttributeContext();
+      DefaultStyledDocument.ElementSpec element;
+      AttributeSet attributes = null;
+
+      // Copy the attribute set, don't use the same object because
+      // it may change
+      if (charAttr != null)
+        attributes = charAttr.copyAttributes();
+      else
+        attributes = ctx.getEmptySet();
+      attributes = ctx.addAttribute(attributes, StyleConstants.NameAttribute,
+                                    HTML.Tag.CONTENT);
+      element = new DefaultStyledDocument.ElementSpec(attributes,
+                                DefaultStyledDocument.ElementSpec.ContentType,
+                                data, offs, length);
+
+      // Add the element to the buffer
+      parseBuffer.addElement(element);
+
+      if (parseBuffer.size() > threshold)
+        {
+          if (threshold <= MAX_THRESHOLD)
+            threshold *= GROW_THRESHOLD;
+          try
+            {
+              flushImpl();
+            }
+          catch (BadLocationException ble)
+            {
+              // TODO: what to do here?
+            }
+        }
+    }
+
+    /**
+     * Adds content that is specified in the attribute set.
+     *
+     * @param t the HTML.Tag
+     * @param a the attribute set specifying the special content
+     */
+    protected void addSpecialElement(HTML.Tag t, MutableAttributeSet a)
+    {
+      if (t != HTML.Tag.FRAME && ! inParagraph())
+        {
+          blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet());
+        }
+
+      a.addAttribute(StyleConstants.NameAttribute, t);
+
+      // The two spaces are required because some special elements like HR
+      // must be broken. At least two characters are needed to break into the
+      // two parts.
+      DefaultStyledDocument.ElementSpec spec =
+        new DefaultStyledDocument.ElementSpec(a.copyAttributes(),
+          DefaultStyledDocument.ElementSpec.ContentType,
+          new char[] {' '}, 0, 1 );
+      parseBuffer.add(spec);
+    }
+
+  }
+
+  /**
+   * Gets the reader for the parser to use when loading the document with HTML.
+   *
+   * @param pos - the starting position
+   * @return - the reader
+   */
+  public HTMLEditorKit.ParserCallback getReader(int pos)
+  {
+    return new HTMLReader(pos);
+  }
+
+  /**
+   * Gets the reader for the parser to use when loading the document with HTML.
+   *
+   * @param pos - the starting position
+   * @param popDepth - the number of EndTagTypes to generate before inserting
+   * @param pushDepth - the number of StartTagTypes with a direction
+   * of JoinNextDirection that should be generated before inserting,
+   * but after the end tags have been generated.
+   * @param insertTag - the first tag to start inserting into document
+   * @return - the reader
+   */
+  public HTMLEditorKit.ParserCallback getReader(int pos,
+                                                int popDepth,
+                                                int pushDepth,
+                                                HTML.Tag insertTag)
+  {
+    return new HTMLReader(pos, popDepth, pushDepth, insertTag);
+  }
+
+  /**
+   * Gets the reader for the parser to use when inserting the HTML fragment into
+   * the document. Checks if the parser is present, sets the parent in the
+   * element stack and removes any actions for BODY (it can be only one body in
+   * a HTMLDocument).
+   *
+   * @param pos - the starting position
+   * @param popDepth - the number of EndTagTypes to generate before inserting
+   * @param pushDepth - the number of StartTagTypes with a direction of
+   *          JoinNextDirection that should be generated before inserting, but
+   *          after the end tags have been generated.
+   * @param insertTag - the first tag to start inserting into document
+   * @param parent the element that will be the parent in the document. HTML
+   *          parsing includes checks for the parent, so it must be available.
+   * @return - the reader
+   * @throws IllegalStateException if the parsert is not set.
+   */
+  public HTMLEditorKit.ParserCallback getInsertingReader(int pos, int popDepth,
+                                                         int pushDepth,
+                                                         HTML.Tag insertTag,
+                                                         final Element parent)
+      throws IllegalStateException
+  {
+    if (parser == null)
+      throw new IllegalStateException("Parser has not been set");
+
+    HTMLReader reader = new HTMLReader(pos, popDepth, pushDepth, insertTag)
+    {
+      /**
+       * Ignore BODY.
+       */
+      public void handleStartTag(HTML.Tag t, MutableAttributeSet a, int pos)
+      {
+        if (t != HTML.Tag.BODY)
+          super.handleStartTag(t, a, pos);
+      }
+
+      /**
+       * Ignore BODY.
+       */
+      public void handleEndTag(HTML.Tag t, int pos)
+      {
+        if (t != HTML.Tag.BODY)
+          super.handleEndTag(t, pos);
+      }
+    };
+
+    return reader;
+  }
+
+  /**
+   * Gets the child element that contains the attribute with the value or null.
+   * Not thread-safe.
+   *
+   * @param e - the element to begin search at
+   * @param attribute - the desired attribute
+   * @param value - the desired value
+   * @return the element found with the attribute and value specified or null if
+   *         it is not found.
+   */
+  public Element getElement(Element e, Object attribute, Object value)
+  {
+    if (e != null)
+      {
+        if (e.getAttributes().containsAttribute(attribute, value))
+          return e;
+
+        int count = e.getElementCount();
+        for (int j = 0; j < count; j++)
+          {
+            Element child = e.getElement(j);
+            if (child.getAttributes().containsAttribute(attribute, value))
+              return child;
+
+            Element grandChild = getElement(child, attribute, value);
+            if (grandChild != null)
+              return grandChild;
+          }
+      }
+    return null;
+  }
+
+  /**
+   * Returns the element that has the given id Attribute (for instance, <p id
+   * ='my paragraph >'). If it is not found, null is returned. The HTML tag,
+   * having this attribute, is not checked by this method and can be any. The
+   * method is not thread-safe.
+   *
+   * @param attrId - the value of the attribute id to look for
+   * @return the element that has the given id.
+   */
+  public Element getElement(String attrId)
+  {
+    return getElement(getDefaultRootElement(), HTML.Attribute.ID,
+                      attrId);
+  }
+
+  /**
+   * Replaces the children of the given element with the contents of
+   * the string. The document must have an HTMLEditorKit.Parser set.
+   * This will be seen as at least two events, n inserts followed by a remove.
+   *
+   * @param elem - the brance element whose children will be replaced
+   * @param htmlText - the string to be parsed and assigned to element.
+   * @throws BadLocationException
+   * @throws IOException
+   * @throws IllegalArgumentException - if elem is a leaf
+   * @throws IllegalStateException - if an HTMLEditorKit.Parser has not been set
+   */
+  public void setInnerHTML(Element elem, String htmlText)
+    throws BadLocationException, IOException
+  {
+    if (elem.isLeaf())
+      throw new IllegalArgumentException("Element is a leaf");
+
+    int start = elem.getStartOffset();
+    int end = elem.getEndOffset();
+
+    HTMLEditorKit.ParserCallback reader = getInsertingReader(
+      end, 0, 0, HTML.Tag.BODY, elem);
+
+    // TODO charset
+    getParser().parse(new StringReader(htmlText), reader, true);
+
+    // Remove the previous content
+    remove(start, end - start);
+  }
+
+  /**
+   * Replaces the given element in the parent with the string. When replacing a
+   * leaf, this will attempt to make sure there is a newline present if one is
+   * needed. This may result in an additional element being inserted. This will
+   * be seen as at least two events, n inserts followed by a remove. The
+   * HTMLEditorKit.Parser must be set.
+   *
+   * @param elem - the branch element whose parent will be replaced
+   * @param htmlText - the string to be parsed and assigned to elem
+   * @throws BadLocationException
+   * @throws IOException
+   * @throws IllegalStateException - if parser is not set
+   */
+public void setOuterHTML(Element elem, String htmlText)
+      throws BadLocationException, IOException
+  {
+    // Remove the current element:
+    int start = elem.getStartOffset();
+    int end = elem.getEndOffset();
+
+    remove(start, end-start);
+
+    HTMLEditorKit.ParserCallback reader = getInsertingReader(
+      start, 0, 0, HTML.Tag.BODY, elem);
+
+    // TODO charset
+    getParser().parse(new StringReader(htmlText), reader, true);
+  }
+
+  /**
+   * Inserts the string before the start of the given element. The parser must
+   * be set.
+   *
+   * @param elem - the element to be the root for the new text.
+   * @param htmlText - the string to be parsed and assigned to elem
+   * @throws BadLocationException
+   * @throws IOException
+   * @throws IllegalStateException - if parser has not been set
+   */
+  public void insertBeforeStart(Element elem, String htmlText)
+      throws BadLocationException, IOException
+  {
+    HTMLEditorKit.ParserCallback reader = getInsertingReader(
+      elem.getStartOffset(), 0, 0, HTML.Tag.BODY, elem);
+
+    // TODO charset
+    getParser().parse(new StringReader(htmlText), reader, true);
+  }
+
+  /**
+   * Inserts the string at the end of the element. If elem's children are
+   * leaves, and the character at elem.getEndOffset() - 1 is a newline, then it
+   * will be inserted before the newline. The parser must be set.
+   *
+   * @param elem - the element to be the root for the new text
+   * @param htmlText - the text to insert
+   * @throws BadLocationException
+   * @throws IOException
+   * @throws IllegalStateException - if parser is not set
+   */
+  public void insertBeforeEnd(Element elem, String htmlText)
+      throws BadLocationException, IOException
+  {
+    HTMLEditorKit.ParserCallback reader = getInsertingReader(
+      elem.getEndOffset(), 0, 0, HTML.Tag.BODY, elem);
+
+    // TODO charset
+    getParser().parse(new StringReader(htmlText), reader, true);
+
+  }
+
+  /**
+   * Inserts the string after the end of the given element.
+   * The parser must be set.
+   *
+   * @param elem - the element to be the root for the new text
+   * @param htmlText - the text to insert
+   * @throws BadLocationException
+   * @throws IOException
+   * @throws IllegalStateException - if parser is not set
+   */
+  public void insertAfterEnd(Element elem, String htmlText)
+      throws BadLocationException, IOException
+  {
+    HTMLEditorKit.ParserCallback reader = getInsertingReader(
+      elem.getEndOffset(), 0, 0, HTML.Tag.BODY, elem);
+
+    // TODO charset
+    getParser().parse(new StringReader(htmlText), reader, true);
+  }
+
+  /**
+   * Inserts the string at the start of the element.
+   * The parser must be set.
+   *
+   * @param elem - the element to be the root for the new text
+   * @param htmlText - the text to insert
+   * @throws BadLocationException
+   * @throws IOException
+   * @throws IllegalStateException - if parser is not set
+   */
+  public void insertAfterStart(Element elem, String htmlText)
+      throws BadLocationException, IOException
+  {
+    HTMLEditorKit.ParserCallback reader = getInsertingReader(
+      elem.getStartOffset(), 0, 0, HTML.Tag.BODY, elem);
+
+    // TODO charset
+    getParser().parse(new StringReader(htmlText), reader, true);
+  }
+
+  /**
+   * Overridden to tag content with the synthetic HTML.Tag.CONTENT
+   * tag.
+   */
+  protected void insertUpdate(DefaultDocumentEvent evt, AttributeSet att)
+  {
+    if (att == null)
+      {
+        SimpleAttributeSet sas = new SimpleAttributeSet();
+        sas.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT);
+        att = sas;
+      }
+    super.insertUpdate(evt, att);
+  }
+
+  /**
+   * Returns true when this document is inside a frame,
+   * false otherwise.
+   *
+   * @return true when this document is inside a frame,
+   *         false otherwise
+   */
+  boolean isFrameDocument()
+  {
+    return frameDocument;
+  }
+
+  /**
+   * Set true when this document is inside a frame,
+   * false otherwise.
+   *
+   * @param frameDoc true when this document is inside a frame,
+   *                 false otherwise
+   */
+  void setFrameDocument(boolean frameDoc)
+  {
+    frameDocument = frameDoc;
+  }
+
+  /**
+   * Returns the target that is specified in the base tag, if this is the case.
+   *
+   * @return the target that is specified in the base tag, if this is the case
+   */
+  String getBaseTarget()
+  {
+    return baseTarget;
+  }
+
+  /**
+   * Updates the A tag's pseudo class value in response to a hyperlink
+   * action.
+   *
+   * @param el the corresponding element
+   * @param value the new value
+   */
+  void updateSpecialClass(Element el, HTML.Attribute cl, String value)
+  {
+    try
+    {
+      writeLock();
+      DefaultDocumentEvent ev =
+        new DefaultDocumentEvent(el.getStartOffset(), 1,
+                                 DocumentEvent.EventType.CHANGE);
+      AttributeSet elAtts = el.getAttributes();
+      AttributeSet anchorAtts = (AttributeSet) elAtts.getAttribute(HTML.Tag.A);
+      if (anchorAtts != null)
+        {
+          AttributeSet copy = elAtts.copyAttributes();
+          StyleSheet ss = getStyleSheet();
+          if (value != null)
+            {
+              anchorAtts = ss.addAttribute(anchorAtts, cl, value);
+            }
+          else
+            {
+              anchorAtts = ss.removeAttribute(anchorAtts, cl);
+            }
+          MutableAttributeSet matts = (MutableAttributeSet) elAtts;
+          ev.addEdit(new AttributeUndoableEdit(el, copy, false));
+          matts.removeAttribute(HTML.Tag.A);
+          matts.addAttribute(HTML.Tag.A, anchorAtts);
+          ev.end();
+          fireChangedUpdate(ev);
+          fireUndoableEditUpdate(new UndoableEditEvent(this, ev));
+        }
+    }
+  finally
+    {
+      writeUnlock();
+    }
+  }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java
new file mode 100644
index 000000000..e3505d3c2
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/HTMLEditorKit.java
@@ -0,0 +1,1520 @@
+/* HTMLEditorKit.java --
+   Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+
+import java.awt.event.ActionEvent;
+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;
+import java.io.InputStreamReader;
+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;
+import javax.swing.text.ViewFactory;
+import javax.swing.text.html.parser.ParserDelegator;
+
+/* Move these imports here after javax.swing.text.html to make it compile
+   with jikes.  */
+import gnu.javax.swing.text.html.parser.GnuParserDelegator;
+import gnu.javax.swing.text.html.parser.HTML_401F;
+
+/**
+ * @author Lillian Angel (langel at redhat dot com)
+ */
+public class HTMLEditorKit
+  extends StyledEditorKit
+  implements Serializable, Cloneable, Accessible
+{
+
+  /**
+   * Fires the hyperlink events on the associated component
+   * when needed.
+   */
+  public static class LinkController
+    extends MouseAdapter
+    implements MouseMotionListener, Serializable
+    {
+
+      /**
+       * The element of the last anchor tag.
+       */
+      private Element lastAnchorElement;
+
+      /**
+       * Constructor
+       */
+      public LinkController()
+      {
+        super();
+      }
+
+      /**
+       * Dispatched when the mouse is clicked. If the component
+       * is read-only, then the clicked event is used to drive an
+       * attempt to follow the reference specified by a link
+       *
+       * @param e - the mouse event
+       */
+      public void mouseClicked(MouseEvent e)
+      {
+        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());
+          }
+      }
+
+      /**
+       * Dispatched when the mouse is dragged on a component.
+       *
+       * @param e - the mouse event.
+       */
+      public void mouseDragged(MouseEvent e)
+      {
+        // Nothing to do here.
+      }
+
+      /**
+       * Dispatched when the mouse cursor has moved into the component.
+       *
+       * @param e - the mouse event.
+       */
+      public void mouseMoved(MouseEvent e)
+      {
+        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)
+                      {
+                        if (el != lastAnchorElement)
+                          {
+                            if (lastAnchorElement != null)
+                              htmlDoc.updateSpecialClass(lastAnchorElement,
+                                                  HTML.Attribute.DYNAMIC_CLASS,
+                                                  null);
+                            lastAnchorElement = el;
+                            htmlDoc.updateSpecialClass(el,
+                                                  HTML.Attribute.DYNAMIC_CLASS,
+                                                  "hover");
+                          }
+                        newCursor = kit.getLinkCursor();
+                      }
+                    else
+                      {
+                        if (lastAnchorElement != null)
+                          htmlDoc.updateSpecialClass(lastAnchorElement,
+                                              HTML.Attribute.DYNAMIC_CLASS,
+                                              null);
+                        lastAnchorElement = null;
+                      }
+                  }
+                else
+                  {
+                    if (lastAnchorElement != null)
+                      htmlDoc.updateSpecialClass(lastAnchorElement,
+                                          HTML.Attribute.DYNAMIC_CLASS,
+                                          null);
+                    lastAnchorElement = null;
+                  }
+                if (editor.getCursor() != newCursor)
+                  {
+                    editor.setCursor(newCursor);
+                  }
+              }
+          }
+      }
+
+      /**
+       * If the given position represents a link, then linkActivated is called
+       * on the JEditorPane.
+       *
+       * @param pos the position
+       * @param editor the editor pane
+       */
+      protected void activateLink(int pos, JEditorPane editor)
+      {
+        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);
+                htmlDoc.updateSpecialClass(el, HTML.Attribute.PSEUDO_CLASS,
+                                           "visited");
+              }
+            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;
+          }
+        HyperlinkEvent ev;
+        if (doc.isFrameDocument())
+          {
+            String target = null;
+            if (anchor != null)
+              target = (String) anchor.getAttribute(HTML.Attribute.TARGET);
+            if (target == null || target.equals(""))
+              target = doc.getBaseTarget();
+            if (target == null || target.equals(""))
+              target = "_self";
+            ev = new HTMLFrameHyperlinkEvent(editor,
+                                            HyperlinkEvent.EventType.ACTIVATED,
+                                            url, href, el, target);
+          }
+        else
+          {
+            ev = new HyperlinkEvent(editor, HyperlinkEvent.EventType.ACTIVATED,
+                                    url, href, el);
+          }
+        return ev;
+      }
+    }
+
+  /**
+   * This class is used to insert a string of HTML into an existing
+   * document. At least 2 HTML.Tags need to be supplied. The first Tag (parentTag)
+   * identifies the parent in the document to add the elements to. The second, (addTag),
+   * identifies that the first tag should be added to the document as seen in the string.
+   * The parser will generate all appropriate (opening/closing tags_ even if they are not
+   * in the HTML string passed in.
+   */
+  public static class InsertHTMLTextAction
+    extends HTMLTextAction
+    {
+
+      /**
+       * Tag in HTML to start adding tags from.
+       */
+      protected HTML.Tag addTag;
+
+      /**
+       * Alternate tag in HTML to start adding tags from if parentTag is
+       * not found and alternateParentTag is not found.
+       */
+      protected HTML.Tag alternateAddTag;
+
+      /**
+       * Alternate tag to check if parentTag is not found.
+       */
+      protected HTML.Tag alternateParentTag;
+
+      /**
+       * HTML to insert.
+       */
+      protected String html;
+
+      /**
+       * Tag to check for in the document.
+       */
+      protected HTML.Tag parentTag;
+
+      /**
+       * Initializes all fields.
+       *
+       * @param name - the name of the document.
+       * @param html - the html to insert
+       * @param parentTag - the parent tag to check for
+       * @param addTag - the tag to start adding from
+       */
+      public InsertHTMLTextAction(String name, String html,
+                                  HTML.Tag parentTag, HTML.Tag addTag)
+      {
+        this(name, html, parentTag, addTag, null, null);
+      }
+
+      /**
+       * Initializes all fields and calls super
+       *
+       * @param name - the name of the document.
+       * @param html - the html to insert
+       * @param parentTag - the parent tag to check for
+       * @param addTag - the tag to start adding from
+       * @param alternateParentTag - the alternate parent tag
+       * @param alternateAddTag - the alternate add tag
+       */
+      public InsertHTMLTextAction(String name, String html, HTML.Tag parentTag,
+                                  HTML.Tag addTag, HTML.Tag alternateParentTag,
+                                  HTML.Tag alternateAddTag)
+      {
+        super(name);
+        // Fields are for easy access when the action is applied to an actual
+        // document.
+        this.html = html;
+        this.parentTag = parentTag;
+        this.addTag = addTag;
+        this.alternateParentTag = alternateParentTag;
+        this.alternateAddTag = alternateAddTag;
+      }
+
+      /**
+       * HTMLEditorKit.insertHTML is called. If an exception is
+       * thrown, it is wrapped in a RuntimeException and thrown.
+       *
+       * @param editor - the editor to use to get the editorkit
+       * @param doc -
+       *          the Document to insert the HTML into.
+       * @param offset -
+       *          where to begin inserting the HTML.
+       * @param html -
+       *          the String to insert
+       * @param popDepth -
+       *          the number of ElementSpec.EndTagTypes to generate before
+       *          inserting
+       * @param pushDepth -
+       *          the number of ElementSpec.StartTagTypes with a direction of
+       *          ElementSpec.JoinNextDirection that should be generated before
+       * @param addTag -
+       *          the first tag to start inserting into document
+       */
+      protected void insertHTML(JEditorPane editor, HTMLDocument doc, int offset,
+                              String html, int popDepth, int pushDepth,
+                              HTML.Tag addTag)
+      {
+        try
+          {
+            super.getHTMLEditorKit(editor).insertHTML(doc, offset, html,
+                                                      popDepth, pushDepth, addTag);
+          }
+        catch (IOException e)
+          {
+            throw (RuntimeException) new RuntimeException("Parser is null.").initCause(e);
+          }
+        catch (BadLocationException ex)
+          {
+            throw (RuntimeException) new RuntimeException("BadLocationException: "
+                                              + offset).initCause(ex);
+          }
+      }
+
+      /**
+       * Invoked when inserting at a boundary. Determines the number of pops,
+       * and then the number of pushes that need to be performed. The it calls
+       * insertHTML.
+       *
+       * @param editor -
+       *          the editor to use to get the editorkit
+       * @param doc -
+       *          the Document to insert the HTML into.
+       * @param offset -
+       *          where to begin inserting the HTML.
+       * @param insertElement -
+       *          the element to insert
+       * @param html -
+       *          the html to insert
+       * @param parentTag -
+       *          the parent tag
+       * @param addTag -
+       *          the first tag
+       */
+      protected void insertAtBoundary(JEditorPane editor,
+                                      HTMLDocument doc, int offset,
+                                      Element insertElement,
+                                      String html, HTML.Tag parentTag,
+                                      HTML.Tag addTag)
+      {
+        insertAtBoundry(editor, doc, offset, insertElement,
+                        html, parentTag, addTag);
+      }
+
+      /**
+       * Invoked when inserting at a boundary. Determines the number of pops,
+       * and then the number of pushes that need to be performed. The it calls
+       * insertHTML.
+       *
+       * @param editor - the editor to use to get the editorkit
+       * @param doc -
+       *          the Document to insert the HTML into.
+       * @param offset -
+       *          where to begin inserting the HTML.
+       * @param insertElement - the element to insert
+       * @param html - the html to insert
+       * @param parentTag - the parent tag
+       * @param addTag - the first tag
+       *
+       * @deprecated as of v1.3, use insertAtBoundary
+       */
+      protected void insertAtBoundry(JEditorPane editor,
+                                     HTMLDocument doc,
+                                     int offset, Element insertElement,
+                                     String html, HTML.Tag parentTag,
+                                     HTML.Tag addTag)
+      {
+        Element parent = insertElement;
+        Element el;
+        // Find common parent element.
+        if (offset > 0 || insertElement == null)
+          {
+            el = doc.getDefaultRootElement();
+            while (el != null && el.getStartOffset() != offset
+                   && ! el.isLeaf())
+              el = el.getElement(el.getElementIndex(offset));
+            parent = el != null ? el.getParentElement() : null;
+          }
+        if (parent != null)
+          {
+            int pops = 0;
+            int pushes = 0;
+            if (offset == 0 && insertElement != null)
+              {
+                el = parent;
+                while (el != null && ! el.isLeaf())
+                  {
+                    el = el.getElement(el.getElementIndex(offset));
+                    pops++;
+                  }
+              }
+            else
+              {
+                el = parent;
+                offset--;
+                while (el != null && ! el.isLeaf())
+                  {
+                    el = el.getElement(el.getElementIndex(offset));
+                    pops++;
+                  }
+                el = parent;
+                offset++;
+                while (el != null && el != insertElement)
+                  {
+                    el = el.getElement(el.getElementIndex(offset));
+                    pushes++;
+                  }
+              }
+            pops = Math.max(0, pops - 1);
+            insertHTML(editor, doc, offset, html, pops, pushes, addTag);
+          }
+      }
+
+      /**
+       * Inserts the HTML.
+       *
+       * @param ae - the action performed
+       */
+      public void actionPerformed(ActionEvent ae)
+      {
+        JEditorPane source = getEditor(ae);
+        if (source != null)
+          {
+            HTMLDocument d = getHTMLDocument(source);
+            int offset = source.getSelectionStart();
+            int length = d.getLength();
+            boolean inserted = true;
+            if (! tryInsert(source, d, offset, parentTag, addTag))
+              {
+                inserted = tryInsert(source, d, offset, alternateParentTag,
+                                     alternateAddTag);
+              }
+            if (inserted)
+              adjustSelection(source, d, offset, length);
+          }
+      }
+
+      /**
+       * Tries to insert the html chunk to the specified addTag.
+       *
+       * @param pane the editor
+       * @param doc the document
+       * @param offset the offset at which to insert
+       * @param tag the tag at which to insert
+       * @param addTag the add tag
+       *
+       * @return true when the html has been inserted successfully,
+       *         false otherwise
+       */
+      private boolean tryInsert(JEditorPane pane, HTMLDocument doc, int offset,
+                                HTML.Tag tag, HTML.Tag addTag)
+      {
+        boolean inserted = false;
+        Element el = findElementMatchingTag(doc, offset, tag);
+        if (el != null && el.getStartOffset() == offset)
+          {
+            insertAtBoundary(pane, doc, offset, el, html, tag, addTag);
+            inserted = true;
+          }
+        else if (offset > 0)
+          {
+            int depth = elementCountToTag(doc, offset - 1, tag);
+            if (depth != -1)
+              {
+                insertHTML(pane, doc, offset, html, depth, 0, addTag);
+                inserted = true;
+              }
+          }
+        return inserted;
+      }
+
+      /**
+       * Adjusts the selection after an insertion has been performed.
+       *
+       * @param pane the editor pane
+       * @param doc the document
+       * @param offset the insert offset
+       * @param oldLen the old document length
+       */
+      private void adjustSelection(JEditorPane pane, HTMLDocument doc,
+                                   int offset, int oldLen)
+      {
+        int newLen = doc.getLength();
+        if (newLen != oldLen && offset < newLen)
+          {
+            if (offset > 0)
+              {
+                String text;
+                try
+                  {
+                    text = doc.getText(offset - 1, 1);
+                  }
+                catch (BadLocationException ex)
+                  {
+                    text = null;
+                  }
+                if (text != null && text.length() > 0
+                    && text.charAt(0) == '\n')
+                  {
+                    pane.select(offset, offset);
+                  }
+                else
+                  {
+                    pane.select(offset + 1, offset + 1);
+                  }
+              }
+            else
+              {
+                pane.select(1, 1);
+              }
+          }
+      }
+  }
+
+  /**
+   * Abstract Action class that helps inserting HTML into an existing document.
+   */
+  public abstract static class HTMLTextAction
+    extends StyledEditorKit.StyledTextAction
+    {
+
+      /**
+       * Constructor
+       */
+      public HTMLTextAction(String name)
+      {
+        super(name);
+      }
+
+      /**
+       * Gets the HTMLDocument from the JEditorPane.
+       *
+       * @param e - the editor pane
+       * @return the html document.
+       */
+      protected HTMLDocument getHTMLDocument(JEditorPane e)
+      {
+        Document d = e.getDocument();
+        if (d instanceof HTMLDocument)
+          return (HTMLDocument) d;
+        throw new IllegalArgumentException("Document is not a HTMLDocument.");
+      }
+
+      /**
+       * Gets the HTMLEditorKit
+       *
+       * @param e - the JEditorPane to get the HTMLEditorKit from.
+       * @return the HTMLEditorKit
+       */
+      protected HTMLEditorKit getHTMLEditorKit(JEditorPane e)
+      {
+        EditorKit d = e.getEditorKit();
+        if (d instanceof HTMLEditorKit)
+          return (HTMLEditorKit) d;
+        throw new IllegalArgumentException("EditorKit is not a HTMLEditorKit.");
+      }
+
+      /**
+       * Returns an array of Elements that contain the offset.
+       * The first elements corresponds to the roots of the doc.
+       *
+       * @param doc - the document to get the Elements from.
+       * @param offset - the offset the Elements must contain
+       * @return an array of all the elements containing the offset.
+       */
+      protected Element[] getElementsAt(HTMLDocument doc,
+                                        int offset)
+      {
+        return getElementsAt(doc.getDefaultRootElement(), offset, 0);
+      }
+
+      /**
+       * Helper function to get all elements using recursion.
+       */
+      private Element[] getElementsAt(Element root, int offset, int depth)
+      {
+        Element[] elements = null;
+        if (root != null)
+          {
+            if (root.isLeaf())
+              {
+                elements = new Element[depth + 1];
+                elements[depth] = root;
+                return elements;
+              }
+            elements = getElementsAt(root.getElement(root.getElementIndex(offset)),
+                                     offset, depth + 1);
+            elements[depth] = root;
+          }
+        return elements;
+      }
+
+      /**
+       * Returns the number of elements, starting at the deepest point, needed
+       * to get an element representing tag. -1 if no elements are found, 0 if
+       * the parent of the leaf at offset represents the tag.
+       *
+       * @param doc -
+       *          the document to search
+       * @param offset -
+       *          the offset to check
+       * @param tag -
+       *          the tag to look for
+       * @return - the number of elements needed to get an element representing
+       *         tag.
+       */
+      protected int elementCountToTag(HTMLDocument doc,
+                                      int offset, HTML.Tag tag)
+      {
+        Element root = doc.getDefaultRootElement();
+        int num = -1;
+        Element next = root.getElement(root.getElementIndex(offset));
+
+        while (!next.isLeaf())
+          {
+            num++;
+            if (next.getAttributes().
+                getAttribute(StyleConstants.NameAttribute).equals(tag))
+              return num;
+            next = next.getElement(next.getElementIndex(offset));
+          }
+        return num;
+      }
+
+      /**
+       * Gets the deepest element at offset with the
+       * matching tag.
+       *
+       * @param doc - the document to search
+       * @param offset - the offset to check for
+       * @param tag - the tag to match
+       * @return - the element that is found, null if not found.
+       */
+      protected Element findElementMatchingTag(HTMLDocument doc,
+                                               int offset, HTML.Tag tag)
+      {
+        Element element = doc.getDefaultRootElement();
+        Element tagElement = null;
+
+        while (element != null)
+          {
+            Object otag = element.getAttributes().getAttribute(
+                                     StyleConstants.NameAttribute);
+            if (otag instanceof HTML.Tag && otag.equals(tag))
+              tagElement = element;
+            element = element.getElement(element.getElementIndex(offset));
+          }
+
+        return tagElement;
+      }
+    }
+
+  /**
+   * A {@link ViewFactory} that is able to create {@link View}s for
+   * the Elements that are supported.
+   */
+  public static class HTMLFactory
+    implements ViewFactory
+  {
+
+    /**
+     * Constructor
+     */
+    public HTMLFactory()
+    {
+      // Do Nothing here.
+    }
+
+    /**
+     * Creates a {@link View} for the specified Element.
+     *
+     * @param element the Element to create a View
+     *        for
+     * @return the View for the specified Element
+     *         or null if the type of element is
+     *         not supported
+     */
+    public View create(Element element)
+    {
+      View view = null;
+      Object attr =
+        element.getAttributes().getAttribute(StyleConstants.NameAttribute);
+      if (attr instanceof HTML.Tag)
+        {
+          HTML.Tag tag = (HTML.Tag) attr;
+
+          if (tag == HTML.Tag.IMPLIED || tag == HTML.Tag.P
+              || tag == HTML.Tag.H1 || tag == HTML.Tag.H2
+              || tag == HTML.Tag.H3 || tag == HTML.Tag.H4
+              || tag == HTML.Tag.H5 || tag == HTML.Tag.H6
+              || tag == HTML.Tag.DT)
+            view = new ParagraphView(element);
+          else if (tag == HTML.Tag.LI || tag == HTML.Tag.DL
+                   || tag == HTML.Tag.DD || tag == HTML.Tag.BODY
+                   || tag == HTML.Tag.HTML || tag == HTML.Tag.CENTER
+                   || tag == HTML.Tag.DIV
+                   || tag == HTML.Tag.BLOCKQUOTE
+                   || tag == HTML.Tag.PRE
+                   || tag == HTML.Tag.FORM
+                   // Misplaced TD and TH tags get mapped as vertical block.
+                   // Note that correctly placed tags get mapped in TableView.
+                   || tag == HTML.Tag.TD || tag == HTML.Tag.TH)
+            view = new BlockView(element, View.Y_AXIS);
+          else if (tag == HTML.Tag.TR)
+            // Misplaced TR tags get mapped as horizontal blocks.
+            // Note that correctly placed tags get mapped in TableView.
+            view = new BlockView(element, View.X_AXIS);
+          else if (tag == HTML.Tag.IMG)
+            view = new ImageView(element);
+
+          else if (tag == HTML.Tag.CONTENT)
+            view = new InlineView(element);
+          else if (tag == HTML.Tag.HEAD)
+            view = new NullView(element);
+          else if (tag == HTML.Tag.TABLE)
+            view = new javax.swing.text.html.TableView(element);
+          else if (tag == HTML.Tag.HR)
+            view = new HRuleView(element);
+          else if (tag == HTML.Tag.BR)
+            view = new BRView(element);
+          else if (tag == HTML.Tag.INPUT || tag == HTML.Tag.SELECT
+                   || tag == HTML.Tag.TEXTAREA)
+            view = new FormView(element);
+
+          else if (tag == HTML.Tag.MENU || tag == HTML.Tag.DIR
+                   || tag == HTML.Tag.UL || tag == HTML.Tag.OL)
+            view = new ListView(element);
+          else if (tag == HTML.Tag.FRAMESET)
+            view = new FrameSetView(element);
+          else if (tag == HTML.Tag.FRAME)
+            view = new FrameView(element);
+          else if (tag == HTML.Tag.OBJECT)
+            view = new ObjectView(element);
+        }
+      if (view == null)
+        {
+          view = new NullView(element);
+        }
+      return view;
+    }
+  }
+
+  /**
+   * The abstract HTML parser declaration.
+   */
+  public abstract static class Parser
+  {
+    /**
+     * Parse the HTML text, calling various methods of the provided callback
+     * in response to the occurence of the corresponding HTML constructions.
+     * @param reader The reader to read the source HTML from.
+     * @param callback The callback to receive information about the parsed
+     * HTML structures
+     * @param ignoreCharSet If true, the parser ignores all charset information
+     * that may be present in HTML documents.
+     * @throws IOException, normally if the reader throws one.
+     */
+    public abstract void parse(Reader reader, ParserCallback callback,
+                               boolean ignoreCharSet) throws IOException;
+  }
+
+  /**
+   * The "hook" that receives all information about the HTML document
+   * structure while parsing it. The methods are invoked by parser
+   * and should be normally overridden.
+   */
+  public static class ParserCallback
+  {
+    /**
+     * If the tag does not occurs in the html stream directly, but
+     * is supposed by parser, the tag attribute set contains this additional
+     * attribute, having value Boolean.True.
+     */
+    public static final Object IMPLIED = "_implied_";
+
+    /**
+     * Constructor
+     */
+    public ParserCallback()
+    {
+      // Nothing to do here.
+    }
+
+    /**
+     * The parser calls this method after it finishes parsing the document.
+     */
+    public void flush() throws BadLocationException
+    {
+      // Nothing to do here.
+    }
+
+    /**
+     * Handle HTML comment, present in the given position.
+     * @param comment the comment
+     * @position the position of the comment in the text being parsed.
+     */
+    public void handleComment(char[] comment, int position)
+    {
+      // Nothing to do here.
+    }
+
+    /**
+     * Notifies about the character sequences, used to separate lines in
+     * this document. The parser calls this method after it finishes
+     * parsing the document, but before flush().
+     * @param end_of_line The "end of line sequence", one of: \r or \n or \r\n.
+     */
+    public void handleEndOfLineString(String end_of_line)
+    {
+      // Nothing to do here.
+    }
+
+    /**
+     * The method is called when the HTML closing tag ((like </table>)
+     * is found or if the parser concludes that the one should be present
+     * in the current position.
+     * @param tag The tag being handled
+     * @param position the tag position in the text being parsed.
+     */
+    public void handleEndTag(HTML.Tag tag, int position)
+    {
+      // Nothing to do here.
+    }
+
+    /**
+     * Handle the error.
+     * @param message The message, explaining the error.
+     * @param position The starting position of the fragment that has caused
+     * the error in the html document being parsed.
+     */
+    public void handleError(String message, int position)
+    {
+      // Nothing to do here.
+    }
+
+    /**
+     * Handle the tag with no content, like <br>. The method is
+     * called for the elements that, in accordance with the current DTD,
+     * has an empty content.
+     * @param tag The tag being handled.
+     * @param position The tag position in the text being parsed.
+     */
+    public void handleSimpleTag(HTML.Tag tag, MutableAttributeSet attributes,
+                                int position)
+    {
+      // Nothing to do here.
+    }
+
+    /**
+     * The method is called when the HTML opening tag ((like <table>)
+     * is found or if the parser concludes that the one should be present
+     * in the current position.
+     * @param tag The tag being handled
+     * @param position The tag position in the text being parsed
+     */
+    public void handleStartTag(HTML.Tag tag, MutableAttributeSet attributes,
+                               int position)
+    {
+      // Nothing to do here.
+    }
+
+    /**
+     * Handle the text section.
+     * @param text A section text.
+     * @param position The text position in the HTML document text being parsed.
+     */
+    public void handleText(char[] text, int position)
+    {
+      // Nothing to do here.
+    }
+  }
+
+  /**
+   * Use serialVersionUID (v1.4) for interoperability.
+   */
+  private static final long serialVersionUID = 8751997116710384592L;
+
+  /**
+   * Default cascading stylesheed file ("default.css").
+   */
+  public static final String DEFAULT_CSS = "default.css";
+
+  /**
+   * The bold action identifier.
+   */
+  public static final String BOLD_ACTION = "html-bold-action";
+
+  /**
+   * The italic action identifier.
+   */
+  public static final String ITALIC_ACTION = "html-italic-action";
+
+  /**
+   * The color action indentifier
+   * (passing the color as an argument).
+   */
+  public static final String COLOR_ACTION = "html-color-action";
+
+  /**
+   * The increase font action identifier.
+   */
+  public static final String FONT_CHANGE_BIGGER = "html-font-bigger";
+
+  /**
+   * The decrease font action identifier.
+   */
+  public static final String FONT_CHANGE_SMALLER = "html-font-smaller";
+
+  /**
+   * Align images at the bottom.
+   */
+  public static final String IMG_ALIGN_BOTTOM = "html-image-align-bottom";
+
+  /**
+   * Align images at the middle.
+   */
+  public static final String IMG_ALIGN_MIDDLE = "html-image-align-middle";
+
+  /**
+   * Align images at the top.
+   */
+  public static final String IMG_ALIGN_TOP = "html-image-align-top";
+
+  /**
+   * Align images at the border.
+   */
+  public static final String IMG_BORDER = "html-image-border";
+
+  /**
+   * The "logical style" action identifier, passing that style as parameter.
+   */
+  public static final String LOGICAL_STYLE_ACTION = "html-logical-style-action";
+
+  /**
+   * The "ident paragraph left" action.
+   */
+  public static final String PARA_INDENT_LEFT = "html-para-indent-left";
+
+  /**
+   * The "ident paragraph right" action.
+   */
+  public static final String PARA_INDENT_RIGHT = "html-para-indent-right";
+
+  /**
+   * Actions for HTML
+   */
+  private static final Action[] defaultActions =
+  {
+    new InsertHTMLTextAction("InsertTable",
+                             "
", + HTML.Tag.BODY, HTML.Tag.TABLE), + new InsertHTMLTextAction("InsertTableRow", + "
", + HTML.Tag.TABLE, HTML.Tag.TR, + HTML.Tag.BODY, HTML.Tag.TABLE), + new InsertHTMLTextAction("InsertTableCell", + "
", + HTML.Tag.TR, HTML.Tag.TD, + HTML.Tag.BODY, HTML.Tag.TABLE), + new InsertHTMLTextAction("InsertUnorderedList", + "
", + HTML.Tag.BODY, HTML.Tag.UL), + new InsertHTMLTextAction("InsertUnorderedListItem", + "
", + HTML.Tag.UL, HTML.Tag.LI, + HTML.Tag.BODY, HTML.Tag.UL), + new InsertHTMLTextAction("InsertOrderedList", + "
", + HTML.Tag.BODY, HTML.Tag.OL), + new InsertHTMLTextAction("InsertOrderedListItem", + "
", + HTML.Tag.OL, HTML.Tag.LI, + HTML.Tag.BODY, HTML.Tag.OL), + new InsertHTMLTextAction("InsertPre", + "
", HTML.Tag.BODY, HTML.Tag.PRE)
+    // TODO: The reference impl has an InsertHRAction too.
+  };
+
+  /**
+   * The current style sheet.
+   */
+  private StyleSheet styleSheet;
+
+  /**
+   * The ViewFactory for HTMLFactory.
+   */
+  HTMLFactory viewFactory;
+
+  /**
+   * The Cursor for links.
+   */
+  Cursor linkCursor;
+
+  /**
+   * The default cursor.
+   */
+  Cursor defaultCursor;
+
+  /**
+   * The parser.
+   */
+  Parser parser;
+
+  /**
+   * The mouse listener used for links.
+   */
+  private LinkController linkController;
+
+  /** The content type */
+  String contentType = "text/html";
+
+  /** The input attributes defined by default.css */
+  MutableAttributeSet inputAttributes;
+
+  /** The editor pane used. */
+  JEditorPane editorPane;
+
+  /**
+   * Whether or not the editor kit handles form submissions.
+   *
+   * @see #isAutoFormSubmission()
+   * @see #setAutoFormSubmission(boolean)
+   */
+  private boolean autoFormSubmission;
+
+  /**
+   * Constructs an HTMLEditorKit, creates a StyleContext, and loads the style sheet.
+   */
+  public HTMLEditorKit()
+  {
+    linkController = new LinkController();
+    autoFormSubmission = true;
+  }
+
+  /**
+   * Gets a factory suitable for producing views of any
+   * models that are produced by this kit.
+   *
+   * @return the view factory suitable for producing views.
+   */
+  public ViewFactory getViewFactory()
+  {
+    if (viewFactory == null)
+      viewFactory = new HTMLFactory();
+    return viewFactory;
+  }
+
+  /**
+   * Create a text storage model for this type of editor.
+   *
+   * @return the model
+   */
+  public Document createDefaultDocument()
+  {
+    // Protect the shared stylesheet.
+    StyleSheet styleSheet = getStyleSheet();
+    StyleSheet ss = new StyleSheet();
+    ss.addStyleSheet(styleSheet);
+
+    HTMLDocument document = new HTMLDocument(ss);
+    document.setParser(getParser());
+    document.setAsynchronousLoadPriority(4);
+    document.setTokenThreshold(100);
+    return document;
+  }
+
+  /**
+   * Get the parser that this editor kit uses for reading HTML streams. This
+   * method can be overridden to use the alternative parser.
+   *
+   * @return the HTML parser (by default, {@link ParserDelegator}).
+   */
+  protected Parser getParser()
+  {
+    if (parser == null)
+      {
+        parser = new GnuParserDelegator(HTML_401F.getInstance());
+      }
+    return parser;
+  }
+
+  /**
+   * Inserts HTML into an existing document.
+   *
+   * @param doc - the Document to insert the HTML into.
+   * @param offset - where to begin inserting the HTML.
+   * @param html - the String to insert
+   * @param popDepth - the number of ElementSpec.EndTagTypes
+   * to generate before inserting
+   * @param pushDepth - the number of ElementSpec.StartTagTypes
+   * with a direction of ElementSpec.JoinNextDirection that
+   * should be generated before
+   * @param insertTag - the first tag to start inserting into document
+   * @throws IOException - on any I/O error
+   * @throws BadLocationException - if pos represents an invalid location
+   * within the document
+   */
+  public void insertHTML(HTMLDocument doc, int offset, String html,
+                         int popDepth, int pushDepth, HTML.Tag insertTag)
+      throws BadLocationException, IOException
+  {
+    Parser parser = getParser();
+    if (offset < 0 || offset > doc.getLength())
+      throw new BadLocationException("Bad location", offset);
+    if (parser == null)
+      throw new IOException("Parser is null.");
+
+    ParserCallback pc = doc.getReader(offset, popDepth, pushDepth, insertTag);
+
+    // FIXME: What should ignoreCharSet be set to?
+
+    // parser.parse inserts html into the buffer
+    parser.parse(new StringReader(html), pc, false);
+    pc.flush();
+  }
+
+  /**
+   * Inserts content from the given stream. Inserting HTML into a non-empty
+   * document must be inside the body Element, if you do not insert into
+   * the body an exception will be thrown. When inserting into a non-empty
+   * document all tags outside of the body (head, title) will be dropped.
+   *
+   * @param in - the stream to read from
+   * @param doc - the destination for the insertion
+   * @param pos - the location in the document to place the content
+   * @throws IOException - on any I/O error
+   * @throws BadLocationException - if pos represents an invalid location
+   * within the document
+   */
+  public void read(Reader in, Document doc, int pos) throws IOException,
+      BadLocationException
+  {
+    if (doc instanceof HTMLDocument)
+      {
+        Parser parser = getParser();
+        if (pos < 0 || pos > doc.getLength())
+          throw new BadLocationException("Bad location", pos);
+        if (parser == null)
+          throw new IOException("Parser is null.");
+
+        HTMLDocument hd = ((HTMLDocument) doc);
+        if (editorPane != null)
+          hd.setBase(editorPane.getPage());
+        ParserCallback pc = hd.getReader(pos);
+
+        // FIXME: What should ignoreCharSet be set to?
+
+        // parser.parse inserts html into the buffer
+        parser.parse(in, pc, false);
+        pc.flush();
+      }
+    else
+      // read in DefaultEditorKit is called.
+      // the string is inserted in the document as usual.
+      super.read(in, doc, pos);
+  }
+
+  /**
+   * Writes content from a document to the given stream in
+   * an appropriate format.
+   *
+   * @param out - the stream to write to
+   * @param doc - the source for the write
+   * @param pos - the location in the document to get the content.
+   * @param len - the amount to write out
+   * @throws IOException - on any I/O error
+   * @throws BadLocationException - if pos represents an invalid location
+   * within the document
+   */
+  public void write(Writer out, Document doc, int pos, int len)
+      throws IOException, BadLocationException
+  {
+    if (doc instanceof HTMLDocument)
+      {
+        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);
+  }
+
+  /**
+   * Gets the content type that the kit supports.
+   * This kit supports the type text/html.
+   *
+   * @returns the content type supported.
+   */
+  public String getContentType()
+  {
+    return contentType;
+  }
+
+  /**
+   * Creates a copy of the editor kit.
+   *
+   * @return a copy of this.
+   */
+  public Object clone()
+  {
+    // FIXME: Need to clone all fields
+    HTMLEditorKit copy = (HTMLEditorKit) super.clone();
+    copy.linkController = new LinkController();
+    return copy;
+  }
+
+  /**
+   * Copies the key/values in elements AttributeSet into set.
+   * This does not copy component, icon, or element names attributes.
+   * This is called anytime the caret moves over a different location.
+   *
+   * @param element - the element to create the input attributes for.
+   * @param set - the set to copy the values into.
+   */
+  protected void createInputAttributes(Element element,
+                                       MutableAttributeSet set)
+  {
+    set.removeAttributes(set);
+    set.addAttributes(element.getAttributes());
+    // FIXME: Not fully implemented.
+  }
+
+  /**
+   * Called when this is installed into the JEditorPane.
+   *
+   * @param c - the JEditorPane installed into.
+   */
+  public void install(JEditorPane c)
+  {
+    super.install(c);
+    c.addMouseListener(linkController);
+    c.addMouseMotionListener(linkController);
+    editorPane = c;
+  }
+
+  /**
+   * Called when the this is removed from the JEditorPane.
+   * It unregisters any listeners.
+   *
+   * @param c - the JEditorPane being removed from.
+   */
+  public void deinstall(JEditorPane c)
+  {
+    super.deinstall(c);
+    c.removeMouseListener(linkController);
+    c.removeMouseMotionListener(linkController);
+    editorPane = null;
+  }
+
+  /**
+   * Gets the AccessibleContext associated with this.
+   *
+   * @return the AccessibleContext for this.
+   */
+  public AccessibleContext getAccessibleContext()
+  {
+    // FIXME: Should return an instance of
+    // javax.swing.text.html.AccessibleHTML$RootHTMLAccessibleContext
+    // Not implemented yet.
+    return null;
+  }
+
+  /**
+   * Gets the action list. This list is supported by the superclass
+   * augmented by the collection of actions defined locally for style
+   * operations.
+   *
+   * @return an array of all the actions
+   */
+  public Action[] getActions()
+  {
+    return TextAction.augmentList(super.getActions(), defaultActions);
+  }
+
+  /**
+   * Returns the default cursor.
+   *
+   * @return the default cursor
+   */
+  public Cursor getDefaultCursor()
+  {
+    if (defaultCursor == null)
+      defaultCursor = Cursor.getDefaultCursor();
+    return defaultCursor;
+  }
+
+  /**
+   * Returns the cursor for links.
+   *
+   * @return the cursor for links.
+   */
+  public Cursor getLinkCursor()
+  {
+    if (linkCursor == null)
+      linkCursor = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
+    return linkCursor;
+  }
+
+  /**
+   * Sets the Cursor for links.
+   *
+   * @param cursor - the new cursor for links.
+   */
+  public void setLinkCursor(Cursor cursor)
+  {
+    linkCursor = cursor;
+  }
+
+  /**
+   * Sets the default cursor.
+   *
+   * @param cursor - the new default cursor.
+   */
+  public void setDefaultCursor(Cursor cursor)
+  {
+    defaultCursor = cursor;
+  }
+
+  /**
+   * Gets the input attributes used for the styled editing actions.
+   *
+   * @return the attribute set
+   */
+  public MutableAttributeSet getInputAttributes()
+  {
+    return inputAttributes;
+  }
+
+  /**
+   * Get the set of styles currently being used to render the HTML elements.
+   * By default the resource specified by DEFAULT_CSS gets loaded, and is
+   * shared by all HTMLEditorKit instances.
+   *
+   * @return the style sheet.
+   */
+  public StyleSheet getStyleSheet()
+  {
+    if (styleSheet == null)
+      {
+        try
+          {
+            styleSheet = new StyleSheet();
+            Class c = HTMLEditorKit.class;
+            InputStream in = c.getResourceAsStream(DEFAULT_CSS);
+            InputStreamReader r = new InputStreamReader(in);
+            styleSheet.loadRules(r,  null);
+            r.close();
+          }
+        catch (IOException ex)
+          {
+            throw new RuntimeException("No style available.", ex);
+          }
+      }
+    return styleSheet;
+  }
+
+  /**
+   * Set the set of styles to be used to render the various HTML elements.
+   * These styles are specified in terms of CSS specifications. Each document
+   * produced by the kit will have a copy of the sheet which it can add the
+   * document specific styles to. By default, the StyleSheet specified is shared
+   * by all HTMLEditorKit instances.
+   *
+   * @param s - the new style sheet
+   */
+  public void setStyleSheet(StyleSheet s)
+  {
+    styleSheet = s;
+  }
+
+  /**
+   * Returns true when forms should be automatically submitted
+   * by the editor kit. Set this to false when you want to
+   * intercept form submission. In this case you'd want to listen for
+   * hyperlink events on the document and handle FormSubmitEvents specially.
+   *
+   * The default is true.
+   *
+   * @return true when forms should be automatically submitted
+   *         by the editor kit, false otherwise
+   *
+   * @since 1.5
+   *
+   * @see #setAutoFormSubmission(boolean)
+   * @see FormSubmitEvent
+   */
+  public boolean isAutoFormSubmission()
+  {
+    return autoFormSubmission;
+  }
+
+  /**
+   * Sets whether or not the editor kit should automatically submit forms.
+   *
+   * @param auto true when the editor kit should handle form
+   *        submission, false otherwise
+   *
+   * @since 1.5
+   *
+   * @see #isAutoFormSubmission()
+   */
+  public void setAutoFormSubmission(boolean auto)
+  {
+    autoFormSubmission = auto;
+  }
+}
diff --git a/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java b/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java
new file mode 100644
index 000000000..e146965d7
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/HTMLFrameHyperlinkEvent.java
@@ -0,0 +1,130 @@
+/* HTMLFrameHyperlinkEvent.java --
+   Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.net.URL;
+
+import javax.swing.event.HyperlinkEvent;
+import javax.swing.text.Element;
+
+/**
+ * HTMLFrameHyperlinkEvent transfers information about the link that was
+ * activated in a frame.
+ *
+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org)
+ */
+public class HTMLFrameHyperlinkEvent extends HyperlinkEvent
+{
+  private final String target_frame;
+
+  /**
+   * Creates a new hypertext link event.
+   *
+   * @param source The object this link is associated to.
+   * @param type The type of event.
+   * @param url The URL this link pointing too.
+   * @param element The element in the document representing the anchor.
+   * @param frame - the Frame to display the document in.
+   */
+  public HTMLFrameHyperlinkEvent(Object source, EventType type, URL url,
+                                 Element element, String frame)
+  {
+    super(source, type, url, frame, element);
+    target_frame = frame;
+  }
+
+  /**
+   * Creates a new hypertext link event.
+   *
+   * @param source The object this link is associated to.
+   * @param type The type of event.
+   * @param url The URL this link pointing too.
+   * @param frame - the Frame to display the document in.
+   */
+  public HTMLFrameHyperlinkEvent(Object source, EventType type, URL url,
+                                 String frame)
+  {
+    super(source, type, url, frame);
+    target_frame = frame;
+  }
+
+  /**
+   * Creates a new hypertext link event.
+   *
+   * @param source The object this link is associated to.
+   * @param type The type of event.
+   * @param url The URL this link pointing too.
+   * @param description The description for this link.
+   * @param element The element in the document representing the anchor.
+   * @param frame - the Frame to display the document in.
+   */
+  public HTMLFrameHyperlinkEvent(Object source, EventType type, URL url,
+                                 String description, Element element,
+                                 String frame)
+  {
+    super(source, type, url, description, element);
+    target_frame = frame;
+  }
+
+  /**
+   * Creates a new hypertext link event.
+   *
+   * @param source The object this link is associated to.
+   * @param type The type of event.
+   * @param url The URL this link pointing too.
+   * @param description The description for this link.
+   * @param frame - the Frame to display the document in.
+   */
+  public HTMLFrameHyperlinkEvent(Object source, EventType type, URL url,
+                                 String description, String frame)
+  {
+    super(source, type, url, description);
+    target_frame = frame;
+  }
+
+  /**
+   * Gets the string, passed as the target frame identifier.
+   *
+   * @return the target for the link.
+   */
+  public String getTarget()
+  {
+    return target_frame;
+  }
+}
diff --git a/libjava/classpath/javax/swing/text/html/HTMLWriter.java b/libjava/classpath/javax/swing/text/html/HTMLWriter.java
new file mode 100644
index 000000000..55d25ccbb
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/HTMLWriter.java
@@ -0,0 +1,1088 @@
+/* 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 gnu.java.lang.CPStringBuilder;
+
+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 embedded has been written out.
+   */
+  private HashSet openEmbeddedTagHashSet = 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;
+    openEmbeddedTagHashSet = 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;
+    openEmbeddedTagHashSet = 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.
+    HTML.Tag[] tag_arr =
+      openEmbeddedTagHashSet.toArray(new HTML.Tag[openEmbeddedTagHashSet.size()]);
+
+    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 (!openEmbeddedTagHashSet.contains(key))
+              {
+                writeRaw("<" + key);
+                writeAttributes((AttributeSet) value);
+                writeRaw(">");
+                openEmbeddedTagHashSet.add((HTML.Tag) 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
+  {
+    HTML.Tag[] tag_arr =
+      openEmbeddedTagHashSet.toArray(new HTML.Tag[openEmbeddedTagHashSet.size()]);
+
+    for (int i = 0; i < tag_arr.length; i++)
+      {
+        HTML.Tag key = tag_arr[i];
+
+        if (!attrSet.isDefined(key))
+          {
+            writeRaw("");
+            openEmbeddedTagHashSet.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
+  {
+    CPStringBuilder strBuffer = new CPStringBuilder();
+
+    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);
+
+    // handle the tag
+    if (synthesizedElement(paramElem))
+      {
+        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))
+          {
+            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)
+    {
+      // handle the tag
+      if (synthesizedElement(paramElem))
+        {
+          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))
+            {
+              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
diff --git a/libjava/classpath/javax/swing/text/html/ImageView.java b/libjava/classpath/javax/swing/text/html/ImageView.java
new file mode 100644
index 000000000..bdbe9b3bb
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ImageView.java
@@ -0,0 +1,591 @@
+package javax.swing.text.html;
+
+import gnu.javax.swing.text.html.ImageViewIconFactory;
+import gnu.javax.swing.text.html.css.Length;
+
+import java.awt.Graphics;
+import java.awt.Image;
+import java.awt.MediaTracker;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.Toolkit;
+import java.awt.image.ImageObserver;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import javax.swing.Icon;
+import javax.swing.SwingUtilities;
+import javax.swing.text.AbstractDocument;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.View;
+import javax.swing.text.Position.Bias;
+import javax.swing.text.html.HTML.Attribute;
+
+/**
+ * A view, representing a single image, represented by the HTML IMG tag.
+ *
+ * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
+ */
+public class ImageView extends View
+{
+  /**
+   * Tracks image loading state and performs the necessary layout updates.
+   */
+  class Observer
+    implements ImageObserver
+  {
+
+    public boolean imageUpdate(Image image, int flags, int x, int y, int width, int height)
+    {
+      boolean widthChanged = false;
+      if ((flags & ImageObserver.WIDTH) != 0 && spans[X_AXIS] == null)
+        widthChanged = true;
+      boolean heightChanged = false;
+      if ((flags & ImageObserver.HEIGHT) != 0 && spans[Y_AXIS] == null)
+        heightChanged = true;
+      if (widthChanged || heightChanged)
+        safePreferenceChanged(ImageView.this, widthChanged, heightChanged);
+      boolean ret = (flags & ALLBITS) != 0;
+      return ret;
+    }
+
+  }
+
+  /**
+   * True if the image loads synchronuosly (on demand). By default, the image
+   * loads asynchronuosly.
+   */
+  boolean loadOnDemand;
+
+  /**
+   * The image icon, wrapping the image,
+   */
+  Image image;
+
+  /**
+   * The image state.
+   */
+  byte imageState = MediaTracker.LOADING;
+
+  /**
+   * True when the image needs re-loading, false otherwise.
+   */
+  private boolean reloadImage;
+
+  /**
+   * True when the image properties need re-loading, false otherwise.
+   */
+  private boolean reloadProperties;
+
+  /**
+   * True when the width is set as CSS/HTML attribute.
+   */
+  private boolean haveWidth;
+
+  /**
+   * True when the height is set as CSS/HTML attribute.
+   */
+  private boolean haveHeight;
+
+  /**
+   * True when the image is currently loading.
+   */
+  private boolean loading;
+
+  /**
+   * The current width of the image.
+   */
+  private int width;
+
+  /**
+   * The current height of the image.
+   */
+  private int height;
+
+  /**
+   * Our ImageObserver for tracking the loading state.
+   */
+  private ImageObserver observer;
+
+  /**
+   * The CSS width and height.
+   *
+   * Package private to avoid synthetic accessor methods.
+   */
+  Length[] spans;
+
+  /**
+   * The cached attributes.
+   */
+  private AttributeSet attributes;
+
+  /**
+   * Creates the image view that represents the given element.
+   *
+   * @param element the element, represented by this image view.
+   */
+  public ImageView(Element element)
+  {
+    super(element);
+    spans = new Length[2];
+    observer = new Observer();
+    reloadProperties = true;
+    reloadImage = true;
+    loadOnDemand = false;
+  }
+
+  /**
+   * Load or reload the image. This method initiates the image reloading. After
+   * the image is ready, the repaint event will be scheduled. The current image,
+   * if it already exists, will be discarded.
+   */
+  private void reloadImage()
+  {
+    loading = true;
+    reloadImage = false;
+    haveWidth = false;
+    haveHeight = false;
+    image = null;
+    width = 0;
+    height = 0;
+    try
+      {
+        loadImage();
+        updateSize();
+      }
+    finally
+      {
+        loading = false;
+      }
+  }
+
+  /**
+   * Get the image alignment. This method works handling standart alignment
+   * attributes in the HTML IMG tag (align = top bottom middle left right).
+   * Depending from the parameter, either horizontal or vertical alingment
+   * information is returned.
+   *
+   * @param axis -
+   *          either X_AXIS or Y_AXIS
+   */
+  public float getAlignment(int axis)
+  {
+    AttributeSet attrs = getAttributes();
+    Object al = attrs.getAttribute(Attribute.ALIGN);
+
+    // Default is top left aligned.
+    if (al == null)
+      return 0.0f;
+
+    String align = al.toString();
+
+    if (axis == View.X_AXIS)
+      {
+        if (align.equals("middle"))
+          return 0.5f;
+        else if (align.equals("left"))
+          return 0.0f;
+        else if (align.equals("right"))
+          return 1.0f;
+        else
+          return 0.0f;
+      }
+    else if (axis == View.Y_AXIS)
+      {
+        if (align.equals("middle"))
+          return 0.5f;
+        else if (align.equals("top"))
+          return 0.0f;
+        else if (align.equals("bottom"))
+          return 1.0f;
+        else
+          return 0.0f;
+      }
+    else
+      throw new IllegalArgumentException("axis " + axis);
+  }
+
+  /**
+   * Get the text that should be shown as the image replacement and also as the
+   * image tool tip text. The method returns the value of the attribute, having
+   * the name {@link Attribute#ALT}. If there is no such attribute, the image
+   * name from the url is returned. If the URL is not available, the empty
+   * string is returned.
+   */
+  public String getAltText()
+  {
+    Object rt = getAttributes().getAttribute(Attribute.ALT);
+    if (rt != null)
+      return rt.toString();
+    else
+      {
+        URL u = getImageURL();
+        if (u == null)
+          return "";
+        else
+          return u.getFile();
+      }
+  }
+
+  /**
+   * Returns the combination of the document and the style sheet attributes.
+   */
+  public AttributeSet getAttributes()
+  {
+    if (attributes == null)
+      attributes = getStyleSheet().getViewAttributes(this);
+    return attributes;
+  }
+
+  /**
+   * Get the image to render. May return null if the image is not yet loaded.
+   */
+  public Image getImage()
+  {
+    updateState();
+    return image;
+  }
+
+  /**
+   * Get the URL location of the image to render. If this method returns null,
+   * the "no image" icon is rendered instead. By defaul, url must be present as
+   * the "src" property of the IMG tag. If it is missing, null is returned and
+   * the "no image" icon is rendered.
+   *
+   * @return the URL location of the image to render.
+   */
+  public URL getImageURL()
+  {
+    Element el = getElement();
+    String src = (String) el.getAttributes().getAttribute(Attribute.SRC);
+    URL url = null;
+    if (src != null)
+      {
+        URL base = ((HTMLDocument) getDocument()).getBase();
+        try
+          {
+            url = new URL(base, src);
+          }
+        catch (MalformedURLException ex)
+          {
+            // Return null.
+          }
+      }
+    return url;
+  }
+
+  /**
+   * Get the icon that should be displayed while the image is loading and hence
+   * not yet available.
+   *
+   * @return an icon, showing a non broken sheet of paper with image.
+   */
+  public Icon getLoadingImageIcon()
+  {
+    return ImageViewIconFactory.getLoadingImageIcon();
+  }
+
+  /**
+   * Get the image loading strategy.
+   *
+   * @return false (default) if the image is loaded when the view is
+   *         constructed, true if the image is only loaded on demand when
+   *         rendering.
+   */
+  public boolean getLoadsSynchronously()
+  {
+    return loadOnDemand;
+  }
+
+  /**
+   * Get the icon that should be displayed when the image is not available.
+   *
+   * @return an icon, showing a broken sheet of paper with image.
+   */
+  public Icon getNoImageIcon()
+  {
+    return ImageViewIconFactory.getNoImageIcon();
+  }
+
+  /**
+   * Get the preferred span of the image along the axis. The image size is first
+   * requested to the attributes {@link Attribute#WIDTH} and
+   * {@link Attribute#HEIGHT}. If they are missing, and the image is already
+   * loaded, the image size is returned. If there are no attributes, and the
+   * image is not loaded, zero is returned.
+   *
+   * @param axis -
+   *          either X_AXIS or Y_AXIS
+   * @return either width of height of the image, depending on the axis.
+   */
+  public float getPreferredSpan(int axis)
+  {
+    Image image = getImage();
+
+    if (axis == View.X_AXIS)
+      {
+        if (spans[axis] != null)
+          return spans[axis].getValue();
+        else if (image != null)
+          return image.getWidth(getContainer());
+        else
+          return getNoImageIcon().getIconWidth();
+      }
+    else if (axis == View.Y_AXIS)
+      {
+        if (spans[axis] != null)
+          return spans[axis].getValue();
+        else if (image != null)
+          return image.getHeight(getContainer());
+        else
+          return getNoImageIcon().getIconHeight();
+      }
+    else
+      throw new IllegalArgumentException("axis " + axis);
+  }
+
+  /**
+   * Get the associated style sheet from the document.
+   *
+   * @return the associated style sheet.
+   */
+  protected StyleSheet getStyleSheet()
+  {
+    HTMLDocument doc = (HTMLDocument) getDocument();
+    return doc.getStyleSheet();
+  }
+
+  /**
+   * Get the tool tip text. This is overridden to return the value of the
+   * {@link #getAltText()}. The parameters are ignored.
+   *
+   * @return that is returned by getAltText().
+   */
+  public String getToolTipText(float x, float y, Shape shape)
+  {
+    return getAltText();
+  }
+
+  /**
+   * Paints the image or one of the two image state icons. The image is resized
+   * to the shape bounds. If there is no image available, the alternative text
+   * is displayed besides the image state icon.
+   *
+   * @param g
+   *          the Graphics, used for painting.
+   * @param bounds
+   *          the bounds of the region where the image or replacing icon must be
+   *          painted.
+   */
+  public void paint(Graphics g, Shape bounds)
+  {
+    updateState();
+    Rectangle r = bounds instanceof Rectangle ? (Rectangle) bounds
+                                              : bounds.getBounds();
+    Image image = getImage();
+    if (image != null)
+      {
+        g.drawImage(image, r.x, r.y, r.width, r.height, observer);
+      }
+    else
+      {
+        Icon icon = getNoImageIcon();
+        if (icon != null)
+          icon.paintIcon(getContainer(), g, r.x, r.y);
+      }
+  }
+
+  /**
+   * Set if the image should be loaded only when needed (synchronuosly). By
+   * default, the image loads asynchronuosly. If the image is not yet ready, the
+   * icon, returned by the {@link #getLoadingImageIcon()}, is displayed.
+   */
+  public void setLoadsSynchronously(boolean load_on_demand)
+  {
+    loadOnDemand = load_on_demand;
+  }
+
+  /**
+   * Update all cached properties from the attribute set, returned by the
+   * {@link #getAttributes}.
+   */
+  protected void setPropertiesFromAttributes()
+  {
+    AttributeSet atts = getAttributes();
+    StyleSheet ss = getStyleSheet();
+    float emBase = ss.getEMBase(atts);
+    float exBase = ss.getEXBase(atts);
+    spans[X_AXIS] = (Length) atts.getAttribute(CSS.Attribute.WIDTH);
+    if (spans[X_AXIS] != null)
+      {
+        spans[X_AXIS].setFontBases(emBase, exBase);
+      }
+    spans[Y_AXIS] = (Length) atts.getAttribute(CSS.Attribute.HEIGHT);
+    if (spans[Y_AXIS] != null)
+      {
+        spans[Y_AXIS].setFontBases(emBase, exBase);
+      }
+  }
+
+  /**
+   * Maps the picture co-ordinates into the image position in the model. As the
+   * image is not divideable, this is currently implemented always to return the
+   * start offset.
+   */
+  public int viewToModel(float x, float y, Shape shape, Bias[] bias)
+  {
+    return getStartOffset();
+  }
+
+  /**
+   * This is currently implemented always to return the area of the image view,
+   * as the image is not divideable by character positions.
+   *
+   * @param pos character position
+   * @param area of the image view
+   * @param bias bias
+   *
+   * @return the shape, where the given character position should be mapped.
+   */
+  public Shape modelToView(int pos, Shape area, Bias bias)
+      throws BadLocationException
+  {
+    return area;
+  }
+
+  /**
+   * Starts loading the image asynchronuosly. If the image must be loaded
+   * synchronuosly instead, the {@link #setLoadsSynchronously} must be
+   * called before calling this method. The passed parameters are not used.
+   */
+  public void setSize(float width, float height)
+  {
+    updateState();
+    // TODO: Implement this when we have an alt view for the alt=... attribute.
+  }
+
+  /**
+   * This makes sure that the image and properties have been loaded.
+   */
+  private void updateState()
+  {
+    if (reloadImage)
+      reloadImage();
+    if (reloadProperties)
+      setPropertiesFromAttributes();
+  }
+
+  /**
+   * Actually loads the image.
+   */
+  private void loadImage()
+  {
+    URL src = getImageURL();
+    Image newImage = null;
+    if (src != null)
+      {
+        // Call getImage(URL) to allow the toolkit caching of that image URL.
+        Toolkit tk = Toolkit.getDefaultToolkit();
+        newImage = tk.getImage(src);
+        tk.prepareImage(newImage, -1, -1, observer);
+        if (newImage != null && getLoadsSynchronously())
+          {
+            // Load image synchronously.
+            MediaTracker tracker = new MediaTracker(getContainer());
+            tracker.addImage(newImage, 0);
+            try
+              {
+                tracker.waitForID(0);
+              }
+            catch (InterruptedException ex)
+              {
+                Thread.interrupted();
+              }
+
+          }
+      }
+    image = newImage;
+  }
+
+  /**
+   * Updates the size parameters of the image.
+   */
+  private void updateSize()
+  {
+    int newW = 0;
+    int newH = 0;
+    Image newIm = getImage();
+    if (newIm != null)
+      {
+        // Fetch width.
+        Length l = spans[X_AXIS];
+        if (l != null)
+          {
+            newW = (int) l.getValue();
+            haveWidth = true;
+          }
+        else
+          {
+            newW = newIm.getWidth(observer);
+          }
+        // Fetch height.
+        l = spans[Y_AXIS];
+        if (l != null)
+          {
+            newH = (int) l.getValue();
+            haveHeight = true;
+          }
+        else
+          {
+            newW = newIm.getWidth(observer);
+          }
+        // Go and trigger loading.
+        Toolkit tk = Toolkit.getDefaultToolkit();
+        if (haveWidth || haveHeight)
+          tk.prepareImage(newIm, width, height, observer);
+        else
+          tk.prepareImage(newIm, -1, -1, observer);
+      }
+  }
+
+  /**
+   * Calls preferenceChanged from the event dispatch thread and within
+   * a read lock to protect us from threading issues.
+   *
+   * @param v the view
+   * @param width true when the width changed
+   * @param height true when the height changed
+   */
+  void safePreferenceChanged(final View v, final boolean width,
+                             final boolean height)
+  {
+    if (SwingUtilities.isEventDispatchThread())
+      {
+        Document doc = getDocument();
+        if (doc instanceof AbstractDocument)
+          ((AbstractDocument) doc).readLock();
+        try
+          {
+            preferenceChanged(v, width, height);
+          }
+        finally
+          {
+            if (doc instanceof AbstractDocument)
+              ((AbstractDocument) doc).readUnlock();
+          }
+      }
+    else
+      {
+        SwingUtilities.invokeLater(new Runnable()
+        {
+          public void run()
+          {
+            safePreferenceChanged(v, width, height);
+          }
+        });
+      }
+  }
+}
diff --git a/libjava/classpath/javax/swing/text/html/InlineView.java b/libjava/classpath/javax/swing/text/html/InlineView.java
new file mode 100644
index 000000000..5376c6b9d
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/InlineView.java
@@ -0,0 +1,307 @@
+/* InlineView.java -- Renders HTML content
+   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.awt.FontMetrics;
+import java.awt.Shape;
+import java.text.BreakIterator;
+
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.LabelView;
+import javax.swing.text.Segment;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+
+/**
+ * Renders HTML content (identified by {@link HTML.Tag#CONTENT}). This is
+ * basically a {@link LabelView} that is adjusted to understand styles defined
+ * by stylesheets.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class InlineView
+  extends LabelView
+{
+
+  /**
+   * The attributes used by this view.
+   */
+  private AttributeSet attributes;
+
+  /**
+   * The span of the longest word in this view.
+   *
+   * @see #getLongestWord()
+   */
+  private float longestWord;
+
+  /**
+   * Indicates if we may wrap or not.
+   */
+  private boolean nowrap;
+
+  /**
+   * Creates a new <code>InlineView</code> that renders the specified element.
+   *
+   * @param element the element for this view
+   */
+  public InlineView(Element element)
+  {
+    super(element);
+  }
+
+  /**
+   * Receives notification that something was inserted into the document in
+   * a location that this view is responsible for.
+   *
+   * @param e the document event
+   * @param a the current allocation of this view
+   * @param f the view factory for creating new views
+   *
+   * @since 1.5
+   */
+  public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f)
+  {
+    // FIXME: What to do here?
+    super.insertUpdate(e, a, f);
+  }
+
+  /**
+   * Receives notification that something was removed from the document in
+   * a location that this view is responsible for.
+   *
+   * @param e the document event
+   * @param a the current allocation of this view
+   * @param f the view factory for creating new views
+   *
+   * @since 1.5
+   */
+  public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f)
+  {
+    // FIXME: What to do here?
+    super.removeUpdate(e, a, f);
+  }
+
+  /**
+   * Receives notification that attributes have changed in the document in
+   * a location that this view is responsible for. This calls
+   * {@link #setPropertiesFromAttributes}.
+   *
+   * @param e the document event
+   * @param a the current allocation of this view
+   * @param f the view factory for creating new views
+   *
+   * @since 1.5
+   */
+  public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f)
+  {
+    super.changedUpdate(e, a, f);
+    StyleSheet ss = getStyleSheet();
+    attributes = ss.getViewAttributes(this);
+    preferenceChanged(null, true, true);
+    setPropertiesFromAttributes();
+  }
+
+  /**
+   * Returns the attributes that are used for rendering. This is implemented
+   * to multiplex the attributes specified in the model with a stylesheet.
+   *
+   * @return the attributes that are used for rendering
+   */
+  public AttributeSet getAttributes()
+  {
+    if (attributes == null)
+      {
+        StyleSheet ss = getStyleSheet();
+        attributes = ss.getViewAttributes(this);
+      }
+    return attributes;
+  }
+
+
+  public int getBreakWeight(int axis, float pos, float len)
+  {
+    int weight;
+    if (nowrap)
+      weight = BadBreakWeight;
+    else
+      weight = super.getBreakWeight(axis, pos, len);
+    return weight;
+  }
+
+  public View breakView(int axis, int offset, float pos, float len)
+  {
+    // FIXME: Implement this.
+    return super.breakView(axis, offset, pos, len);
+  }
+
+  /**
+   * Loads the character style properties from the stylesheet.
+   */
+  protected void setPropertiesFromAttributes()
+  {
+    super.setPropertiesFromAttributes();
+    AttributeSet atts = getAttributes();
+    Object o = atts.getAttribute(CSS.Attribute.TEXT_DECORATION);
+
+    // Check for underline.
+    boolean b = false;
+    if (o != null && o.toString().contains("underline"))
+      b = true;
+    setUnderline(b);
+
+    // Check for line-through.
+    b = false;
+    if (o != null && o.toString().contains("line-through"))
+      b = true;
+    setStrikeThrough(b);
+
+    // Check for vertical alignment (subscript/superscript).
+    o = atts.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
+
+    // Subscript.
+    b = false;
+    if (o != null && o.toString().contains("sub"))
+      b = true;
+    setSubscript(b);
+
+    // Superscript.
+    b = false;
+    if (o != null && o.toString().contains("sup"))
+      b = true;
+    setSuperscript(b);
+
+    // Fetch nowrap setting.
+    o = atts.getAttribute(CSS.Attribute.WHITE_SPACE);
+    if (o != null && o.equals("nowrap"))
+      nowrap = true;
+    else
+      nowrap = false;
+  }
+
+  /**
+   * Returns the stylesheet used by this view. This returns the stylesheet
+   * of the <code>HTMLDocument</code> that is rendered by this view.
+   *
+   * @return the stylesheet used by this view
+   */
+  protected StyleSheet getStyleSheet()
+  {
+    Document doc = getDocument();
+    StyleSheet styleSheet = null;
+    if (doc instanceof HTMLDocument)
+      styleSheet = ((HTMLDocument) doc).getStyleSheet();
+    return styleSheet;
+  }
+
+  /**
+   * Returns the minimum span for the specified axis. This returns the
+   * width of the longest word for the X axis and the super behaviour for
+   * the Y axis. This is a slight deviation from the reference implementation.
+   * IMO this should improve rendering behaviour so that an InlineView never
+   * gets smaller than the longest word in it.
+   */
+  public float getMinimumSpan(int axis)
+  {
+    float min = super.getMinimumSpan(axis);
+    if (axis == X_AXIS)
+      min = Math.max(getLongestWord(), min);
+    return min;
+  }
+
+  /**
+   * Returns the span of the longest word in this view.
+   *
+   * @return the span of the longest word in this view
+   */
+  private float getLongestWord()
+  {
+    if (longestWord == -1)
+      longestWord = calculateLongestWord();
+    return longestWord;
+  }
+
+  /**
+   * Calculates the span of the longest word in this view.
+   *
+   * @return the span of the longest word in this view
+   */
+  private float calculateLongestWord()
+  {
+    float span = 0;
+    try
+      {
+        Document doc = getDocument();
+        int p0 = getStartOffset();
+        int p1 = getEndOffset();
+        Segment s = new Segment();
+        doc.getText(p0, p1 - p0, s);
+        BreakIterator iter = BreakIterator.getWordInstance();
+        iter.setText(s);
+        int wordStart = p0;
+        int wordEnd = p0;
+        int start = iter.first();
+        for (int end = iter.next(); end != BreakIterator.DONE;
+             start = end, end = iter.next())
+          {
+            if ((end - start) > (wordEnd - wordStart))
+              {
+                wordStart = start;
+                wordEnd = end;
+              }
+          }
+        if (wordEnd - wordStart > 0)
+          {
+            FontMetrics fm = getFontMetrics();
+            int offset = s.offset + wordStart - s.getBeginIndex();
+            span = fm.charsWidth(s.array, offset, wordEnd - wordStart);
+          }
+      }
+    catch (BadLocationException ex)
+      {
+        // Return 0.
+      }
+    return span;
+  }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/ListView.java b/libjava/classpath/javax/swing/text/html/ListView.java
new file mode 100644
index 000000000..f05051a4d
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ListView.java
@@ -0,0 +1,128 @@
+/* ListView.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.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.text.Element;
+
+/**
+ * A View to render HTML lists, like the <code><ul></code> and
+ * <code><ol></code> tags.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class ListView
+  extends BlockView
+{
+
+  /**
+   * The painter used to paint the list items.
+   */
+  private StyleSheet.ListPainter painter;
+
+  /**
+   * Creates a new <code>ListView</code> for the specified element.
+   *
+   * @param el the element to create a list view for
+   */
+  public ListView(Element el)
+  {
+    super(el, Y_AXIS);
+  }
+
+  /**
+   * Returns the alignment of this view along the specified axis.
+   *
+   * This returns <code>0.5</code> unconditionally.
+   *
+   * @param axis the axis
+   *
+   * @return the alignment of this view along the specified axis
+   */
+  public float getAlignment(int axis)
+  {
+    if (axis != X_AXIS && axis != Y_AXIS)
+      throw new IllegalArgumentException("Illegal axis parameter: " + axis);
+
+    return 0.5F;
+  }
+
+  /**
+   * Paints the <code>ListView</code>.
+   *
+   * @param g the graphics context to use for painting
+   * @param allocation the allocation given to this view
+   */
+  public void paint(Graphics g, Shape allocation)
+  {
+    super.paint(g, allocation);
+  }
+
+  /**
+   * Paints the child with the specified index into the specified allocation.
+   *
+   * This implementation forwards to the list painter fetched from the
+   * {@link StyleSheet} and then calls
+   * <code>super.paintChild(g, a, index)</code>.
+   *
+   * @param g the graphics context to use
+   * @param a the allocation for the child
+   * @param index the child index
+   */
+  protected void paintChild(Graphics g, Rectangle a, int index)
+  {
+    painter.paint(g, a.x, a.y, a.width, a.height, this, index);
+    super.paintChild(g, a, index);
+  }
+
+  /**
+   * Fetches this view's properties from the style attributes of this view's
+   * element.
+   *
+   * This forwards to super and then fetches a {@link StyleSheet.ListPainter}
+   * from the stylesheet suitable for painting the list.
+   */
+  protected void setPropertiesFromAttributes()
+  {
+    super.setPropertiesFromAttributes();
+    painter = getStyleSheet().getListPainter(getAttributes());
+  }
+}
diff --git a/libjava/classpath/javax/swing/text/html/MinimalHTMLWriter.java b/libjava/classpath/javax/swing/text/html/MinimalHTMLWriter.java
new file mode 100644
index 000000000..3dddfc3de
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/MinimalHTMLWriter.java
@@ -0,0 +1,453 @@
+/* MinimalHTMLWriter.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 javax.swing.text.AttributeSet;
+import javax.swing.text.AbstractWriter;
+import javax.swing.text.BadLocationException;
+import javax.swing.text.DefaultStyledDocument;
+import javax.swing.text.Element;
+import javax.swing.text.ElementIterator;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.Style;
+import javax.swing.text.StyledDocument;
+import java.io.Writer;
+import java.io.IOException;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.Enumeration;
+import java.awt.Color;
+
+/**
+ * MinimalHTMLWriter,
+ * A minimal AbstractWriter implementation for HTML.
+ *
+ * @author Sven de Marothy
+ */
+public class MinimalHTMLWriter extends AbstractWriter
+{
+  private StyledDocument doc;
+  private Deque<String> tagStack;
+  private boolean inFontTag = false;
+
+  /**
+   * Constructs a MinimalHTMLWriter.
+   * @param w - a Writer, for output.
+   * @param doc - the document
+   */
+  public MinimalHTMLWriter(Writer w, StyledDocument doc)
+  {
+    super(w, doc);
+    this.doc = doc;
+    tagStack = new ArrayDeque<String>();
+  }
+
+  /**
+   * Constructs a MinimalHTMLWriter.
+   * @param w - a Writer, for output.
+   * @param doc - the document
+   * @param pos - start position
+   * @param len - length
+   */
+  public MinimalHTMLWriter(Writer w, StyledDocument doc, int pos, int len)
+  {
+    super(w, doc, pos, len);
+    this.doc = doc;
+    tagStack = new ArrayDeque<String>();
+  }
+
+  /**
+   * Starts a span tag.
+   */
+  protected void startFontTag(String style) throws IOException
+  {
+    if( inFontTag() )
+      endOpenTags();
+    writeStartTag("<span style=\""+style+"\">");
+    inFontTag = true;
+  }
+
+  /**
+   * Returns whether the writer is within two span tags.
+   */
+  protected boolean inFontTag()
+  {
+    return inFontTag;
+  }
+
+  /**
+   * Ends a span tag.
+   */
+  protected void endFontTag() throws IOException
+  {
+    writeEndTag("</span>");
+    inFontTag = false;
+  }
+
+  /**
+   * Write the entire HTML document.
+   */
+  public synchronized void write() throws IOException, BadLocationException
+  {
+    writeStartTag("<html>");
+    writeHeader();
+    writeBody();
+    writeEndTag("</html>");
+  }
+
+  /**
+   * Write a start tag and increment the indent.
+   */
+  protected void writeStartTag(String tag) throws IOException
+  {
+    indent();
+    write(tag+NEWLINE);
+    incrIndent();
+  }
+
+  /**
+   * Write an ending tag and decrement the indent.
+   */
+  protected void writeEndTag(String endTag) throws IOException
+  {
+    decrIndent();
+    indent();
+    write(endTag+NEWLINE);
+  }
+
+  /**
+   * Write the HTML header.
+   */
+  protected void writeHeader() throws IOException
+  {
+    writeStartTag("<head>");
+    writeStartTag("<style>");
+    writeStartTag("<!--");
+    writeStyles();
+    writeEndTag("-->");
+    writeEndTag("</style>");
+    writeEndTag("</head>");
+  }
+
+  /**
+   * Write a paragraph start tag.
+   */
+  protected void writeStartParagraph(Element elem) throws IOException
+  {
+    indent();
+    write("<p class=default>"+NEWLINE); // FIXME: Class value = ?
+    incrIndent();
+  }
+
+  /**
+   * Write a paragraph end tag, closes any other open tags.
+   */
+  protected void writeEndParagraph() throws IOException
+  {
+    endOpenTags();
+    writeEndTag("</p>");
+  }
+
+  /**
+   * Writes the body of the HTML document.
+   */
+  protected void writeBody() throws IOException, BadLocationException
+  {
+    writeStartTag("<body>");
+
+    ElementIterator ei = getElementIterator();
+    Element e = ei.first();
+    boolean inParagraph = false;
+    do
+      {
+        if( e.isLeaf() )
+          {
+            boolean hasNL = (getText(e).indexOf(NEWLINE) != -1);
+            if( !inParagraph && hasText( e ) )
+              {
+                writeStartParagraph(e);
+                inParagraph = true;
+              }
+
+            if( hasText( e ) )
+              writeContent(e, true);
+
+            if( hasNL && inParagraph )
+              {
+                writeEndParagraph();
+                inParagraph = false;
+              }
+            else
+              endOpenTags();
+          }
+      }
+    while((e = ei.next()) != null);
+
+    writeEndTag("</body>");
+  }
+
+  protected void text(Element elem) throws IOException, BadLocationException
+  {
+    write( getText(elem).trim() );
+  }
+
+  /**
+   * Write bold, indent and underline tags.
+   */
+  protected void writeHTMLTags(AttributeSet attr) throws IOException
+  {
+    if(attr.getAttribute(StyleConstants.Bold) != null)
+      if(((Boolean)attr.getAttribute(StyleConstants.Bold)).booleanValue())
+        {
+          write("<b>");
+          tagStack.push("</b>");
+        }
+    if(attr.getAttribute(StyleConstants.Italic) != null)
+      if(((Boolean)attr.getAttribute(StyleConstants.Italic)).booleanValue())
+        {
+          write("<i>");
+          tagStack.push("</i>");
+        }
+    if(attr.getAttribute(StyleConstants.Underline) != null)
+      if(((Boolean)attr.getAttribute(StyleConstants.Underline)).booleanValue())
+        {
+          write("<u>");
+          tagStack.push("</u>");
+        }
+  }
+
+  /**
+   * Returns whether the element contains text or not.
+   */
+  protected boolean isText(Element elem)
+  {
+    return (elem.getEndOffset() != elem.getStartOffset());
+  }
+
+  /**
+   * Writes the content of an element.
+   */
+  protected void writeContent(Element elem, boolean needsIndenting)
+    throws IOException, BadLocationException
+  {
+    writeNonHTMLAttributes(elem.getAttributes());
+    if(needsIndenting)
+      indent();
+    writeHTMLTags(elem.getAttributes());
+    if( isText(elem) )
+      text(elem);
+    else
+      writeLeaf(elem);
+
+    endOpenTags();
+  }
+
+  /**
+   * Writes a non-text leaf element.
+   */
+  protected void writeLeaf(Element e) throws IOException
+  {
+    // NOTE: Haven't tested if this is correct.
+    if(e.getName().equals(StyleConstants.IconElementName))
+      writeImage(e);
+    else
+      writeComponent(e);
+  }
+
+  /**
+   * Write the HTML attributes which do not have tag equivalents,
+   * e.g. attributes other than bold/italic/underlined.
+   */
+  protected void writeNonHTMLAttributes(AttributeSet attr) throws IOException
+  {
+    String style = "";
+
+    // Alignment? Background?
+
+    if( StyleConstants.getForeground(attr) != null )
+      style = style + "color: " +
+        getColor(StyleConstants.getForeground(attr)) + "; ";
+
+    style = style + "font-size: "+StyleConstants.getFontSize(attr)+"pt; ";
+    style = style + "font-family: "+StyleConstants.getFontFamily(attr);
+
+    startFontTag(style);
+  }
+
+  /**
+   * Write the styles used.
+   */
+  protected void writeStyles() throws IOException
+  {
+    if(doc instanceof DefaultStyledDocument)
+      {
+        Enumeration<?> styles = ((DefaultStyledDocument)doc).getStyleNames();
+        while(styles.hasMoreElements())
+          writeStyle(doc.getStyle((String)styles.nextElement()));
+      }
+    else
+      { // What else to do here?
+        Style s = doc.getStyle("default");
+        if(s != null)
+          writeStyle( s );
+      }
+  }
+
+  /**
+   * Write a set of attributes.
+   */
+  protected void writeAttributes(AttributeSet attr) throws IOException
+  {
+    Enumeration<?> attribs = attr.getAttributeNames();
+    while(attribs.hasMoreElements())
+      {
+        Object attribName = attribs.nextElement();
+        String name = attribName.toString();
+        String output = getAttribute(name, attr.getAttribute(attribName));
+        if( output != null )
+          {
+            indent();
+            write( output + NEWLINE );
+          }
+      }
+  }
+
+  /**
+   * Deliberately unimplemented, handles component elements.
+   */
+  protected void writeComponent(Element elem) throws IOException
+  {
+  }
+
+  /**
+   * Deliberately unimplemented.
+   * Writes StyleConstants.IconElementName elements.
+   */
+  protected void writeImage(Element elem) throws IOException
+  {
+  }
+
+  // -------------------- Private methods. --------------------------------
+
+  /**
+   * Write a single style attribute
+   */
+  private String getAttribute(String name, Object a) throws IOException
+  {
+    if(name.equals("foreground"))
+      return "foreground:"+getColor((Color)a)+";";
+    if(name.equals("background"))
+      return "background:"+getColor((Color)a)+";";
+    if(name.equals("italic"))
+      return "italic:"+(((Boolean)a).booleanValue() ? "italic;" : ";");
+    if(name.equals("bold"))
+      return "bold:"+(((Boolean)a).booleanValue() ? "bold;" : "normal;");
+    if(name.equals("family"))
+      return "family:" + a + ";";
+    if(name.equals("size"))
+      {
+        int size = ((Integer)a).intValue();
+        int htmlSize;
+        if( size > 24 )
+          htmlSize = 7;
+        else if( size > 18 )
+          htmlSize = 6;
+        else if( size > 14 )
+          htmlSize = 5;
+        else if( size > 12 )
+          htmlSize = 4;
+        else if( size > 10 )
+          htmlSize = 3;
+        else if( size > 8 )
+          htmlSize = 2;
+        else
+          htmlSize = 1;
+
+        return "size:" + htmlSize + ";";
+      }
+
+    return null;
+  }
+
+  /**
+   * Stupid that Color doesn't have a method for this.
+   */
+  private String getColor(Color c)
+  {
+    String r = "00" + Integer.toHexString(c.getRed());
+    r = r.substring(r.length() - 2);
+    String g = "00" + Integer.toHexString(c.getGreen());
+    g = g.substring(g.length() - 2);
+    String b = "00" + Integer.toHexString(c.getBlue());
+    b = b.substring(b.length() - 2);
+    return "#" + r + g + b;
+  }
+
+  /**
+   * Empty the stack of open tags
+   */
+  private void endOpenTags() throws IOException
+  {
+    while(tagStack.size() > 0)
+      write(tagStack.pop());
+
+    if( inFontTag() )
+      {
+        write(""+NEWLINE);
+        endFontTag();
+      }
+  }
+
+  /**
+   * Output a single style
+   */
+  private void writeStyle(Style s) throws IOException
+  {
+    if( s == null )
+      return;
+
+    writeStartTag("p."+s.getName()+" {");
+    writeAttributes(s);
+    writeEndTag("}");
+  }
+
+  private boolean hasText(Element e) throws BadLocationException
+  {
+    return (getText(e).trim().length() > 0);
+  }
+}
diff --git a/libjava/classpath/javax/swing/text/html/MultiAttributeSet.java b/libjava/classpath/javax/swing/text/html/MultiAttributeSet.java
new file mode 100644
index 000000000..e7fa9f373
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/MultiAttributeSet.java
@@ -0,0 +1,213 @@
+/* MultiAttributeSet.java -- Multiplexes between a set of AttributeSets
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+
+/**
+ * An AttributeSet impl that multiplexes between a set of other AttributeSets.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class MultiAttributeSet
+  implements AttributeSet
+{
+
+  /**
+   * The Enumeration for the multiplexed names.
+   */
+  private class MultiNameEnumeration
+    implements Enumeration<Object>
+  {
+    /**
+     * The index of the current AttributeSet.
+     */
+    private int index;
+
+    /**
+     * The names Enumeration of the current AttributeSet.
+     */
+    private Enumeration<?> current;
+
+    /**
+     * Creates a new instance.
+     */
+    MultiNameEnumeration()
+    {
+      index = 0;
+      current = multi[0].getAttributeNames();
+    }
+
+    public boolean hasMoreElements()
+    {
+      return current.hasMoreElements() || index < multi.length - 1;
+    }
+
+    public Object nextElement()
+    {
+      if (! current.hasMoreElements())
+        {
+          if (index < multi.length - 1)
+            {
+              index++;
+              current = multi[index].getAttributeNames();
+            }
+          else
+            throw new NoSuchElementException();
+        }
+      return current.nextElement();
+    }
+
+  }
+
+  /**
+   * The AttributeSets to multiplex.
+   */
+  AttributeSet[] multi;
+
+  /**
+   * Provided for subclasses that need to initialize via {@link #init}.
+   */
+  MultiAttributeSet()
+  {
+    // Nothing to do here.
+  }
+
+  /**
+   * Creates a new instance.
+   *
+   * @param m the AttributeSets to multiplex
+   */
+  MultiAttributeSet(AttributeSet[] m)
+  {
+    init(m);
+  }
+
+  /**
+   * Provided for subclasses to initialize the attribute set.
+   *
+   * @param m the attributes to multiplex
+   */
+  void init(AttributeSet[] m)
+  {
+    multi = m;
+  }
+
+  public boolean containsAttribute(Object name, Object value)
+  {
+    boolean ret = false;
+    for (int i = 0; i < multi.length && ret == false; i++)
+      {
+        if (multi[i].containsAttribute(name, value))
+          ret = true;
+      }
+    return ret;
+  }
+
+  public boolean containsAttributes(AttributeSet attributes)
+  {
+    boolean ret = true;
+    Enumeration<?> e = attributes.getAttributeNames();
+    while (ret && e.hasMoreElements())
+      {
+        Object key = e.nextElement();
+        ret = attributes.getAttribute(key).equals(getAttribute(key));
+      }
+    return ret;
+  }
+
+  public AttributeSet copyAttributes()
+  {
+    SimpleAttributeSet copy = new SimpleAttributeSet();
+    for (int i = 0; i < multi.length; i++)
+      {
+        copy.addAttributes(multi[i]);
+      }
+    return copy;
+  }
+
+  public Object getAttribute(Object key)
+  {
+    Object ret = null;
+    for (int i = 0; i < multi.length && ret == null; i++)
+      {
+        ret = multi[i].getAttribute(key);
+      }
+    return ret;
+  }
+
+  public int getAttributeCount()
+  {
+    int n = 0;
+    for (int i = 0; i < multi.length; i++)
+      {
+        n += multi[i].getAttributeCount();
+      }
+    return n;
+  }
+
+  public Enumeration<?> getAttributeNames()
+  {
+    return new MultiNameEnumeration();
+  }
+
+  public AttributeSet getResolveParent()
+  {
+    return null;
+  }
+
+  public boolean isDefined(Object attrName)
+  {
+    boolean ret = false;
+    for (int i = 0; i < multi.length && ! ret; i++)
+      ret = multi[i].isDefined(attrName);
+    return ret;
+  }
+
+  public boolean isEqual(AttributeSet attr)
+  {
+    return getAttributeCount() == attr.getAttributeCount()
+           && containsAttributes(attr);
+  }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/MultiStyle.java b/libjava/classpath/javax/swing/text/html/MultiStyle.java
new file mode 100644
index 000000000..eb6215035
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/MultiStyle.java
@@ -0,0 +1,136 @@
+/* MultiStyle.java -- Multiplexes between several Styles
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.util.Enumeration;
+
+import javax.swing.event.ChangeListener;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.Style;
+
+/**
+ * A Style implementation that is able to multiplex between several other
+ * Styles. This is used for CSS style resolving.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class MultiStyle
+  extends MultiAttributeSet
+  implements Style
+{
+
+  // FIXME: Fix the implementation to also return attributes that
+  // are added to this style, etc. However, this is not really needed
+  // now for CSS, but would be nice for correctness.
+
+  /**
+   * The name of the style.
+   */
+  private String name;
+
+  /**
+   * The attributes added to this style.
+   */
+  private SimpleAttributeSet attributes;
+
+  /**
+   * Creates a new instance.
+   *
+   * @param n the name
+   * @param m the styles to multiplex
+   */
+  public MultiStyle(String n, AttributeSet[] m)
+  {
+    super(m);
+    name = n;
+    attributes = new SimpleAttributeSet();
+  }
+
+  /**
+   * Returns the name of the style.
+   *
+   * @return the name of the style
+   */
+  public String getName()
+  {
+    return name;
+  }
+
+  public void addChangeListener(ChangeListener listener)
+  {
+    // TODO: Implement.
+  }
+
+  public void removeChangeListener(ChangeListener listener)
+  {
+    // TODO: Implement.
+  }
+
+  public void addAttribute(Object name, Object value)
+  {
+    attributes.addAttribute(name, value);
+  }
+
+  public void addAttributes(AttributeSet atts)
+  {
+    attributes.addAttributes(atts);
+  }
+
+  public void removeAttribute(Object name)
+  {
+    attributes.removeAttribute(name);
+  }
+
+  public void removeAttributes(Enumeration<?> names)
+  {
+    attributes.removeAttribute(names);
+  }
+
+  public void removeAttributes(AttributeSet atts)
+  {
+    attributes.removeAttribute(atts);
+  }
+
+  public void setResolveParent(AttributeSet parent)
+  {
+    // TODO: Implement.
+  }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/NullView.java b/libjava/classpath/javax/swing/text/html/NullView.java
new file mode 100644
index 000000000..86ce0c10f
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/NullView.java
@@ -0,0 +1,102 @@
+/* NullView.java -- A dummy view that renders nothing
+   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.awt.Graphics;
+import java.awt.Shape;
+
+import javax.swing.text.BadLocationException;
+import javax.swing.text.Element;
+import javax.swing.text.View;
+import javax.swing.text.Position.Bias;
+
+/**
+ * A dummy view that renders nothing. This is used for invisible HTML elements
+ * like <head>.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class NullView
+  extends View
+{
+
+  /**
+   * Creates a new NullView.
+   *
+   * @param elem the element
+   */
+  public NullView(Element elem)
+  {
+    super(elem);
+  }
+
+  /**
+   * Does nothing.
+   */
+  public void paint(Graphics g, Shape s)
+  {
+    // Nothing to do here.
+  }
+
+  /**
+   * Returns zero for both directions.
+   */
+  public float getPreferredSpan(int axis)
+  {
+    return 0;
+  }
+
+  /**
+   * Returns the allocation of this view, which should be empty anyway.
+   */
+  public Shape modelToView(int pos, Shape a, Bias b)
+      throws BadLocationException
+  {
+    return a;
+  }
+
+  /**
+   * Returns the start offset of the element.
+   */
+  public int viewToModel(float x, float y, Shape a, Bias[] b)
+  {
+    return getElement().getStartOffset();
+  }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/ObjectView.java b/libjava/classpath/javax/swing/text/html/ObjectView.java
new file mode 100644
index 000000000..9d900441b
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ObjectView.java
@@ -0,0 +1,110 @@
+/* ObjectView.java -- A view for HTML object tags
+   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.awt.Component;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.ComponentView;
+import javax.swing.text.Element;
+
+/**
+ * A view for HTML <code><object></code> tags.
+ *
+ * This is a {@link ComponentView} that creates special components depending
+ * on the object specification. If the object tag has a classid attribute, then
+ * this view will try to load the class specified by this attribute using the
+ * classloader that loaded the associated document. If the class could be
+ * loaded, an instance is created and the type narrowed to {@link Component}.
+ *
+ * It is also possible to set bean properties on the created component using
+ * nested <code><param></code> tags. For example:
+ * <pre>
+ * <object classid="javax.swing.JLabel">
+ *   <param name="text" value="sample text">
+ * </object>
+ * </pre>
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class ObjectView extends ComponentView
+{
+
+  /**
+   * Creates a new <code>ObjectView</code>.
+   *
+   * @param el the element for which to create a view
+   */
+  public ObjectView(Element el)
+  {
+    super(el);
+  }
+
+  /**
+   * Creates a component based on the specification in the element of this
+   * view. See the class description for details.
+   */
+  protected Component createComponent()
+  {
+    Component comp = null;
+    Element el = getElement();
+    AttributeSet atts = el.getAttributes();
+    String classId = (String) atts.getAttribute("classid");
+    try
+      {
+        Class<?> objectClass = Class.forName(classId);
+        Object instance = objectClass.newInstance();
+        comp = (Component) instance;
+      }
+    catch (ClassNotFoundException ex)
+      {
+        // Ignored.
+      }
+    catch (IllegalAccessException ex)
+      {
+        // Ignored.
+      }
+    catch (InstantiationException ex)
+      {
+        // Ignored.
+      }
+    // FIXME: Handle param tags and set bean properties accordingly.
+    return comp;
+  }
+}
diff --git a/libjava/classpath/javax/swing/text/html/Option.java b/libjava/classpath/javax/swing/text/html/Option.java
new file mode 100644
index 000000000..18d5c2bd8
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/Option.java
@@ -0,0 +1,159 @@
+/* Option.java -- Value class for <option> list model elements
+   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 javax.swing.text.AttributeSet;
+
+/**
+ * Value class for the combobox model that renders <code><option></code>
+ * elements.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class Option
+{
+
+  /**
+   * The attributes of the <option> tag.
+   */
+  private AttributeSet attributes;
+
+  /**
+   * The label.
+   */
+  private String label;
+
+  /**
+   * The selected state of this label.
+   */
+  private boolean selected;
+
+  /**
+   * Creates a new <code>Option</code> instance that uses the specified
+   * tag attributes.
+   *
+   * @param attr the attributes to use
+   */
+  public Option(AttributeSet attr)
+  {
+    // Protect the attribute set.
+    attributes = attr.copyAttributes();
+    label = null;
+    selected = attr.getAttribute(HTML.Attribute.SELECTED) != null;
+  }
+
+  /**
+   * Sets the label to use for this <code><option></code> tag.
+   *
+   * @param l the label to set
+   */
+  public void setLabel(String l)
+  {
+    label = l;
+  }
+
+  /**
+   * Returns the label of this <code><option></code> tag.
+   *
+   * @return the label of this <code><option></code> tag
+   */
+  public String getLabel()
+  {
+    return label;
+  }
+
+  /**
+   * Returns the attributes used to render this <code><option></code>
+   * tag.
+   *
+   * @return the attributes used to render this <code><option></code> tag
+   */
+  public AttributeSet getAttributes()
+  {
+    return attributes;
+  }
+
+  /**
+   * Returns a string representation of this <code><option></code> tag.
+   * This returns the <code>label</code> property.
+   *
+   * @return a string representation of this <code><option></code> tag
+   */
+  public String toString()
+  {
+    return label;
+  }
+
+  /**
+   * Sets the selected state of this <code><option></code> tag.
+   *
+   * @param s the selected state to set
+   */
+  protected void setSelection(boolean s)
+  {
+    selected = s;
+  }
+
+  /**
+   * Returns <code>true</code> when this option is selected, <code>false</code>
+   * otherwise.
+   *
+   * @return <code>true</code> when this option is selected, <code>false</code>
+   *         otherwise
+   */
+  public boolean isSelected()
+  {
+    return selected;
+  }
+
+  /**
+   * Returns the string associated with the <code>value</code> attribute or
+   * the label, if no such attribute is specified.
+   *
+   * @return the string associated with the <code>value</code> attribute or
+   *         the label, if no such attribute is specified
+   */
+  public String getValue()
+  {
+    String value = (String) attributes.getAttribute(HTML.Attribute.VALUE);
+    if (value == null)
+      value = label;
+    return value;
+  }
+}
diff --git a/libjava/classpath/javax/swing/text/html/ParagraphView.java b/libjava/classpath/javax/swing/text/html/ParagraphView.java
new file mode 100644
index 000000000..cab8edb28
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ParagraphView.java
@@ -0,0 +1,322 @@
+/* ParagraphView.java -- Renders a paragraph in HTML
+   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 gnu.javax.swing.text.html.css.Length;
+
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import javax.swing.SizeRequirements;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Document;
+import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.View;
+
+/**
+ * Renders a paragraph in HTML. This is a subclass of
+ * {@link javax.swing.text.ParagraphView} with some adjustments for
+ * understanding stylesheets.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+public class ParagraphView
+  extends javax.swing.text.ParagraphView
+{
+
+  /**
+   * The attributes used by this view.
+   */
+  private AttributeSet attributes;
+
+  /**
+   * The stylesheet's box painter.
+   */
+  private StyleSheet.BoxPainter painter;
+
+  /**
+   * The width as specified in the stylesheet or null if not specified.
+   */
+  private Length cssWidth;
+
+  /**
+   * The height as specified in the stylesheet or null if not specified.
+   */
+  private Length cssHeight;
+
+  /**
+   * Creates a new ParagraphView for the specified element.
+   *
+   * @param element the element
+   */
+  public ParagraphView(Element element)
+  {
+    super(element);
+  }
+
+  /**
+   * Sets the parent of this view. This is implemented to call the parent
+   * functionality and then trigger {@link #setPropertiesFromAttributes} in
+   * order to load the stylesheet attributes.
+   *
+   * @param parent the parent view to set
+   */
+  public void setParent(View parent)
+  {
+    super.setParent(parent);
+    if (parent != null)
+      setPropertiesFromAttributes();
+  }
+
+  /**
+   * Returns the attributes used by this view. This is implemented to multiplex
+   * the attributes of the model with the attributes of the stylesheet.
+   */
+  public AttributeSet getAttributes()
+  {
+    if (attributes == null)
+      {
+        attributes = getStyleSheet().getViewAttributes(this);
+      }
+    return attributes;
+  }
+
+  /**
+   * Loads the visual properties of the ParagraphView from the element's
+   * attributes and the stylesheet of the HTML document.
+   */
+  protected void setPropertiesFromAttributes()
+  {
+    super.setPropertiesFromAttributes();
+
+    // Fetch CSS attributes.
+    attributes = getAttributes();
+    if (attributes != null)
+      {
+        super.setPropertiesFromAttributes();
+        Object o = attributes.getAttribute(CSS.Attribute.TEXT_ALIGN);
+        if (o != null)
+          {
+            String align = o.toString();
+            if (align.equals("left"))
+              setJustification(StyleConstants.ALIGN_LEFT);
+            else if (align.equals("right"))
+              setJustification(StyleConstants.ALIGN_RIGHT);
+            else if (align.equals("center"))
+              setJustification(StyleConstants.ALIGN_CENTER);
+            else if (align.equals("justify"))
+              setJustification(StyleConstants.ALIGN_JUSTIFIED);
+          }
+
+        // Fetch StyleSheet's box painter.
+        painter = getStyleSheet().getBoxPainter(attributes);
+        setInsets((short) painter.getInset(TOP, this),
+                  (short) painter.getInset(LEFT, this),
+                  (short) painter.getInset(BOTTOM, this),
+                  (short) painter.getInset(RIGHT, this));
+
+        StyleSheet ss = getStyleSheet();
+        float emBase = ss.getEMBase(attributes);
+        float exBase = ss.getEXBase(attributes);
+        cssWidth = (Length) attributes.getAttribute(CSS.Attribute.WIDTH);
+        if (cssWidth != null)
+          cssWidth.setFontBases(emBase, exBase);
+        cssHeight = (Length) attributes.getAttribute(CSS.Attribute.WIDTH);
+        if (cssHeight != null)
+          cssHeight.setFontBases(emBase, exBase);
+      }
+  }
+
+  /**
+   * Returns the stylesheet used by this view.
+   *
+   * @return the stylesheet used by this view
+   */
+  protected StyleSheet getStyleSheet()
+  {
+    Document doc = getDocument();
+    StyleSheet styleSheet = null;
+    if (doc instanceof HTMLDocument)
+      styleSheet = ((HTMLDocument) doc).getStyleSheet();
+    return styleSheet;
+  }
+
+  /**
+   * Calculates the minor axis requirements of this view. This is implemented
+   * to return the super class'es requirements and modifies the minimumSpan
+   * slightly so that it is not smaller than the length of the longest word.
+   *
+   * @param axis the axis
+   * @param r the SizeRequirements object to be used as return parameter;
+   *        if <code>null</code> a new one will be created
+   *
+   * @return the requirements along the minor layout axis
+   */
+  protected SizeRequirements calculateMinorAxisRequirements(int axis,
+                                                            SizeRequirements r)
+  {
+    r = super.calculateMinorAxisRequirements(axis, r);
+    if (! setCSSSpan(r, axis))
+      {
+        int margin = axis == X_AXIS ? getLeftInset() + getRightInset()
+                                    : getTopInset() + getBottomInset();
+        r.minimum -= margin;
+        r.preferred -= margin;
+        r.maximum -= margin;
+      }
+    return r;
+  }
+
+  /**
+   * Sets the span on the SizeRequirements object according to the
+   * according CSS span value, when it is set.
+   *
+   * @param r the size requirements
+   * @param axis the axis
+   *
+   * @return <code>true</code> when the CSS span has been set,
+   *         <code>false</code> otherwise
+   */
+  private boolean setCSSSpan(SizeRequirements r, int axis)
+  {
+    boolean ret = false;
+    if (axis == X_AXIS)
+      {
+        if (cssWidth != null && ! cssWidth.isPercentage())
+          {
+            r.minimum = (int) cssWidth.getValue();
+            r.preferred = (int) cssWidth.getValue();
+            r.maximum = (int) cssWidth.getValue();
+            ret = true;
+          }
+      }
+    else
+      {
+        if (cssHeight != null && ! cssWidth.isPercentage())
+          {
+            r.minimum = (int) cssHeight.getValue();
+            r.preferred = (int) cssHeight.getValue();
+            r.maximum = (int) cssHeight.getValue();
+            ret = true;
+          }
+      }
+    return ret;
+  }
+
+  /**
+   * Determines if this view is visible or not. If none of the children is
+   * visible and the only visible child is the break that ends the paragraph,
+   * this paragraph is not considered to be visible.
+   *
+   * @return the visibility of this paragraph
+   */
+  public boolean isVisible()
+  {
+    // FIXME: Implement the above specified behaviour.
+    return super.isVisible();
+  }
+
+  /**
+   * Paints this view. This paints the box using the stylesheet's
+   * box painter for this view and delegates to the super class paint()
+   * afterwards.
+   *
+   * @param g the graphics object
+   * @param a the current allocation of this view
+   */
+  public void paint(Graphics g, Shape a)
+  {
+    if (a != null)
+      {
+        Rectangle r = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+        painter.paint(g, r.x, r.y, r.width, r.height, this);
+      }
+    super.paint(g, a);
+  }
+
+  /**
+   * Returns the preferred span of this view. If this view is not visible,
+   * we return <code>0</code>, otherwise the super class is called.
+   *
+   * @param axis the axis
+   *
+   * @return the preferred span of this view
+   */
+  public float getPreferredSpan(int axis)
+  {
+    float span = 0;
+    if (isVisible())
+      span = super.getPreferredSpan(axis);
+    return span;
+  }
+
+  /**
+   * Returns the minimum span of this view. If this view is not visible,
+   * we return <code>0</code>, otherwise the super class is called.
+   *
+   * @param axis the axis
+   *
+   * @return the minimum span of this view
+   */
+  public float getMinimumSpan(int axis)
+  {
+    float span = 0;
+    if (isVisible())
+      span = super.getMinimumSpan(axis);
+    return span;
+  }
+
+  /**
+   * Returns the maximum span of this view. If this view is not visible,
+   * we return <code>0</code>, otherwise the super class is called.
+   *
+   * @param axis the axis
+   *
+   * @return the maximum span of this view
+   */
+  public float getMaximumSpan(int axis)
+  {
+    float span = 0;
+    if (isVisible())
+      span = super.getMaximumSpan(axis);
+    return span;
+  }
+}
diff --git a/libjava/classpath/javax/swing/text/html/ResetableModel.java b/libjava/classpath/javax/swing/text/html/ResetableModel.java
new file mode 100644
index 000000000..17f65b97d
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ResetableModel.java
@@ -0,0 +1,50 @@
+/* ResetableModel.java -- Form models that can be resetted
+   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;
+
+/**
+ * Form models that can be resetted implement this.
+ */
+interface ResetableModel
+{
+  /**
+   * Resets the model.
+   */
+  void reset();
+}
diff --git a/libjava/classpath/javax/swing/text/html/ResetablePlainDocument.java b/libjava/classpath/javax/swing/text/html/ResetablePlainDocument.java
new file mode 100644
index 000000000..6177f9b86
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ResetablePlainDocument.java
@@ -0,0 +1,82 @@
+/* ResetablePlainDocument.java -- A plain document for use in the HTML renderer
+   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 javax.swing.text.BadLocationException;
+import javax.swing.text.PlainDocument;
+
+/**
+ * A PlainDocument that can be resetted.
+ */
+class ResetablePlainDocument
+  extends PlainDocument
+  implements ResetableModel
+{
+  /**
+   * The initial text.
+   */
+  private String initial;
+
+  /**
+   * Stores the initial text.
+   *
+   * @param text the initial text
+   */
+  void setInitialText(String text)
+  {
+    initial = text;
+  }
+
+  /**
+   * Resets the model.
+   */
+  public void reset()
+  {
+    try
+      {
+        replace(0, getLength(), initial, null);
+      }
+    catch (BadLocationException ex)
+      {
+        // Shouldn't happen.
+        assert false;
+      }
+  }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/ResetableToggleButtonModel.java b/libjava/classpath/javax/swing/text/html/ResetableToggleButtonModel.java
new file mode 100644
index 000000000..637ece151
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ResetableToggleButtonModel.java
@@ -0,0 +1,70 @@
+/* ResetableToggleButtonModel.java -- A toggle button model with reset support
+   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 javax.swing.JToggleButton.ToggleButtonModel;
+
+class ResetableToggleButtonModel
+  extends ToggleButtonModel
+  implements ResetableModel
+{
+
+  /**
+   * The initial state.
+   */
+  private boolean initial;
+
+  /**
+   * Sets the initial selection value.
+   *
+   * @param state the initial value
+   */
+  public void setInitial(boolean state)
+  {
+    initial = state;
+  }
+
+  /**
+   * Resets the model.
+   */
+  public void reset()
+  {
+    setSelected(initial);
+  }
+}
diff --git a/libjava/classpath/javax/swing/text/html/SelectComboBoxModel.java b/libjava/classpath/javax/swing/text/html/SelectComboBoxModel.java
new file mode 100644
index 000000000..999746413
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/SelectComboBoxModel.java
@@ -0,0 +1,84 @@
+/* SelectComboBoxModel.java -- A special ComboBoxModel for use in HTML renderer
+   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 javax.swing.DefaultComboBoxModel;
+
+/**
+ * A special ComboBoxModel that supports storing the initial value so that
+ * the combobox can be resetted later.
+ */
+class SelectComboBoxModel
+  extends DefaultComboBoxModel
+  implements ResetableModel
+{
+
+  /**
+   * The initial selection.
+   */
+  private Option initial;
+
+  /**
+   * Sets the initial selection.
+   *
+   * @param option the initial selection
+   */
+  void setInitialSelection(Option option)
+  {
+    initial = option;
+  }
+
+  /**
+   * Returns the initial selection.
+   *
+   * @return the initial selection
+   */
+  Option getInitialSelection()
+  {
+    return initial;
+  }
+
+  /**
+   * Resets the model.
+   */
+  public void reset()
+  {
+    setSelectedItem(initial);
+  }
+}
diff --git a/libjava/classpath/javax/swing/text/html/SelectListModel.java b/libjava/classpath/javax/swing/text/html/SelectListModel.java
new file mode 100644
index 000000000..23bfaa11b
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/SelectListModel.java
@@ -0,0 +1,106 @@
+/* OptionListModel.java -- A special ListModel for use in the HTML renderer
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.util.BitSet;
+
+import javax.swing.DefaultListModel;
+import javax.swing.DefaultListSelectionModel;
+import javax.swing.ListSelectionModel;
+
+/**
+ * A special list model that encapsulates its selection model and supports
+ * storing of the initial value so that it can be resetted.
+ */
+class SelectListModel
+  extends DefaultListModel
+  implements ResetableModel
+{
+  /**
+   * The selection model.
+   */
+  private DefaultListSelectionModel selectionModel;
+
+  /**
+   * The initial selection.
+   */
+  private BitSet initialSelection;
+
+  /**
+   * Creates a new SelectListModel.
+   */
+  SelectListModel()
+  {
+    selectionModel = new DefaultListSelectionModel();
+    initialSelection = new BitSet();
+  }
+
+  /**
+   * Sets the initial selection.
+   *
+   * @param init the initial selection
+   */
+  void addInitialSelection(int init)
+  {
+    initialSelection.set(init);
+  }
+
+  /**
+   * Resets the model.
+   */
+  public void reset()
+  {
+    selectionModel.clearSelection();
+    for (int i = initialSelection.size(); i >= 0; i--)
+      {
+        if (initialSelection.get(i))
+          selectionModel.addSelectionInterval(i, i);
+      }
+  }
+
+  /**
+   * Returns the associated selection model.
+   *
+   * @return the associated selection model
+   */
+  ListSelectionModel getSelectionModel()
+  {
+    return selectionModel;
+  }
+}
diff --git a/libjava/classpath/javax/swing/text/html/StyleSheet.java b/libjava/classpath/javax/swing/text/html/StyleSheet.java
new file mode 100644
index 000000000..5cf015bc5
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/StyleSheet.java
@@ -0,0 +1,1455 @@
+/* StyleSheet.java --
+   Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import gnu.javax.swing.text.html.css.BorderWidth;
+import gnu.javax.swing.text.html.css.CSSColor;
+import gnu.javax.swing.text.html.css.CSSParser;
+import gnu.javax.swing.text.html.css.CSSParserCallback;
+import gnu.javax.swing.text.html.css.FontSize;
+import gnu.javax.swing.text.html.css.FontStyle;
+import gnu.javax.swing.text.html.css.FontWeight;
+import gnu.javax.swing.text.html.css.Length;
+import gnu.javax.swing.text.html.css.Selector;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.geom.Rectangle2D;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.border.Border;
+import javax.swing.event.ChangeListener;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Element;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyleContext;
+import javax.swing.text.View;
+
+
+/**
+ * This class adds support for defining the visual characteristics of HTML views
+ * being rendered. This enables views to be customized by a look-and-feel, mulitple
+ * views over the same model can be rendered differently. Each EditorPane has its
+ * own StyleSheet, but by default one sheet will be shared by all of the HTMLEditorKit
+ * instances. An HTMLDocument can also have a StyleSheet, which holds specific CSS
+ * specs.
+ *
+ *  In order for Views to store less state and therefore be more lightweight,
+ *  the StyleSheet can act as a factory for painters that handle some of the
+ *  rendering tasks. Since the StyleSheet may be used by views over multiple
+ *  documents the HTML attributes don't effect the selector being used.
+ *
+ *  The rules are stored as named styles, and other information is stored to
+ *  translate the context of an element to a rule.
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ */
+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.
+   */
+  class CSSStyleSheetParserCallback
+    implements CSSParserCallback
+  {
+    /**
+     * The current styles.
+     */
+    private CSSStyle[] styles;
+
+    /**
+     * The precedence of the stylesheet to be parsed.
+     */
+    private int precedence;
+
+    /**
+     * Creates a new CSS parser. This parser parses a CSS stylesheet with
+     * the specified precedence.
+     *
+     * @param prec the precedence, according to the constants defined in
+     *        CSSStyle
+     */
+    CSSStyleSheetParserCallback(int prec)
+    {
+      precedence = prec;
+    }
+
+    /**
+     * Called at the beginning of a statement.
+     *
+     * @param sel the selector
+     */
+    public void startStatement(Selector[] sel)
+    {
+      styles = new CSSStyle[sel.length];
+      for (int i = 0; i < sel.length; i++)
+        styles[i] = new CSSStyle(precedence, sel[i]);
+    }
+
+    /**
+     * Called at the end of a statement.
+     */
+    public void endStatement()
+    {
+      for (int i = 0; i < styles.length; i++)
+        css.add(styles[i]);
+      styles = null;
+    }
+
+    /**
+     * Called when a declaration is parsed.
+     *
+     * @param property the property
+     * @param value the value
+     */
+    public void declaration(String property, String value)
+    {
+      CSS.Attribute cssAtt = CSS.getAttribute(property);
+      Object val = CSS.getValue(cssAtt, value);
+      for (int i = 0; i < styles.length; i++)
+        {
+          CSSStyle style = styles[i];
+          CSS.addInternal(style, cssAtt, value);
+          if (cssAtt != null)
+            style.addAttribute(cssAtt, val);
+        }
+    }
+
+  }
+
+  /**
+   * Represents a style that is defined by a CSS rule.
+   */
+  private class CSSStyle
+    extends SimpleAttributeSet
+    implements Style, Comparable<CSSStyle>
+  {
+
+    static final int PREC_UA = 0;
+    static final int PREC_NORM = 100000;
+    static final int PREC_AUTHOR_NORMAL = 200000;
+    static final int PREC_AUTHOR_IMPORTANT = 300000;
+    static final int PREC_USER_IMPORTANT = 400000;
+
+    /**
+     * The priority of this style when matching CSS selectors.
+     */
+    private int precedence;
+
+    /**
+     * The selector for this rule.
+     *
+     * This is package private to avoid accessor methods.
+     */
+    Selector selector;
+
+    CSSStyle(int prec, Selector sel)
+    {
+      precedence = prec;
+      selector = sel;
+    }
+
+    public String getName()
+    {
+      // TODO: Implement this for correctness.
+      return null;
+    }
+
+    public void addChangeListener(ChangeListener listener)
+    {
+      // TODO: Implement this for correctness.
+    }
+
+    public void removeChangeListener(ChangeListener listener)
+    {
+      // TODO: Implement this for correctness.
+    }
+
+    /**
+     * Sorts the rule according to the style's precedence and the
+     * selectors specificity.
+     */
+    public int compareTo(CSSStyle other)
+    {
+      return other.precedence + other.selector.getSpecificity()
+             - precedence - selector.getSpecificity();
+    }
+
+  }
+
+  /** The base URL */
+  URL base;
+
+  /** Base font size (int) */
+  int baseFontSize;
+
+  /**
+   * The linked style sheets stored.
+   */
+  private ArrayList<StyleSheet> linked;
+
+  /**
+   * Maps element names (selectors) to AttributSet (the corresponding style
+   * information).
+   */
+  ArrayList<CSSStyle> css = new ArrayList<CSSStyle>();
+
+  /**
+   * Maps selectors to their resolved styles.
+   */
+  private HashMap<String,Style> resolvedStyles;
+
+  /**
+   * Constructs a StyleSheet.
+   */
+  public StyleSheet()
+  {
+    super();
+    baseFontSize = 4; // Default font size from CSS
+    resolvedStyles = new HashMap<String,Style>();
+  }
+
+  /**
+   * Gets the style used to render the given tag. The element represents the tag
+   * and can be used to determine the nesting, where the attributes will differ
+   * if there is nesting inside of elements.
+   *
+   * @param t - the tag to translate to visual attributes
+   * @param e - the element representing the tag
+   * @return the set of CSS attributes to use to render the tag.
+   */
+  public Style getRule(HTML.Tag t, Element e)
+  {
+    // Create list of the element and all of its parents, starting
+    // with the bottommost element.
+    ArrayList<Element> path = new ArrayList<Element>();
+    Element el;
+    AttributeSet atts;
+    for (el = e; el != null; el = el.getParentElement())
+      path.add(el);
+
+    // Create fully qualified selector.
+    StringBuilder selector = new StringBuilder();
+    int count = path.size();
+    // We append the actual element after this loop.
+    for (int i = count - 1; i > 0; i--)
+      {
+        el = path.get(i);
+        atts = el.getAttributes();
+        Object name = atts.getAttribute(StyleConstants.NameAttribute);
+        selector.append(name.toString());
+        if (atts.isDefined(HTML.Attribute.ID))
+          {
+            selector.append('#');
+            selector.append(atts.getAttribute(HTML.Attribute.ID));
+          }
+        if (atts.isDefined(HTML.Attribute.CLASS))
+          {
+            selector.append('.');
+            selector.append(atts.getAttribute(HTML.Attribute.CLASS));
+          }
+        if (atts.isDefined(HTML.Attribute.DYNAMIC_CLASS))
+          {
+            selector.append(':');
+            selector.append(atts.getAttribute(HTML.Attribute.DYNAMIC_CLASS));
+          }
+        if (atts.isDefined(HTML.Attribute.PSEUDO_CLASS))
+          {
+            selector.append(':');
+            selector.append(atts.getAttribute(HTML.Attribute.PSEUDO_CLASS));
+          }
+        selector.append(' ');
+      }
+    selector.append(t.toString());
+    el = path.get(0);
+    atts = el.getAttributes();
+    // For leaf elements, we have to fetch the tag specific attributes.
+    if (el.isLeaf())
+      {
+        Object o = atts.getAttribute(t);
+        if (o instanceof AttributeSet)
+          atts = (AttributeSet) o;
+        else
+          atts = null;
+      }
+    if (atts != null)
+      {
+        if (atts.isDefined(HTML.Attribute.ID))
+          {
+            selector.append('#');
+            selector.append(atts.getAttribute(HTML.Attribute.ID));
+          }
+        if (atts.isDefined(HTML.Attribute.CLASS))
+          {
+            selector.append('.');
+            selector.append(atts.getAttribute(HTML.Attribute.CLASS));
+          }
+        if (atts.isDefined(HTML.Attribute.DYNAMIC_CLASS))
+          {
+            selector.append(':');
+            selector.append(atts.getAttribute(HTML.Attribute.DYNAMIC_CLASS));
+          }
+        if (atts.isDefined(HTML.Attribute.PSEUDO_CLASS))
+          {
+            selector.append(':');
+            selector.append(atts.getAttribute(HTML.Attribute.PSEUDO_CLASS));
+          }
+      }
+    return getResolvedStyle(selector.toString(), path, t);
+  }
+
+  /**
+   * Fetches a resolved style. If there is no resolved style for the
+   * specified selector, the resolve the style using
+   * {@link #resolveStyle(String, List, HTML.Tag)}.
+   *
+   * @param selector the selector for which to resolve the style
+   * @param path the Element path, used in the resolving algorithm
+   * @param tag the tag for which to resolve
+   *
+   * @return the resolved style
+   */
+  private Style getResolvedStyle(String selector, List<Element> path, HTML.Tag tag)
+  {
+    Style style = resolvedStyles.get(selector);
+    if (style == null)
+      style = resolveStyle(selector, path, tag);
+    return style;
+  }
+
+  /**
+   * Resolves a style. This creates arrays that hold the tag names,
+   * class and id attributes and delegates the work to
+   * {@link #resolveStyle(String, String[], List<Map<String,String>>)}.
+   *
+   * @param selector the selector
+   * @param path the Element path
+   * @param tag the tag
+   *
+   * @return the resolved style
+   */
+  private Style resolveStyle(String selector, List<Element> path, HTML.Tag tag)
+  {
+    int count = path.size();
+    String[] tags = new String[count];
+    List<Map<String,String>> attributes =
+      new ArrayList<Map<String,String>>(count);
+    for (int i = 0; i < count; i++)
+      {
+        Element el = path.get(i);
+        AttributeSet atts = el.getAttributes();
+        if (i == 0 && el.isLeaf())
+          {
+            Object o = atts.getAttribute(tag);
+            if (o instanceof AttributeSet)
+              atts = (AttributeSet) o;
+            else
+              atts = null;
+          }
+        if (atts != null)
+          {
+            HTML.Tag t =
+              (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute);
+            if (t != null)
+              tags[i] = t.toString();
+            else
+              tags[i] = null;
+            attributes.set(i, attributeSetToMap(atts));
+          }
+        else
+          {
+            tags[i] = null;
+          }
+      }
+    tags[0] = tag.toString();
+    return resolveStyle(selector, tags, attributes);
+  }
+
+  /**
+   * Performs style resolving.
+   *
+   * @param selector the selector
+   * @param tags the tags
+   * @param attributes the attributes of the tags
+   *
+   * @return the resolved style
+   */
+  private Style resolveStyle(String selector, String[] tags,
+                             List<Map<String,String>> attributes)
+  {
+    // FIXME: This style resolver is not correct. But it works good enough for
+    // the default.css.
+    ArrayList<CSSStyle> styles = new ArrayList<CSSStyle>();
+    for (CSSStyle style : css)
+      {
+        if (style.selector.matches(tags, attributes))
+          styles.add(style);
+      }
+
+    // Add styles from linked stylesheets.
+    if (linked != null)
+      {
+        for (int i = linked.size() - 1; i >= 0; i--)
+          {
+            StyleSheet ss = linked.get(i);
+            for (int j = ss.css.size() - 1; j >= 0; j--)
+              {
+                CSSStyle style = ss.css.get(j);
+                if (style.selector.matches(tags, attributes))
+                  styles.add(style);
+              }
+          }
+      }
+
+    // Sort selectors.
+    Collections.sort(styles);
+    Style[] styleArray = styles.toArray(new Style[styles.size()]);
+    Style resolved = new MultiStyle(selector, styleArray);
+    resolvedStyles.put(selector, resolved);
+    return resolved;
+  }
+
+  /**
+   * Gets the rule that best matches the selector. selector is a space
+   * separated String of element names. The attributes of the returned
+   * Style will change as rules are added and removed.
+   *
+   * @param selector - the element names separated by spaces
+   * @return the set of CSS attributes to use to render
+   */
+  public Style getRule(String selector)
+  {
+    CSSStyle best = null;
+    for (Iterator<CSSStyle> i = css.iterator(); i.hasNext();)
+      {
+        CSSStyle style = i.next();
+        if (style.compareTo(best) < 0)
+          best = style;
+      }
+    return best;
+  }
+
+  /**
+   * Adds a set of rules to the sheet. The rules are expected to be in valid
+   * CSS format. This is called as a result of parsing a <style> tag
+   *
+   * @param rule - the rule to add to the sheet
+   */
+  public void addRule(String rule)
+  {
+    CSSStyleSheetParserCallback cb =
+      new CSSStyleSheetParserCallback(CSSStyle.PREC_AUTHOR_NORMAL);
+    // 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 don't let it bork the outside code.
+      }
+    // Clean up resolved styles cache so that the new styles are recognized
+    // on next stylesheet request.
+    resolvedStyles.clear();
+  }
+
+  /**
+   * Translates a CSS declaration into an AttributeSet. This is called
+   * as a result of encountering an HTML style attribute.
+   *
+   * @param decl - the declaration to get
+   * @return the AttributeSet representing the declaration
+   */
+  public AttributeSet getDeclaration(String decl)
+  {
+    if (decl == null)
+      return SimpleAttributeSet.EMPTY;
+    // FIXME: Not implemented.
+    return null;
+  }
+
+  /**
+   * Loads a set of rules that have been specified in terms of CSS grammar.
+   * If there are any conflicts with existing rules, the new rule is added.
+   *
+   * @param in - the stream to read the CSS grammar from.
+   * @param ref - the reference URL. It is the location of the stream, it may
+   * be null. All relative URLs specified in the stream will be based upon this
+   * parameter.
+   * @throws IOException - For any IO error while reading
+   */
+  public void loadRules(Reader in, URL ref)
+    throws IOException
+  {
+    CSSStyleSheetParserCallback cb =
+      new CSSStyleSheetParserCallback(CSSStyle.PREC_UA);
+    // FIXME: Handle ref.
+    CSSParser parser = new CSSParser(in, cb);
+    parser.parse();
+  }
+
+  /**
+   * Gets a set of attributes to use in the view. This is a set of
+   * attributes that can be used for View.getAttributes
+   *
+   * @param v - the view to get the set for
+   * @return the AttributeSet to use in the view.
+   */
+  public AttributeSet getViewAttributes(View v)
+  {
+    return new ViewAttributeSet(v, this);
+  }
+
+  /**
+   * Removes a style previously added.
+   *
+   * @param nm - the name of the style to remove
+   */
+  public void removeStyle(String nm)
+  {
+    // FIXME: Not implemented.
+    super.removeStyle(nm);
+  }
+
+  /**
+   * Adds the rules from ss to those of the receiver. ss's rules will
+   * override the old rules. An added StyleSheet will never override the rules
+   * of the receiving style sheet.
+   *
+   * @param ss - the new StyleSheet.
+   */
+  public void addStyleSheet(StyleSheet ss)
+  {
+    if (linked == null)
+      linked = new ArrayList<StyleSheet>();
+    linked.add(ss);
+  }
+
+  /**
+   * Removes ss from those of the receiver
+   *
+   * @param ss - the StyleSheet to remove.
+   */
+  public void removeStyleSheet(StyleSheet ss)
+  {
+    if (linked != null)
+      {
+        linked.remove(ss);
+      }
+  }
+
+  /**
+   * Returns an array of the linked StyleSheets. May return null.
+   *
+   * @return - An array of the linked StyleSheets.
+   */
+  public StyleSheet[] getStyleSheets()
+  {
+    StyleSheet[] linkedSS;
+    if (linked != null)
+      {
+        linkedSS = new StyleSheet[linked.size()];
+        linkedSS = linked.toArray(linkedSS);
+      }
+    else
+      {
+        linkedSS = null;
+      }
+    return linkedSS;
+  }
+
+  /**
+   * Imports a style sheet from the url. The rules are directly added to the
+   * receiver. This is usually called when a <link> tag is resolved in an
+   * HTML document.
+   *
+   * @param url the URL to import the StyleSheet from
+   */
+  public void importStyleSheet(URL url)
+  {
+    try
+      {
+        InputStream in = url.openStream();
+        Reader r = new BufferedReader(new InputStreamReader(in));
+        CSSStyleSheetParserCallback cb =
+          new CSSStyleSheetParserCallback(CSSStyle.PREC_AUTHOR_NORMAL);
+        CSSParser parser = new CSSParser(r, cb);
+        parser.parse();
+      }
+    catch (IOException ex)
+      {
+        // We can't do anything about it I guess.
+      }
+  }
+
+  /**
+   * Sets the base url. All import statements that are relative, will be
+   * relative to base.
+   *
+   * @param base -
+   *          the base URL.
+   */
+  public void setBase(URL base)
+  {
+    this.base = base;
+  }
+
+  /**
+   * Gets the base url.
+   *
+   * @return - the base
+   */
+  public URL getBase()
+  {
+    return base;
+  }
+
+  /**
+   * Adds a CSS attribute to the given set.
+   *
+   * @param attr - the attribute set
+   * @param key - the attribute to add
+   * @param value - the value of the key
+   */
+  public void addCSSAttribute(MutableAttributeSet attr, CSS.Attribute key,
+                              String value)
+  {
+    Object val = CSS.getValue(key, value);
+    CSS.addInternal(attr, key, value);
+    attr.addAttribute(key, val);
+  }
+
+  /**
+   * Adds a CSS attribute to the given set.
+   * This method parses the value argument from HTML based on key.
+   * Returns true if it finds a valid value for the given key,
+   * and false otherwise.
+   *
+   * @param attr - the attribute set
+   * @param key - the attribute to add
+   * @param value - the value of the key
+   * @return true if a valid value was found.
+   */
+  public boolean addCSSAttributeFromHTML(MutableAttributeSet attr, CSS.Attribute key,
+                                         String value)
+  {
+    // FIXME: Need to parse value from HTML based on key.
+    attr.addAttribute(key, value);
+    return attr.containsAttribute(key, value);
+  }
+
+  /**
+   * Converts a set of HTML attributes to an equivalent set of CSS attributes.
+   *
+   * @param htmlAttrSet - the set containing the HTML attributes.
+   * @return the set of CSS attributes
+   */
+  public AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet)
+  {
+    AttributeSet cssAttr = htmlAttrSet.copyAttributes();
+
+    // The HTML align attribute maps directly to the CSS text-align attribute.
+    Object o = htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
+    if (o != null)
+      cssAttr = addAttribute(cssAttr, CSS.Attribute.TEXT_ALIGN, o);
+
+    // The HTML width attribute maps directly to CSS width.
+    o = htmlAttrSet.getAttribute(HTML.Attribute.WIDTH);
+    if (o != null)
+      cssAttr = addAttribute(cssAttr, CSS.Attribute.WIDTH,
+                             new Length(o.toString()));
+
+    // The HTML height attribute maps directly to CSS height.
+    o = htmlAttrSet.getAttribute(HTML.Attribute.HEIGHT);
+    if (o != null)
+      cssAttr = addAttribute(cssAttr, CSS.Attribute.HEIGHT,
+                             new Length(o.toString()));
+
+    o = htmlAttrSet.getAttribute(HTML.Attribute.NOWRAP);
+    if (o != null)
+      cssAttr = addAttribute(cssAttr, CSS.Attribute.WHITE_SPACE, "nowrap");
+
+    // Map cellspacing attr of tables to CSS border-spacing.
+    o = htmlAttrSet.getAttribute(HTML.Attribute.CELLSPACING);
+    if (o != null)
+      cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_SPACING,
+                             new Length(o.toString()));
+
+    // For table cells and headers, fetch the cellpadding value from the
+    // parent table and set it as CSS padding attribute.
+    HTML.Tag tag = (HTML.Tag)
+                   htmlAttrSet.getAttribute(StyleConstants.NameAttribute);
+    if ((tag == HTML.Tag.TD || tag == HTML.Tag.TH)
+        && htmlAttrSet instanceof Element)
+      {
+        Element el = (Element) htmlAttrSet;
+        AttributeSet tableAttrs = el.getParentElement().getParentElement()
+                                  .getAttributes();
+        o = tableAttrs.getAttribute(HTML.Attribute.CELLPADDING);
+        if (o != null)
+          {
+            Length l = new Length(o.toString());
+            cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_BOTTOM, l);
+            cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_LEFT, l);
+            cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_RIGHT, l);
+            cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_TOP, l);
+          }
+        o = tableAttrs.getAttribute(HTML.Attribute.BORDER);
+        cssAttr = translateBorder(cssAttr, o);
+      }
+
+    // Translate border attribute.
+    o = cssAttr.getAttribute(HTML.Attribute.BORDER);
+    cssAttr = translateBorder(cssAttr, o);
+
+    // TODO: Add more mappings.
+    return cssAttr;
+  }
+
+  /**
+   * Translates a HTML border attribute to a corresponding set of CSS
+   * attributes.
+   *
+   * @param cssAttr the original set of CSS attributes to add to
+   * @param o the value of the border attribute
+   *
+   * @return the new set of CSS attributes
+   */
+  private AttributeSet translateBorder(AttributeSet cssAttr, Object o)
+  {
+    if (o != null)
+      {
+        BorderWidth l = new BorderWidth(o.toString());
+        if (l.getValue() > 0)
+          {
+            cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_WIDTH, l);
+            cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_STYLE,
+                                   "solid");
+            cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_COLOR,
+                                   new CSSColor("black"));
+          }
+      }
+    return cssAttr;
+  }
+
+  /**
+   * Adds an attribute to the given set and returns a new set. This is implemented
+   * to convert StyleConstants attributes to CSS before forwarding them to the superclass.
+   * The StyleConstants attribute do not have corresponding CSS entry, the attribute
+   * is stored (but will likely not be used).
+   *
+   * @param old - the old set
+   * @param key - the non-null attribute key
+   * @param value - the attribute value
+   * @return the updated set
+   */
+  public AttributeSet addAttribute(AttributeSet old, Object key,
+                                   Object value)
+  {
+    // FIXME: Not implemented.
+    return super.addAttribute(old, key, value);
+  }
+
+  /**
+   * Adds a set of attributes to the element. If any of these attributes are
+   * StyleConstants, they will be converted to CSS before forwarding to the
+   * superclass.
+   *
+   * @param old - the old set
+   * @param attr - the attributes to add
+   * @return the updated attribute set
+   */
+  public AttributeSet addAttributes(AttributeSet old, AttributeSet attr)
+  {
+    // FIXME: Not implemented.
+    return super.addAttributes(old, attr);
+  }
+
+  /**
+   * Removes an attribute from the set. If the attribute is a
+   * StyleConstants, it will be converted to CSS before forwarding to the
+   * superclass.
+   *
+   * @param old - the old set
+   * @param key - the non-null attribute key
+   * @return the updated set
+   */
+  public AttributeSet removeAttribute(AttributeSet old, Object key)
+  {
+    // FIXME: Not implemented.
+    return super.removeAttribute(old, key);
+  }
+
+  /**
+   * Removes an attribute from the set. If any of the attributes are
+   * StyleConstants, they will be converted to CSS before forwarding to the
+   * superclass.
+   *
+   * @param old - the old set
+   * @param attrs - the attributes to remove
+   * @return the updated set
+   */
+  public AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs)
+  {
+    // FIXME: Not implemented.
+    return super.removeAttributes(old, attrs);
+  }
+
+  /**
+   * Removes a set of attributes for the element. If any of the attributes is a
+   * StyleConstants, they will be converted to CSS before forwarding to the
+   * superclass.
+   *
+   * @param old - the old attribute set
+   * @param names - the attribute names
+   * @return the update attribute set
+   */
+  public AttributeSet removeAttributes(AttributeSet old, Enumeration<?> names)
+  {
+    // FIXME: Not implemented.
+    return super.removeAttributes(old, names);
+  }
+
+  /**
+   * Creates a compact set of attributes that might be shared. This is a hook
+   * for subclasses that want to change the behaviour of SmallAttributeSet.
+   *
+   * @param a - the set of attributes to be represented in the compact form.
+   * @return the set of attributes created
+   */
+  protected StyleContext.SmallAttributeSet createSmallAttributeSet(AttributeSet a)
+  {
+    return super.createSmallAttributeSet(a);
+  }
+
+  /**
+   * Creates a large set of attributes. This set is not shared. This is a hook
+   * for subclasses that want to change the behaviour of the larger attribute
+   * storage format.
+   *
+   * @param a - the set of attributes to be represented in the larger form.
+   * @return the large set of attributes.
+   */
+  protected MutableAttributeSet createLargeAttributeSet(AttributeSet a)
+  {
+    return super.createLargeAttributeSet(a);
+  }
+
+  /**
+   * Gets the font to use for the given set.
+   *
+   * @param a - the set to get the font for.
+   * @return the font for the set
+   */
+  public Font getFont(AttributeSet a)
+  {
+    int realSize = getFontSize(a);
+
+    // Decrement size for subscript and superscript.
+    Object valign = a.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
+    if (valign != null)
+      {
+        String v = valign.toString();
+        if (v.contains("sup") || v.contains("sub"))
+          realSize -= 2;
+      }
+
+    // TODO: Convert font family.
+    String family = "SansSerif";
+
+    int style = Font.PLAIN;
+    FontWeight weight = (FontWeight) a.getAttribute(CSS.Attribute.FONT_WEIGHT);
+    if (weight != null)
+      style |= weight.getValue();
+    FontStyle fStyle = (FontStyle) a.getAttribute(CSS.Attribute.FONT_STYLE);
+    if (fStyle != null)
+      style |= fStyle.getValue();
+    return new Font(family, style, realSize);
+  }
+
+  /**
+   * Determines the EM base value based on the specified attributes.
+   *
+   * @param atts the attibutes
+   *
+   * @return the EM base value
+   */
+  float getEMBase(AttributeSet atts)
+  {
+    Font font = getFont(atts);
+    FontRenderContext ctx = new FontRenderContext(null, false, false);
+    Rectangle2D bounds = font.getStringBounds("M", ctx);
+    return (float) bounds.getWidth();
+  }
+
+  /**
+   * Determines the EX base value based on the specified attributes.
+   *
+   * @param atts the attibutes
+   *
+   * @return the EX base value
+   */
+  float getEXBase(AttributeSet atts)
+  {
+    Font font = getFont(atts);
+    FontRenderContext ctx = new FontRenderContext(null, false, false);
+    Rectangle2D bounds = font.getStringBounds("x", ctx);
+    return (float) bounds.getHeight();
+  }
+
+  /**
+   * Resolves the fontsize for a given set of attributes.
+   *
+   * @param atts the attributes
+   *
+   * @return the resolved font size
+   */
+  private int getFontSize(AttributeSet atts)
+  {
+    int size = 12;
+    if (atts.isDefined(CSS.Attribute.FONT_SIZE))
+      {
+        FontSize fs = (FontSize) atts.getAttribute(CSS.Attribute.FONT_SIZE);
+        if (fs.isRelative())
+          {
+            int parSize = 12;
+            AttributeSet resolver = atts.getResolveParent();
+            if (resolver != null)
+              parSize = getFontSize(resolver);
+            size = fs.getValue(parSize);
+          }
+        else
+          {
+            size = fs.getValue();
+          }
+      }
+    else
+      {
+        AttributeSet resolver = atts.getResolveParent();
+        if (resolver != null)
+          size = getFontSize(resolver);
+      }
+    return size;
+  }
+
+  /**
+   * Takes a set of attributes and turns it into a foreground
+   * color specification. This is used to specify things like, brigher, more hue
+   * etc.
+   *
+   * @param a - the set to get the foreground color for
+   * @return the foreground color for the set
+   */
+  public Color getForeground(AttributeSet a)
+  {
+    CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.COLOR);
+    Color color = null;
+    if (c != null)
+      color = c.getValue();
+    return color;
+  }
+
+  /**
+   * Takes a set of attributes and turns it into a background
+   * color specification. This is used to specify things like, brigher, more hue
+   * etc.
+   *
+   * @param a - the set to get the background color for
+   * @return the background color for the set
+   */
+  public Color getBackground(AttributeSet a)
+  {
+    CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.BACKGROUND_COLOR);
+    Color color = null;
+    if (c != null)
+      color = c.getValue();
+    return color;
+  }
+
+  /**
+   * Gets the box formatter to use for the given set of CSS attributes.
+   *
+   * @param a - the given set
+   * @return the box formatter
+   */
+  public BoxPainter getBoxPainter(AttributeSet a)
+  {
+    return new BoxPainter(a, this);
+  }
+
+  /**
+   * Gets the list formatter to use for the given set of CSS attributes.
+   *
+   * @param a - the given set
+   * @return the list formatter
+   */
+  public ListPainter getListPainter(AttributeSet a)
+  {
+    return new ListPainter(a, this);
+  }
+
+  /**
+   * Sets the base font size between 1 and 7.
+   *
+   * @param sz - the new font size for the base.
+   */
+  public void setBaseFontSize(int sz)
+  {
+    if (sz <= 7 && sz >= 1)
+      baseFontSize = sz;
+  }
+
+  /**
+   * Sets the base font size from the String. It can either identify
+   * a specific font size (between 1 and 7) or identify a relative
+   * font size such as +1 or -2.
+   *
+   * @param size - the new font size as a String.
+   */
+  public void setBaseFontSize(String size)
+  {
+    size = size.trim();
+    int temp = 0;
+    try
+      {
+        if (size.length() == 2)
+          {
+            int i = new Integer(size.substring(1)).intValue();
+            if (size.startsWith("+"))
+              temp = baseFontSize + i;
+            else if (size.startsWith("-"))
+              temp = baseFontSize - i;
+          }
+        else if (size.length() == 1)
+          temp = new Integer(size.substring(0)).intValue();
+
+        if (temp <= 7 && temp >= 1)
+          baseFontSize = temp;
+      }
+    catch (NumberFormatException nfe)
+      {
+        // Do nothing here
+      }
+  }
+
+  /**
+   * TODO
+   *
+   * @param pt - TODO
+   * @return TODO
+   */
+  public static int getIndexOfSize(float pt)
+  {
+    // FIXME: Not implemented.
+    return 0;
+  }
+
+  /**
+   * Gets the point size, given a size index.
+   *
+   * @param index - the size index
+   * @return the point size.
+   */
+  public float getPointSize(int index)
+  {
+    // FIXME: Not implemented.
+    return 0;
+  }
+
+  /**
+   * Given the string of the size, returns the point size value.
+   *
+   * @param size - the string representation of the size.
+   * @return - the point size value.
+   */
+  public float getPointSize(String size)
+  {
+    // FIXME: Not implemented.
+    return 0;
+  }
+
+  /**
+   * Convert the color string represenation into java.awt.Color. The valid
+   * values are like "aqua" , "#00FFFF" or "rgb(1,6,44)".
+   *
+   * @param colorName the color to convert.
+   * @return the matching java.awt.color
+   */
+  public Color stringToColor(String colorName)
+  {
+    return CSSColor.convertValue(colorName);
+  }
+
+  /**
+   * This class carries out some of the duties of CSS formatting. This enables views
+   * to present the CSS formatting while not knowing how the CSS values are cached.
+   *
+   * This object is reponsible for the insets of a View and making sure
+   * the background is maintained according to the CSS attributes.
+   *
+   * @author Lillian Angel (langel@redhat.com)
+   */
+  public static class BoxPainter extends Object implements Serializable
+  {
+
+    /**
+     * The left inset.
+     */
+    private float leftInset;
+
+    /**
+     * The right inset.
+     */
+    private float rightInset;
+
+    /**
+     * The top inset.
+     */
+    private float topInset;
+
+    /**
+     * The bottom inset.
+     */
+    private float bottomInset;
+
+    /**
+     * The border of the box.
+     */
+    private Border border;
+
+    private float leftPadding;
+    private float rightPadding;
+    private float topPadding;
+    private float bottomPadding;
+
+    /**
+     * The background color.
+     */
+    private Color background;
+
+    /**
+     * Package-private constructor.
+     *
+     * @param as - AttributeSet for painter
+     */
+    BoxPainter(AttributeSet as, StyleSheet ss)
+    {
+      float emBase = ss.getEMBase(as);
+      float exBase = ss.getEXBase(as);
+      // Fetch margins.
+      Length l = (Length) as.getAttribute(CSS.Attribute.MARGIN_LEFT);
+      if (l != null)
+        {
+          l.setFontBases(emBase, exBase);
+          leftInset = l.getValue();
+        }
+      l = (Length) as.getAttribute(CSS.Attribute.MARGIN_RIGHT);
+      if (l != null)
+        {
+          l.setFontBases(emBase, exBase);
+          rightInset = l.getValue();
+        }
+      l = (Length) as.getAttribute(CSS.Attribute.MARGIN_TOP);
+      if (l != null)
+        {
+          l.setFontBases(emBase, exBase);
+          topInset = l.getValue();
+        }
+      l = (Length) as.getAttribute(CSS.Attribute.MARGIN_BOTTOM);
+      if (l != null)
+        {
+          l.setFontBases(emBase, exBase);
+          bottomInset = l.getValue();
+        }
+
+      // Fetch padding.
+      l = (Length) as.getAttribute(CSS.Attribute.PADDING_LEFT);
+      if (l != null)
+        {
+          l.setFontBases(emBase, exBase);
+          leftPadding = l.getValue();
+        }
+      l = (Length) as.getAttribute(CSS.Attribute.PADDING_RIGHT);
+      if (l != null)
+        {
+          l.setFontBases(emBase, exBase);
+          rightPadding = l.getValue();
+        }
+      l = (Length) as.getAttribute(CSS.Attribute.PADDING_TOP);
+      if (l != null)
+        {
+          l.setFontBases(emBase, exBase);
+          topPadding = l.getValue();
+        }
+      l = (Length) as.getAttribute(CSS.Attribute.PADDING_BOTTOM);
+      if (l != null)
+        {
+          l.setFontBases(emBase, exBase);
+          bottomPadding = l.getValue();
+        }
+
+      // Determine border.
+      border = new CSSBorder(as, ss);
+
+      // Determine background.
+      background = ss.getBackground(as);
+
+    }
+
+
+    /**
+     * Gets the inset needed on a given side to account for the margin, border
+     * and padding.
+     *
+     * @param size - the size of the box to get the inset for. View.TOP, View.LEFT,
+     * View.BOTTOM or View.RIGHT.
+     * @param v - the view making the request. This is used to get the AttributeSet,
+     * amd may be used to resolve percentage arguments.
+     * @return the inset
+     * @throws IllegalArgumentException - for an invalid direction.
+     */
+    public float getInset(int size, View v)
+    {
+      float inset;
+      switch (size)
+        {
+        case View.TOP:
+          inset = topInset;
+          if (border != null)
+            inset += border.getBorderInsets(null).top;
+          inset += topPadding;
+          break;
+        case View.BOTTOM:
+          inset = bottomInset;
+          if (border != null)
+            inset += border.getBorderInsets(null).bottom;
+          inset += bottomPadding;
+          break;
+        case View.LEFT:
+          inset = leftInset;
+          if (border != null)
+            inset += border.getBorderInsets(null).left;
+          inset += leftPadding;
+          break;
+        case View.RIGHT:
+          inset = rightInset;
+          if (border != null)
+            inset += border.getBorderInsets(null).right;
+          inset += rightPadding;
+          break;
+        default:
+          inset = 0.0F;
+      }
+      return inset;
+    }
+
+    /**
+     * Paints the CSS box according to the attributes given. This should
+     * paint the border, padding and background.
+     *
+     * @param g - the graphics configuration
+     * @param x - the x coordinate
+     * @param y - the y coordinate
+     * @param w - the width of the allocated area
+     * @param h - the height of the allocated area
+     * @param v - the view making the request
+     */
+    public void paint(Graphics g, float x, float y, float w, float h, View v)
+    {
+      int inX = (int) (x + leftInset);
+      int inY = (int) (y + topInset);
+      int inW = (int) (w - leftInset - rightInset);
+      int inH = (int) (h - topInset - bottomInset);
+      if (background != null)
+        {
+          g.setColor(background);
+          g.fillRect(inX, inY, inW, inH);
+        }
+      if (border != null)
+        {
+          border.paintBorder(null, g, inX, inY, inW, inH);
+        }
+    }
+  }
+
+  /**
+   * This class carries out some of the CSS list formatting duties. Implementations
+   * of this class enable views to present the CSS formatting while not knowing anything
+   * about how the CSS values are being cached.
+   *
+   * @author Lillian Angel (langel@redhat.com)
+   */
+  public static class ListPainter implements Serializable
+  {
+
+    /**
+     * Attribute set for painter
+     */
+    private AttributeSet attributes;
+
+    /**
+     * The associated style sheet.
+     */
+    private StyleSheet styleSheet;
+
+    /**
+     * The bullet type.
+     */
+    private String type;
+
+    /**
+     * Package-private constructor.
+     *
+     * @param as - AttributeSet for painter
+     */
+    ListPainter(AttributeSet as, StyleSheet ss)
+    {
+      attributes = as;
+      styleSheet = ss;
+      type = (String) as.getAttribute(CSS.Attribute.LIST_STYLE_TYPE);
+    }
+
+    /**
+     * Cached rectangle re-used in the paint method below.
+     */
+    private final Rectangle tmpRect = new Rectangle();
+
+    /**
+     * Paints the CSS list decoration according to the attributes given.
+     *
+     * @param g - the graphics configuration
+     * @param x - the x coordinate
+     * @param y - the y coordinate
+     * @param w - the width of the allocated area
+     * @param h - the height of the allocated area
+     * @param v - the view making the request
+     * @param item - the list item to be painted >=0.
+     */
+    public void paint(Graphics g, float x, float y, float w, float h, View v,
+                      int item)
+    {
+      // FIXME: This is a very simplistic list rendering. We still need
+      // to implement different bullet types (see type field) and custom
+      // bullets via images.
+      View itemView = v.getView(item);
+      AttributeSet viewAtts = itemView.getAttributes();
+      Object tag = viewAtts.getAttribute(StyleConstants.NameAttribute);
+      // Only paint something here when the child view is an LI tag
+      // and the calling view is some of the list tags then).
+      if (tag != null && tag == HTML.Tag.LI)
+        {
+          g.setColor(Color.BLACK);
+          int centerX = (int) (x - 12);
+          int centerY = -1;
+          // For paragraphs (almost all cases) center bullet vertically
+          // in the middle of the first line.
+          tmpRect.setBounds((int) x, (int) y, (int) w, (int) h);
+          if (itemView.getViewCount() > 0)
+            {
+              View v1 = itemView.getView(0);
+              if (v1 instanceof ParagraphView && v1.getViewCount() > 0)
+                {
+                  Shape a1 = itemView.getChildAllocation(0, tmpRect);
+                  Rectangle r1 = a1 instanceof Rectangle ? (Rectangle) a1
+                                                         : a1.getBounds();
+                  ParagraphView par = (ParagraphView) v1;
+                  Shape a = par.getChildAllocation(0, r1);
+                  if (a != null)
+                    {
+                      Rectangle r = a instanceof Rectangle ? (Rectangle) a
+                                                           : a.getBounds();
+                      centerY = (int) (r.height / 2 + r.y);
+                    }
+                }
+            }
+          if (centerY == -1)
+            {
+              centerY =(int) (h / 2 + y);
+            }
+          g.fillOval(centerX - 3, centerY - 3, 6, 6);
+        }
+    }
+  }
+
+  /**
+   * Converts an AttributeSet to a Map. This is used for CSS resolving.
+   *
+   * @param atts the attributes to convert
+   *
+   * @return the converted map
+   */
+  private Map<String,String> attributeSetToMap(AttributeSet atts)
+  {
+    HashMap<String,String> map = new HashMap<String,String>();
+    Enumeration<?> keys = atts.getAttributeNames();
+    while (keys.hasMoreElements())
+      {
+        Object key = keys.nextElement();
+        Object value = atts.getAttribute(key);
+        map.put(key.toString(), value.toString());
+      }
+    return map;
+  }
+}
diff --git a/libjava/classpath/javax/swing/text/html/TableView.java b/libjava/classpath/javax/swing/text/html/TableView.java
new file mode 100644
index 000000000..2ad1b6d38
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/TableView.java
@@ -0,0 +1,974 @@
+/* TableView.java -- A table view for HTML tables
+   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.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+
+import gnu.javax.swing.text.html.css.Length;
+
+import javax.swing.SizeRequirements;
+import javax.swing.event.DocumentEvent;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.View;
+import javax.swing.text.ViewFactory;
+
+/**
+ * A view implementation that renders HTML tables.
+ *
+ * This is basically a vertical BoxView that contains the rows of the table
+ * and the rows are horizontal BoxViews that contain the actual columns.
+ */
+class TableView
+  extends BlockView
+  implements ViewFactory
+{
+
+  /**
+   * Represents a single table row.
+   */
+  class RowView
+    extends BlockView
+  {
+    /**
+     * Has true at column positions where an above row's cell overlaps into
+     * this row.
+     */
+    boolean[] overlap;
+
+    /**
+     * Stores the row index of this row.
+     */
+    int rowIndex;
+
+    /**
+     * Creates a new RowView.
+     *
+     * @param el the element for the row view
+     */
+    RowView(Element el)
+    {
+      super(el, X_AXIS);
+    }
+
+    public void replace(int offset, int len, View[] views)
+    {
+      gridValid = false;
+      super.replace(offset, len, views);
+    }
+
+    /**
+     * Overridden to make rows not resizable along the Y axis.
+     */
+    public float getMaximumSpan(int axis)
+    {
+      float span;
+      if (axis == Y_AXIS)
+        span = super.getPreferredSpan(axis);
+      else
+        span = Integer.MAX_VALUE;
+      return span;
+    }
+
+    public float getMinimumSpan(int axis)
+    {
+      float span;
+      if (axis == X_AXIS)
+        span = totalColumnRequirements.minimum;
+      else
+        span = super.getMinimumSpan(axis);
+      return span;
+    }
+
+    public float getPreferredSpan(int axis)
+    {
+      float span;
+      if (axis == X_AXIS)
+        span = totalColumnRequirements.preferred;
+      else
+        span = super.getPreferredSpan(axis);
+      return span;
+    }
+
+    /**
+     * Calculates the overall size requirements for the row along the
+     * major axis. This will be the sum of the column requirements.
+     */
+    protected SizeRequirements calculateMajorAxisRequirements(int axis,
+                                                            SizeRequirements r)
+    {
+      if (r == null)
+        r = new SizeRequirements();
+      int adjust = (columnRequirements.length + 1) * cellSpacing;
+      r.minimum = totalColumnRequirements.minimum + adjust;
+      r.preferred = totalColumnRequirements.preferred + adjust;
+      r.maximum = totalColumnRequirements.maximum + adjust;
+      r.alignment = 0.0F;
+      return r;
+    }
+
+    /**
+     * Lays out the columns in this row.
+     */
+    protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+                                   int spans[])
+    {
+      super.layoutMinorAxis(targetSpan, axis, offsets, spans);
+
+      // Adjust columns that have rowSpan > 1.
+      int numCols = getViewCount();
+      for (int i = 0; i < numCols; i++)
+        {
+          View v = getView(i);
+          if (v instanceof CellView)
+            {
+              CellView cell = (CellView) v;
+              if (cell.rowSpan > 1)
+                {
+                  for (int r = 1; r < cell.rowSpan; r++)
+                    {
+                      spans[i] += TableView.this.getSpan(axis, rowIndex + r);
+                      spans[i] += cellSpacing;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Lays out the columns in this row.
+     */
+    protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+                                   int spans[])
+    {
+      updateGrid();
+      int realColumn = 0;
+      int colCount = getViewCount();
+      for (int i = 0; i < numColumns;)
+        {
+          if (! overlap[i] && realColumn < colCount)
+            {
+              View v = getView(realColumn);
+              if (v instanceof CellView)
+                {
+                  CellView cv = (CellView) v;
+                  offsets[realColumn] = columnOffsets[i];
+                  spans[realColumn] = 0;
+                  for (int j = 0; j < cv.colSpan; j++, i++)
+                    {
+                      spans[realColumn] += columnSpans[i];
+                      if (j < cv.colSpan - 1)
+                        spans[realColumn] += cellSpacing;
+                    }
+                }
+              realColumn++;
+            }
+          else
+            {
+              i++;
+            }
+        }
+    }
+  }
+
+  /**
+   * A view that renders HTML table cells (TD and TH tags).
+   */
+  class CellView
+    extends BlockView
+  {
+
+    /**
+     * The number of columns that this view spans.
+     */
+    int colSpan;
+
+    /**
+     * The number of rows that this cell spans.
+     */
+    int rowSpan;
+
+    /**
+     * Creates a new CellView for the specified element.
+     *
+     * @param el the element for which to create the colspan
+     */
+    CellView(Element el)
+    {
+      super(el, Y_AXIS);
+    }
+
+    protected SizeRequirements calculateMajorAxisRequirements(int axis,
+                                                            SizeRequirements r)
+    {
+      r = super.calculateMajorAxisRequirements(axis, r);
+      r.maximum = Integer.MAX_VALUE;
+      return r;
+    }
+
+    /**
+     * Overridden to fetch the columnSpan attibute.
+     */
+    protected void setPropertiesFromAttributes()
+    {
+      super.setPropertiesFromAttributes();
+      colSpan = 1;
+      AttributeSet atts = getAttributes();
+      Object o = atts.getAttribute(HTML.Attribute.COLSPAN);
+      if (o != null)
+        {
+          try
+            {
+              colSpan = Integer.parseInt(o.toString());
+            }
+          catch (NumberFormatException ex)
+            {
+              // Couldn't parse the colspan, assume 1.
+              colSpan = 1;
+            }
+        }
+      rowSpan = 1;
+      o = atts.getAttribute(HTML.Attribute.ROWSPAN);
+      if (o != null)
+        {
+          try
+            {
+              rowSpan = Integer.parseInt(o.toString());
+            }
+          catch (NumberFormatException ex)
+            {
+              // Couldn't parse the colspan, assume 1.
+              rowSpan = 1;
+            }
+        }
+    }
+  }
+
+
+  /**
+   * The attributes of this view.
+   */
+  private AttributeSet attributes;
+
+  /**
+   * The column requirements.
+   *
+   * Package private to avoid accessor methods.
+   */
+  SizeRequirements[] columnRequirements;
+
+  /**
+   * The overall requirements across all columns.
+   *
+   * Package private to avoid accessor methods.
+   */
+  SizeRequirements totalColumnRequirements;
+
+  /**
+   * The column layout, offsets.
+   *
+   * Package private to avoid accessor methods.
+   */
+  int[] columnOffsets;
+
+  /**
+   * The column layout, spans.
+   *
+   * Package private to avoid accessor methods.
+   */
+  int[] columnSpans;
+
+  /**
+   * The widths of the columns that have been explicitly specified.
+   */
+  Length[] columnWidths;
+
+  /**
+   * The total number of columns.
+   */
+  int numColumns;
+
+  /**
+   * The table width.
+   */
+  private Length width;
+
+  /**
+   * Indicates if the grid setup is ok.
+   */
+  boolean gridValid = false;
+
+  /**
+   * Additional space that is added _between_ table cells.
+   *
+   * This is package private to avoid accessor methods.
+   */
+  int cellSpacing;
+
+  /**
+   * A cached Rectangle object for reuse in paint().
+   */
+  private Rectangle tmpRect;
+
+  /**
+   * Creates a new HTML table view for the specified element.
+   *
+   * @param el the element for the table view
+   */
+  public TableView(Element el)
+  {
+    super(el, Y_AXIS);
+    totalColumnRequirements = new SizeRequirements();
+    tmpRect = new Rectangle();
+  }
+
+  /**
+   * Implementation of the ViewFactory interface for creating the
+   * child views correctly.
+   */
+  public View create(Element elem)
+  {
+    View view = null;
+    AttributeSet atts = elem.getAttributes();
+    Object name = atts.getAttribute(StyleConstants.NameAttribute);
+    AttributeSet pAtts = elem.getParentElement().getAttributes();
+    Object pName = pAtts.getAttribute(StyleConstants.NameAttribute);
+
+    if (name == HTML.Tag.TR && pName == HTML.Tag.TABLE)
+      view = new RowView(elem);
+    else if ((name == HTML.Tag.TD || name == HTML.Tag.TH)
+             && pName == HTML.Tag.TR)
+      view = new CellView(elem);
+    else if (name == HTML.Tag.CAPTION)
+      view = new ParagraphView(elem);
+    else
+      {
+        // If we haven't mapped the element, then fall back to the standard
+        // view factory.
+        View parent = getParent();
+        if (parent != null)
+          {
+            ViewFactory vf = parent.getViewFactory();
+            if (vf != null)
+              view = vf.create(elem);
+          }
+      }
+    return view;
+  }
+
+  /**
+   * Returns this object as view factory so that we get our TR, TD, TH
+   * and CAPTION subelements created correctly.
+   */
+  public ViewFactory getViewFactory()
+  {
+    return this;
+  }
+
+  /**
+   * Returns the attributes of this view. This is overridden to provide
+   * the attributes merged with the CSS stuff.
+   */
+  public AttributeSet getAttributes()
+  {
+    if (attributes == null)
+      attributes = getStyleSheet().getViewAttributes(this);
+    return attributes;
+  }
+
+  /**
+   * Returns the stylesheet associated with this view.
+   *
+   * @return the stylesheet associated with this view
+   */
+  protected StyleSheet getStyleSheet()
+  {
+    HTMLDocument doc = (HTMLDocument) getDocument();
+    return doc.getStyleSheet();
+  }
+
+  /**
+   * Overridden to calculate the size requirements according to the
+   * columns distribution.
+   */
+  protected SizeRequirements calculateMinorAxisRequirements(int axis,
+                                                            SizeRequirements r)
+  {
+    updateGrid();
+    calculateColumnRequirements();
+
+    // Calculate the horizontal requirements according to the superclass.
+    // This will return the maximum of the row's widths.
+    r = super.calculateMinorAxisRequirements(axis, r);
+
+    // Try to set the CSS width if it fits.
+    if (width != null)
+      {
+        int w = (int) width.getValue();
+        if (r.minimum < w)
+          r.minimum = w;
+      }
+
+    // Adjust requirements when we have cell spacing.
+    int adjust = (columnRequirements.length + 1) * cellSpacing;
+    r.minimum += adjust;
+    r.preferred += adjust;
+
+    // Apply the alignment.
+    AttributeSet atts = getAttributes();
+    Object o = atts.getAttribute(CSS.Attribute.TEXT_ALIGN);
+    r.alignment = 0.0F;
+    if (o != null)
+      {
+        String al = o.toString();
+        if (al.equals("left"))
+          r.alignment = 0.0F;
+        else if (al.equals("center"))
+          r.alignment = 0.5F;
+        else if (al.equals("right"))
+          r.alignment = 1.0F;
+      }
+
+    // Make it not resize in the horizontal direction.
+    r.maximum = r.preferred;
+    return r;
+  }
+
+  /**
+   * Overridden to perform the table layout before calling the super
+   * implementation.
+   */
+  protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets,
+                                 int[] spans)
+  {
+    updateGrid();
+
+    // Mark all rows as invalid along their minor axis to force correct
+    // layout of multi-row cells.
+    int n = getViewCount();
+    for (int i = 0; i < n; i++)
+      {
+        View row = getView(i);
+        if (row instanceof RowView)
+          ((RowView) row).layoutChanged(axis);
+      }
+
+    layoutColumns(targetSpan);
+    super.layoutMinorAxis(targetSpan, axis, offsets, spans);
+  }
+
+  /**
+   * Calculates the size requirements for the columns.
+   */
+  private void calculateColumnRequirements()
+  {
+    int numRows = getViewCount();
+    totalColumnRequirements.minimum = 0;
+    totalColumnRequirements.preferred = 0;
+    totalColumnRequirements.maximum = 0;
+
+    // In this first pass we find out a suitable total width to fit in
+    // all columns of all rows.
+    for (int r = 0; r < numRows; r++)
+      {
+        View rowView = getView(r);
+        int numCols;
+        if (rowView instanceof RowView)
+          numCols = ((RowView) rowView).getViewCount();
+        else
+          numCols = 0;
+
+        // We collect the normal (non-relative) column requirements in the
+        // total variable and the relative requirements in the relTotal
+        // variable. In the end we create the maximum of both to get the
+        // real requirements.
+        SizeRequirements total = new SizeRequirements();
+        SizeRequirements relTotal = new SizeRequirements();
+        float totalPercent = 0.F;
+        int realCol = 0;
+        for (int c = 0; c < numCols; c++)
+          {
+            View v = rowView.getView(c);
+            if (v instanceof CellView)
+              {
+                CellView cellView = (CellView) v;
+                int colSpan = cellView.colSpan;
+                if (colSpan > 1)
+                  {
+                    int cellMin = (int) cellView.getMinimumSpan(X_AXIS);
+                    int cellPref = (int) cellView.getPreferredSpan(X_AXIS);
+                    int cellMax = (int) cellView.getMaximumSpan(X_AXIS);
+                    int currentMin = 0;
+                    int currentPref = 0;
+                    long currentMax = 0;
+                    for (int i = 0; i < colSpan; i++)
+                      {
+                        SizeRequirements req = columnRequirements[realCol];
+                        currentMin += req.minimum;
+                        currentPref += req.preferred;
+                        currentMax += req.maximum;
+                      }
+                    int deltaMin = cellMin - currentMin;
+                    int deltaPref = cellPref - currentPref;
+                    int deltaMax = (int) (cellMax - currentMax);
+                    // Distribute delta.
+                    for (int i = 0; i < colSpan; i++)
+                      {
+                        SizeRequirements req = columnRequirements[realCol];
+                        if (deltaMin > 0)
+                          req.minimum += deltaMin / colSpan;
+                        if (deltaPref > 0)
+                          req.preferred += deltaPref / colSpan;
+                        if (deltaMax > 0)
+                          req.maximum += deltaMax / colSpan;
+                        if (columnWidths[realCol] == null
+                            || ! columnWidths[realCol].isPercentage())
+                          {
+                            total.minimum += req.minimum;
+                            total.preferred += req.preferred;
+                            total.maximum += req.maximum;
+                          }
+                        else
+                          {
+                            relTotal.minimum =
+                              Math.max(relTotal.minimum,
+                                     (int) (req.minimum
+                                          * columnWidths[realCol].getValue()));
+                            relTotal.preferred =
+                              Math.max(relTotal.preferred,
+                                     (int) (req.preferred
+                                          * columnWidths[realCol].getValue()));
+                            relTotal.maximum =
+                              Math.max(relTotal.maximum,
+                                     (int) (req.maximum
+                                          * columnWidths[realCol].getValue()));
+                            totalPercent += columnWidths[realCol].getValue();
+                          }
+                      }
+                    realCol += colSpan;
+                  }
+                else
+                  {
+                    // Shortcut for colSpan == 1.
+                    SizeRequirements req = columnRequirements[realCol];
+                    req.minimum = Math.max(req.minimum,
+                                        (int) cellView.getMinimumSpan(X_AXIS));
+                    req.preferred = Math.max(req.preferred,
+                                      (int) cellView.getPreferredSpan(X_AXIS));
+                    req.maximum = Math.max(req.maximum,
+                                        (int) cellView.getMaximumSpan(X_AXIS));
+                    if (columnWidths[realCol] == null
+                        || ! columnWidths[realCol].isPercentage())
+                      {
+                        total.minimum += columnRequirements[realCol].minimum;
+                        total.preferred +=
+                          columnRequirements[realCol].preferred;
+                        total.maximum += columnRequirements[realCol].maximum;
+                      }
+                    else
+                      {
+                        relTotal.minimum =
+                          Math.max(relTotal.minimum,
+                                 (int) (req.minimum
+                                        / columnWidths[c].getValue()));
+                        relTotal.preferred =
+                          Math.max(relTotal.preferred,
+                                 (int) (req.preferred
+                                        / columnWidths[c].getValue()));
+                        relTotal.maximum =
+                          Math.max(relTotal.maximum,
+                                 (int) (req.maximum
+                                        / columnWidths[c].getValue()));
+                        totalPercent += columnWidths[c].getValue();
+                      }
+                    realCol += 1;
+                  }
+              }
+          }
+
+        // Update the total requirements as follows:
+        // 1. Multiply the absolute requirements with 1 - totalPercent. This
+        //    gives the total requirements based on the wishes of the absolute
+        //    cells.
+        // 2. Take the maximum of this value and the total relative
+        //    requirements. Now we should have enough space for whatever cell
+        //    in this column.
+        // 3. Take the maximum of this value and the previous maximum value.
+        total.minimum *= 1.F / (1.F - totalPercent);
+        total.preferred *= 1.F / (1.F - totalPercent);
+        total.maximum *= 1.F / (1.F - totalPercent);
+
+        int rowTotalMin = Math.max(total.minimum, relTotal.minimum);
+        int rowTotalPref = Math.max(total.preferred, relTotal.preferred);
+        int rowTotalMax = Math.max(total.maximum, relTotal.maximum);
+        totalColumnRequirements.minimum =
+          Math.max(totalColumnRequirements.minimum, rowTotalMin);
+        totalColumnRequirements.preferred =
+          Math.max(totalColumnRequirements.preferred, rowTotalPref);
+        totalColumnRequirements.maximum =
+          Math.max(totalColumnRequirements.maximum, rowTotalMax);
+      }
+
+    // Now we know what we want and can fix up the actual relative
+    // column requirements.
+    int numCols = columnRequirements.length;
+    for (int i = 0; i < numCols; i++)
+      {
+        if (columnWidths[i] != null)
+          {
+            columnRequirements[i].minimum = (int)
+              columnWidths[i].getValue(totalColumnRequirements.minimum);
+            columnRequirements[i].preferred = (int)
+              columnWidths[i].getValue(totalColumnRequirements.preferred);
+            columnRequirements[i].maximum = (int)
+              columnWidths[i].getValue(totalColumnRequirements.maximum);
+          }
+      }
+  }
+
+  /**
+   * Lays out the columns.
+   *
+   * @param targetSpan the target span into which the table is laid out
+   */
+  private void layoutColumns(int targetSpan)
+  {
+    // Set the spans to the preferred sizes. Determine the space
+    // that we have to adjust the sizes afterwards.
+    long sumPref = 0;
+    int n = columnRequirements.length;
+    for (int i = 0; i < n; i++)
+      {
+        SizeRequirements col = columnRequirements[i];
+        if (columnWidths[i] != null)
+          columnSpans[i] = (int) columnWidths[i].getValue(targetSpan);
+        else
+          columnSpans[i] = col.preferred;
+        sumPref += columnSpans[i];
+      }
+
+    // Try to adjust the spans so that we fill the targetSpan.
+    // For adjustments we have to use the targetSpan minus the cumulated
+    // cell spacings.
+    long diff = targetSpan - (n + 1) * cellSpacing - sumPref;
+    float factor = 0.0F;
+    int[] diffs = null;
+    if (diff != 0)
+      {
+        long total = 0;
+        diffs = new int[n];
+        for (int i = 0; i < n; i++)
+          {
+            // Only adjust the width if we haven't set a column width here.
+            if (columnWidths[i] == null)
+              {
+                SizeRequirements col = columnRequirements[i];
+                int span;
+                if (diff < 0)
+                  {
+                    span = col.minimum;
+                    diffs[i] = columnSpans[i] - span;
+                  }
+                else
+                  {
+                    span = col.maximum;
+                    diffs[i] = span - columnSpans[i];
+                  }
+                total += span;
+              }
+            else
+              total += columnSpans[i];
+          }
+
+        float maxAdjust = Math.abs(total - sumPref);
+        factor = diff / maxAdjust;
+        factor = Math.min(factor, 1.0F);
+        factor = Math.max(factor, -1.0F);
+      }
+
+    // Actually perform adjustments.
+    int totalOffs = cellSpacing;
+    for (int i = 0; i < n; i++)
+      {
+        columnOffsets[i] = totalOffs;
+        if (diff != 0)
+          {
+            float adjust = factor * diffs[i];
+            columnSpans[i] += Math.round(adjust);
+          }
+        // Avoid overflow here.
+        totalOffs = (int) Math.min((long) totalOffs + (long) columnSpans[i]
+                                   + (long) cellSpacing, Integer.MAX_VALUE);
+      }
+  }
+
+  /**
+   * Updates the arrays that contain the row and column data in response
+   * to a change to the table structure.
+   *
+   * Package private to avoid accessor methods.
+   */
+  void updateGrid()
+  {
+    if (! gridValid)
+      {
+        AttributeSet atts = getAttributes();
+        StyleSheet ss = getStyleSheet();
+        float emBase = ss.getEMBase(atts);
+        float exBase = ss.getEXBase(atts);
+        int maxColumns = 0;
+        int numRows = getViewCount();
+        for (int r = 0; r < numRows; r++)
+          {
+            View rowView = getView(r);
+            int numCols = 0;
+            if (rowView instanceof RowView)
+              {
+                int numCells = ((RowView) rowView).getViewCount();
+                for (int i = 0; i < numCells; i++)
+                  {
+                    View v = rowView.getView(i);
+                    if (v instanceof CellView)
+                      numCols += ((CellView) v).colSpan;
+                  }
+              }
+            maxColumns = Math.max(numCols, maxColumns);
+          }
+        numColumns = maxColumns;
+        columnWidths = new Length[maxColumns];
+        int[] rowSpans = new int[maxColumns];
+        for (int r = 0; r < numRows; r++)
+          {
+            View view = getView(r);
+            if (view instanceof RowView)
+              {
+                RowView rowView = (RowView) view;
+                rowView.rowIndex = r;
+                rowView.overlap = new boolean[maxColumns];
+                int colIndex = 0;
+                int colCount = rowView.getViewCount();
+                for (int c = 0; c < maxColumns;)
+                  {
+                    if (rowSpans[c] > 0)
+                      {
+                        rowSpans[c]--;
+                        rowView.overlap[c] = true;
+                        c++;
+                      }
+                    else if (colIndex < colCount)
+                      {
+                        View v = rowView.getView(colIndex);
+                        colIndex++;
+                        if (v instanceof CellView)
+                          {
+                            CellView cv = (CellView) v;
+                            Object o =
+                              cv.getAttributes().getAttribute(CSS.Attribute.WIDTH);
+                            if (o != null && columnWidths[c] == null
+                                && o instanceof Length)
+                              {
+                                columnWidths[c]= (Length) o;
+                                columnWidths[c].setFontBases(emBase, exBase);
+                              }
+                            int rs = cv.rowSpan - 1;
+                            for (int col = cv.colSpan - 1; col >= 0; col--)
+                              {
+                                rowSpans[c] = rs;
+                                c++;
+                              }
+                          }
+                      }
+                    else
+                      {
+                        c++;
+                      }
+                  }
+              }
+          }
+        columnRequirements = new SizeRequirements[maxColumns];
+        for (int i = 0; i < maxColumns; i++)
+          columnRequirements[i] = new SizeRequirements();
+        columnOffsets = new int[maxColumns];
+        columnSpans = new int[maxColumns];
+
+        gridValid = true;
+      }
+  }
+
+  /**
+   * Overridden to restrict the table width to the preferred size.
+   */
+  public float getMaximumSpan(int axis)
+  {
+    float span;
+    if (axis == X_AXIS)
+      span = super.getPreferredSpan(axis);
+    else
+      span = super.getMaximumSpan(axis);
+    return span;
+  }
+
+  /**
+   * Overridden to fetch the CSS attributes when view gets connected.
+   */
+  public void setParent(View parent)
+  {
+    super.setParent(parent);
+    if (parent != null)
+      setPropertiesFromAttributes();
+  }
+
+  /**
+   * Fetches CSS and HTML layout attributes.
+   */
+  protected void setPropertiesFromAttributes()
+  {
+    super.setPropertiesFromAttributes();
+
+    // Fetch and parse cell spacing.
+    AttributeSet atts = getAttributes();
+    StyleSheet ss = getStyleSheet();
+    float emBase = ss.getEMBase(atts);
+    float exBase = ss.getEXBase(atts);
+    Object o = atts.getAttribute(CSS.Attribute.BORDER_SPACING);
+    if (o != null && o instanceof Length)
+      {
+        Length l = (Length) o;
+        l.setFontBases(emBase, exBase);
+        cellSpacing = (int) l.getValue();
+      }
+    o = atts.getAttribute(CSS.Attribute.WIDTH);
+    if (o != null && o instanceof Length)
+      {
+        width = (Length) o;
+        width.setFontBases(emBase, exBase);
+      }
+  }
+
+  /**
+   * Overridden to adjust for cellSpacing.
+   */
+  protected SizeRequirements calculateMajorAxisRequirements(int axis,
+                                                            SizeRequirements r)
+  {
+    r = super.calculateMajorAxisRequirements(axis, r);
+    int adjust = (getViewCount() + 1) * cellSpacing;
+    r.minimum += adjust;
+    r.preferred += adjust;
+    r.maximum += adjust;
+    return r;
+  }
+
+  /**
+   * Overridden to adjust for cellSpacing.
+   */
+  protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
+                                 int spans[])
+  {
+    // Mark all rows as invalid along their minor axis to force correct
+    // layout of multi-row cells.
+    int n = getViewCount();
+    for (int i = 0; i < n; i++)
+      {
+        View row = getView(i);
+        if (row instanceof RowView)
+          ((RowView) row).layoutChanged(axis);
+      }
+
+    int adjust = (getViewCount() + 1) * cellSpacing;
+    super.layoutMajorAxis(targetSpan - adjust, axis, offsets, spans);
+    for (int i = 0; i < offsets.length; i++)
+      {
+        offsets[i] += (i + 1) * cellSpacing;
+      }
+  }
+
+  /**
+   * Overridden to replace view factory with this one.
+   */
+  public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f)
+  {
+    super.insertUpdate(e, a, this);
+  }
+
+  /**
+   * Overridden to replace view factory with this one.
+   */
+  public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f)
+  {
+    super.removeUpdate(e, a, this);
+  }
+
+  /**
+   * Overridden to replace view factory with this one.
+   */
+  public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f)
+  {
+    super.changedUpdate(e, a, this);
+  }
+
+  public void replace(int offset, int len, View[] views)
+  {
+    gridValid = false;
+    super.replace(offset, len, views);
+  }
+
+  /**
+   * We can't use the super class's paint() method because it might cut
+   * off multi-row children. Instead we trigger painting for all rows
+   * and let the rows sort out what to paint and what not.
+   */
+  public void paint(Graphics g, Shape a)
+  {
+    Rectangle rect = a instanceof Rectangle ? (Rectangle) a : a.getBounds();
+    painter.paint(g, rect.x, rect.y, rect.width, rect.height, this);
+    int nRows = getViewCount();
+    Rectangle inside = getInsideAllocation(a);
+    for (int r = 0; r < nRows; r++)
+      {
+        tmpRect.setBounds(inside);
+        childAllocation(r, tmpRect);
+        paintChild(g, tmpRect, r);
+      }
+  }
+
+}
diff --git a/libjava/classpath/javax/swing/text/html/ViewAttributeSet.java b/libjava/classpath/javax/swing/text/html/ViewAttributeSet.java
new file mode 100644
index 000000000..fb57872ce
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/ViewAttributeSet.java
@@ -0,0 +1,163 @@
+/* ViewAttributeSet.java -- The AttributeSet used by HTML views
+   Copyright (C) 2006 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import java.util.ArrayList;
+import java.util.Enumeration;
+
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Element;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.View;
+
+/**
+ * An AttributeSet implemenation that is used by the HTML views. This
+ * AttributeSet is created by StyleSheet.getViewAttributes() and combines
+ * the following attributes:
+ * - The original attributes of the View's element.
+ * - Any translated (HTML->CSS) attributes, as returned by
+ *   StyleSheet.translateHTMLToCS().
+ * - CSS Styles as resolved by the CSS stylesheet.
+ *
+ * In addition to that, it resolves attributes to the parent views, if
+ * a CSS attribute is requested that is inheritable.
+ *
+ * @author Roman Kennke (kennke@aicas.com)
+ */
+class ViewAttributeSet
+  extends MultiAttributeSet
+{
+
+  /**
+   * The view for which we are the AttributeSet.
+   */
+  private View view;
+
+  /**
+   * The stylesheet to use.
+   */
+  private StyleSheet styleSheet;
+
+  /**
+   * Creates a new instance.
+   *
+   * @param v the view for which to do the AttributeSet
+   */
+  ViewAttributeSet(View v, StyleSheet ss)
+  {
+    styleSheet = ss;
+    view = v;
+    ArrayList<AttributeSet> atts = new ArrayList<AttributeSet>();
+
+    Element el = v.getElement();
+    AttributeSet elAtts = el.getAttributes();
+    AttributeSet htmlAtts = styleSheet.translateHTMLToCSS(elAtts);
+    if (htmlAtts.getAttributeCount() > 0)
+      atts.add(htmlAtts);
+
+    if (el.isLeaf())
+      {
+        Enumeration<?> n = elAtts.getAttributeNames();
+        while (n.hasMoreElements())
+          {
+            Object key = n.nextElement();
+            if (key instanceof HTML.Tag)
+              {
+                AttributeSet rule = styleSheet.getRule((HTML.Tag) key, el);
+                if (rule != null)
+                  atts.add(rule);
+              }
+          }
+      }
+    else
+      {
+        HTML.Tag tag =
+          (HTML.Tag) elAtts.getAttribute(StyleConstants.NameAttribute);
+        AttributeSet rule = styleSheet.getRule(tag, el);
+        if (rule != null)
+          atts.add(rule);
+      }
+
+    AttributeSet[] atts1 = new AttributeSet[atts.size()];
+    atts1 = atts.toArray(atts1);
+    init(atts1);
+  }
+
+  /**
+   * Fetches the attribute for the specific ckey. If the attribute
+   * can't be found and the key is a CSS.Attribute that is inherited,
+   * then the attribute is looked up in the resolve parent.
+   */
+  public Object getAttribute(Object key)
+  {
+    Object val = super.getAttribute(key);
+    if (val == null)
+      {
+        // Didn't find value. If the key is a CSS.Attribute, and is
+        // inherited, then ask the resolve parent.
+        if (key instanceof CSS.Attribute)
+          {
+            CSS.Attribute cssKey = (CSS.Attribute) key;
+            if (cssKey.isInherited())
+              {
+                AttributeSet resolveParent = getResolveParent();
+                if (resolveParent != null)
+                  val = resolveParent.getAttribute(cssKey);
+              }
+          }
+      }
+    return val;
+  }
+
+  /**
+   * Returns the resolve parent of this AttributeSet. This is the AttributeSet
+   * returned by the parent view if available.
+   */
+  public AttributeSet getResolveParent()
+  {
+    AttributeSet parent = null;
+    if (view != null)
+      {
+        View parentView = view.getParent();
+        if (parentView != null)
+          parent = parentView.getAttributes();
+      }
+    return parent;
+  }
+}
diff --git a/libjava/classpath/javax/swing/text/html/package.html b/libjava/classpath/javax/swing/text/html/package.html
new file mode 100644
index 000000000..c7e774428
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/package.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
+<!-- package.html - describes classes in javax.swing.text.html package.
+   Copyright (C) 2002 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING.  If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library.  Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module.  An independent module is a module which is not derived from
+or based on this library.  If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so.  If you do not wish to do so, delete this
+exception statement from your version. -->
+
+<html>
+<head><title>GNU Classpath - javax.swing.text.html
+
+
+

Provides supporting classes for web browsers, + web robots, web page content analysers, web editors and + other applications applications working with Hypertext + Markup Language (HTML). +

+ + + diff --git a/libjava/classpath/javax/swing/text/html/parser/AttributeList.java b/libjava/classpath/javax/swing/text/html/parser/AttributeList.java new file mode 100644 index 000000000..a943f056d --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/parser/AttributeList.java @@ -0,0 +1,294 @@ +/* AttributeList.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html.parser; + +import gnu.javax.swing.text.html.parser.support.gnuStringIntMapper; + +import java.io.Serializable; + +import java.util.Enumeration; +import java.util.Vector; + +/** + *

+ * Stores the attribute information, obtained by parsing SGML (DTD) tag + * <!ATTLIST .. >

+ *

+ * Elements can have a associated named properties (attributes) having the + * assigned values. The element start tag can have any number of attribute + * value pairs, separated by spaces. They can appear in any order. + * SGML requires you to delimit the attribute values using either double (") + * or single (') quotation marks. In HTML, it is possible + * (but not recommended) to specify the value of an attribute without + * quotation marks. Such attribute value may only contain + * letters, digits, hyphens (-) and periods (.) . + *

+ *

+ * The AttributeList defines a single attribute that additionally + * has a pointer referencing the possible subsequent attribute. + * The whole structure is just a simple linked list, storing all attributes of + * some Element. + * Use the getNext() method repeatedly to see all attributes in + * the list. + *

+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public final class AttributeList + implements DTDConstants, Serializable +{ + /** Maps between type names and they string values. */ + private static final gnuStringIntMapper mapper = + new gnuStringIntMapper() + { + protected void create() + { + add("CDATA", DTDConstants.CDATA); + add("ENTITY", DTDConstants.ENTITY); + add("ENTITIES", DTDConstants.ENTITIES); + add("ID", DTDConstants.ID); + add("IDREF", DTDConstants.IDREF); + add("IDREFS", DTDConstants.IDREFS); + add("NAME", DTDConstants.NAME); + add("NAMES", DTDConstants.NAMES); + add("NMTOKEN", DTDConstants.NMTOKEN); + add("NMTOKENS", DTDConstants.NMTOKENS); + add("NOTATION", DTDConstants.NOTATION); + add("NUMBER", DTDConstants.NUMBER); + add("NUMBERS", DTDConstants.NUMBERS); + add("NUTOKEN", DTDConstants.NUTOKEN); + add("NUTOKENS", DTDConstants.NUTOKENS); + } + }; + + /** Use serialVersionUID for interoperability. */ + private static final long serialVersionUID = -1361214058742015233L; + + /** + * The value of ( = pointer to ) the next attribute in the linked list, + * storing all attributes of some Element. Contains null for the + * last attribute. + */ + public AttributeList next; + + /** + * The name of the attribute. The attribute names are case insensitive. + */ + public String name; + + /** + * The default value of this attribute. Equals to null if no default value + * is specified. + */ + public String value; + + /** + * The explicit set of the allowed values of this attribute. Equals to + * null, if this parameter was not specified. + * Values, defined in DTD, are case insensitive. + */ + public Vector values; + + /** + * The modifier of this attribute. This field contains one of the + * following DTD constants: + *
    + *
  • REQUIRED if the attribute value is always required,
  • + *
  • IMPLIED if the user agent must supply the default value itself,
  • + *
  • FIXED if the attribute value is fixed to some value and cannot + * be changed.
  • + *
  • DEFAULT if the attribute default value has been supplied.
  • + *
  • CURRENT the value that at any point in the document is + * the last value supplied for that element. A value is required to be + * supplied for the first* occurrence of an element
  • + *
  • CONREF specifies the IDREF value of + * the reference to content in another location of the document. + * The element with this attribute is empty, the content from + * that another location must be used instead.
  • + *
+ */ + public int modifier; + + /** + * The type of the attribute. The possible values of this field + * (NUMBER, NAME, ID, CDATA and so on) are defined in DTDConstants. + */ + public int type; + + /** + * Creates the attribute with the given name, initializing other fields + * to the default values ( 0 and null ). + * + * @param a_name The name of the attribute. + */ + public AttributeList(String a_name) + { + name = a_name; + } + + /** + * Creates the attribute with the given properties. + * @param a_name The name of the attribute + * @param a_type The type of the attribute. The possible values are defined + * in DTDConstants. + * @param a_modifier The modifier of this attribute. The possible values + * are defined in DTDConstants. + * @param a_default The default value of this attribute + * @param allowed_values The explicit set of the allowed values of + * this attribute + * @param a_next The value of the subsequent instance of the AttributeList, + * representing the next attribute definition for the same element. + * Equals to null for the last attribute definition. + */ + public AttributeList(String a_name, int a_type, int a_modifier, + String a_default, Vector allowed_values, + AttributeList a_next + ) + { + this(a_name); + type = a_type; + modifier = a_modifier; + value = a_default; + values = allowed_values; + next = a_next; + } + + /** + * Get the modifier of this attribute. This field contains one of the + * following DTD constants: + *
    + *
  • REQUIRED if the attribute value is always required,
  • + *
  • IMPLIED if the user agent must supply the default value itself,
  • + *
  • FIXED if the attribute value is fixed to some value and cannot + * be changed.
  • + *
  • DEFAULT if the attribute default value has been supplied.
  • + *
  • CURRENT the value that at any point in the document is + * the last value supplied for that element. A value is required to be + * supplied for the first* occurrence of an element
  • + *
  • CONREF specifies the IDREF value of + * the reference to content in another location of the document. + * The element with this attribute is empty, the content from + * that another location must be used instead.
  • + *
+ */ + public int getModifier() + { + return modifier; + } + + /** + * Get the name of the attribute. + * The value is returned as it was supplied to a + * constructor, preserving the character case. + */ + public String getName() + { + return name; + } + + /** + * Get the value of ( = pointer to ) the next attribute in the linked list, + * storing all attributes of some Element. Contains null for the + * last attribute. + */ + public AttributeList getNext() + { + return next; + } + + /** + * Get the type of the attribute. The possible values of this field + * (NUMBER, NAME, ID, CDATA and so on) are defined in DTDConstants. + */ + public int getType() + { + return type; + } + + /** + * Get the default value of this attribute. + */ + public String getValue() + { + return value; + } + + /** + * Get the allowed values of this attribute. + */ + public Enumeration getValues() + { + return (values != null) ? values.elements() : null; + } + + /** + * Converts a string value, representing a valid SGLM attribute type, + * into the corresponding value, defined in DTDConstants. + * @param typeName the name of the type (character case is ignored). + * @return a value from DTDConstants or DTDConstants.ANY if the + * string is not representing a known type. The known attribute types + * in this implementation are CDATA, ENTITY, ENTITIES, ID, IDREF, IDREFS, + * NAME, NAMES, NMTOKEN, NMTOKENS, NOTATION, NUMBER, NUMBERS, NUTOKEN and + * NUTOKENS. + * @throws NullPointerException if the passed parameter is null. + */ + public static int name2type(String typeName) + { + return mapper.get(typeName.toUpperCase()); + } + + /** + * Returns the attribute name. + */ + public String toString() + { + return name; + } + + /** + * Converts a value from DTDConstants into the string representation. + * @param type - an integer value of the public static integer field, + * defined in the DTDConstants class. + * @return a corresponding SGML DTD keyword (UPPERCASE) or null if there + * are no attribute type constant having the given value. + */ + public static String type2name(int type) + { + return mapper.get(type); + } +} diff --git a/libjava/classpath/javax/swing/text/html/parser/ContentModel.java b/libjava/classpath/javax/swing/text/html/parser/ContentModel.java new file mode 100644 index 000000000..d5c4418de --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/parser/ContentModel.java @@ -0,0 +1,223 @@ +/* ContentModel.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html.parser; + +import gnu.javax.swing.text.html.parser.models.transformer; + +import java.io.Serializable; + +import java.util.Vector; + +/** + * A representation of the element content. The instances of this class + * can be arranged into the linked list, representing a BNF expression. + * The content model is constructed as a branched tree structure in the + * following way: + *
+ * a = new ContentModel('+', A, null); // a reprensents A+
+ * b = new ContentModel('&', B, a); // b represents B & A+
+ * c = new ContentModel('*', b, null); // c represents ( B & A+) *
+ * d = new ContentModel('|', new ContentModel('*', A, null),
+ *          new ContentModel('?', B, null)); // d represents ( A* | B? )
+ * 
+ * where the valid operations are: + *
    + *
  • E* E occurs zero or more times
  • + *
  • E+ E occurs one or more times
  • + *
  • E? E occurs once or not atl all
  • + *
  • A,B A occurs before B
  • + *
  • A|B both A and B are permitted in any order. + * The '|' alone does not permit the repetetive occurence of A or B + * (use (A|B)*.
  • + *
  • A&B both A and B must occur once (in any order)
  • + *
+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public final class ContentModel + implements Serializable +{ + /** Use serialVersionUID for interoperability. */ + private static final long serialVersionUID = -1130825523866321257L; + + /** + * The next content model model ( = pointer to the next element of + * the linked list) for the binary expression (',','&' or '|'). Null + * for the last element in the list. + */ + public ContentModel next; + + /** + * The document content, containing either Element or the enclosed + * content model (that would be in the parentheses in BNF expression). + */ + public Object content; + + /** + * Specifies the BNF operation between this node and the node, + * stored in the field next (or for this node, if it is + * an unary operation. + */ + public int type; + + /** + * Create a content model initializing all fields to default values. + */ + public ContentModel() + { + // Nothing to do here. + } + + /** + * Create a content model, consisting of the single element. + * Examples: + * + * a = new ContentModel('+', A, null); // a reprensents A+ + * b = new ContentModel('&', B, a); // b represents B & A+ + * c = new ContentModel('*', b, null); // c represents ( B & A+) * + * d = new ContentModel('|', A, + * new ContentModel('?',b, null); + * // d represents + * + */ + public ContentModel(Element a_content) + { + content = a_content; + } + + /** + * Create a content model, involving expression of the given type. + * @param a_type The expression operation type ('*','?' or '+' + * @param a_content The content for that the expression is applied. + */ + public ContentModel(int a_type, ContentModel a_content) + { + content = a_content; + type = a_type; + } + + /** + * Create a content model, involving binary expression of the given type. + * @param a_type The expression operation type ( ',', '|' or '&'). + * @param a_content The content of the left part of the expression. + * @param a_next The content model, representing the right part of the + * expression. + */ + public ContentModel(int a_type, Object a_content, ContentModel a_next) + { + content = a_content; + type = a_type; + next = a_next; + } + + /** + * Adds all list elements to the given vector, ignoring the + * operations between the elements. The old vector values are not + * discarded. + * @param elements - a vector to add the values to. + */ + public void getElements(Vector elements) + { + ContentModel c = this; + + while (c != null) + { + // FIXME: correct? + if (c.content instanceof Element) + elements.add((Element) c.content); + c = c.next; + } + } + + /** + * Checks if the content model matches an empty input stream. + * The empty content is created using SGML DTD keyword EMPTY. + * The empty model is a model with the content field equal to null. + * + * @return true if the content field is equal to null. + */ + public boolean empty() + { + return content == null; + } + + /** + * Get the element, stored in the next.content. + * The method is programmed as the part of the standard API, but not + * used in this implementation. + * @return the value of the field next. + */ + public Element first() + { + return (Element) next.content; + } + + /** + * Checks if this object can potentially be the first token in the + * ContenModel list. The method is programmed as the part of the + * standard API, but not used in this implementation. + */ + public boolean first(Object token) + { + ContentModel c = this; + while (c.next != null) + { + if (c.content != null && c.content.toString().equals(token.toString()) && + c.type != ',' + ) + + // Agree if the operation with the preceeding element + // is not the comma operation. + return true; + c = c.next; + } + return false; + } + + /** + * Returns a string representation (an expression) of this content model. + * The expression has BNF-like syntax, except the absence of the + * unary operator is additionally indicated by " ' ". It is + * advisable to check the created models for correctness using this + * method. + */ + public String toString() + { + return transformer.transform(this).toString(); + } +} diff --git a/libjava/classpath/javax/swing/text/html/parser/DTD.java b/libjava/classpath/javax/swing/text/html/parser/DTD.java new file mode 100644 index 000000000..09b50fee7 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/parser/DTD.java @@ -0,0 +1,609 @@ +/* DTD.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html.parser; + +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.BitSet; +import java.util.Hashtable; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + *

Representation or the SGML DTD document. + * Provides basis for describing a syntax of the + * HTML documents. The fields of this class are NOT initialized in + * constructor. You need to do this separately before passing this data + * structure to the HTML parser. The subclasses with the fields, pre- + * initialized, for example, for HTML 4.01, can be available only between + * the implementation specific classes + * ( for example, {@link gnu.javax.swing.text.html.parser.HTML_401F } + * in this implementation).

+ *

+ * If you need more information about SGML DTD documents, + * the author suggests to read SGML tutorial on + * http://www.w3.org/TR/WD-html40-970708/intro/sgmltut.html. + * We also recommend Goldfarb C.F (1991) The SGML Handbook, + * Oxford University Press, 688 p, ISBN: 0198537379. + *

+ *

+ * Warning: the html, head and other tag fields will only be automatically + * assigned if the VM has the correctly implemented reflection mechanism. + * As these fields are not used anywhere in the implementation, not + * exception will be thrown in the opposite case. + *

+ * + * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public class DTD + implements DTDConstants +{ + /** + * The version of the persistent data format. + * @specnote This was made final in 1.5. + */ + public static final int FILE_VERSION = 1; + + /** + * The table of existing available DTDs. + */ + static Hashtable dtdHash = new Hashtable(); + + /** + * The applet element for this DTD. + */ + public Element applet; + + /** + * The base element for this DTD. + */ + public Element base; + + /** + * The body element for this DTD. + */ + public Element body; + + /** + * The head element for this DTD. + */ + public Element head; + + /** + * The html element for this DTD. + */ + public Element html; + + /** + * The isindex element of for this DTD. + */ + public Element isindex; + + /** + * The meta element for this DTD. + */ + public Element meta; + + /** + * The p element for this DTD. + */ + public Element p; + + /** + * The param element for this DTD. + */ + public Element param; + + /** + * The pcdata for this DTD. + */ + public Element pcdata; + + /** + * The title element for this DTD. + */ + public Element title; + + /** + * The element for accessing all DTD elements by name. + */ + public Hashtable elementHash = + new Hashtable(); + + /** + * The entity table for accessing all DTD entities by name. + */ + public Hashtable entityHash = new Hashtable(); + + /** + * The name of this DTD. + */ + public String name; + + /** + * Contains all elements in this DTD. The + * javax.swing.text.html.parser.Element#index field of all elements + * in this vector is set to the element position in this vector. + */ + public Vector elements = new Vector(); + + /** Create a new DTD with the specified name. */ + protected DTD(String a_name) + { + name = a_name; + } + + /** Get this DTD by name. The current implementation + * only looks in the internal table of DTD documents. If no corresponding + * entry is found, the new entry is created, placed into + * the table and returned. */ + public static DTD getDTD(String name) + throws IOException + { + DTD d = dtdHash.get(name); + + if (d == null) + { + d = new DTD(name); + dtdHash.put(d.name, d); + } + + return d; + } + + /** + * Get the element by the element name. If the element is not yet + * defined, it is newly created and placed into the element table. + * If the element name matches (ingoring case) a public non static + * element field in this class, this field is assigned to the value + * of the newly created element. + */ + public Element getElement(String element_name) + { + return newElement(element_name); + } + + /** + * Get the element by the value of its + * {@link javax.swing.text.html.parser.Element#index} field. + */ + public Element getElement(int index) + { + return elements.get(index); + } + + /** + * Get the entity with the given identifier. + * @param id that can be returned by + * {@link javax.swing.text.html.parser.Entity#name2type(String an_entity)} + * @return The entity from this DTD or null if there is no entity with + * such id or such entity is not present in the table of this instance. + */ + public Entity getEntity(int id) + { + String name = Entity.mapper.get(id); + + if (name != null) + return entityHash.get(name); + else + return null; + } + + /** + * Get the named entity by its name. + */ + public Entity getEntity(String entity_name) + { + return entityHash.get(entity_name); + } + + /** + * Get the name of this instance of DTD + */ + public String getName() + { + return name; + } + + /** + * Creates, adds into the entity table and returns the + * character entity like &lt; + * (means '<' ); + * @param name The entity name (without heading & and closing ;) + * @param type The entity type + * @param character The entity value (single character) + * @return The created entity + */ + public Entity defEntity(String name, int type, int character) + { + Entity e = newEntity(name, type); + e.data = new char[] { (char) character }; + return e; + } + + /** + * Define the attributes for the element with the given name. + * If the element is not exist, it is created. + * @param forElement + * @param attributes + */ + public void defineAttributes(String forElement, AttributeList attributes) + { + Element e = elementHash.get(forElement.toLowerCase()); + + if (e == null) + e = newElement(forElement); + + e.atts = attributes; + } + + /** + * Defines the element and adds it to the element table. Sets the + * Element.index field to the value, unique for this + * instance of DTD. If the element with the given name already exists, + * replaces all other its settings by the method argument values. + * @param name the name of the element + * @param type the type of the element + * @param headless true if the element needs no starting tag + * (should not occur in HTML). + * @param tailless true if the element needs no ending tag (like + * <hr> + * @param content the element content + * @param exclusions the set of elements that must not occur inside + * this element. The Element.index value defines which + * bit in this bitset corresponds to that element. + * @param inclusions the set of elements that can occur inside this + * element. the Element.index value defines which + * bit in this bitset corresponds to that element. + * @param attributes the element attributes. + * @return the newly defined element. + */ + public Element defineElement(String name, int type, boolean headless, + boolean tailless, ContentModel content, + BitSet exclusions, BitSet inclusions, + AttributeList attributes + ) + { + Element e = newElement(name); + e.type = type; + e.oStart = headless; + e.oEnd = tailless; + e.content = content; + e.exclusions = exclusions; + e.inclusions = inclusions; + e.atts = attributes; + + return e; + } + + /** + * Creates, intializes and adds to the entity table the new + * entity. + * @param name the name of the entity + * @param type the type of the entity + * @param data the data section of the entity + * @return the created entity + */ + public Entity defineEntity(String name, int type, char[] data) + { + Entity e = newEntity(name, type); + e.data = data; + + return e; + } + + /** Place this DTD into the DTD table. */ + public static void putDTDHash(String name, DTD dtd) + { + dtdHash.put(name, dtd); + } + + /** + *

Reads DTD from an archived format. This format is not standardized + * and differs between implementations.

This implementation + * reads and defines all entities and elements using + * ObjectInputStream. The elements and entities can be written into the + * stream in any order. The objects other than elements and entities + * are ignored.

+ * @param stream A data stream to read from. + * @throws java.io.IOException If one is thrown by the input stream + */ + public void read(DataInputStream stream) + throws java.io.IOException + { + ObjectInputStream oi = new ObjectInputStream(stream); + Object def; + try + { + while (true) + { + def = oi.readObject(); + if (def instanceof Element) + { + Element e = (Element) def; + elementHash.put(e.name.toLowerCase(), e); + assignField(e); + } + else if (def instanceof Entity) + { + Entity e = (Entity) def; + entityHash.put(e.name, e); + } + } + } + catch (ClassNotFoundException ex) + { + throw new IOException(ex.getMessage()); + } + catch (EOFException ex) + { + // ok EOF + } + } + + /** + * Returns the name of this instance of DTD. + */ + public String toString() + { + return name; + } + + /** + * Creates and returns new attribute (not an attribute list). + * @param name the name of this attribute + * @param type the type of this attribute (FIXED, IMPLIED or + * REQUIRED from DTDConstants). + * @param modifier the modifier of this attribute + * @param default_value the default value of this attribute + * @param allowed_values the allowed values of this attribute. The multiple + * possible values in this parameter are supposed to be separated by + * '|', same as in SGML DTD <!ATTLIST tag. This parameter + * can be null if no list of allowed values is specified. + * @param atts the previous attribute of this element. This is + * placed to the field + * {@link javax.swing.text.html.parser.AttributeList#next }, + * creating a linked list. + * @return The attributes. + */ + protected AttributeList defAttributeList(String name, int type, int modifier, + String default_value, + String allowed_values, + AttributeList atts + ) + { + AttributeList al = new AttributeList(name); + al.modifier = modifier; + al.value = default_value; + al.next = atts; + + if (allowed_values != null) + { + StringTokenizer st = new StringTokenizer(allowed_values, " \t|"); + Vector v = new Vector(st.countTokens()); + + while (st.hasMoreTokens()) + v.add(st.nextToken()); + + al.values = v; + } + + return al; + } + + /** + * Creates a new content model. + * @param type specifies the BNF operation for this content model. + * The valid operations are documented in the + * {@link javax.swing.text.html.parser.ContentModel#type }. + * @param content the content of this content model + * @param next if the content model is specified by BNF-like + * expression, contains the rest of this expression. + * @return The newly created content model. + */ + protected ContentModel defContentModel(int type, Object content, + ContentModel next + ) + { + ContentModel model = new ContentModel(); + model.type = type; + model.next = next; + model.content = content; + + return model; + } + + /** + * Defines a new element and adds it to the element table. + * If the element alredy exists, + * overrides it settings with the specified values. + * @param name the name of the new element + * @param type the type of the element + * @param headless true if the element needs no starting tag + * @param tailless true if the element needs no closing tag + * @param content the element content. + * @param exclusions the elements that must be excluded from the + * content of this element, in all levels of the hierarchy. + * @param inclusions the elements that can be included as the + * content of this element. + * @param attributes the element attributes. + * @return the created or updated element. + */ + protected Element defElement(String name, int type, boolean headless, + boolean tailless, ContentModel content, + String[] exclusions, String[] inclusions, + AttributeList attributes + ) + { + // compute the bit sets + BitSet exclude = bitSet(exclusions); + BitSet include = bitSet(inclusions); + + Element e = + defineElement(name, type, headless, tailless, content, exclude, include, + attributes + ); + + return e; + } + + /** + * Creates, intializes and adds to the entity table the new + * entity. + * @param name the name of the entity + * @param type the type of the entity + * @param data the data section of the entity + * @return the created entity + */ + protected Entity defEntity(String name, int type, String data) + { + Entity e = newEntity(name, type); + e.data = data.toCharArray(); + + return e; + } + + private void assignField(Element e) + { + String element_name = e.name; + try + { + // Assign the field via reflection. + Field f = getClass().getField(element_name.toLowerCase()); + if ((f.getModifiers() & Modifier.PUBLIC) != 0) + if ((f.getModifiers() & Modifier.STATIC) == 0) + if (f.getType().isAssignableFrom(e.getClass())) + f.set(this, e); + } + catch (IllegalAccessException ex) + { + unexpected(ex); + } + catch (NoSuchFieldException ex) + { + // This is ok. + } + + // Some virtual machines may still lack the proper + // implementation of reflection. As the tag fields + // are not used anywhere in this implementation, + // (and this class is also rarely used by the end user), + // it may be better not to crash everything by throwing an error + // for each case when the HTML parsing is required. + catch (Throwable t) + { + // This VM has no reflection mechanism implemented! + if (t instanceof OutOfMemoryError) + throw (Error) t; + } + } + + /** + * Create the bit set for this array of elements. + * The unknown elements are automatically defined and added + * to the element table. + * @param elements + * @return The bit set. + */ + private BitSet bitSet(String[] elements) + { + BitSet b = new BitSet(); + + for (int i = 0; i < elements.length; i++) + { + Element e = getElement(elements [ i ]); + + if (e == null) + e = newElement(elements [ i ]); + + b.set(e.index); + } + + return b; + } + + /** + * Find the element with the given name in the element table. + * If not find, create a new element with this name and add to the + * table. + * @param name the name of the element + * @return the found or created element. + */ + private Element newElement(String name) + { + Element e = elementHash.get(name.toLowerCase()); + + if (e == null) + { + e = new Element(); + e.name = name; + e.index = elements.size(); + elements.add(e); + elementHash.put(e.name.toLowerCase(), e); + assignField(e); + } + return e; + } + + /** + * Creates and adds to the element table the entity with an + * unitialized data section. Used internally. + * @param name the name of the entity + * @param type the type of the entity, a bitwise combination + * of GENERAL, PARAMETER, SYSTEM and PUBLIC. + * + * @return the created entity + */ + private Entity newEntity(String name, int type) + { + Entity e = new Entity(name, type, null); + entityHash.put(e.name, e); + return e; + } + + private void unexpected(Exception ex) + { + throw new Error("This should never happen, report a bug", ex); + } +} diff --git a/libjava/classpath/javax/swing/text/html/parser/DTDConstants.java b/libjava/classpath/javax/swing/text/html/parser/DTDConstants.java new file mode 100644 index 000000000..75e7afb4d --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/parser/DTDConstants.java @@ -0,0 +1,292 @@ +/* DTDConstants.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html.parser; + +/** + *

This class defines the SGML basic types, used for describing HTML 4.01 + * at http://www.w3.org/TR/html4/types.html. Not all constants, + * defined here, are actually used in HTML 4.01 SGML specification. Some others + * are defined just as part of the required implementation. + *

+ *

+ * If you need more information about SGML DTD documents, + * the author suggests to read SGML tutorial on + * http://www.w3.org/TR/WD-html40-970708/intro/sgmltut.html. + * We also recommend Goldfarb C.F (1991) The SGML Handbook, + * Oxford University Press, 688 p, ISBN: 0198537379. + *

+ * + * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public interface DTDConstants +{ + /* ----- The data types, used in HTML 4.01 SGML definition: ---- */ + + /** + * The CDATA (Character data) constant, specifes the content model, + * consisting of characters only. In SGML for HTML 4.01, the character + * entities must be replaced by characters, the line feeds must be + * ignored and any number of the subsequent carriage returns or tabs + * must be replaced by a single space. + */ + int CDATA = 1; + + /** + * The EMPTY constant, means the element with no content. + */ + int EMPTY = 17; + + /** + * The ID constant, means that the token is the unique identifier. + * This identifier can be referenced by attribute with value of IDREF. + * The identifier must begin with letter, followed by any number of + * letters, digits, hyphens, underscores, colons and periods. + */ + int ID = 4; + + /** + * The IDREF constant, specifies reference to a valid ID within + * the document. + */ + int IDREF = 5; + + /** + * The IDREFS constant, a space separated list of IDREFs + */ + int IDREFS = 6; + + /** + * The NAME constant, means the token that + * must begin with letter, followed by any number of + * letters, digits, hyphens, underscores, colons and periods. + */ + int NAME = 7; + + /** + * The NAMES constant, specifies a space separated of NAMEs. + */ + int NAMES = 8; + + /** + * The NMTOKEN constant, specifies the attribute, consisting of + * characters that can be either digits or alphabetic characters). + */ + int NMTOKEN = 9; + + /** + * The NMTOKENS constant, specifies a list of NMTOKENs. + */ + int NMTOKENS = 10; + + /** + * The NOTATION constant, a previously defined data type. + */ + int NOTATION = 11; + + /** + * The NUMBER constant (means that the attribute consists of at least + * one decimal digit). + */ + int NUMBER = 12; + + /** + * The NUMBERS constant, specifies a space separated list of NUMBERs. + */ + int NUMBERS = 13; + + /** + * The NUTOKEN constant. + */ + int NUTOKEN = 14; + + /** + * The NUTOKENS constant. + */ + int NUTOKENS = 15; + + /* ------- + The entity scope constants. + As these four constants are combined with the bitwise OR, + they are defined in the hexadecimal notation. + The reason of setting the two bits at once (for PUBLIC and SYSTEM) + is probably historical. ----- */ + + /** + * The PUBLIC constant, specifies the public entity. The PUBLIC entities + * are assumed to be known to many systems so that a full declaration + * need not be transmitted. For example, + * <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0//EN"> + */ + int PUBLIC = 0xA; + + /** + * The SYSTEM constant, specifies the system entitiy. The system entities + * are assumed to be known but require the clear identifer + * (like the file path), where they can be found in the system. + * For example, + * <DOCTYPE html SYSTEM "/path/to/file.dtd"> . + */ + int SYSTEM = 0x11; + + /** + * The PARAMETER constant, specifies that entity is only valid + * inside SGML DTD scope. + */ + int PARAMETER = 0x40000; + + /** + * The GENERAL constant, specifies theat the entity is valid in the + * whole HTML document scope. + */ + int GENERAL = 0x10000; + + /* ---- The constants, defining if the element attribute is required, + fixed or implied. ---- */ + + /** + * The attribute modifier #REQUIRED constant, indicates that the + * value must be supplied. + */ + int REQUIRED = 2; + + /** + * The attribute modifier #FIXED constant, means that the attribute has + * the fixed value that cannot be changed. + */ + int FIXED = 1; + + /** + * The attribute modifier #IMPLIED constant, + * indicating that for this attribute the user agent must provide + * the value itself. + */ + int IMPLIED = 5; + + /** + * The attribute modifier #CURRENT constant, specifies the value + * that at any point in the document is the last value supplied for + * that element. A value is required to be supplied for the first + * occurrence of an element + */ + int CURRENT = 3; + + /** + * The attribute modifier #CONREF constant, specifies the IDREF value of + * the reference to content in another location of the document. + * The element with this attribute is empty, the content from + * that another location must be used instead. + */ + int CONREF = 4; + + /* ----- Constants, defining if the element + start and end tags are required. ---- */ + + /** + * The STARTTAG, meaning that the element needs a starting tag. + */ + int STARTTAG = 13; + + /** + * The ENDTAG constant, meaning that the element needs a closing tag. + */ + int ENDTAG = 14; + + /* ----- Other constants: ----- */ + + /** + * The ANY constant, specifies + * an attribute, consisting from arbitrary characters. + */ + int ANY = 19; + + /** + * The DEFAULT constant, specifies the default value. + */ + int DEFAULT = 131072; + + /** + * The ENTITIES constant (list of ENTITYes) + */ + int ENTITIES = 3; + + /** + * The ENTITY constant, meaning the numeric or symbolic name of some + * HTML data. + */ + int ENTITY = 2; + + /** + * The MD constant. + */ + int MD = 16; + + /** + * The MODEL constant. + */ + int MODEL = 18; + + /** + * The MS constant. + */ + int MS = 15; + + /** + * The PI (Processing Instruction) constant, specifies a processing + * instruction. Processing instructions are used to embed information + * intended for specific applications. + */ + int PI = 12; + + /** + * The RCDATA constant (Entity References and Character Data), specifies + * the content model, consisting of characters AND entities. The + * "<" is threated as an ordinary character, but + * "&name;" still means the general entity with + * the given name. + */ + int RCDATA = 16; + + /** + * The SDATA constant. Means that the value contains the entity name + * and the replacement value of a character entity reference. + */ + int SDATA = 11; +} diff --git a/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java b/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java new file mode 100644 index 000000000..f717d69cb --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/parser/DocumentParser.java @@ -0,0 +1,268 @@ +/* DocumentParser.java -- A parser for HTML documents. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html.parser; + +import javax.swing.text.html.parser.Parser; + +import java.io.IOException; +import java.io.Reader; + +import javax.swing.text.BadLocationException; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.html.HTMLEditorKit; + +/** + *

A simple error-tolerant HTML parser that uses a DTD document + * to access data on the possible tokens, arguments and syntax.

+ *

The parser reads an HTML content from a Reader and calls various + * notifying methods (which should be overridden in a subclass) + * when tags or data are encountered.

+ *

Some HTML elements need no opening or closing tags. The + * task of this parser is to invoke the tag handling methods also when + * the tags are not explicitly specified and must be supposed using + * information, stored in the DTD. + * For example, parsing the document + *

<table><tr><td>a<td>b<td>c</tr>
+ * will invoke exactly the handling methods exactly in the same order + * (and with the same parameters) as if parsing the document:
+ * <html><head></head><body><table>< + * tbody><tr><td>a</td><td>b + * </td><td>c</td></tr>< + * /tbody></table></body></html>

+ * (supposed tags are given in italics). The parser also supports + * obsolete elements of HTML syntax.

+ *

+ * In this implementation, DocumentParser is directly derived from its + * ancestor without changes of functionality. + * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public class DocumentParser + extends Parser + implements DTDConstants +{ + /** + * The enclosed working parser class. + */ + private class gnuParser + extends gnu.javax.swing.text.html.parser.support.Parser + { + private gnuParser(DTD d) + { + super(d); + } + + protected final void handleComment(char[] comment) + { + parser.handleComment(comment); + callBack.handleComment(comment, hTag.where.startPosition); + } + + protected final void handleEmptyTag(TagElement tag) + throws javax.swing.text.ChangedCharSetException + { + parser.handleEmptyTag(tag); + callBack.handleSimpleTag(tag.getHTMLTag(), getAttributes(), + hTag.where.startPosition + ); + } + + protected final void handleEndTag(TagElement tag) + { + parser.handleEndTag(tag); + callBack.handleEndTag(tag.getHTMLTag(), hTag.where.startPosition); + } + + protected final void handleError(int line, String message) + { + parser.handleError(line, message); + callBack.handleError(message, hTag.where.startPosition); + } + + protected final void handleStartTag(TagElement tag) + { + parser.handleStartTag(tag); + SimpleAttributeSet attributes = gnu.getAttributes(); + + if (tag.fictional()) + attributes.addAttribute(HTMLEditorKit.ParserCallback.IMPLIED, + Boolean.TRUE + ); + + callBack.handleStartTag(tag.getHTMLTag(), attributes, + hTag.where.startPosition + ); + } + + protected final void handleText(char[] text) + { + parser.handleText(text); + callBack.handleText(text, hTag.where.startPosition); + } + + DTD getDTD() + { + return dtd; + } + } + + /** + * This field is used to access the identically named + * methods of the outer class. + * This is package-private to avoid an accessor method. + */ + DocumentParser parser = this; + + /** + * The callback. + * This is package-private to avoid an accessor method. + */ + HTMLEditorKit.ParserCallback callBack; + + /** + * The reference to the working class of HTML parser that is + * actually used to parse the document. + * This is package-private to avoid an accessor method. + */ + gnuParser gnu; + + /** + * Creates a new parser that uses the given DTD to access data on the + * possible tokens, arguments and syntax. There is no single - step way + * to get a default DTD; you must either refer to the implementation - + * specific packages, write your own DTD or obtain the working instance + * of parser in other way, for example, by calling + * {@link javax.swing.text.html.HTMLEditorKit#getParser()}. + * + * @param a_dtd a DTD to use. + */ + public DocumentParser(DTD a_dtd) + { + super(a_dtd); + gnu = new gnuParser(a_dtd); + } + + /** + * Parses the HTML document, calling methods of the provided + * callback. This method must be multithread - safe. + * @param reader The reader to read the HTML document from + * @param aCallback The callback that is notifyed about the presence + * of HTML elements in the document. + * @param ignoreCharSet If thrue, any charset changes during parsing + * are ignored. + * @throws java.io.IOException + */ + public void parse(Reader reader, HTMLEditorKit.ParserCallback aCallback, + boolean ignoreCharSet + ) + throws IOException + { + callBack = aCallback; + gnu.parse(reader); + + callBack.handleEndOfLineString(gnu.getEndOfLineSequence()); + try + { + callBack.flush(); + } + catch (BadLocationException ex) + { + // Convert this into the supported type of exception. + throw new IOException(ex.getMessage()); + } + } + + /** + * Handle HTML comment. The default method returns without action. + * @param comment the comment being handled + */ + protected void handleComment(char[] comment) + { + // This default implementation does nothing. + } + + /** + * Handle the tag with no content, like <br>. The method is + * called for the elements that, in accordance with the current DTD, + * has an empty content. + * @param tag the tag being handled. + * @throws javax.swing.text.ChangedCharSetException + */ + protected void handleEmptyTag(TagElement tag) + throws javax.swing.text.ChangedCharSetException + { + // This default implementation does nothing. + } + + /** + * The method is called when the HTML closing tag ((like </table>) + * is found or if the parser concludes that the one should be present + * in the current position. + * @param tag The tag being handled + */ + protected void handleEndTag(TagElement tag) + { + // This default implementation does nothing. + } + + /* Handle error that has occured in the given line. */ + protected void handleError(int line, String message) + { + // This default implementation does nothing. + } + + /** + * The method is called when the HTML opening tag ((like <table>) + * is found or if the parser concludes that the one should be present + * in the current position. + * @param tag The tag being handled + */ + protected void handleStartTag(TagElement tag) + { + // This default implementation does nothing. + } + + /** + * Handle the text section. + * @param text a section text. + */ + protected void handleText(char[] text) + { + // This default implementation does nothing. + } +} diff --git a/libjava/classpath/javax/swing/text/html/parser/Element.java b/libjava/classpath/javax/swing/text/html/parser/Element.java new file mode 100644 index 000000000..c07c07f54 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/parser/Element.java @@ -0,0 +1,317 @@ +/* Element.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html.parser; + +import gnu.javax.swing.text.html.parser.support.gnuStringIntMapper; + +import java.io.Serializable; + +import java.util.BitSet; + +/** + *

+ * Stores the element information, obtained by parsing SGML DTD + * tag <!ELEMENT .. >. This class has no public + * constructor and can only be instantiated using the + * {@link javax.swing.text.html.parser.DTD } methods

+ * + *

SGML defines elements that represent structures or + * behavior. An element typically consists of a start tag, content, and an + * end tag. Hence the elements are not tags. The HTML 4.0 definition specifies + * that some elements are not required to have the end tags. Also, some + * HTML elements (like <hr>) have no content. Element names + * are case sensitive.

+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public final class Element + implements DTDConstants, Serializable +{ + /** + * Package level mapper between type names and they string values. + */ + static final gnuStringIntMapper mapper = + new gnuStringIntMapper() + { + protected void create() + { + add("CDATA", DTDConstants.CDATA); + add("RCDATA", DTDConstants.RCDATA); + add("EMPTY", DTDConstants.EMPTY); + add("ANY", DTDConstants.ANY); + } + }; + + /** Use serialVersionUID for interoperability. */ + private static final long serialVersionUID = -6717939384601675586L; + + /** + * The element attributes. + */ + public AttributeList atts; + + /** + * Contains refernces to elements that must NOT occur inside this element, + * at any level of hierarchy. + */ + public BitSet exclusions; + + /** + * Contains refernces to elements that must CAN occur inside this element, + * at any level of hierarchy. + */ + public BitSet inclusions; + + /** + * The content model, defining elements, entities and DTD text + * that may/may not occur inside this element. + */ + public ContentModel content; + + /** + * A field to store additional user data for this Element. + */ + public Object data; + + /** + * The element name. + */ + public String name; + + /** + * True is this element need not to have the closing tag, false + * otherwise. The HTML 4.0 definition specifies + * that some elements (like <hr>are + * not required to have the end tags. + */ + public boolean oEnd; + + /** + * True is this element need not to have the starting tag, false + * otherwise. The HTML 4.0 definition specifies + * that some elements (like <head> or + * <body>) are + * not required to have the start tags. + + */ + public boolean oStart; + + /** + * This field contains the unique integer identifier of this Element, + * used to refer the element (more exactly, the element flag) + * in inclusions and exclusions bit set. + */ + public int index; + + /** + * The element type, containing value, defined in DTDConstants. + * In this implementation, the element type can be + * CDATA, RCDATA, EMPTY or ANY. + */ + public int type; + + /** + * The default constructor must have package level access in this + * class. Use DTD.defineElement(..) to create an element when required. + */ + Element() + { + // Nothing to do here. + } + + /** + * Converts the string representation of the element type + * into its unique integer identifier, defined in DTDConstants. + * @param a_type A name of the type + * @return DTDConstants.CDATA, DTDConstants.RCDATA, DTDConstants.EMPTY, + * DTDConstants.ANY or null if the type name is not + * "CDATA", "RCDATA", "EMPTY" or "ANY". This function is case sensitive. + * @throws NullPointerException if a_type is null. + */ + public static int name2type(String a_type) + { + return mapper.get(a_type); + } + + /** + * Get the element attribute by name. + * @param attribute the attribute name, case insensitive. + * @return the correspoding attribute of this element. The class, + * for storing as attribute list, as a single attribute, is used to + * store a single attribute in this case. + * @throws NullPointerException if the attribute name is null. + */ + public AttributeList getAttribute(String attribute) + { + AttributeList a = atts; + + while (a != null && !attribute.equalsIgnoreCase(a.name)) + a = a.next; + + return a; + } + + /** + * Get the element attribute by its value. + * @param a_value the attribute value, case insensitive. + * @return the correspoding attribute of this element. The class, + * for storing as attribute list, as a single attribute, is used to + * store a single attribute in this case. If there are several + * attributes with the same value, there is no garranty, which one + * is returned. + */ + public AttributeList getAttributeByValue(String a_value) + { + AttributeList a = atts; + + if (a_value == null) + { + while (a != null) + { + if (a.value == null) + return a; + + a = a.next; + } + } + else + { + while (a != null) + { + if (a.value != null && a_value.equalsIgnoreCase(a.value)) + return a; + + a = a.next; + } + } + + return null; + } + + /** + * Get all attributes of this document as an attribute list. + * @return The attribute list. + */ + public AttributeList getAttributes() + { + return atts; + } + + /** + * Get the content model, defining elements, entities and DTD text + * that may/may not occur inside this element. + */ + public ContentModel getContent() + { + return content; + } + + /** + * Returns true for the element with no content. + * Empty elements are defined with the SGML DTD keyword "EMPTY". + * @return true if content model field (content) method is equal to + * null or its method empty() returns true. + */ + public boolean isEmpty() + { + return content == null || content.empty(); + } + + /** + * Get the unique integer identifier of this Element, + * used to refer the element (more exactly, the element flag) + * in inclusions and exclusions bit set. + * WARNING: This value may not be the same between different + * implementations. + */ + public int getIndex() + { + return index; + } + + /** + * Get the element name. + */ + public String getName() + { + return name; + } + + /** + * Get the element type. + * @return one of the values, defined DTDConstants. + * In this implementation, the element type can be + * CDATA, RCDATA, EMPTY or ANY. + */ + public int getType() + { + return type; + } + + /** + * True is this element need not to have the starting tag, false + * otherwise.s element need not to have the closing tag, false + * otherwise. The HTML 4.0 definition specifies + * that some elements (like <hr>are + * not required to have the end tags. + */ + public boolean omitEnd() + { + return oEnd; + } + + /** + * True is this element need not to have the closing tag, false + * otherwise. The HTML 4.0 definition specifies + * that some elements (like <head> or + * <body>) are + * not required to have the start tags. + */ + public boolean omitStart() + { + return oStart; + } + + /** + * Returns the name of this element. + */ + public String toString() + { + return name; + } +} diff --git a/libjava/classpath/javax/swing/text/html/parser/Entity.java b/libjava/classpath/javax/swing/text/html/parser/Entity.java new file mode 100644 index 000000000..d40fb94f3 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/parser/Entity.java @@ -0,0 +1,183 @@ +/* Entity.java -- Stores information, obtained by parsing SGML DTL + * <!ENTITY % .. > tag + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html.parser; + +import gnu.javax.swing.text.html.parser.support.gnuStringIntMapper; + +/** + *

Stores information, obtained by parsing SGML DTL + * <!ENTITY % .. > tag.

+ *

+ * The entity defines some kind of macro that can be used elsewhere in + * the document. + * When the macro is referred to by the name in the DTD, it is expanded into + * a string + * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public final class Entity + implements DTDConstants +{ + /** + * Package level mapper between type names and they string values. + */ + final static gnuStringIntMapper mapper = + new gnuStringIntMapper() + { + protected void create() + { + add("ANY", DTDConstants.ANY); + add("CDATA", DTDConstants.CDATA); + add("PUBLIC", DTDConstants.PUBLIC); + add("SDATA", DTDConstants.SDATA); + add("PI", DTDConstants.PI); + add("STARTTAG", DTDConstants.STARTTAG); + add("ENDTAG", DTDConstants.ENDTAG); + add("MS", DTDConstants.MS); + add("MD", DTDConstants.MD); + add("SYSTEM", DTDConstants.SYSTEM); + } + }; + + /** + * The entity name. + */ + public String name; + + /** + * The entity data + */ + public char[] data; + + /** + * The entity type. + */ + public int type; + + /** + * String representation of the entity data. + */ + private String sdata; + + /** + * Create a new entity + * @param a_name the entity name + * @param a_type the entity type + * @param a_data the data replacing the entity reference + */ + public Entity(String a_name, int a_type, char[] a_data) + { + name = a_name; + type = a_type; + data = a_data; + } + + /** + * Converts a given string to the corresponding entity type. + * @return a value, defined in DTDConstants (one of + * PUBLIC, CDATA, SDATA, PI, STARTTAG, ENDTAG, MS, MD, SYSTEM) + * or CDATA if the parameter is not a valid entity type. + */ + public static int name2type(String an_entity) + { + int r = mapper.get(an_entity); + return (r == 0) ? DTDConstants.CDATA : r; + } + + /** + * Get the entity data. + */ + public char[] getData() + { + return data; + } + + /** + * Returns true for general entities. Each general entity can be + * referenced as &entity-name;. Such entities are + * defined by the SGML DTD tag + * <!ENTITY name "value">. The general + * entities can be used anywhere in the document. + */ + public boolean isGeneral() + { + return (type & DTDConstants.GENERAL) != 0; + } + + /** + * Get the entity name. + */ + public String getName() + { + return name; + } + + /** + * Returns true for parameter entities. Each parameter entity can be + * referenced as &entity-name;. Such entities are + * defined by the SGML DTD tag + * <!ENTITY % name "value">. The parameter + * entities can be used only in SGML context. + */ + public boolean isParameter() + { + return (type & DTDConstants.PARAMETER) != 0; + } + + /** + * Returns a data as String + */ + public String getString() + { + if (sdata == null) + sdata = new String(data); + + return sdata; + } + + /** + * Get the entity type. + * @return the value of the {@link #type}. + */ + public int getType() + { + return type; + } + +} diff --git a/libjava/classpath/javax/swing/text/html/parser/Parser.java b/libjava/classpath/javax/swing/text/html/parser/Parser.java new file mode 100644 index 000000000..f3faa2524 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/parser/Parser.java @@ -0,0 +1,446 @@ +/* Parser.java -- HTML parser + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html.parser; + +import java.io.IOException; +import java.io.Reader; + +import javax.swing.text.ChangedCharSetException; +import javax.swing.text.SimpleAttributeSet; + +/* + * FOR DEVELOPERS: To avoid regression, please run the package test + * textsuite/javax.swing.text.html.parser/AllParserTests after your + * modifications. + */ + +/** + *

A simple error-tolerant HTML parser that uses a DTD document + * to access data on the possible tokens, arguments and syntax.

+ *

The parser reads an HTML content from a Reader and calls various + * notifying methods (which should be overridden in a subclass) + * when tags or data are encountered.

+ *

Some HTML elements need no opening or closing tags. The + * task of this parser is to invoke the tag handling methods also when + * the tags are not explicitly specified and must be supposed using + * information, stored in the DTD. + * For example, parsing the document + *

<table><tr><td>a<td>b<td>c</tr>
+ * will invoke exactly the handling methods exactly in the same order + * (and with the same parameters) as if parsing the document:
+ * <html><head></head><body><table>< + * tbody><tr><td>a</td><td>b + * </td><td>c</td></tr>< + * /tbody></table></body></html>

+ * (supposed tags are given in italics). The parser also supports + * obsolete elements of HTML syntax.

+ *

+ * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public class Parser + implements DTDConstants +{ + /** + * The document template description that will be used to parse the documents. + */ + protected DTD dtd; + + /** + * The value of this field determines whether or not the Parser will be + * strict in enforcing SGML compatibility. The default value is false, + * stating that the parser should do everything to parse and get at least + * some information even from the incorrectly written HTML input. + */ + protected boolean strict; + + /** + * The package level reference to the working HTML parser in this + * implementation. + */ + final gnu.javax.swing.text.html.parser.support.Parser gnu; + + /** + * Creates a new parser that uses the given DTD to access data on the + * possible tokens, arguments and syntax. There is no single - step way + * to get a default DTD; you must either refer to the implementation - + * specific packages, write your own DTD or obtain the working instance + * of parser in other way, for example, by calling + * {@link javax.swing.text.html.HTMLEditorKit#getParser() }. + * @param a_dtd A DTD to use. + */ + public Parser(DTD a_dtd) + { + dtd = a_dtd; + + final Parser j = this; + + gnu = + new gnu.javax.swing.text.html.parser.support.Parser(dtd) + { + protected final void handleComment(char[] comment) + { + j.handleComment(comment); + } + + protected final void handleEOFInComment() + { + j.handleEOFInComment(); + } + + protected final void handleEmptyTag(TagElement tag) + throws javax.swing.text.ChangedCharSetException + { + j.handleEmptyTag(tag); + } + + protected final void handleStartTag(TagElement tag) + { + j.handleStartTag(tag); + } + + protected final void handleEndTag(TagElement tag) + { + j.handleEndTag(tag); + } + + protected final void handleError(int line, String message) + { + j.handleError(line, message); + } + + protected final void handleText(char[] text) + { + j.handleText(text); + } + + protected final void handleTitle(char[] title) + { + j.handleTitle(title); + } + + protected final void markFirstTime(Element element) + { + j.markFirstTime(element); + } + + protected final void startTag(TagElement tag) + throws ChangedCharSetException + { + j.startTag(tag); + } + + protected final void endTag(boolean omitted) + { + j.endTag(omitted); + } + + protected TagElement makeTag(Element element) + { + return j.makeTag(element); + } + + protected TagElement makeTag(Element element, boolean isSupposed) + { + return j.makeTag(element, isSupposed); + } + }; + } + + /** + * Parse the HTML text, calling various methods in response to the + * occurence of the corresponding HTML constructions. + * @param reader The reader to read the source HTML from. + * @throws IOException If the reader throws one. + */ + public synchronized void parse(Reader reader) + throws IOException + { + gnu.parse(reader); + } + + /** + * Parses DTD markup declaration. Currently returns without action. + * @return null. + * @throws java.io.IOException + */ + public String parseDTDMarkup() + throws IOException + { + return gnu.parseDTDMarkup(); + } + + /** + * Parse DTD document declarations. Currently only parses the document + * type declaration markup. + * @param strBuff + * @return true if this is a valid DTD markup declaration. + * @throws IOException + */ + protected boolean parseMarkupDeclarations(StringBuffer strBuff) + throws IOException + { + return gnu.parseMarkupDeclarations(strBuff); + } + + /** + * Get the attributes of the current tag. + * @return The attribute set, representing the attributes of the current tag. + */ + protected SimpleAttributeSet getAttributes() + { + return gnu.getAttributes(); + } + + /** + * Get the number of the document line being parsed. + * @return The current line. + */ + protected int getCurrentLine() + { + return gnu.hTag.where.beginLine; + } + + /** + * Get the current position in the document being parsed. + * @return The current position. + */ + protected int getCurrentPos() + { + return gnu.hTag.where.startPosition; + } + + /** + * The method is called when the HTML end (closing) tag is found or if + * the parser concludes that the one should be present in the + * current position. The method is called immediatly + * before calling the handleEndTag(). + * @param omitted True if the tag is no actually present in the document, + * but is supposed by the parser (like </html> at the end of the + * document). + */ + protected void endTag(boolean omitted) + { + // This default implementation does nothing. + } + + /** + * Invokes the error handler. The default method in this implementation + * finally delegates the call to handleError, also providing the number of the + * current line. + */ + protected void error(String msg) + { + gnu.error(msg); + } + + /** + * Invokes the error handler. The default method in this implementation + * finally delegates the call to error (msg+": '"+invalid+"'"). + */ + protected void error(String msg, String invalid) + { + gnu.error(msg, invalid); + } + + /** + * Invokes the error handler. The default method in this implementation + * finally delegates the call to error (parm1+" "+ parm2+" "+ parm3). + */ + protected void error(String parm1, String parm2, String parm3) + { + gnu.error(parm1, parm2, parm3); + } + + /** + * Invokes the error handler. The default method in this implementation + * finally delegates the call to error + * (parm1+" "+ parm2+" "+ parm3+" "+ parm4). + */ + protected void error(String parm1, String parm2, String parm3, String parm4) + { + gnu.error(parm1, parm2, parm3, parm4); + } + + /** + * In this implementation, this is never called and returns without action. + */ + protected void flushAttributes() + { + gnu.flushAttributes(); + } + + /** + * Handle HTML comment. The default method returns without action. + * @param comment The comment being handled + */ + protected void handleComment(char[] comment) + { + // This default implementation does nothing. + } + + /** + * This is additionally called in when the HTML content terminates + * without closing the HTML comment. This can only happen if the + * HTML document contains errors (for example, the closing --;gt is + * missing. The default method calls the error handler. + */ + protected void handleEOFInComment() + { + gnu.error("Unclosed comment"); + } + + /** + * Handle the tag with no content, like <br>. The method is + * called for the elements that, in accordance with the current DTD, + * has an empty content. + * @param tag The tag being handled. + * @throws javax.swing.text.ChangedCharSetException + */ + protected void handleEmptyTag(TagElement tag) + throws ChangedCharSetException + { + // This default implementation does nothing. + } + + /** + * The method is called when the HTML closing tag ((like </table>) + * is found or if the parser concludes that the one should be present + * in the current position. + * @param tag The tag being handled + */ + protected void handleEndTag(TagElement tag) + { + // This default implementation does nothing. + } + + /* Handle error that has occured in the given line. */ + protected void handleError(int line, String message) + { + // This default implementation does nothing. + } + + /** + * The method is called when the HTML opening tag ((like <table>) + * is found or if the parser concludes that the one should be present + * in the current position. + * @param tag The tag being handled + */ + protected void handleStartTag(TagElement tag) + { + // This default implementation does nothing. + } + + /** + * Handle the text section. + *

For non-preformatted section, the parser replaces + * \t, \r and \n by spaces and then multiple spaces + * by a single space. Additionaly, all whitespace around + * tags is discarded. + *

+ *

For pre-formatted text (inside TEXAREA and PRE), the parser preserves + * all tabs and spaces, but removes one bounding \r, \n or \r\n, + * if it is present. Additionally, it replaces each occurence of \r or \r\n + * by a single \n.

+ * + * @param text A section text. + */ + protected void handleText(char[] text) + { + // This default implementation does nothing. + } + + /** + * Handle HTML <title> tag. This method is invoked when + * both title starting and closing tags are already behind. + * The passed argument contains the concatenation of all + * title text sections. + * @param title The title text. + */ + protected void handleTitle(char[] title) + { + // This default implementation does nothing. + } + + /** + * Constructs the tag from the given element. In this implementation, + * this is defined, but never called. + * @param element the base element of the tag. + * @return the tag + */ + protected TagElement makeTag(Element element) + { + return makeTag(element, false); + } + + /** + * Constructs the tag from the given element. + * @param element the tag base {@link javax.swing.text.html.parser.Element} + * @param isSupposed true if the tag is not actually present in the + * html input, but the parser supposes that it should to occur in + * the current location. + * @return the tag + */ + protected TagElement makeTag(Element element, boolean isSupposed) + { + return new TagElement(element, isSupposed); + } + + /** + * This is called when the tag, representing the given element, + * occurs first time in the document. + * @param element + */ + protected void markFirstTime(Element element) + { + // This default implementation does nothing. + } + + /** + * The method is called when the HTML opening tag ((like <table>) + * is found or if the parser concludes that the one should be present + * in the current position. The method is called immediately before + * calling the handleStartTag. + * @param tag The tag + */ + protected void startTag(TagElement tag) + throws ChangedCharSetException + { + // This default implementation does nothing. + } +} diff --git a/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java b/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java new file mode 100644 index 000000000..cdd339b8f --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/parser/ParserDelegator.java @@ -0,0 +1,207 @@ +/* ParserDelegator.java -- Delegator for ParserDocument. + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + +package javax.swing.text.html.parser; + +import gnu.javax.swing.text.html.parser.HTML_401F; + +import java.io.IOException; +import java.io.Reader; +import java.io.Serializable; + +import javax.swing.text.BadLocationException; +import javax.swing.text.SimpleAttributeSet; +import javax.swing.text.html.HTMLEditorKit; +import javax.swing.text.html.HTMLEditorKit.ParserCallback; + +/** + * This class instantiates and starts the working instance of + * html parser, being responsible for providing the default DTD. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ +public class ParserDelegator + extends javax.swing.text.html.HTMLEditorKit.Parser + implements Serializable +{ + private class gnuParser + extends gnu.javax.swing.text.html.parser.support.Parser + { + private static final long serialVersionUID = 1; + + private gnuParser(DTD d) + { + super(d); + } + + protected final void handleComment(char[] comment) + { + callBack.handleComment(comment, hTag.where.startPosition); + } + + protected final void handleEmptyTag(TagElement tag) + throws javax.swing.text.ChangedCharSetException + { + callBack.handleSimpleTag(tag.getHTMLTag(), getAttributes(), + hTag.where.startPosition + ); + } + + protected final void handleEndTag(TagElement tag) + { + callBack.handleEndTag(tag.getHTMLTag(), hTag.where.startPosition); + } + + protected final void handleError(int line, String message) + { + callBack.handleError(message, hTag.where.startPosition); + } + + protected final void handleStartTag(TagElement tag) + { + SimpleAttributeSet attributes = gnu.getAttributes(); + + if (tag.fictional()) + attributes.addAttribute(ParserCallback.IMPLIED, Boolean.TRUE); + + callBack.handleStartTag(tag.getHTMLTag(), attributes, + hTag.where.startPosition + ); + } + + protected final void handleText(char[] text) + { + callBack.handleText(text, hTag.where.startPosition); + } + + DTD getDTD() + { + // Accessing the inherited gnu.javax.swing.text.html.parser.support.Parser + // field. super. is a workaround, required to support JDK1.3's javac. + return super.dtd; + } + } + + /** + * Use serialVersionUID for interoperability. + */ + private static final long serialVersionUID = -1276686502624777206L; + + private static DTD dtd = HTML_401F.getInstance(); + + /** + * The callback. + * This is package-private to avoid an accessor method. + */ + HTMLEditorKit.ParserCallback callBack; + + /** + * The reference to the working class of HTML parser that is + * actually used to parse the document. + * This is package-private to avoid an accessor method. + */ + gnuParser gnu; + + /** + * Parses the HTML document, calling methods of the provided + * callback. This method must be multithread - safe. + * @param reader The reader to read the HTML document from + * @param a_callback The callback that is notifyed about the presence + * of HTML elements in the document. + * @param ignoreCharSet If thrue, any charset changes during parsing + * are ignored. + * @throws java.io.IOException + */ + public void parse(Reader reader, HTMLEditorKit.ParserCallback a_callback, + boolean ignoreCharSet + ) + throws IOException + { + callBack = a_callback; + + if (gnu == null || !dtd.equals(gnu.getDTD())) + { + gnu = new gnuParser(dtd); + } + + gnu.parse(reader); + + callBack.handleEndOfLineString(gnu.getEndOfLineSequence()); + try + { + callBack.flush(); + } + catch (BadLocationException ex) + { + // Convert this into the supported type of exception. + throw new IOException(ex.getMessage()); + } + } + + /** + * Calling this method instructs that, if not specified directly, + * the documents will be parsed using the default + * DTD of the implementation. + */ + protected static void setDefaultDTD() + { + dtd = HTML_401F.getInstance(); + } + + /** + * Registers the user - written DTD under the given name, also + * making it default for the subsequent parsings. This has effect on + * all subsequent calls to the parse(...) . If you need to specify + * your DTD locally, simply {@link javax.swing.text.html.parser.Parser} + * instead. + * @param a_dtd The DTD that will be used to parse documents by this class. + * @param name The name of this DTD. + * @return No standard is specified on which instance of DTD must be + * returned by this method, and it is recommended to leave the returned + * value without consideration. This implementation returns the DTD + * that was previously set as the default DTD, or the implementations + * default DTD if none was set. + */ + protected static DTD createDTD(DTD a_dtd, String name) + { + DTD.putDTDHash(name, a_dtd); + + DTD dtd_prev = dtd; + dtd = a_dtd; + return dtd_prev; + } +} diff --git a/libjava/classpath/javax/swing/text/html/parser/TagElement.java b/libjava/classpath/javax/swing/text/html/parser/TagElement.java new file mode 100644 index 000000000..4558b15eb --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/parser/TagElement.java @@ -0,0 +1,142 @@ +/* TagElement.java -- + Copyright (C) 2005 Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package javax.swing.text.html.parser; + +import javax.swing.text.html.HTML; + +/** + * The SGML element, defining a single html tag. + * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public class TagElement +{ + /** + * The Element the tag was constructed from. + */ + private final Element element; + + /** + * The coresponding HTML tag, assigned once in constructor. + */ + private final HTML.Tag tag; + + /** + * The 'fictional' flag. + */ + private final boolean fictional; + + /** + * Creates the html tag element from the defintion, stored in the + * given element. Sets the flag 'fictional' to false. + * @param an_element + */ + public TagElement(Element an_element) + { + this(an_element, false); + } + + /** + * Creates the html tag element from the defintion, stored in the + * given element, setting the flag 'fictional' to the given value. + */ + public TagElement(Element an_element, boolean is_fictional) + { + element = an_element; + fictional = is_fictional; + + HTML.Tag t = HTML.getTag(element.getName()); + + if (t != null) + tag = t; + else + tag = new HTML.UnknownTag(element.getName()); + } + + /** + * Get the element from that the tag was constructed. + */ + public Element getElement() + { + return element; + } + + /** + * Get the corresponding HTML tag. This is either one of the + * pre-defined HTML tags or the instance of the UnknownTag with the + * element name. + */ + public HTML.Tag getHTMLTag() + { + return tag; + } + + /** + * Calls isPreformatted() for the corresponding html tag and returns + * the obtained value. + */ + public boolean isPreformatted() + { + return tag.isPreformatted(); + } + + /** + * Calls breaksFlow() for the corresponding html tag and returns + * the obtained value. + */ + public boolean breaksFlow() + { + return tag.breaksFlow(); + } + + /** + * Get the value of the flag 'fictional'. + */ + public boolean fictional() + { + return fictional; + } + + /** + * Returns string representation of this object. + */ + public String toString() + { + return getElement() + (fictional ? "?" : ""); + } +} diff --git a/libjava/classpath/javax/swing/text/html/parser/package.html b/libjava/classpath/javax/swing/text/html/parser/package.html new file mode 100644 index 000000000..5d5157fb2 --- /dev/null +++ b/libjava/classpath/javax/swing/text/html/parser/package.html @@ -0,0 +1,50 @@ + + + + +GNU Classpath - javax.swing.text.html.parser + + +

Provides the DTD driven for web browsers, + web robots, web page content analysers, web editors and + other applications applications working with Hypertext + Markup Language (HTML). +

+ + + -- cgit v1.2.3