diff options
Diffstat (limited to 'libjava/classpath/javax/swing/text/html')
45 files changed, 19073 insertions, 0 deletions
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 <langel@redhat.com> + */ +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 <code>true</code> when the CSS span has been set, + * <code>false</code> 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 <code>r</code> requirements according to + * <code>min</code>. + * + * @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 <code>null</code> 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 <code>null</code> + */ + 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 <code>true</code> if the attribute should be inherited from + * the parent, <code>false</code> otherwise. + * + * @return <code>true</code> if the attribute should be inherited from + * the parent, <code>false</code> otherwise + */ + public boolean isInherited() + { + return isInherited; + } + + /** + * Returns the default value of this attribute if one exists, + * <code>null</code> otherwise. + * + * @return the default value of this attribute if one exists, + * <code>null</code> 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: + * <table> + * <tr><th>Element type</th><th>Swing component</th></tr> + * <tr><td>input, button</td><td>JButton</td></tr> + * <tr><td>input, checkbox</td><td>JButton</td></tr> + * <tr><td>input, image</td><td>JButton</td></tr> + * <tr><td>input, password</td><td>JButton</td></tr> + * <tr><td>input, radio</td><td>JButton</td></tr> + * <tr><td>input, reset</td><td>JButton</td></tr> + * <tr><td>input, submit</td><td>JButton</td></tr> + * <tr><td>input, text</td><td>JButton</td></tr> + * <tr><td>select, size > 1 or with multiple attribute</td> + * <td>JList in JScrollPane</td></tr> + * <tr><td>select, size unspecified or == 1</td><td>JComboBox</td></tr> + * <tr><td>textarea, text</td><td>JTextArea in JScrollPane</td></tr> + * <tr><td>input, file</td><td>JTextField</td></tr> + * </table> + * + * @author Roman Kennke (kennke@aicas.com) + */ +public class FormView + extends ComponentView + implements ActionListener +{ + + protected class MouseEventListener + extends MouseAdapter + { + /** + * Creates a new <code>MouseEventListener</code>. + */ + 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, <code>null</code> + * 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 <code><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 + * <code>FormView.submitButtonText</code>. + */ + public static final String SUBMIT = + UIManager.getString("FormView.submitButtonText"); + + /** + * If the value attribute of an <code><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 + * <code>FormView.resetButtonText</code>. + */ + 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 <code>FormView</code>. + * + * @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 + * <code><input type="image"></code> 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=<p.x>&y=<p.y>' if the name + * attribute of the element is null or '' or + * <name>.x=<p.x>&<name>.y=<p.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 <code>null</code> 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 <code>null</code>. + * + * @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<String,Tag> tagMap; + private static Map<String,Attribute> 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<String,Attribute>(); + + 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:<p><code> + * 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". + * </code></p> + * + * + * @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<String,Tag>(); + + 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 <code>base</code> 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, <code>null</code> + * 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("<frame"); + if (url != null) + { + html.append(" src=\""); + html.append(url.toString()); + html.append("\""); + } + 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 <code>AttributeSet</code> (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<ElementSpec> parseBuffer = new Vector<ElementSpec>(); + + /** + * The parse stack. It holds the current element tree path. + */ + private Stack<HTML.Tag> parseStack = new Stack<HTML.Tag>(); + + /** + * 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 '</html>' 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 <code>a</code>. + 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 </html> 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 <PRE> 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 <code>true</code> when this document is inside a frame, + * <code>false</code> otherwise. + * + * @return <code>true</code> when this document is inside a frame, + * <code>false</code> otherwise + */ + boolean isFrameDocument() + { + return frameDocument; + } + + /** + * Set <code>true</code> when this document is inside a frame, + * <code>false</code> otherwise. + * + * @param frameDoc <code>true</code> when this document is inside a frame, + * <code>false</code> 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 <code>null</code> 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 <code>addTag</code>. + * + * @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 <code>true</code> when the html has been inserted successfully, + * <code>false</code> 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 <code>Element</code>s that are supported. + */ + public static class HTMLFactory + implements ViewFactory + { + + /** + * Constructor + */ + public HTMLFactory() + { + // Do Nothing here. + } + + /** + * Creates a {@link View} for the specified <code>Element</code>. + * + * @param element the <code>Element</code> to create a <code>View</code> + * for + * @return the <code>View</code> for the specified <code>Element</code> + * or <code>null</code> if the type of <code>element</code> 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 <b>bold</b> action identifier. + */ + public static final String BOLD_ACTION = "html-bold-action"; + + /** + * The <i>italic</i> action identifier. + */ + public static final String ITALIC_ACTION = "html-italic-action"; + + /** + * The <font color="#FF0000">color</font> action indentifier + * (passing the color as an argument). + */ + public static final String COLOR_ACTION = "html-color-action"; + + /** + * The <font size="+1">increase</font> font action identifier. + */ + public static final String FONT_CHANGE_BIGGER = "html-font-bigger"; + + /** + * The <font size="-1">decrease</font> 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", + "<table border=1><tr><td></td></tr></table>", + HTML.Tag.BODY, HTML.Tag.TABLE), + new InsertHTMLTextAction("InsertTableRow", + "<table border=1><tr><td></td></tr></table>", + HTML.Tag.TABLE, HTML.Tag.TR, + HTML.Tag.BODY, HTML.Tag.TABLE), + new InsertHTMLTextAction("InsertTableCell", + "<table border=1><tr><td></td></tr></table>", + HTML.Tag.TR, HTML.Tag.TD, + HTML.Tag.BODY, HTML.Tag.TABLE), + new InsertHTMLTextAction("InsertUnorderedList", + "<ul><li></li></ul>", + HTML.Tag.BODY, HTML.Tag.UL), + new InsertHTMLTextAction("InsertUnorderedListItem", + "<ul><li></li></ul>", + HTML.Tag.UL, HTML.Tag.LI, + HTML.Tag.BODY, HTML.Tag.UL), + new InsertHTMLTextAction("InsertOrderedList", + "<ol><li></li></ol>", + HTML.Tag.BODY, HTML.Tag.OL), + new InsertHTMLTextAction("InsertOrderedListItem", + "<ol><li></li></ol>", + HTML.Tag.OL, HTML.Tag.LI, + HTML.Tag.BODY, HTML.Tag.OL), + new InsertHTMLTextAction("InsertPre", + "<pre></pre>", 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 <code>true</code> when forms should be automatically submitted + * by the editor kit. Set this to <code>false</code> 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 <code>true</code>. + * + * @return <code>true</code> when forms should be automatically submitted + * by the editor kit, <code>false</code> 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 <code>true</code> when the editor kit should handle form + * submission, <code>false</code> 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<HTML.Tag> 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<Element> 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<HTML.Tag>(); + } // public HTMLWriter(Writer writer, HTMLDocument doc) + + /** + * Constructs a HTMLWriter which outputs a Html Fragment. + * + * @param writer <code>Writer</code> to write output to + * @param doc the <code>javax.swing.text.html.HTMLDocument</code> + * 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<HTML.Tag>(); + + doc_pos = pos; + doc_offset_remaining = pos; + doc_len = len; + doc_len_remaining = len; + htmlFragmentParentHashSet = new HashSet<Element>(); + } // 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("</" + tag_arr[i].toString() + ">"); + } // 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 <code>javax.swing.text.html.HTML.Tag</code>, + * <code>javax.swing.text.StyleConstants</code> or + * <code>javax.swing.text.html.HTML.Attribute.ENDTAG</code>. + * + * @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("</" + elem_name + ">"); + } // 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 <code>true</code> if it is a block tag + * <code>false</code> 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("<textarea"); + writeAttributes(attrSet); + writeRaw(">"); + + Document tempDocument = + (Document) attrSet.getAttribute(StyleConstants.ModelAttribute); + + writeRaw(tempDocument.getText(0, tempDocument.getLength())); + indent(); + writeRaw("</textarea>"); + + } // 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("<select"); + writeAttributes(attrSet); + 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("</select>"); + + } // 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("<option"); + writeAttributes(option.getAttributes()); + writeRaw(">"); + + writeContent(option.getLabel()); + + writeRaw("</option>"); + 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("</" + elem_name + ">"); + 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("<!--" + comment_str + "-->"); + + } // protected void comment(Element paramElem) + // throws IOException, BadLocationException + + /** + * Determines if element is a synthesized + * <code>javax.swing.text.Element</code> or not. + * + * @param element the element to test + * + * @return <code>true</code> if it is a synthesized element, + * <code>false</code> 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 + * <code>javax.swing.text.StyleConstants.NameAttribute</code> + * matches tag or not. + * + * @param attrSet the <code>javax.swing.text.AttributeSet</code> of + * element to be matched + * @param tag the HTML.Tag to match + * + * @return <code>true</code> if it matches, + * <code>false</code> 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 <code>javax.swing.text.AttributeSet</code> 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("</" + key.toString() + ">"); + 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("</title>"); + else + { + indent(); + writeRaw("<title>"); + + 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("</title>"); + else + { + indent(); + writeRaw("<title>"); + + 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</title></head> + +<body> +<p> Provides supporting classes for web browsers, + web robots, web page content analysers, web editors and + other applications applications working with Hypertext + Markup Language (HTML). +</p> + +</body> +</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; + +/** + * <p> + * Stores the attribute information, obtained by parsing SGML (DTD) tag + * <code><!ATTLIST .. ></code></p> + * <p> + * 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 (.) . + * </p> + * <p> + * The <code>AttributeList</code> 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 <code>Element</code>. + * Use the <code>getNext()</code> method repeatedly to see all attributes in + * the list. + * </p> + * @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: + * <ul> + * <li> REQUIRED if the attribute value is always required,</li> + * <li> IMPLIED if the user agent must supply the default value itself,</li> + * <li> FIXED if the attribute value is fixed to some value and cannot + * be changed.</li> + * <li> DEFAULT if the attribute default value has been supplied.</li> + * <li> 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</li> + * <li> 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.</li> + * </ul> + */ + 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 <code> DTDConstants</code>. + * @param a_modifier The modifier of this attribute. The possible values + * are defined in <code> DTDConstants</code>. + * @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: + * <ul> + * <li> REQUIRED if the attribute value is always required,</li> + * <li> IMPLIED if the user agent must supply the default value itself,</li> + * <li> FIXED if the attribute value is fixed to some value and cannot + * be changed.</li> + * <li> DEFAULT if the attribute default value has been supplied.</li> + * <li> 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</li> + * <li> 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.</li> + * </ul> + */ + 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: + * <pre> + * 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? ) + * </pre> + * where the valid operations are: + * <ul> + * <li><code>E* </code> E occurs zero or more times</li> + * <li><code>E+ </code> E occurs one or more times</li> + * <li><code>E? </code> E occurs once or not atl all</li> + * <li><code>A,B</code> A occurs before B</li> + * <li><code>A|B</code> both A and B are permitted in any order. + * The '|' alone does not permit the repetetive occurence of A or B + * (use <code>(A|B)*</code>.</li> + * <li><code>A&B</code> both A and B must occur once (in any order)</li> + * </ul> + * @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 <code>next</code> (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: + *<code> + * 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 + * </code> + */ + 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<Element> 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 <code>next.content</code>. + * The method is programmed as the part of the standard API, but not + * used in this implementation. + * @return the value of the field <code>next</code>. + */ + 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; + +/** + * <p>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).</p> + * <p> + * If you need more information about SGML DTD documents, + * the author suggests to read SGML tutorial on + * <a href="http://www.w3.org/TR/WD-html40-970708/intro/sgmltut.html" + * >http://www.w3.org/TR/WD-html40-970708/intro/sgmltut.html</a>. + * We also recommend Goldfarb C.F (1991) <i>The SGML Handbook</i>, + * Oxford University Press, 688 p, ISBN: 0198537379. + * </p> + * <p> + * 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. + * </p> + * + * @author Audrius Meskauskas, Lithuania (AudriusA@Bioinformatics.org) + */ +public class DTD + implements DTDConstants +{ + /** + * The version of the persistent data format. + * @specnote This was made <code>final</code> in 1.5. + */ + public static final int FILE_VERSION = 1; + + /** + * The table of existing available DTDs. + */ + static Hashtable<String,DTD> dtdHash = new Hashtable<String,DTD>(); + + /** + * 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<String,Element> elementHash = + new Hashtable<String,Element>(); + + /** + * The entity table for accessing all DTD entities by name. + */ + public Hashtable<Object, Entity> entityHash = new Hashtable<Object, Entity>(); + + /** + * 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<Element> elements = new Vector<Element>(); + + /** 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 <code>&lt;</code> + * (means '<code><</code>' ); + * @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 + * <code>Element.index</code> 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 + * <code><hr></code> + * @param content the element content + * @param exclusions the set of elements that must not occur inside + * this element. The <code>Element.index</code> value defines which + * bit in this bitset corresponds to that element. + * @param inclusions the set of elements that can occur inside this + * element. the <code>Element.index</code> 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); + } + + /** + * <p>Reads DTD from an archived format. This format is not standardized + * and differs between implementations.</p><p> 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.</p> + * @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 <code>DTDConstants</code>). + * @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 <code><!ATTLIST </code>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<String> v = new Vector<String>(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; + +/** + * <p>This class defines the SGML basic types, used for describing HTML 4.01 + * at <a href="http://www.w3.org/TR/html4/types.html" + * >http://www.w3.org/TR/html4/types.html</a>. 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. + * </p> + * <p> + * If you need more information about SGML DTD documents, + * the author suggests to read SGML tutorial on + * <a href="http://www.w3.org/TR/WD-html40-970708/intro/sgmltut.html" + * >http://www.w3.org/TR/WD-html40-970708/intro/sgmltut.html</a>. + * We also recommend Goldfarb C.F (1991) <i>The SGML Handbook</i>, + * Oxford University Press, 688 p, ISBN: 0198537379. + * </p> + * + * @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, <code> + * <DOCTYPE html SYSTEM "/path/to/file.dtd"> </code>. + */ + 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 + * "<code>&name;</code>" 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; + +/** + * <p>A simple error-tolerant HTML parser that uses a DTD document + * to access data on the possible tokens, arguments and syntax.</p> + * <p> 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.</p> + * <p>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 + * <p><table><tr><td>a<td>b<td>c</tr> <br> + * will invoke exactly the handling methods exactly in the same order + * (and with the same parameters) as if parsing the document: <br> + * <em><html><head></head><body><table>< + * tbody></em><tr><td>a<em></td></em><td>b<em> + * </td></em><td>c<em></td></tr></em>< + * <em>/tbody></table></body></html></em></p> + * (supposed tags are given in italics). The parser also supports + * obsolete elements of HTML syntax.<p> + * </p> + * 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; + +/** + * <p> + * Stores the element information, obtained by parsing SGML DTD + * tag <code><!ELEMENT .. ></code>. This class has no public + * constructor and can only be instantiated using the + * {@link javax.swing.text.html.parser.DTD } methods</p> + * + * <p>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 <code><hr></code>) have no content. Element names + * are case sensitive.</p> + * @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 <code><hr></code>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 <code><head></code> or + * <code><body></code>) 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 <code>inclusions</code> and <code>exclusions</code> 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 <code>a_type</code> 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 <code>inclusions</code> and <code>exclusions</code> 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 <code><hr></code>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 <code><head></code> or + * <code><body></code>) 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; + +/** + * <p>Stores information, obtained by parsing SGML DTL + * <!ENTITY % .. > tag.</p> + * <p> + * 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 <code>&entity-name;</code>. Such entities are + * defined by the SGML DTD tag + * <code><!ENTITY <i>name</i> "<i>value</i>"></code>. 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 <code>&entity-name;</code>. Such entities are + * defined by the SGML DTD tag + * <code><!ENTITY % <i>name</i> "<i>value</i>"></code>. 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. + */ + +/** + * <p>A simple error-tolerant HTML parser that uses a DTD document + * to access data on the possible tokens, arguments and syntax.</p> + * <p> 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.</p> + * <p>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 + * <p><table><tr><td>a<td>b<td>c</tr> <br> + * will invoke exactly the handling methods exactly in the same order + * (and with the same parameters) as if parsing the document: <br> + * <em><html><head></head><body><table>< + * tbody></em><tr><td>a<em></td></em><td>b<em> + * </td></em><td>c<em></td></tr></em>< + * <em>/tbody></table></body></html></em></p> + * (supposed tags are given in italics). The parser also supports + * obsolete elements of HTML syntax.<p> + * </p> + * @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. + * <p> 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. + * </p> + * <p> For pre-formatted text (inside TEXAREA and PRE), the parser preserves + * all tabs and spaces, but removes <b>one</b> bounding \r, \n or \r\n, + * if it is present. Additionally, it replaces each occurence of \r or \r\n + * by a single \n.</p> + * + * @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 @@ +<!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.parser</title></head> + +<body> +<p> 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). +</p> + +</body> +</html> |