feat(jdk8): move files to new folder to avoid resources compiled.

This commit is contained in:
2025-09-07 15:25:52 +08:00
parent 3f0047bf6f
commit 8c35cfb1c0
17415 changed files with 217 additions and 213 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,57 @@
/*
* Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import javax.swing.text.*;
/**
* Processes the <BR> tag. In other words, forces a line break.
*
* @author Sunita Mani
*/
class BRView extends InlineView {
/**
* Creates a new view that represents a <BR> element.
*
* @param elem the element to create a view for
*/
public BRView(Element elem) {
super(elem);
}
/**
* Forces a line break.
*
* @return View.ForcedBreakWeight
*/
public int getBreakWeight(int axis, float pos, float len) {
if (axis == X_AXIS) {
return ForcedBreakWeight;
} else {
return super.getBreakWeight(axis, pos, len);
}
}
}

View File

@@ -0,0 +1,443 @@
/*
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.util.Enumeration;
import java.awt.*;
import javax.swing.SizeRequirements;
import javax.swing.border.*;
import javax.swing.event.DocumentEvent;
import javax.swing.text.*;
/**
* A view implementation to display a block (as a box)
* with CSS specifications.
*
* @author Timothy Prinzing
*/
public class BlockView extends BoxView {
/**
* 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);
}
/**
* Establishes the parent view for this view. This is
* guaranteed to be called before any other methods if the
* parent view is functioning properly.
* <p>
* This is implemented
* to forward to the superclass as well as call the
* {@link #setPropertiesFromAttributes()}
* method to set the paragraph properties from the css
* attributes. The call is made at this time to ensure
* the ability to resolve upward through the parents
* view attributes.
*
* @param parent the new parent, or null if the view is
* being removed from a parent it was previously added
* to
*/
public void setParent(View parent) {
super.setParent(parent);
if (parent != null) {
setPropertiesFromAttributes();
}
}
/**
* Calculate the requirements of the block along the major
* axis (i.e. the axis along with it tiles). This is implemented
* to provide the superclass behavior and then adjust it if the
* CSS width or height attribute is specified and applicable to
* the axis.
*/
protected SizeRequirements calculateMajorAxisRequirements(int axis, SizeRequirements r) {
if (r == null) {
r = new SizeRequirements();
}
if (! spanSetFromAttributes(axis, r, cssWidth, cssHeight)) {
r = super.calculateMajorAxisRequirements(axis, r);
}
else {
// Offset by the margins so that pref/min/max return the
// right value.
SizeRequirements parentR = 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, parentR);
}
return r;
}
/**
* Calculate the requirements of the block along the minor
* axis (i.e. the axis orthogonal to the axis along with it tiles).
* This is implemented
* to provide the superclass behavior and then adjust it if the
* CSS width or height attribute is specified and applicable to
* the axis.
*/
protected SizeRequirements calculateMinorAxisRequirements(int axis, SizeRequirements r) {
if (r == null) {
r = new SizeRequirements();
}
if (! spanSetFromAttributes(axis, r, cssWidth, cssHeight)) {
/*
* The requirements were not directly specified by attributes, so
* compute the aggregate of the requirements of the children. The
* children that have a percentage value specified will be treated
* as completely stretchable since that child is not limited in any
* way.
*/
/*
int min = 0;
long pref = 0;
int max = 0;
int n = getViewCount();
for (int i = 0; i < n; i++) {
View v = getView(i);
min = Math.max((int) v.getMinimumSpan(axis), min);
pref = Math.max((int) v.getPreferredSpan(axis), pref);
if (
max = Math.max((int) v.getMaximumSpan(axis), max);
}
r.preferred = (int) pref;
r.minimum = min;
r.maximum = max;
*/
r = super.calculateMinorAxisRequirements(axis, r);
}
else {
// Offset by the margins so that pref/min/max return the
// right value.
SizeRequirements parentR = 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, parentR);
}
/*
* Set the alignment based upon the CSS properties if it is
* specified. For X_AXIS this would be text-align, for
* Y_AXIS this would be vertical-align.
*/
if (axis == X_AXIS) {
Object o = getAttributes().getAttribute(CSS.Attribute.TEXT_ALIGN);
if (o != null) {
String align = o.toString();
if (align.equals("center")) {
r.alignment = 0.5f;
} else if (align.equals("right")) {
r.alignment = 1.0f;
} else {
r.alignment = 0.0f;
}
}
}
// Y_AXIS TBD
return r;
}
boolean isPercentage(int axis, AttributeSet a) {
if (axis == X_AXIS) {
if (cssWidth != null) {
return cssWidth.isPercentage();
}
} else {
if (cssHeight != null) {
return cssHeight.isPercentage();
}
}
return false;
}
/**
* Adjust the given requirements to the CSS width or height if
* it is specified along the applicable axis. Return true if the
* size is exactly specified, false if the span is not specified
* in an attribute or the size specified is a percentage.
*/
static boolean spanSetFromAttributes(int axis, SizeRequirements r,
CSS.LengthValue cssWidth,
CSS.LengthValue cssHeight) {
if (axis == X_AXIS) {
if ((cssWidth != null) && (! cssWidth.isPercentage())) {
r.minimum = r.preferred = r.maximum = (int) cssWidth.getValue();
return true;
}
} else {
if ((cssHeight != null) && (! cssHeight.isPercentage())) {
r.minimum = r.preferred = r.maximum = (int) cssHeight.getValue();
return true;
}
}
return false;
}
/**
* Performs layout for the minor axis of the box (i.e. the
* axis orthogonal to the axis that it represents). The results
* of the layout (the offset and span for each children) are
* placed in the given arrays which represent the allocations to
* the children along the minor axis.
*
* @param targetSpan the total span given to the view, which
* would be used to layout the children.
* @param axis the axis being layed out
* @param offsets the offsets from the origin of the view for
* each of the child views; this is a return value and is
* filled in by the implementation of this method
* @param spans the span of each child view; this is a return
* value and is filled in by the implementation of this method
*/
protected void layoutMinorAxis(int targetSpan, int axis, int[] offsets, int[] spans) {
int n = getViewCount();
Object key = (axis == X_AXIS) ? CSS.Attribute.WIDTH : CSS.Attribute.HEIGHT;
for (int i = 0; i < n; i++) {
View v = getView(i);
int min = (int) v.getMinimumSpan(axis);
int max;
// check for percentage span
AttributeSet a = v.getAttributes();
CSS.LengthValue lv = (CSS.LengthValue) a.getAttribute(key);
if ((lv != null) && lv.isPercentage()) {
// bound the span to the percentage specified
min = Math.max((int) lv.getValue(targetSpan), min);
max = min;
} else {
max = (int)v.getMaximumSpan(axis);
}
// assign the offset and span for the child
if (max < targetSpan) {
// can't make the child this wide, align it
float align = v.getAlignment(axis);
offsets[i] = (int) ((targetSpan - max) * align);
spans[i] = max;
} else {
// make it the target width, or as small as it can get.
offsets[i] = 0;
spans[i] = Math.max(min, targetSpan);
}
}
}
/**
* Renders using the given rendering surface and area on that
* surface. This is implemented to delegate to the css box
* painter to paint the border and background prior to the
* interior.
*
* @param g the rendering surface to use
* @param allocation the allocated region to render into
* @see View#paint
*/
public void paint(Graphics g, Shape allocation) {
Rectangle a = (Rectangle) allocation;
painter.paint(g, a.x, a.y, a.width, a.height, this);
super.paint(g, a);
}
/**
* Fetches the attributes to use when rendering. This is
* implemented to multiplex the attributes specified in the
* model with a StyleSheet.
*/
public AttributeSet getAttributes() {
if (attr == null) {
StyleSheet sheet = getStyleSheet();
attr = sheet.getViewAttributes(this);
}
return attr;
}
/**
* Gets the resize weight.
*
* @param axis may be either X_AXIS or Y_AXIS
* @return the weight
* @exception IllegalArgumentException for an invalid axis
*/
public int getResizeWeight(int axis) {
switch (axis) {
case View.X_AXIS:
return 1;
case View.Y_AXIS:
return 0;
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
/**
* Gets the alignment.
*
* @param axis may be either X_AXIS or Y_AXIS
* @return the alignment
*/
public float getAlignment(int axis) {
switch (axis) {
case View.X_AXIS:
return 0;
case View.Y_AXIS:
if (getViewCount() == 0) {
return 0;
}
float span = getPreferredSpan(View.Y_AXIS);
View v = getView(0);
float above = v.getPreferredSpan(View.Y_AXIS);
float a = (((int)span) != 0) ? (above * v.getAlignment(View.Y_AXIS)) / span: 0;
return a;
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
super.changedUpdate(changes, a, f);
int pos = changes.getOffset();
if (pos <= getStartOffset() && (pos + changes.getLength()) >=
getEndOffset()) {
setPropertiesFromAttributes();
}
}
/**
* Determines the preferred span for this view along an
* axis.
*
* @param axis may be either <code>View.X_AXIS</code>
* or <code>View.Y_AXIS</code>
* @return the span the view would like to be rendered into &gt;= 0;
* typically the view is told to render into the span
* that is returned, although there is no guarantee;
* the parent may choose to resize or break the view
* @exception IllegalArgumentException for an invalid axis type
*/
public float getPreferredSpan(int axis) {
return super.getPreferredSpan(axis);
}
/**
* Determines the minimum span for this view along an
* axis.
*
* @param axis may be either <code>View.X_AXIS</code>
* or <code>View.Y_AXIS</code>
* @return the span the view would like to be rendered into &gt;= 0;
* typically the view is told to render into the span
* that is returned, although there is no guarantee;
* the parent may choose to resize or break the view
* @exception IllegalArgumentException for an invalid axis type
*/
public float getMinimumSpan(int axis) {
return super.getMinimumSpan(axis);
}
/**
* Determines the maximum span for this view along an
* axis.
*
* @param axis may be either <code>View.X_AXIS</code>
* or <code>View.Y_AXIS</code>
* @return the span the view would like to be rendered into &gt;= 0;
* typically the view is told to render into the span
* that is returned, although there is no guarantee;
* the parent may choose to resize or break the view
* @exception IllegalArgumentException for an invalid axis type
*/
public float getMaximumSpan(int axis) {
return super.getMaximumSpan(axis);
}
/**
* Update any cached values that come from attributes.
*/
protected void setPropertiesFromAttributes() {
// update attributes
StyleSheet sheet = getStyleSheet();
attr = sheet.getViewAttributes(this);
// Reset the painter
painter = sheet.getBoxPainter(attr);
if (attr != null) {
setInsets((short) painter.getInset(TOP, this),
(short) painter.getInset(LEFT, this),
(short) painter.getInset(BOTTOM, this),
(short) painter.getInset(RIGHT, this));
}
// Get the width/height
cssWidth = (CSS.LengthValue) attr.getAttribute(CSS.Attribute.WIDTH);
cssHeight = (CSS.LengthValue) attr.getAttribute(CSS.Attribute.HEIGHT);
}
protected StyleSheet getStyleSheet() {
HTMLDocument doc = (HTMLDocument) getDocument();
return doc.getStyleSheet();
}
/**
* Constrains <code>want</code> to fit in the minimum size specified
* by <code>min</code>.
*/
private void constrainSize(int axis, SizeRequirements want,
SizeRequirements min) {
if (min.minimum > want.minimum) {
want.minimum = want.preferred = min.minimum;
want.maximum = Math.max(want.maximum, min.maximum);
}
}
private AttributeSet attr;
private StyleSheet.BoxPainter painter;
private CSS.LengthValue cssWidth;
private CSS.LengthValue cssHeight;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,436 @@
/*
* Copyright (c) 2007, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import java.util.HashMap;
import java.util.Map;
import javax.swing.border.AbstractBorder;
import javax.swing.text.AttributeSet;
import javax.swing.text.View;
import javax.swing.text.html.CSS.Attribute;
import javax.swing.text.html.CSS.BorderStyle;
import javax.swing.text.html.CSS.BorderWidthValue;
import javax.swing.text.html.CSS.ColorValue;
import javax.swing.text.html.CSS.CssValue;
import javax.swing.text.html.CSS.LengthValue;
import javax.swing.text.html.CSS.Value;
/**
* CSS-style borders for HTML elements.
*
* @author Sergey Groznyh
*/
class CSSBorder extends AbstractBorder {
/** Indices for the attribute groups. */
final static int COLOR = 0, STYLE = 1, WIDTH = 2;
/** Indices for the box sides within the attribute group. */
final static int TOP = 0, RIGHT = 1, BOTTOM = 2, LEFT = 3;
/** The attribute groups. */
final static Attribute[][] ATTRIBUTES = {
{ Attribute.BORDER_TOP_COLOR, Attribute.BORDER_RIGHT_COLOR,
Attribute.BORDER_BOTTOM_COLOR, Attribute.BORDER_LEFT_COLOR, },
{ Attribute.BORDER_TOP_STYLE, Attribute.BORDER_RIGHT_STYLE,
Attribute.BORDER_BOTTOM_STYLE, Attribute.BORDER_LEFT_STYLE, },
{ Attribute.BORDER_TOP_WIDTH, Attribute.BORDER_RIGHT_WIDTH,
Attribute.BORDER_BOTTOM_WIDTH, Attribute.BORDER_LEFT_WIDTH, },
};
/** Parsers for the border properties. */
final static CssValue PARSERS[] = {
new ColorValue(), new BorderStyle(), new BorderWidthValue(null, 0),
};
/** Default values for the border properties. */
final static Object[] DEFAULTS = {
Attribute.BORDER_COLOR, // marker: value will be computed on request
PARSERS[1].parseCssValue(Attribute.BORDER_STYLE.getDefaultValue()),
PARSERS[2].parseCssValue(Attribute.BORDER_WIDTH.getDefaultValue()),
};
/** Attribute set containing border properties. */
final AttributeSet attrs;
/**
* Initialize the attribute set.
*/
CSSBorder(AttributeSet attrs) {
this.attrs = attrs;
}
/**
* Return the border color for the given side.
*/
private Color getBorderColor(int side) {
Object o = attrs.getAttribute(ATTRIBUTES[COLOR][side]);
ColorValue cv;
if (o instanceof ColorValue) {
cv = (ColorValue) o;
} else {
// Marker for the default value. Use 'color' property value as the
// computed value of the 'border-color' property (CSS2 8.5.2)
cv = (ColorValue) attrs.getAttribute(Attribute.COLOR);
if (cv == null) {
cv = (ColorValue) PARSERS[COLOR].parseCssValue(
Attribute.COLOR.getDefaultValue());
}
}
return cv.getValue();
}
/**
* Return the border width for the given side.
*/
private int getBorderWidth(int side) {
int width = 0;
BorderStyle bs = (BorderStyle) attrs.getAttribute(
ATTRIBUTES[STYLE][side]);
if ((bs != null) && (bs.getValue() != Value.NONE)) {
// The 'border-style' value of "none" forces the computed value
// of 'border-width' to be 0 (CSS2 8.5.3)
LengthValue bw = (LengthValue) attrs.getAttribute(
ATTRIBUTES[WIDTH][side]);
if (bw == null) {
bw = (LengthValue) DEFAULTS[WIDTH];
}
width = (int) bw.getValue(true);
}
return width;
}
/**
* Return an array of border widths in the TOP, RIGHT, BOTTOM, LEFT order.
*/
private int[] getWidths() {
int[] widths = new int[4];
for (int i = 0; i < widths.length; i++) {
widths[i] = getBorderWidth(i);
}
return widths;
}
/**
* Return the border style for the given side.
*/
private Value getBorderStyle(int side) {
BorderStyle style =
(BorderStyle) attrs.getAttribute(ATTRIBUTES[STYLE][side]);
if (style == null) {
style = (BorderStyle) DEFAULTS[STYLE];
}
return style.getValue();
}
/**
* Return border shape for {@code side} as if the border has zero interior
* length. Shape start is at (0,0); points are added clockwise.
*/
private Polygon getBorderShape(int side) {
Polygon shape = null;
int[] widths = getWidths();
if (widths[side] != 0) {
shape = new Polygon(new int[4], new int[4], 0);
shape.addPoint(0, 0);
shape.addPoint(-widths[(side + 3) % 4], -widths[side]);
shape.addPoint(widths[(side + 1) % 4], -widths[side]);
shape.addPoint(0, 0);
}
return shape;
}
/**
* Return the border painter appropriate for the given side.
*/
private BorderPainter getBorderPainter(int side) {
Value style = getBorderStyle(side);
return borderPainters.get(style);
}
/**
* Return the color with brightness adjusted by the specified factor.
*
* The factor values are between 0.0 (no change) and 1.0 (turn into white).
* Negative factor values decrease brigthness (ie, 1.0 turns into black).
*/
static Color getAdjustedColor(Color c, double factor) {
double f = 1 - Math.min(Math.abs(factor), 1);
double inc = (factor > 0 ? 255 * (1 - f) : 0);
return new Color((int) (c.getRed() * f + inc),
(int) (c.getGreen() * f + inc),
(int) (c.getBlue() * f + inc));
}
/* The javax.swing.border.Border methods. */
public Insets getBorderInsets(Component c, Insets insets) {
int[] widths = getWidths();
insets.set(widths[TOP], widths[LEFT], widths[BOTTOM], widths[RIGHT]);
return insets;
}
public void paintBorder(Component c, Graphics g,
int x, int y, int width, int height) {
if (!(g instanceof Graphics2D)) {
return;
}
Graphics2D g2 = (Graphics2D) g.create();
int[] widths = getWidths();
// Position and size of the border interior.
int intX = x + widths[LEFT];
int intY = y + widths[TOP];
int intWidth = width - (widths[RIGHT] + widths[LEFT]);
int intHeight = height - (widths[TOP] + widths[BOTTOM]);
// Coordinates of the interior corners, from NW clockwise.
int[][] intCorners = {
{ intX, intY },
{ intX + intWidth, intY },
{ intX + intWidth, intY + intHeight },
{ intX, intY + intHeight, },
};
// Draw the borders for all sides.
for (int i = 0; i < 4; i++) {
Value style = getBorderStyle(i);
Polygon shape = getBorderShape(i);
if ((style != Value.NONE) && (shape != null)) {
int sideLength = (i % 2 == 0 ? intWidth : intHeight);
// "stretch" the border shape by the interior area dimension
shape.xpoints[2] += sideLength;
shape.xpoints[3] += sideLength;
Color color = getBorderColor(i);
BorderPainter painter = getBorderPainter(i);
double angle = i * Math.PI / 2;
g2.setClip(g.getClip()); // Restore initial clip
g2.translate(intCorners[i][0], intCorners[i][1]);
g2.rotate(angle);
g2.clip(shape);
painter.paint(shape, g2, color, i);
g2.rotate(-angle);
g2.translate(-intCorners[i][0], -intCorners[i][1]);
}
}
g2.dispose();
}
/* Border painters. */
interface BorderPainter {
/**
* The painter should paint the border as if it were at the top and the
* coordinates of the NW corner of the interior area is (0, 0). The
* caller is responsible for the appropriate affine transformations.
*
* Clip is set by the caller to the exact border shape so it's safe to
* simply draw into the shape's bounding rectangle.
*/
void paint(Polygon shape, Graphics g, Color color, int side);
}
/**
* Painter for the "none" and "hidden" CSS border styles.
*/
static class NullPainter implements BorderPainter {
public void paint(Polygon shape, Graphics g, Color color, int side) {
// Do nothing.
}
}
/**
* Painter for the "solid" CSS border style.
*/
static class SolidPainter implements BorderPainter {
public void paint(Polygon shape, Graphics g, Color color, int side) {
g.setColor(color);
g.fillPolygon(shape);
}
}
/**
* Defines a method for painting strokes in the specified direction using
* the given length and color patterns.
*/
abstract static class StrokePainter implements BorderPainter {
/**
* Paint strokes repeatedly using the given length and color patterns.
*/
void paintStrokes(Rectangle r, Graphics g, int axis,
int[] lengthPattern, Color[] colorPattern) {
boolean xAxis = (axis == View.X_AXIS);
int start = 0;
int end = (xAxis ? r.width : r.height);
while (start < end) {
for (int i = 0; i < lengthPattern.length; i++) {
if (start >= end) {
break;
}
int length = lengthPattern[i];
Color c = colorPattern[i];
if (c != null) {
int x = r.x + (xAxis ? start : 0);
int y = r.y + (xAxis ? 0 : start);
int width = xAxis ? length : r.width;
int height = xAxis ? r.height : length;
g.setColor(c);
g.fillRect(x, y, width, height);
}
start += length;
}
}
}
}
/**
* Painter for the "double" CSS border style.
*/
static class DoublePainter extends StrokePainter {
public void paint(Polygon shape, Graphics g, Color color, int side) {
Rectangle r = shape.getBounds();
int length = Math.max(r.height / 3, 1);
int[] lengthPattern = { length, length };
Color[] colorPattern = { color, null };
paintStrokes(r, g, View.Y_AXIS, lengthPattern, colorPattern);
}
}
/**
* Painter for the "dotted" and "dashed" CSS border styles.
*/
static class DottedDashedPainter extends StrokePainter {
final int factor;
DottedDashedPainter(int factor) {
this.factor = factor;
}
public void paint(Polygon shape, Graphics g, Color color, int side) {
Rectangle r = shape.getBounds();
int length = r.height * factor;
int[] lengthPattern = { length, length };
Color[] colorPattern = { color, null };
paintStrokes(r, g, View.X_AXIS, lengthPattern, colorPattern);
}
}
/**
* Painter that defines colors for "shadow" and "light" border sides.
*/
abstract static class ShadowLightPainter extends StrokePainter {
/**
* Return the "shadow" border side color.
*/
static Color getShadowColor(Color c) {
return CSSBorder.getAdjustedColor(c, -0.3);
}
/**
* Return the "light" border side color.
*/
static Color getLightColor(Color c) {
return CSSBorder.getAdjustedColor(c, 0.7);
}
}
/**
* Painter for the "groove" and "ridge" CSS border styles.
*/
static class GrooveRidgePainter extends ShadowLightPainter {
final Value type;
GrooveRidgePainter(Value type) {
this.type = type;
}
public void paint(Polygon shape, Graphics g, Color color, int side) {
Rectangle r = shape.getBounds();
int length = Math.max(r.height / 2, 1);
int[] lengthPattern = { length, length };
Color[] colorPattern =
((side + 1) % 4 < 2) == (type == Value.GROOVE) ?
new Color[] { getShadowColor(color), getLightColor(color) } :
new Color[] { getLightColor(color), getShadowColor(color) };
paintStrokes(r, g, View.Y_AXIS, lengthPattern, colorPattern);
}
}
/**
* Painter for the "inset" and "outset" CSS border styles.
*/
static class InsetOutsetPainter extends ShadowLightPainter {
Value type;
InsetOutsetPainter(Value type) {
this.type = type;
}
public void paint(Polygon shape, Graphics g, Color color, int side) {
g.setColor(((side + 1) % 4 < 2) == (type == Value.INSET) ?
getShadowColor(color) : getLightColor(color));
g.fillPolygon(shape);
}
}
/**
* Add the specified painter to the painters map.
*/
static void registerBorderPainter(Value style, BorderPainter painter) {
borderPainters.put(style, painter);
}
/** Map the border style values to the border painter objects. */
static Map<Value, BorderPainter> borderPainters =
new HashMap<Value, BorderPainter>();
/* Initialize the border painters map with the pre-defined values. */
static {
registerBorderPainter(Value.NONE, new NullPainter());
registerBorderPainter(Value.HIDDEN, new NullPainter());
registerBorderPainter(Value.SOLID, new SolidPainter());
registerBorderPainter(Value.DOUBLE, new DoublePainter());
registerBorderPainter(Value.DOTTED, new DottedDashedPainter(1));
registerBorderPainter(Value.DASHED, new DottedDashedPainter(3));
registerBorderPainter(Value.GROOVE, new GrooveRidgePainter(Value.GROOVE));
registerBorderPainter(Value.RIDGE, new GrooveRidgePainter(Value.RIDGE));
registerBorderPainter(Value.INSET, new InsetOutsetPainter(Value.INSET));
registerBorderPainter(Value.OUTSET, new InsetOutsetPainter(Value.OUTSET));
}
}

View File

@@ -0,0 +1,854 @@
/*
* Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.io.*;
/**
* A CSS parser. This works by way of a delegate that implements the
* CSSParserCallback interface. The delegate is notified of the following
* events:
* <ul>
* <li>Import statement: <code>handleImport</code>
* <li>Selectors <code>handleSelector</code>. 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'.
* <li>When a rule starts, <code>startRule</code>
* <li>Properties in the rule via the <code>handleProperty</code>. 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'.
* <li>Values in the rule via the <code>handleValue</code>, this is notified
* for the total value.
* <li>When a rule ends, <code>endRule</code>
* </ul>
* This will parse much more than CSS 1, and loosely implements the
* recommendation for <i>Forward-compatible parsing</i> in section
* 7.1 of the CSS spec found at:
* <a href=http://www.w3.org/TR/REC-CSS1>http://www.w3.org/TR/REC-CSS1</a>.
* If an error results in parsing, a RuntimeException will be thrown.
* <p>
* This will preserve case. If the callback wishes to treat certain poritions
* case insensitively (such as selectors), it should use toLowerCase, or
* something similar.
*
* @author Scott Violet
*/
class CSSParser {
// Parsing something like the following:
// (@rule | ruleset | block)*
//
// @rule (block | identifier)*; (block with {} ends @rule)
// block matching [] () {} (that is, [()] is a block, [(){}{[]}]
// is a block, ()[] is two blocks)
// identifier "*" | '*' | anything but a [](){} and whitespace
//
// ruleset selector decblock
// selector (identifier | (block, except block '{}') )*
// declblock declaration* block*
// declaration (identifier* stopping when identifier ends with :)
// (identifier* stopping when identifier ends with ;)
//
// comments /* */ can appear any where, and are stripped.
// identifier - letters, digits, dashes and escaped characters
// block starts with { ends with matching }, () [] and {} always occur
// in matching pairs, '' and "" also occur in pairs, except " may be
// Indicates the type of token being parsed.
private static final int IDENTIFIER = 1;
private static final int BRACKET_OPEN = 2;
private static final int BRACKET_CLOSE = 3;
private static final int BRACE_OPEN = 4;
private static final int BRACE_CLOSE = 5;
private static final int PAREN_OPEN = 6;
private static final int PAREN_CLOSE = 7;
private static final int END = -1;
private static final char[] charMapping = { 0, 0, '[', ']', '{', '}', '(',
')', 0};
/** Set to true if one character has been read ahead. */
private boolean didPushChar;
/** The read ahead character. */
private int pushedChar;
/** Temporary place to hold identifiers. */
private StringBuffer unitBuffer;
/** 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;
/** Notified of state. */
private 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;
// The delegate interface.
static interface CSSParserCallback {
/** Called when an @import is encountered. */
void handleImport(String importString);
// There is currently no way to distinguish between '"foo,"' and
// 'foo,'. But this generally isn't valid CSS. If it becomes
// a problem, handleSelector will have to be told if the string is
// quoted.
void handleSelector(String selector);
void startRule();
// Property names are mapped to lower case before being passed to
// the delegate.
void handleProperty(String property);
void handleValue(String value);
void endRule();
}
CSSParser() {
unitStack = new int[2];
tokenBuffer = new char[80];
unitBuffer = new StringBuffer();
}
void parse(Reader reader, CSSParserCallback callback,
boolean inRule) throws IOException {
this.callback = callback;
stackCount = tokenBufferLength = 0;
this.reader = reader;
encounteredRuleSet = false;
try {
if (inRule) {
parseDeclarationBlock();
}
else {
while (getNextStatement());
}
} finally {
callback = null;
reader = null;
}
}
/**
* Gets the next statement, returning false if the end is reached. A
* statement is either an @rule, or a ruleset.
*/
private boolean getNextStatement() throws IOException {
unitBuffer.setLength(0);
int token = nextToken((char)0);
switch (token) {
case IDENTIFIER:
if (tokenBufferLength > 0) {
if (tokenBuffer[0] == '@') {
parseAtRule();
}
else {
encounteredRuleSet = true;
parseRuleSet();
}
}
return true;
case BRACKET_OPEN:
case BRACE_OPEN:
case PAREN_OPEN:
parseTillClosed(token);
return true;
case BRACKET_CLOSE:
case BRACE_CLOSE:
case PAREN_CLOSE:
// Shouldn't happen...
throw new RuntimeException("Unexpected top level block close");
case END:
return false;
}
return true;
}
/**
* Parses an @ rule, stopping at a matching brace pair, or ;.
*/
private void parseAtRule() throws IOException {
// PENDING: make this more effecient.
boolean done = false;
boolean isImport = (tokenBufferLength == 7 &&
tokenBuffer[0] == '@' && tokenBuffer[1] == 'i' &&
tokenBuffer[2] == 'm' && tokenBuffer[3] == 'p' &&
tokenBuffer[4] == 'o' && tokenBuffer[5] == 'r' &&
tokenBuffer[6] == 't');
unitBuffer.setLength(0);
while (!done) {
int nextToken = nextToken(';');
switch (nextToken) {
case IDENTIFIER:
if (tokenBufferLength > 0 &&
tokenBuffer[tokenBufferLength - 1] == ';') {
--tokenBufferLength;
done = true;
}
if (tokenBufferLength > 0) {
if (unitBuffer.length() > 0 && readWS) {
unitBuffer.append(' ');
}
unitBuffer.append(tokenBuffer, 0, tokenBufferLength);
}
break;
case BRACE_OPEN:
if (unitBuffer.length() > 0 && readWS) {
unitBuffer.append(' ');
}
unitBuffer.append(charMapping[nextToken]);
parseTillClosed(nextToken);
done = true;
// Skip a tailing ';', not really to spec.
{
int nextChar = readWS();
if (nextChar != -1 && nextChar != ';') {
pushChar(nextChar);
}
}
break;
case BRACKET_OPEN: case PAREN_OPEN:
unitBuffer.append(charMapping[nextToken]);
parseTillClosed(nextToken);
break;
case BRACKET_CLOSE: case BRACE_CLOSE: case PAREN_CLOSE:
throw new RuntimeException("Unexpected close in @ rule");
case END:
done = true;
break;
}
}
if (isImport && !encounteredRuleSet) {
callback.handleImport(unitBuffer.toString());
}
}
/**
* Parses the next rule set, which is a selector followed by a
* declaration block.
*/
private void parseRuleSet() throws IOException {
if (parseSelectors()) {
callback.startRule();
parseDeclarationBlock();
callback.endRule();
}
}
/**
* Parses a set of selectors, returning false if the end of the stream
* is reached.
*/
private boolean parseSelectors() throws IOException {
// Parse the selectors
int nextToken;
if (tokenBufferLength > 0) {
callback.handleSelector(new String(tokenBuffer, 0,
tokenBufferLength));
}
unitBuffer.setLength(0);
for (;;) {
while ((nextToken = nextToken((char)0)) == IDENTIFIER) {
if (tokenBufferLength > 0) {
callback.handleSelector(new String(tokenBuffer, 0,
tokenBufferLength));
}
}
switch (nextToken) {
case BRACE_OPEN:
return true;
case BRACKET_OPEN: case PAREN_OPEN:
parseTillClosed(nextToken);
// Not too sure about this, how we handle this isn't very
// well spec'd.
unitBuffer.setLength(0);
break;
case BRACKET_CLOSE: case BRACE_CLOSE: case PAREN_CLOSE:
throw new RuntimeException("Unexpected block close in selector");
case END:
// Prematurely hit end.
return false;
}
}
}
/**
* Parses a declaration block. Which a number of declarations followed
* by a })].
*/
private void parseDeclarationBlock() throws IOException {
for (;;) {
int token = parseDeclaration();
switch (token) {
case END: case BRACE_CLOSE:
return;
case BRACKET_CLOSE: case PAREN_CLOSE:
// Bail
throw new RuntimeException("Unexpected close in declaration block");
case IDENTIFIER:
break;
}
}
}
/**
* Parses a single declaration, which is an identifier a : and another
* identifier. This returns the last token seen.
*/
// identifier+: identifier* ;|}
private int parseDeclaration() throws IOException {
int token;
if ((token = parseIdentifiers(':', false)) != IDENTIFIER) {
return token;
}
// Make the property name to lowercase
for (int counter = unitBuffer.length() - 1; counter >= 0; counter--) {
unitBuffer.setCharAt(counter, Character.toLowerCase
(unitBuffer.charAt(counter)));
}
callback.handleProperty(unitBuffer.toString());
token = parseIdentifiers(';', true);
callback.handleValue(unitBuffer.toString());
return token;
}
/**
* Parses identifiers until <code>extraChar</code> is encountered,
* returning the ending token, which will be IDENTIFIER if extraChar
* is found.
*/
private int parseIdentifiers(char extraChar,
boolean wantsBlocks) throws IOException {
int nextToken;
int ubl;
unitBuffer.setLength(0);
for (;;) {
nextToken = nextToken(extraChar);
switch (nextToken) {
case IDENTIFIER:
if (tokenBufferLength > 0) {
if (tokenBuffer[tokenBufferLength - 1] == extraChar) {
if (--tokenBufferLength > 0) {
if (readWS && unitBuffer.length() > 0) {
unitBuffer.append(' ');
}
unitBuffer.append(tokenBuffer, 0,
tokenBufferLength);
}
return IDENTIFIER;
}
if (readWS && unitBuffer.length() > 0) {
unitBuffer.append(' ');
}
unitBuffer.append(tokenBuffer, 0, tokenBufferLength);
}
break;
case BRACKET_OPEN:
case BRACE_OPEN:
case PAREN_OPEN:
ubl = unitBuffer.length();
if (wantsBlocks) {
unitBuffer.append(charMapping[nextToken]);
}
parseTillClosed(nextToken);
if (!wantsBlocks) {
unitBuffer.setLength(ubl);
}
break;
case BRACE_CLOSE:
// No need to throw for these two, we return token and
// caller can do whatever.
case BRACKET_CLOSE:
case PAREN_CLOSE:
case END:
// Hit the end
return nextToken;
}
}
}
/**
* Parses till a matching block close is encountered. This is only
* appropriate to be called at the top level (no nesting).
*/
private void parseTillClosed(int openToken) throws IOException {
int nextToken;
boolean done = false;
startBlock(openToken);
while (!done) {
nextToken = nextToken((char)0);
switch (nextToken) {
case IDENTIFIER:
if (unitBuffer.length() > 0 && readWS) {
unitBuffer.append(' ');
}
if (tokenBufferLength > 0) {
unitBuffer.append(tokenBuffer, 0, tokenBufferLength);
}
break;
case BRACKET_OPEN: case BRACE_OPEN: case PAREN_OPEN:
if (unitBuffer.length() > 0 && readWS) {
unitBuffer.append(' ');
}
unitBuffer.append(charMapping[nextToken]);
startBlock(nextToken);
break;
case BRACKET_CLOSE: case BRACE_CLOSE: case PAREN_CLOSE:
if (unitBuffer.length() > 0 && readWS) {
unitBuffer.append(' ');
}
unitBuffer.append(charMapping[nextToken]);
endBlock(nextToken);
if (!inBlock()) {
done = true;
}
break;
case END:
// Prematurely hit end.
throw new RuntimeException("Unclosed block");
}
}
}
/**
* Fetches the next token.
*/
private int nextToken(char idChar) throws IOException {
readWS = false;
int nextChar = readWS();
switch (nextChar) {
case '\'':
readTill('\'');
if (tokenBufferLength > 0) {
tokenBufferLength--;
}
return IDENTIFIER;
case '"':
readTill('"');
if (tokenBufferLength > 0) {
tokenBufferLength--;
}
return IDENTIFIER;
case '[':
return BRACKET_OPEN;
case ']':
return BRACKET_CLOSE;
case '{':
return BRACE_OPEN;
case '}':
return BRACE_CLOSE;
case '(':
return PAREN_OPEN;
case ')':
return PAREN_CLOSE;
case -1:
return END;
default:
pushChar(nextChar);
getIdentifier(idChar);
return IDENTIFIER;
}
}
/**
* Gets an identifier, returning true if the length of the string is greater than 0,
* stopping when <code>stopChar</code>, whitespace, or one of {}()[] is
* hit.
*/
// NOTE: this could be combined with readTill, as they contain somewhat
// similar functionality.
private boolean getIdentifier(char stopChar) throws IOException {
boolean lastWasEscape = false;
boolean done = false;
int escapeCount = 0;
int escapeChar = 0;
int nextChar;
int intStopChar = (int)stopChar;
// 1 for '\', 2 for valid escape char [0-9a-fA-F], 3 for
// stop character (white space, ()[]{}) 0 otherwise
short type;
int escapeOffset = 0;
tokenBufferLength = 0;
while (!done) {
nextChar = readChar();
switch (nextChar) {
case '\\':
type = 1;
break;
case '0': case '1': case '2': case '3': case '4': case '5':
case '6': case '7': case '8': case '9':
type = 2;
escapeOffset = nextChar - '0';
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
type = 2;
escapeOffset = nextChar - 'a' + 10;
break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
type = 2;
escapeOffset = nextChar - 'A' + 10;
break;
case '\'': case '"': case '[': case ']': case '{': case '}':
case '(': case ')':
case ' ': case '\n': case '\t': case '\r':
type = 3;
break;
case '/':
type = 4;
break;
case -1:
// Reached the end
done = true;
type = 0;
break;
default:
type = 0;
break;
}
if (lastWasEscape) {
if (type == 2) {
// Continue with escape.
escapeChar = escapeChar * 16 + escapeOffset;
if (++escapeCount == 4) {
lastWasEscape = false;
append((char)escapeChar);
}
}
else {
// no longer escaped
lastWasEscape = false;
if (escapeCount > 0) {
append((char)escapeChar);
// Make this simpler, reprocess the character.
pushChar(nextChar);
}
else if (!done) {
append((char)nextChar);
}
}
}
else if (!done) {
if (type == 1) {
lastWasEscape = true;
escapeChar = escapeCount = 0;
}
else if (type == 3) {
done = true;
pushChar(nextChar);
}
else if (type == 4) {
// Potential comment
nextChar = readChar();
if (nextChar == '*') {
done = true;
readComment();
readWS = true;
}
else {
append('/');
if (nextChar == -1) {
done = true;
}
else {
pushChar(nextChar);
}
}
}
else {
append((char)nextChar);
if (nextChar == intStopChar) {
done = true;
}
}
}
}
return (tokenBufferLength > 0);
}
/**
* Reads till a <code>stopChar</code> is encountered, escaping characters
* as necessary.
*/
private void readTill(char stopChar) throws IOException {
boolean lastWasEscape = false;
int escapeCount = 0;
int escapeChar = 0;
int nextChar;
boolean done = false;
int intStopChar = (int)stopChar;
// 1 for '\', 2 for valid escape char [0-9a-fA-F], 0 otherwise
short type;
int escapeOffset = 0;
tokenBufferLength = 0;
while (!done) {
nextChar = readChar();
switch (nextChar) {
case '\\':
type = 1;
break;
case '0': case '1': case '2': case '3': case '4':case '5':
case '6': case '7': case '8': case '9':
type = 2;
escapeOffset = nextChar - '0';
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
type = 2;
escapeOffset = nextChar - 'a' + 10;
break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
type = 2;
escapeOffset = nextChar - 'A' + 10;
break;
case -1:
// Prematurely reached the end!
throw new RuntimeException("Unclosed " + stopChar);
default:
type = 0;
break;
}
if (lastWasEscape) {
if (type == 2) {
// Continue with escape.
escapeChar = escapeChar * 16 + escapeOffset;
if (++escapeCount == 4) {
lastWasEscape = false;
append((char)escapeChar);
}
}
else {
// no longer escaped
if (escapeCount > 0) {
append((char)escapeChar);
if (type == 1) {
lastWasEscape = true;
escapeChar = escapeCount = 0;
}
else {
if (nextChar == intStopChar) {
done = true;
}
append((char)nextChar);
lastWasEscape = false;
}
}
else {
append((char)nextChar);
lastWasEscape = false;
}
}
}
else if (type == 1) {
lastWasEscape = true;
escapeChar = escapeCount = 0;
}
else {
if (nextChar == intStopChar) {
done = true;
}
append((char)nextChar);
}
}
}
private void append(char character) {
if (tokenBufferLength == tokenBuffer.length) {
char[] newBuffer = new char[tokenBuffer.length * 2];
System.arraycopy(tokenBuffer, 0, newBuffer, 0, tokenBuffer.length);
tokenBuffer = newBuffer;
}
tokenBuffer[tokenBufferLength++] = character;
}
/**
* Parses a comment block.
*/
private void readComment() throws IOException {
int nextChar;
for(;;) {
nextChar = readChar();
switch (nextChar) {
case -1:
throw new RuntimeException("Unclosed comment");
case '*':
nextChar = readChar();
if (nextChar == '/') {
return;
}
else if (nextChar == -1) {
throw new RuntimeException("Unclosed comment");
}
else {
pushChar(nextChar);
}
break;
default:
break;
}
}
}
/**
* Called when a block start is encountered ({[.
*/
private void startBlock(int startToken) {
if (stackCount == unitStack.length) {
int[] newUS = new int[stackCount * 2];
System.arraycopy(unitStack, 0, newUS, 0, stackCount);
unitStack = newUS;
}
unitStack[stackCount++] = startToken;
}
/**
* Called when an end block is encountered )]}
*/
private void endBlock(int endToken) {
int startToken;
switch (endToken) {
case BRACKET_CLOSE:
startToken = BRACKET_OPEN;
break;
case BRACE_CLOSE:
startToken = BRACE_OPEN;
break;
case PAREN_CLOSE:
startToken = PAREN_OPEN;
break;
default:
// Will never happen.
startToken = -1;
break;
}
if (stackCount > 0 && unitStack[stackCount - 1] == startToken) {
stackCount--;
}
else {
// Invalid state, should do something.
throw new RuntimeException("Unmatched block");
}
}
/**
* @return true if currently in a block.
*/
private boolean inBlock() {
return (stackCount > 0);
}
/**
* Skips any white space, returning the character after the white space.
*/
private int readWS() throws IOException {
int nextChar;
while ((nextChar = readChar()) != -1 &&
Character.isWhitespace((char)nextChar)) {
readWS = true;
}
return nextChar;
}
/**
* Reads a character from the stream.
*/
private int readChar() throws IOException {
if (didPushChar) {
didPushChar = false;
return pushedChar;
}
return reader.read();
// Uncomment the following to do case insensitive parsing.
/*
if (retValue != -1) {
return (int)Character.toLowerCase((char)retValue);
}
return retValue;
*/
}
/**
* Supports one character look ahead, this will throw if called twice
* in a row.
*/
private void pushChar(int tempChar) {
if (didPushChar) {
// Should never happen.
throw new RuntimeException("Can not handle look ahead of more than one character");
}
didPushChar = true;
pushedChar = tempChar;
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.util.*;
/**
* CommentView subclasses HiddenTagView to contain a JTextArea showing
* a comment. When the textarea is edited the comment is
* reset. As this inherits from EditableView if the JTextComponent is
* not editable, the textarea will not be visible.
*
* @author Scott Violet
*/
class CommentView extends HiddenTagView {
CommentView(Element e) {
super(e);
}
protected Component createComponent() {
Container host = getContainer();
if (host != null && !((JTextComponent)host).isEditable()) {
return null;
}
JTextArea ta = new JTextArea(getRepresentedText());
Document doc = getDocument();
Font font;
if (doc instanceof StyledDocument) {
font = ((StyledDocument)doc).getFont(getAttributes());
ta.setFont(font);
}
else {
font = ta.getFont();
}
updateYAlign(font);
ta.setBorder(CBorder);
ta.getDocument().addDocumentListener(this);
ta.setFocusable(isVisible());
return ta;
}
void resetBorder() {
}
/**
* This is subclassed to put the text on the Comment attribute of
* the Element's AttributeSet.
*/
void _updateModelFromText() {
JTextComponent textC = getTextComponent();
Document doc = getDocument();
if (textC != null && doc != null) {
String text = textC.getText();
SimpleAttributeSet sas = new SimpleAttributeSet();
isSettingAttributes = true;
try {
sas.addAttribute(HTML.Attribute.COMMENT, text);
((StyledDocument)doc).setCharacterAttributes
(getStartOffset(), getEndOffset() -
getStartOffset(), sas, false);
}
finally {
isSettingAttributes = false;
}
}
}
JTextComponent getTextComponent() {
return (JTextComponent)getComponent();
}
String getRepresentedText() {
AttributeSet as = getElement().getAttributes();
if (as != null) {
Object comment = as.getAttribute(HTML.Attribute.COMMENT);
if (comment instanceof String) {
return (String)comment;
}
}
return "";
}
static final Border CBorder = new CommentBorder();
static final int commentPadding = 3;
static final int commentPaddingD = commentPadding * 3;
static class CommentBorder extends LineBorder {
CommentBorder() {
super(Color.black, 1);
}
public void paintBorder(Component c, Graphics g, int x, int y,
int width, int height) {
super.paintBorder(c, g, x + commentPadding, y,
width - commentPaddingD, height);
}
public Insets getBorderInsets(Component c, Insets insets) {
Insets retI = super.getBorderInsets(c, insets);
retI.left += commentPadding;
retI.right += commentPadding;
return retI;
}
public boolean isBorderOpaque() {
return false;
}
} // End of class CommentView.CommentBorder
} // End of CommentView

View File

@@ -0,0 +1,128 @@
/*
* Copyright (c) 1998, 2004, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.util.*;
/**
* EditableView sets the view it contains to be visible only when the
* JTextComponent the view is contained in is editable. The min/pref/max
* size is 0 when not visible.
*
* @author Scott Violet
*/
class EditableView extends ComponentView {
EditableView(Element e) {
super(e);
}
public float getMinimumSpan(int axis) {
if (isVisible) {
return super.getMinimumSpan(axis);
}
return 0;
}
public float getPreferredSpan(int axis) {
if (isVisible) {
return super.getPreferredSpan(axis);
}
return 0;
}
public float getMaximumSpan(int axis) {
if (isVisible) {
return super.getMaximumSpan(axis);
}
return 0;
}
public void paint(Graphics g, Shape allocation) {
Component c = getComponent();
Container host = getContainer();
if (host instanceof JTextComponent &&
isVisible != ((JTextComponent)host).isEditable()) {
isVisible = ((JTextComponent)host).isEditable();
preferenceChanged(null, true, true);
host.repaint();
}
/*
* Note: we cannot tweak the visible state of the
* component in createComponent() even though it
* gets called after the setParent() call where
* the value of the boolean is set. This
* because, the setComponentParent() in the
* superclass, always does a setVisible(false)
* after calling createComponent(). We therefore
* use this flag in the paint() method to
* setVisible() to true if required.
*/
if (isVisible) {
super.paint(g, allocation);
}
else {
setSize(0, 0);
}
if (c != null) {
c.setFocusable(isVisible);
}
}
public void setParent(View parent) {
if (parent != null) {
Container host = parent.getContainer();
if (host != null) {
if (host instanceof JTextComponent) {
isVisible = ((JTextComponent)host).isEditable();
} else {
isVisible = false;
}
}
}
super.setParent(parent);
}
/**
* @return true if the Component is visible.
*/
public boolean isVisible() {
return isVisible;
}
/** Set to true if the component is visible. This is based off the
* editability of the container. */
private boolean isVisible;
} // End of EditableView

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import javax.swing.text.*;
import java.net.URL;
/**
* FormSubmitEvent is used to notify interested
* parties that a form was submitted.
*
* @since 1.5
* @author Denis Sharypov
*/
public class FormSubmitEvent extends HTMLFrameHyperlinkEvent {
/**
* Represents an HTML form method type.
* <UL>
* <LI><code>GET</code> corresponds to the GET form method</LI>
* <LI><code>POST</code> corresponds to the POST from method</LI>
* </UL>
* @since 1.5
*/
public enum MethodType { GET, POST };
/**
* Creates a new object representing an html form submit event.
*
* @param source the object responsible for the event
* @param type the event type
* @param actionURL the form action URL
* @param sourceElement the element that corresponds to the source
* of the event
* @param targetFrame the Frame to display the document in
* @param method the form method type
* @param data the form submission data
*/
FormSubmitEvent(Object source, EventType type, URL targetURL,
Element sourceElement, String targetFrame,
MethodType method, String data) {
super(source, type, targetURL, sourceElement, targetFrame);
this.method = method;
this.data = data;
}
/**
* Gets the form method type.
*
* @return the form method type, either
* <code>Method.GET</code> or <code>Method.POST</code>.
*/
public MethodType getMethod() {
return method;
}
/**
* Gets the form submission data.
*
* @return the string representing the form submission data.
*/
public String getData() {
return data;
}
private MethodType method;
private String data;
}

View File

@@ -0,0 +1,970 @@
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.net.*;
import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.text.*;
/**
* Component decorator that implements the view interface
* for form elements, &lt;input&gt;, &lt;textarea&gt;,
* and &lt;select&gt;. The model for the component is stored
* as an attribute of the the element (using StyleConstants.ModelAttribute),
* and is used to build the component of the view. The type
* of the model is assumed to of the type that would be set by
* <code>HTMLDocument.HTMLReader.FormAction</code>. If there are
* multiple views mapped over the document, they will share the
* embedded component models.
* <p>
* The following table shows what components get built
* by this view.
* <table summary="shows what components get built by this view">
* <tr>
* <th>Element Type</th>
* <th>Component built</th>
* </tr>
* <tr>
* <td>input, type button</td>
* <td>JButton</td>
* </tr>
* <tr>
* <td>input, type checkbox</td>
* <td>JCheckBox</td>
* </tr>
* <tr>
* <td>input, type image</td>
* <td>JButton</td>
* </tr>
* <tr>
* <td>input, type password</td>
* <td>JPasswordField</td>
* </tr>
* <tr>
* <td>input, type radio</td>
* <td>JRadioButton</td>
* </tr>
* <tr>
* <td>input, type reset</td>
* <td>JButton</td>
* </tr>
* <tr>
* <td>input, type submit</td>
* <td>JButton</td>
* </tr>
* <tr>
* <td>input, type text</td>
* <td>JTextField</td>
* </tr>
* <tr>
* <td>select, size &gt; 1 or multiple attribute defined</td>
* <td>JList in a JScrollPane</td>
* </tr>
* <tr>
* <td>select, size unspecified or 1</td>
* <td>JComboBox</td>
* </tr>
* <tr>
* <td>textarea</td>
* <td>JTextArea in a JScrollPane</td>
* </tr>
* <tr>
* <td>input, type file</td>
* <td>JTextField</td>
* </tr>
* </table>
*
* @author Timothy Prinzing
* @author Sunita Mani
*/
public class FormView extends ComponentView implements ActionListener {
/**
* If a value attribute is not specified for a FORM input element
* of type "submit", then this default string is used.
*
* @deprecated As of 1.3, value now comes from UIManager property
* FormView.submitButtonText
*/
@Deprecated
public static final String SUBMIT = new String("Submit Query");
/**
* If a value attribute is not specified for a FORM input element
* of type "reset", then this default string is used.
*
* @deprecated As of 1.3, value comes from UIManager UIManager property
* FormView.resetButtonText
*/
@Deprecated
public static final String RESET = new String("Reset");
/**
* Document attribute name for storing POST data. JEditorPane.getPostData()
* uses the same name, should be kept in sync.
*/
final static String PostDataProperty = "javax.swing.JEditorPane.postdata";
/**
* Used to indicate if the maximum span should be the same as the
* preferred span. This is used so that the Component's size doesn't
* change if there is extra room on a line. The first bit is used for
* the X direction, and the second for the y direction.
*/
private short maxIsPreferred;
/**
* Creates a new FormView object.
*
* @param elem the element to decorate
*/
public FormView(Element elem) {
super(elem);
}
/**
* Create the component. This is basically a
* big switch statement based upon the tag type
* and html attributes of the associated element.
*/
protected Component createComponent() {
AttributeSet attr = getElement().getAttributes();
HTML.Tag t = (HTML.Tag)
attr.getAttribute(StyleConstants.NameAttribute);
JComponent c = null;
Object model = attr.getAttribute(StyleConstants.ModelAttribute);
// Remove listeners previously registered in shared model
// when a new UI component is replaced. See bug 7189299.
removeStaleListenerForModel(model);
if (t == HTML.Tag.INPUT) {
c = createInputComponent(attr, model);
} else if (t == HTML.Tag.SELECT) {
if (model instanceof OptionListModel) {
JList list = new JList((ListModel) model);
int size = HTML.getIntegerAttributeValue(attr,
HTML.Attribute.SIZE,
1);
list.setVisibleRowCount(size);
list.setSelectionModel((ListSelectionModel)model);
c = new JScrollPane(list);
} else {
c = new JComboBox((ComboBoxModel) model);
maxIsPreferred = 3;
}
} else if (t == HTML.Tag.TEXTAREA) {
JTextArea area = new JTextArea((Document) model);
int rows = HTML.getIntegerAttributeValue(attr,
HTML.Attribute.ROWS,
1);
area.setRows(rows);
int cols = HTML.getIntegerAttributeValue(attr,
HTML.Attribute.COLS,
20);
maxIsPreferred = 3;
area.setColumns(cols);
c = new JScrollPane(area,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
}
if (c != null) {
c.setAlignmentY(1.0f);
}
return c;
}
/**
* Creates a component for an &lt;INPUT&gt; element based on the
* value of the "type" attribute.
*
* @param set of attributes associated with the &lt;INPUT&gt; element.
* @param model the value of the StyleConstants.ModelAttribute
* @return the component.
*/
private JComponent createInputComponent(AttributeSet attr, Object model) {
JComponent c = null;
String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
if (type.equals("submit") || type.equals("reset")) {
String value = (String)
attr.getAttribute(HTML.Attribute.VALUE);
if (value == null) {
if (type.equals("submit")) {
value = UIManager.getString("FormView.submitButtonText");
} else {
value = UIManager.getString("FormView.resetButtonText");
}
}
JButton button = new JButton(value);
if (model != null) {
button.setModel((ButtonModel)model);
button.addActionListener(this);
}
c = button;
maxIsPreferred = 3;
} else if (type.equals("image")) {
String srcAtt = (String) attr.getAttribute(HTML.Attribute.SRC);
JButton button;
try {
URL base = ((HTMLDocument)getElement().getDocument()).getBase();
URL srcURL = new URL(base, srcAtt);
Icon icon = new ImageIcon(srcURL);
button = new JButton(icon);
} catch (MalformedURLException e) {
button = new JButton(srcAtt);
}
if (model != null) {
button.setModel((ButtonModel)model);
button.addMouseListener(new MouseEventListener());
}
c = button;
maxIsPreferred = 3;
} else if (type.equals("checkbox")) {
c = new JCheckBox();
if (model != null) {
((JCheckBox)c).setModel((JToggleButton.ToggleButtonModel) model);
}
maxIsPreferred = 3;
} else if (type.equals("radio")) {
c = new JRadioButton();
if (model != null) {
((JRadioButton)c).setModel((JToggleButton.ToggleButtonModel)model);
}
maxIsPreferred = 3;
} else if (type.equals("text")) {
int size = HTML.getIntegerAttributeValue(attr,
HTML.Attribute.SIZE,
-1);
JTextField field;
if (size > 0) {
field = new JTextField();
field.setColumns(size);
}
else {
field = new JTextField();
field.setColumns(20);
}
c = field;
if (model != null) {
field.setDocument((Document) model);
}
field.addActionListener(this);
maxIsPreferred = 3;
} else if (type.equals("password")) {
JPasswordField field = new JPasswordField();
c = field;
if (model != null) {
field.setDocument((Document) model);
}
int size = HTML.getIntegerAttributeValue(attr,
HTML.Attribute.SIZE,
-1);
field.setColumns((size > 0) ? size : 20);
field.addActionListener(this);
maxIsPreferred = 3;
} else if (type.equals("file")) {
JTextField field = new JTextField();
if (model != null) {
field.setDocument((Document)model);
}
int size = HTML.getIntegerAttributeValue(attr, HTML.Attribute.SIZE,
-1);
field.setColumns((size > 0) ? size : 20);
JButton browseButton = new JButton(UIManager.getString
("FormView.browseFileButtonText"));
Box box = Box.createHorizontalBox();
box.add(field);
box.add(Box.createHorizontalStrut(5));
box.add(browseButton);
browseButton.addActionListener(new BrowseFileAction(
attr, (Document)model));
c = box;
maxIsPreferred = 3;
}
return c;
}
private void removeStaleListenerForModel(Object model) {
if (model instanceof DefaultButtonModel) {
// case of JButton whose model is DefaultButtonModel
// Need to remove stale ActionListener, ChangeListener and
// ItemListener that are instance of AbstractButton$Handler.
DefaultButtonModel buttonModel = (DefaultButtonModel) model;
String listenerClass = "javax.swing.AbstractButton$Handler";
for (ActionListener listener : buttonModel.getActionListeners()) {
if (listenerClass.equals(listener.getClass().getName())) {
buttonModel.removeActionListener(listener);
}
}
for (ChangeListener listener : buttonModel.getChangeListeners()) {
if (listenerClass.equals(listener.getClass().getName())) {
buttonModel.removeChangeListener(listener);
}
}
for (ItemListener listener : buttonModel.getItemListeners()) {
if (listenerClass.equals(listener.getClass().getName())) {
buttonModel.removeItemListener(listener);
}
}
} else if (model instanceof AbstractListModel) {
// case of JComboBox and JList
// For JList, the stale ListDataListener is instance
// BasicListUI$Handler.
// For JComboBox, there are 2 stale ListDataListeners, which are
// BasicListUI$Handler and BasicComboBoxUI$Handler.
AbstractListModel listModel = (AbstractListModel) model;
String listenerClass1 =
"javax.swing.plaf.basic.BasicListUI$Handler";
String listenerClass2 =
"javax.swing.plaf.basic.BasicComboBoxUI$Handler";
for (ListDataListener listener : listModel.getListDataListeners()) {
if (listenerClass1.equals(listener.getClass().getName())
|| listenerClass2.equals(listener.getClass().getName()))
{
listModel.removeListDataListener(listener);
}
}
} else if (model instanceof AbstractDocument) {
// case of JPasswordField, JTextField and JTextArea
// All have 2 stale DocumentListeners.
String listenerClass1 =
"javax.swing.plaf.basic.BasicTextUI$UpdateHandler";
String listenerClass2 =
"javax.swing.text.DefaultCaret$Handler";
AbstractDocument docModel = (AbstractDocument) model;
for (DocumentListener listener : docModel.getDocumentListeners()) {
if (listenerClass1.equals(listener.getClass().getName())
|| listenerClass2.equals(listener.getClass().getName()))
{
docModel.removeDocumentListener(listener);
}
}
}
}
/**
* Determines the maximum span for this view along an
* axis. For certain components, the maximum and preferred span are the
* same. For others this will return the value
* returned by Component.getMaximumSize along the
* axis of interest.
*
* @param axis may be either View.X_AXIS or View.Y_AXIS
* @return the span the view would like to be rendered into &gt;= 0.
* Typically the view is told to render into the span
* that is returned, although there is no guarantee.
* The parent may choose to resize or break the view.
* @exception IllegalArgumentException for an invalid axis
*/
public float getMaximumSpan(int axis) {
switch (axis) {
case View.X_AXIS:
if ((maxIsPreferred & 1) == 1) {
super.getMaximumSpan(axis);
return getPreferredSpan(axis);
}
return super.getMaximumSpan(axis);
case View.Y_AXIS:
if ((maxIsPreferred & 2) == 2) {
super.getMaximumSpan(axis);
return getPreferredSpan(axis);
}
return super.getMaximumSpan(axis);
default:
break;
}
return super.getMaximumSpan(axis);
}
/**
* Responsible for processing the ActionEvent.
* If the element associated with the FormView,
* has a type of "submit", "reset", "text" or "password"
* then the action is processed. In the case of a "submit"
* the form is submitted. In the case of a "reset"
* the form is reset to its original state.
* In the case of "text" or "password", if the
* element is the last one of type "text" or "password",
* the form is submitted. Otherwise, focus is transferred
* to the next component in the form.
*
* @param evt the ActionEvent.
*/
public void actionPerformed(ActionEvent evt) {
Element element = getElement();
StringBuilder dataBuffer = new StringBuilder();
HTMLDocument doc = (HTMLDocument)getDocument();
AttributeSet attr = element.getAttributes();
String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
if (type.equals("submit")) {
getFormData(dataBuffer);
submitData(dataBuffer.toString());
} else if (type.equals("reset")) {
resetForm();
} else if (type.equals("text") || type.equals("password")) {
if (isLastTextOrPasswordField()) {
getFormData(dataBuffer);
submitData(dataBuffer.toString());
} else {
getComponent().transferFocus();
}
}
}
/**
* This method is responsible for submitting the form data.
* A thread is forked to undertake the submission.
*/
protected void submitData(String data) {
Element form = getFormElement();
AttributeSet attrs = form.getAttributes();
HTMLDocument doc = (HTMLDocument) form.getDocument();
URL base = doc.getBase();
String target = (String) attrs.getAttribute(HTML.Attribute.TARGET);
if (target == null) {
target = "_self";
}
String method = (String) attrs.getAttribute(HTML.Attribute.METHOD);
if (method == null) {
method = "GET";
}
method = method.toLowerCase();
boolean isPostMethod = method.equals("post");
if (isPostMethod) {
storePostData(doc, target, data);
}
String action = (String) attrs.getAttribute(HTML.Attribute.ACTION);
URL actionURL;
try {
actionURL = (action == null)
? new URL(base.getProtocol(), base.getHost(),
base.getPort(), base.getFile())
: new URL(base, action);
if (!isPostMethod) {
String query = data.toString();
actionURL = new URL(actionURL + "?" + query);
}
} catch (MalformedURLException e) {
actionURL = null;
}
final JEditorPane c = (JEditorPane) getContainer();
HTMLEditorKit kit = (HTMLEditorKit) c.getEditorKit();
FormSubmitEvent formEvent = null;
if (!kit.isAutoFormSubmission() || doc.isFrameDocument()) {
FormSubmitEvent.MethodType methodType = isPostMethod
? FormSubmitEvent.MethodType.POST
: FormSubmitEvent.MethodType.GET;
formEvent = new FormSubmitEvent(
FormView.this, HyperlinkEvent.EventType.ACTIVATED,
actionURL, form, target, methodType, data);
}
// setPage() may take significant time so schedule it to run later.
final FormSubmitEvent fse = formEvent;
final URL url = actionURL;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (fse != null) {
c.fireHyperlinkUpdate(fse);
} else {
try {
c.setPage(url);
} catch (IOException e) {
UIManager.getLookAndFeel().provideErrorFeedback(c);
}
}
}
});
}
private void storePostData(HTMLDocument doc, String target, String data) {
/* POST data is stored into the document property named by constant
* PostDataProperty from where it is later retrieved by method
* JEditorPane.getPostData(). If the current document is in a frame,
* the data is initially put into the toplevel (frameset) document
* property (named <PostDataProperty>.<Target frame name>). It is the
* responsibility of FrameView which updates the target frame
* to move data from the frameset document property into the frame
* document property.
*/
Document propDoc = doc;
String propName = PostDataProperty;
if (doc.isFrameDocument()) {
// find the top-most JEditorPane holding the frameset view.
FrameView.FrameEditorPane p =
(FrameView.FrameEditorPane) getContainer();
FrameView v = p.getFrameView();
JEditorPane c = v.getOutermostJEditorPane();
if (c != null) {
propDoc = c.getDocument();
propName += ("." + target);
}
}
propDoc.putProperty(propName, data);
}
/**
* MouseEventListener class to handle form submissions when
* an input with type equal to image is clicked on.
* A MouseListener is necessary since along with the image
* data the coordinates associated with the mouse click
* need to be submitted.
*/
protected class MouseEventListener extends MouseAdapter {
public void mouseReleased(MouseEvent evt) {
String imageData = getImageData(evt.getPoint());
imageSubmit(imageData);
}
}
/**
* This method is called to submit a form in response
* to a click on an image -- an &lt;INPUT&gt; form
* element of type "image".
*
* @param imageData the mouse click coordinates.
*/
protected void imageSubmit(String imageData) {
StringBuilder dataBuffer = new StringBuilder();
Element elem = getElement();
HTMLDocument hdoc = (HTMLDocument)elem.getDocument();
getFormData(dataBuffer);
if (dataBuffer.length() > 0) {
dataBuffer.append('&');
}
dataBuffer.append(imageData);
submitData(dataBuffer.toString());
return;
}
/**
* Extracts the value of the name attribute
* associated with the input element of type
* image. If name is defined it is encoded using
* the URLEncoder.encode() method and the
* image data is returned in the following format:
* name + ".x" +"="+ x +"&"+ name +".y"+"="+ y
* otherwise,
* "x="+ x +"&y="+ y
*
* @param point associated with the mouse click.
* @return the image data.
*/
private String getImageData(Point point) {
String mouseCoords = point.x + ":" + point.y;
int sep = mouseCoords.indexOf(':');
String x = mouseCoords.substring(0, sep);
String y = mouseCoords.substring(++sep);
String name = (String) getElement().getAttributes().getAttribute(HTML.Attribute.NAME);
String data;
if (name == null || name.equals("")) {
data = "x="+ x +"&y="+ y;
} else {
name = URLEncoder.encode(name);
data = name + ".x" +"="+ x +"&"+ name +".y"+"="+ y;
}
return data;
}
/**
* The following methods provide functionality required to
* iterate over a the elements of the form and in the case
* of a form submission, extract the data from each model
* that is associated with each form element, and in the
* case of reset, reinitialize the each model to its
* initial state.
*/
/**
* Returns the Element representing the <code>FORM</code>.
*/
private Element getFormElement() {
Element elem = getElement();
while (elem != null) {
if (elem.getAttributes().getAttribute
(StyleConstants.NameAttribute) == HTML.Tag.FORM) {
return elem;
}
elem = elem.getParentElement();
}
return null;
}
/**
* Iterates over the
* element hierarchy, extracting data from the
* models associated with the relevant form elements.
* "Relevant" means the form elements that are part
* of the same form whose element triggered the submit
* action.
*
* @param buffer the buffer that contains that data to submit
* @param targetElement the element that triggered the
* form submission
*/
private void getFormData(StringBuilder buffer) {
Element formE = getFormElement();
if (formE != null) {
ElementIterator it = new ElementIterator(formE);
Element next;
while ((next = it.next()) != null) {
if (isControl(next)) {
String type = (String)next.getAttributes().getAttribute
(HTML.Attribute.TYPE);
if (type != null && type.equals("submit") &&
next != getElement()) {
// do nothing - this submit is not the trigger
} else if (type == null || !type.equals("image")) {
// images only result in data if they triggered
// the submit and they require that the mouse click
// coords be appended to the data. Hence its
// processing is handled by the view.
loadElementDataIntoBuffer(next, buffer);
}
}
}
}
}
/**
* Loads the data
* associated with the element into the buffer.
* The format in which data is appended depends
* on the type of the form element. Essentially
* data is loaded in name/value pairs.
*
*/
private void loadElementDataIntoBuffer(Element elem, StringBuilder buffer) {
AttributeSet attr = elem.getAttributes();
String name = (String)attr.getAttribute(HTML.Attribute.NAME);
if (name == null) {
return;
}
String value = null;
HTML.Tag tag = (HTML.Tag)elem.getAttributes().getAttribute
(StyleConstants.NameAttribute);
if (tag == HTML.Tag.INPUT) {
value = getInputElementData(attr);
} else if (tag == HTML.Tag.TEXTAREA) {
value = getTextAreaData(attr);
} else if (tag == HTML.Tag.SELECT) {
loadSelectData(attr, buffer);
}
if (name != null && value != null) {
appendBuffer(buffer, name, value);
}
}
/**
* Returns the data associated with an &lt;INPUT&gt; form
* element. The value of "type" attributes is
* used to determine the type of the model associated
* with the element and then the relevant data is
* extracted.
*/
private String getInputElementData(AttributeSet attr) {
Object model = attr.getAttribute(StyleConstants.ModelAttribute);
String type = (String) attr.getAttribute(HTML.Attribute.TYPE);
String value = null;
if (type.equals("text") || type.equals("password")) {
Document doc = (Document)model;
try {
value = doc.getText(0, doc.getLength());
} catch (BadLocationException e) {
value = null;
}
} else if (type.equals("submit") || type.equals("hidden")) {
value = (String) attr.getAttribute(HTML.Attribute.VALUE);
if (value == null) {
value = "";
}
} else if (type.equals("radio") || type.equals("checkbox")) {
ButtonModel m = (ButtonModel)model;
if (m.isSelected()) {
value = (String) attr.getAttribute(HTML.Attribute.VALUE);
if (value == null) {
value = "on";
}
}
} else if (type.equals("file")) {
Document doc = (Document)model;
String path;
try {
path = doc.getText(0, doc.getLength());
} catch (BadLocationException e) {
path = null;
}
if (path != null && path.length() > 0) {
value = path;
}
}
return value;
}
/**
* Returns the data associated with the &lt;TEXTAREA&gt; form
* element. This is done by getting the text stored in the
* Document model.
*/
private String getTextAreaData(AttributeSet attr) {
Document doc = (Document)attr.getAttribute(StyleConstants.ModelAttribute);
try {
return doc.getText(0, doc.getLength());
} catch (BadLocationException e) {
return null;
}
}
/**
* Loads the buffer with the data associated with the Select
* form element. Basically, only items that are selected
* and have their name attribute set are added to the buffer.
*/
private void loadSelectData(AttributeSet attr, StringBuilder buffer) {
String name = (String)attr.getAttribute(HTML.Attribute.NAME);
if (name == null) {
return;
}
Object m = attr.getAttribute(StyleConstants.ModelAttribute);
if (m instanceof OptionListModel) {
OptionListModel<Option> model = (OptionListModel<Option>) m;
for (int i = 0; i < model.getSize(); i++) {
if (model.isSelectedIndex(i)) {
Option option = model.getElementAt(i);
appendBuffer(buffer, name, option.getValue());
}
}
} else if (m instanceof ComboBoxModel) {
ComboBoxModel model = (ComboBoxModel)m;
Option option = (Option)model.getSelectedItem();
if (option != null) {
appendBuffer(buffer, name, option.getValue());
}
}
}
/**
* Appends name / value pairs into the
* buffer. Both names and values are encoded using the
* URLEncoder.encode() method before being added to the
* buffer.
*/
private void appendBuffer(StringBuilder buffer, String name, String value) {
if (buffer.length() > 0) {
buffer.append('&');
}
String encodedName = URLEncoder.encode(name);
buffer.append(encodedName);
buffer.append('=');
String encodedValue = URLEncoder.encode(value);
buffer.append(encodedValue);
}
/**
* Returns true if the Element <code>elem</code> represents a control.
*/
private boolean isControl(Element elem) {
return elem.isLeaf();
}
/**
* Iterates over the element hierarchy to determine if
* the element parameter, which is assumed to be an
* &lt;INPUT&gt; element of type password or text, is the last
* one of either kind, in the form to which it belongs.
*/
boolean isLastTextOrPasswordField() {
Element parent = getFormElement();
Element elem = getElement();
if (parent != null) {
ElementIterator it = new ElementIterator(parent);
Element next;
boolean found = false;
while ((next = it.next()) != null) {
if (next == elem) {
found = true;
}
else if (found && isControl(next)) {
AttributeSet elemAttr = next.getAttributes();
if (HTMLDocument.matchNameAttribute
(elemAttr, HTML.Tag.INPUT)) {
String type = (String)elemAttr.getAttribute
(HTML.Attribute.TYPE);
if ("text".equals(type) || "password".equals(type)) {
return false;
}
}
}
}
}
return true;
}
/**
* Resets the form
* to its initial state by reinitializing the models
* associated with each form element to their initial
* values.
*
* param elem the element that triggered the reset
*/
void resetForm() {
Element parent = getFormElement();
if (parent != null) {
ElementIterator it = new ElementIterator(parent);
Element next;
while((next = it.next()) != null) {
if (isControl(next)) {
AttributeSet elemAttr = next.getAttributes();
Object m = elemAttr.getAttribute(StyleConstants.
ModelAttribute);
if (m instanceof TextAreaDocument) {
TextAreaDocument doc = (TextAreaDocument)m;
doc.reset();
} else if (m instanceof PlainDocument) {
try {
PlainDocument doc = (PlainDocument)m;
doc.remove(0, doc.getLength());
if (HTMLDocument.matchNameAttribute
(elemAttr, HTML.Tag.INPUT)) {
String value = (String)elemAttr.
getAttribute(HTML.Attribute.VALUE);
if (value != null) {
doc.insertString(0, value, null);
}
}
} catch (BadLocationException e) {
}
} else if (m instanceof OptionListModel) {
OptionListModel model = (OptionListModel) m;
int size = model.getSize();
for (int i = 0; i < size; i++) {
model.removeIndexInterval(i, i);
}
BitSet selectionRange = model.getInitialSelection();
for (int i = 0; i < selectionRange.size(); i++) {
if (selectionRange.get(i)) {
model.addSelectionInterval(i, i);
}
}
} else if (m instanceof OptionComboBoxModel) {
OptionComboBoxModel model = (OptionComboBoxModel) m;
Option option = model.getInitialSelection();
if (option != null) {
model.setSelectedItem(option);
}
} else if (m instanceof JToggleButton.ToggleButtonModel) {
boolean checked = ((String)elemAttr.getAttribute
(HTML.Attribute.CHECKED) != null);
JToggleButton.ToggleButtonModel model =
(JToggleButton.ToggleButtonModel)m;
model.setSelected(checked);
}
}
}
}
}
/**
* BrowseFileAction is used for input type == file. When the user
* clicks the button a JFileChooser is brought up allowing the user
* to select a file in the file system. The resulting path to the selected
* file is set in the text field (actually an instance of Document).
*/
private class BrowseFileAction implements ActionListener {
private AttributeSet attrs;
private Document model;
BrowseFileAction(AttributeSet attrs, Document model) {
this.attrs = attrs;
this.model = model;
}
public void actionPerformed(ActionEvent ae) {
// PENDING: When mime support is added to JFileChooser use the
// accept value of attrs.
JFileChooser fc = new JFileChooser();
fc.setMultiSelectionEnabled(false);
if (fc.showOpenDialog(getContainer()) ==
JFileChooser.APPROVE_OPTION) {
File selected = fc.getSelectedFile();
if (selected != null) {
try {
if (model.getLength() > 0) {
model.remove(0, model.getLength());
}
model.insertString(0, selected.getPath(), null);
} catch (BadLocationException ble) {}
}
}
}
}
}

View File

@@ -0,0 +1,322 @@
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
/**
* Implements a FrameSetView, intended to support the HTML
* &lt;FRAMESET&gt; tag. Supports the ROWS and COLS attributes.
*
* @author Sunita Mani
*
* Credit also to the hotjava browser engineers that
* worked on making the allocation of space algorithms
* conform to the HTML 4.0 standard and also be netscape
* compatible.
*
*/
class FrameSetView extends javax.swing.text.BoxView {
String[] children;
int[] percentChildren;
int[] absoluteChildren;
int[] relativeChildren;
int percentTotals;
int absoluteTotals;
int relativeTotals;
/**
* Constructs a FrameSetView for the given element.
*
* @param elem the element that this view is responsible for
*/
public FrameSetView(Element elem, int axis) {
super(elem, axis);
children = null;
}
/**
* Parses the ROW or COL attributes and returns
* an array of strings that represent the space
* distribution.
*
*/
private String[] parseRowColSpec(HTML.Attribute key) {
AttributeSet attributes = getElement().getAttributes();
String spec = "*";
if (attributes != null) {
if (attributes.getAttribute(key) != null) {
spec = (String)attributes.getAttribute(key);
}
}
StringTokenizer tokenizer = new StringTokenizer(spec, ",");
int nTokens = tokenizer.countTokens();
int n = getViewCount();
String[] items = new String[Math.max(nTokens, n)];
int i = 0;
for (; i < nTokens; i++) {
items[i] = tokenizer.nextToken().trim();
// As per the spec, 100% is the same as *
// hence the mapping.
//
if (items[i].equals("100%")) {
items[i] = "*";
}
}
// extend spec if we have more children than specified
// in ROWS or COLS attribute
for (; i < items.length; i++) {
items[i] = "*";
}
return items;
}
/**
* Initializes a number of internal state variables
* that store information about space allocation
* for the frames contained within the frameset.
*/
private void init() {
if (getAxis() == View.Y_AXIS) {
children = parseRowColSpec(HTML.Attribute.ROWS);
} else {
children = parseRowColSpec(HTML.Attribute.COLS);
}
percentChildren = new int[children.length];
relativeChildren = new int[children.length];
absoluteChildren = new int[children.length];
for (int i = 0; i < children.length; i++) {
percentChildren[i] = -1;
relativeChildren[i] = -1;
absoluteChildren[i] = -1;
if (children[i].endsWith("*")) {
if (children[i].length() > 1) {
relativeChildren[i] =
Integer.parseInt(children[i].substring(
0, children[i].length()-1));
relativeTotals += relativeChildren[i];
} else {
relativeChildren[i] = 1;
relativeTotals += 1;
}
} else if (children[i].indexOf('%') != -1) {
percentChildren[i] = parseDigits(children[i]);
percentTotals += percentChildren[i];
} else {
absoluteChildren[i] = Integer.parseInt(children[i]);
}
}
if (percentTotals > 100) {
for (int i = 0; i < percentChildren.length; i++) {
if (percentChildren[i] > 0) {
percentChildren[i] =
(percentChildren[i] * 100) / percentTotals;
}
}
percentTotals = 100;
}
}
/**
* Perform layout for the major axis of the box (i.e. the
* axis that it represents). The results of the layout should
* be placed in the given arrays which represent the allocations
* to the children along the major axis.
*
* @param targetSpan the total span given to the view, which
* would be used to layout the children
* @param axis the axis being layed out
* @param offsets the offsets from the origin of the view for
* each of the child views; this is a return value and is
* filled in by the implementation of this method
* @param spans the span of each child view; this is a return
* value and is filled in by the implementation of this method
* @return the offset and span for each child view in the
* offsets and spans parameters
*/
protected void layoutMajorAxis(int targetSpan, int axis, int[] offsets,
int[] spans) {
if (children == null) {
init();
}
SizeRequirements.calculateTiledPositions(targetSpan, null,
getChildRequests(targetSpan,
axis),
offsets, spans);
}
protected SizeRequirements[] getChildRequests(int targetSpan, int axis) {
int span[] = new int[children.length];
spread(targetSpan, span);
int n = getViewCount();
SizeRequirements[] reqs = new SizeRequirements[n];
for (int i = 0, sIndex = 0; i < n; i++) {
View v = getView(i);
if ((v instanceof FrameView) || (v instanceof FrameSetView)) {
reqs[i] = new SizeRequirements((int) v.getMinimumSpan(axis),
span[sIndex],
(int) v.getMaximumSpan(axis),
0.5f);
sIndex++;
} else {
int min = (int) v.getMinimumSpan(axis);
int pref = (int) v.getPreferredSpan(axis);
int max = (int) v.getMaximumSpan(axis);
float a = v.getAlignment(axis);
reqs[i] = new SizeRequirements(min, pref, max, a);
}
}
return reqs;
}
/**
* This method is responsible for returning in span[] the
* span for each child view along the major axis. it
* computes this based on the information that extracted
* from the value of the ROW/COL attribute.
*/
private void spread(int targetSpan, int span[]) {
if (targetSpan == 0) {
return;
}
int tempSpace = 0;
int remainingSpace = targetSpan;
// allocate the absolute's first, they have
// precedence
//
for (int i = 0; i < span.length; i++) {
if (absoluteChildren[i] > 0) {
span[i] = absoluteChildren[i];
remainingSpace -= span[i];
}
}
// then deal with percents.
//
tempSpace = remainingSpace;
for (int i = 0; i < span.length; i++) {
if (percentChildren[i] > 0 && tempSpace > 0) {
span[i] = (percentChildren[i] * tempSpace) / 100;
remainingSpace -= span[i];
} else if (percentChildren[i] > 0 && tempSpace <= 0) {
span[i] = targetSpan / span.length;
remainingSpace -= span[i];
}
}
// allocate remainingSpace to relative
if (remainingSpace > 0 && relativeTotals > 0) {
for (int i = 0; i < span.length; i++) {
if (relativeChildren[i] > 0) {
span[i] = (remainingSpace *
relativeChildren[i]) / relativeTotals;
}
}
} else if (remainingSpace > 0) {
// There are no relative columns and the space has been
// under- or overallocated. In this case, turn all the
// percentage and pixel specified columns to percentage
// columns based on the ratio of their pixel count to the
// total "virtual" size. (In the case of percentage columns,
// the pixel count would equal the specified percentage
// of the screen size.
// This action is in accordance with the HTML
// 4.0 spec (see section 8.3, the end of the discussion of
// the FRAMESET tag). The precedence of percentage and pixel
// specified columns is unclear (spec seems to indicate that
// they share priority, however, unspecified what happens when
// overallocation occurs.)
// addendum is that we behave similar to netscape in that specified
// widths have precedance over percentage widths...
float vTotal = (float)(targetSpan - remainingSpace);
float[] tempPercents = new float[span.length];
remainingSpace = targetSpan;
for (int i = 0; i < span.length; i++) {
// ok we know what our total space is, and we know how large each
// column should be relative to each other... therefore we can use
// that relative information to deduce their percentages of a whole
// and then scale them appropriately for the correct size
tempPercents[i] = ((float)span[i] / vTotal) * 100.00f;
span[i] = (int) ( ((float)targetSpan * tempPercents[i]) / 100.00f);
remainingSpace -= span[i];
}
// this is for just in case there is something left over.. if there is we just
// add it one pixel at a time to the frames in order.. We shouldn't really ever get
// here and if we do it shouldn't be with more than 1 pixel, maybe two.
int i = 0;
while (remainingSpace != 0) {
if (remainingSpace < 0) {
span[i++]--;
remainingSpace++;
}
else {
span[i++]++;
remainingSpace--;
}
// just in case there are more pixels than frames...should never happen..
if (i == span.length)i = 0;
}
}
}
/*
* Users have been known to type things like "%25" and "25 %". Deal
* with it.
*/
private int parseDigits(String mixedStr) {
int result = 0;
for (int i = 0; i < mixedStr.length(); i++) {
char ch = mixedStr.charAt(i);
if (Character.isDigit(ch)) {
result = (result * 10) + Character.digit(ch, 10);
}
}
return result;
}
}

View File

@@ -0,0 +1,479 @@
/*
* Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.*;
import java.util.*;
import java.net.*;
import java.io.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import sun.swing.text.html.FrameEditorPaneTag;
/**
* Implements a FrameView, intended to support the HTML
* &lt;FRAME&gt; tag. Supports the frameborder, scrolling,
* marginwidth and marginheight attributes.
*
* @author Sunita Mani
*/
class FrameView extends ComponentView implements HyperlinkListener {
JEditorPane htmlPane;
JScrollPane scroller;
boolean editable;
float width;
float height;
URL src;
/** Set to true when the component has been created. */
private boolean createdComponent;
/**
* Creates a new Frame.
*
* @param elem the element to represent.
*/
public FrameView(Element elem) {
super(elem);
}
protected Component createComponent() {
Element elem = getElement();
AttributeSet attributes = elem.getAttributes();
String srcAtt = (String)attributes.getAttribute(HTML.Attribute.SRC);
if ((srcAtt != null) && (!srcAtt.equals(""))) {
try {
URL base = ((HTMLDocument)elem.getDocument()).getBase();
src = new URL(base, srcAtt);
htmlPane = new FrameEditorPane();
htmlPane.addHyperlinkListener(this);
JEditorPane host = getHostPane();
boolean isAutoFormSubmission = true;
if (host != null) {
htmlPane.setEditable(host.isEditable());
String charset = (String) host.getClientProperty("charset");
if (charset != null) {
htmlPane.putClientProperty("charset", charset);
}
HTMLEditorKit hostKit = (HTMLEditorKit)host.getEditorKit();
if (hostKit != null) {
isAutoFormSubmission = hostKit.isAutoFormSubmission();
}
}
htmlPane.setPage(src);
HTMLEditorKit kit = (HTMLEditorKit)htmlPane.getEditorKit();
if (kit != null) {
kit.setAutoFormSubmission(isAutoFormSubmission);
}
Document doc = htmlPane.getDocument();
if (doc instanceof HTMLDocument) {
((HTMLDocument)doc).setFrameDocumentState(true);
}
setMargin();
createScrollPane();
setBorder();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
createdComponent = true;
return scroller;
}
JEditorPane getHostPane() {
Container c = getContainer();
while ((c != null) && ! (c instanceof JEditorPane)) {
c = c.getParent();
}
return (JEditorPane) c;
}
/**
* Sets the parent view for the FrameView.
* Also determines if the FrameView should be editable
* or not based on whether the JTextComponent that
* contains it is editable.
*
* @param parent View
*/
public void setParent(View parent) {
if (parent != null) {
JTextComponent t = (JTextComponent)parent.getContainer();
editable = t.isEditable();
}
super.setParent(parent);
}
/**
* Also determines if the FrameView should be editable
* or not based on whether the JTextComponent that
* contains it is editable. And then proceeds to call
* the superclass to do the paint().
*
* @param parent View
* @see text.ComponentView#paint
*/
public void paint(Graphics g, Shape allocation) {
Container host = getContainer();
if (host != null && htmlPane != null &&
htmlPane.isEditable() != ((JTextComponent)host).isEditable()) {
editable = ((JTextComponent)host).isEditable();
htmlPane.setEditable(editable);
}
super.paint(g, allocation);
}
/**
* If the marginwidth or marginheight attributes have been specified,
* then the JEditorPane's margin's are set to the new values.
*/
private void setMargin() {
int margin = 0;
Insets in = htmlPane.getMargin();
Insets newInsets;
boolean modified = false;
AttributeSet attributes = getElement().getAttributes();
String marginStr = (String)attributes.getAttribute(HTML.Attribute.MARGINWIDTH);
if ( in != null) {
newInsets = new Insets(in.top, in.left, in.right, in.bottom);
} else {
newInsets = new Insets(0,0,0,0);
}
if (marginStr != null) {
margin = Integer.parseInt(marginStr);
if (margin > 0) {
newInsets.left = margin;
newInsets.right = margin;
modified = true;
}
}
marginStr = (String)attributes.getAttribute(HTML.Attribute.MARGINHEIGHT);
if (marginStr != null) {
margin = Integer.parseInt(marginStr);
if (margin > 0) {
newInsets.top = margin;
newInsets.bottom = margin;
modified = true;
}
}
if (modified) {
htmlPane.setMargin(newInsets);
}
}
/**
* If the frameborder attribute has been specified, either in the frame,
* or by the frames enclosing frameset, the JScrollPane's setBorder()
* method is invoked to achieve the desired look.
*/
private void setBorder() {
AttributeSet attributes = getElement().getAttributes();
String frameBorder = (String)attributes.getAttribute(HTML.Attribute.FRAMEBORDER);
if ((frameBorder != null) &&
(frameBorder.equals("no") || frameBorder.equals("0"))) {
// make invisible borders.
scroller.setBorder(null);
}
}
/**
* This method creates the JScrollPane. The scrollbar policy is determined by
* the scrolling attribute. If not defined, the default is "auto" which
* maps to the scrollbar's being displayed as needed.
*/
private void createScrollPane() {
AttributeSet attributes = getElement().getAttributes();
String scrolling = (String)attributes.getAttribute(HTML.Attribute.SCROLLING);
if (scrolling == null) {
scrolling = "auto";
}
if (!scrolling.equals("no")) {
if (scrolling.equals("yes")) {
scroller = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
} else {
// scrollbars will be displayed if needed
//
scroller = new JScrollPane();
}
} else {
scroller = new JScrollPane(JScrollPane.VERTICAL_SCROLLBAR_NEVER,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
}
JViewport vp = scroller.getViewport();
vp.add(htmlPane);
vp.setBackingStoreEnabled(true);
scroller.setMinimumSize(new Dimension(5,5));
scroller.setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
}
/**
* Finds the outermost FrameSetView. It then
* returns that FrameSetView's container.
*/
JEditorPane getOutermostJEditorPane() {
View parent = getParent();
FrameSetView frameSetView = null;
while (parent != null) {
if (parent instanceof FrameSetView) {
frameSetView = (FrameSetView)parent;
}
parent = parent.getParent();
}
if (frameSetView != null) {
return (JEditorPane)frameSetView.getContainer();
}
return null;
}
/**
* Returns true if this frame is contained within
* a nested frameset.
*/
private boolean inNestedFrameSet() {
FrameSetView parent = (FrameSetView)getParent();
return (parent.getParent() instanceof FrameSetView);
}
/**
* Notification of a change relative to a
* hyperlink. This method searches for the outermost
* JEditorPane, and then fires an HTMLFrameHyperlinkEvent
* to that frame. In addition, if the target is _parent,
* and there is not nested framesets then the target is
* reset to _top. If the target is _top, in addition to
* firing the event to the outermost JEditorPane, this
* method also invokes the setPage() method and explicitly
* replaces the current document with the destination url.
*
* @param HyperlinkEvent
*/
public void hyperlinkUpdate(HyperlinkEvent evt) {
JEditorPane c = getOutermostJEditorPane();
if (c == null) {
return;
}
if (!(evt instanceof HTMLFrameHyperlinkEvent)) {
c.fireHyperlinkUpdate(evt);
return;
}
HTMLFrameHyperlinkEvent e = (HTMLFrameHyperlinkEvent)evt;
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
String target = e.getTarget();
String postTarget = target;
if (target.equals("_parent") && !inNestedFrameSet()){
target = "_top";
}
if (evt instanceof FormSubmitEvent) {
HTMLEditorKit kit = (HTMLEditorKit)c.getEditorKit();
if (kit != null && kit.isAutoFormSubmission()) {
if (target.equals("_top")) {
try {
movePostData(c, postTarget);
c.setPage(e.getURL());
} catch (IOException ex) {
// Need a way to handle exceptions
}
} else {
HTMLDocument doc = (HTMLDocument)c.getDocument();
doc.processHTMLFrameHyperlinkEvent(e);
}
} else {
c.fireHyperlinkUpdate(evt);
}
return;
}
if (target.equals("_top")) {
try {
c.setPage(e.getURL());
} catch (IOException ex) {
// Need a way to handle exceptions
// ex.printStackTrace();
}
}
if (!c.isEditable()) {
c.fireHyperlinkUpdate(new HTMLFrameHyperlinkEvent(c,
e.getEventType(),
e.getURL(),
e.getDescription(),
getElement(),
e.getInputEvent(),
target));
}
}
}
/**
* Gives notification from the document that attributes were changed
* in a location that this view is responsible for. Currently this view
* handles changes to its SRC attribute.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
*
*/
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
Element elem = getElement();
AttributeSet attributes = elem.getAttributes();
URL oldPage = src;
String srcAtt = (String)attributes.getAttribute(HTML.Attribute.SRC);
URL base = ((HTMLDocument)elem.getDocument()).getBase();
try {
if (!createdComponent) {
return;
}
Object postData = movePostData(htmlPane, null);
src = new URL(base, srcAtt);
if (oldPage.equals(src) && (src.getRef() == null) && (postData == null)) {
return;
}
htmlPane.setPage(src);
Document newDoc = htmlPane.getDocument();
if (newDoc instanceof HTMLDocument) {
((HTMLDocument)newDoc).setFrameDocumentState(true);
}
} catch (MalformedURLException e1) {
// Need a way to handle exceptions
//e1.printStackTrace();
} catch (IOException e2) {
// Need a way to handle exceptions
//e2.printStackTrace();
}
}
/**
* Move POST data from temporary storage into the target document property.
*
* @return the POST data or null if no data found
*/
private Object movePostData(JEditorPane targetPane, String frameName) {
Object postData = null;
JEditorPane p = getOutermostJEditorPane();
if (p != null) {
if (frameName == null) {
frameName = (String) getElement().getAttributes().getAttribute(
HTML.Attribute.NAME);
}
if (frameName != null) {
String propName = FormView.PostDataProperty + "." + frameName;
Document d = p.getDocument();
postData = d.getProperty(propName);
if (postData != null) {
targetPane.getDocument().putProperty(
FormView.PostDataProperty, postData);
d.putProperty(propName, null);
}
}
}
return postData;
}
/**
* Determines the minimum span for this view along an
* axis.
*
* @param axis may be either <code>View.X_AXIS</code> or
* <code>View.Y_AXIS</code>
* @return the preferred span; given that we do not
* support resizing of frames, the minimum span returned
* is the same as the preferred span
*
*/
public float getMinimumSpan(int axis) {
return 5;
}
/**
* Determines the maximum span for this view along an
* axis.
*
* @param axis may be either <code>View.X_AXIS</code> or
* <code>View.Y_AXIS</code>
* @return the preferred span; given that we do not
* support resizing of frames, the maximum span returned
* is the same as the preferred span
*
*/
public float getMaximumSpan(int axis) {
return Integer.MAX_VALUE;
}
/** Editor pane rendering frame of HTML document
* It uses the same editor kits classes as outermost JEditorPane
*/
class FrameEditorPane extends JEditorPane implements FrameEditorPaneTag {
public EditorKit getEditorKitForContentType(String type) {
EditorKit editorKit = super.getEditorKitForContentType(type);
JEditorPane outerMostJEditorPane = null;
if ((outerMostJEditorPane = getOutermostJEditorPane()) != null) {
EditorKit inheritedEditorKit = outerMostJEditorPane.getEditorKitForContentType(type);
if (! editorKit.getClass().equals(inheritedEditorKit.getClass())) {
editorKit = (EditorKit) inheritedEditorKit.clone();
setEditorKitForContentType(type, editorKit);
}
}
return editorKit;
}
FrameView getFrameView() {
return FrameView.this;
}
}
}

View File

@@ -0,0 +1,321 @@
/*
* Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.*;
import javax.swing.event.DocumentEvent;
import javax.swing.text.*;
import java.util.Enumeration;
import java.lang.Integer;
/**
* A view implementation to display an html horizontal
* rule.
*
* @author Timothy Prinzing
* @author Sara Swanson
*/
class HRuleView extends View {
/**
* Creates a new view that represents an &lt;hr&gt; element.
*
* @param elem the element to create a view for
*/
public HRuleView(Element elem) {
super(elem);
setPropertiesFromAttributes();
}
/**
* Update any cached values that come from attributes.
*/
protected void setPropertiesFromAttributes() {
StyleSheet sheet = ((HTMLDocument)getDocument()).getStyleSheet();
AttributeSet eAttr = getElement().getAttributes();
attr = sheet.getViewAttributes(this);
alignment = StyleConstants.ALIGN_CENTER;
size = 0;
noshade = null;
widthValue = null;
if (attr != null) {
// getAlignment() returns ALIGN_LEFT by default, and HR should
// use ALIGN_CENTER by default, so we check if the alignment
// attribute is actually defined
if (attr.getAttribute(StyleConstants.Alignment) != null) {
alignment = StyleConstants.getAlignment(attr);
}
noshade = (String)eAttr.getAttribute(HTML.Attribute.NOSHADE);
Object value = eAttr.getAttribute(HTML.Attribute.SIZE);
if (value != null && (value instanceof String)) {
try {
size = Integer.parseInt((String)value);
} catch (NumberFormatException e) {
size = 1;
}
}
value = attr.getAttribute(CSS.Attribute.WIDTH);
if (value != null && (value instanceof CSS.LengthValue)) {
widthValue = (CSS.LengthValue)value;
}
topMargin = getLength(CSS.Attribute.MARGIN_TOP, attr);
bottomMargin = getLength(CSS.Attribute.MARGIN_BOTTOM, attr);
leftMargin = getLength(CSS.Attribute.MARGIN_LEFT, attr);
rightMargin = getLength(CSS.Attribute.MARGIN_RIGHT, attr);
}
else {
topMargin = bottomMargin = leftMargin = rightMargin = 0;
}
size = Math.max(2, size);
}
// This will be removed and centralized at some point, need to unify this
// and avoid private classes.
private float getLength(CSS.Attribute key, AttributeSet a) {
CSS.LengthValue lv = (CSS.LengthValue) a.getAttribute(key);
float len = (lv != null) ? lv.getValue() : 0;
return len;
}
// --- View methods ---------------------------------------------
/**
* Paints the view.
*
* @param g the graphics context
* @param a the allocation region for the view
* @see View#paint
*/
public void paint(Graphics g, Shape a) {
Rectangle alloc = (a instanceof Rectangle) ? (Rectangle)a :
a.getBounds();
int x = 0;
int y = alloc.y + SPACE_ABOVE + (int)topMargin;
int width = alloc.width - (int)(leftMargin + rightMargin);
if (widthValue != null) {
width = (int)widthValue.getValue((float)width);
}
int height = alloc.height - (SPACE_ABOVE + SPACE_BELOW +
(int)topMargin + (int)bottomMargin);
if (size > 0)
height = size;
// Align the rule horizontally.
switch (alignment) {
case StyleConstants.ALIGN_CENTER:
x = alloc.x + (alloc.width / 2) - (width / 2);
break;
case StyleConstants.ALIGN_RIGHT:
x = alloc.x + alloc.width - width - (int)rightMargin;
break;
case StyleConstants.ALIGN_LEFT:
default:
x = alloc.x + (int)leftMargin;
break;
}
// Paint either a shaded rule or a solid line.
if (noshade != null) {
g.setColor(Color.black);
g.fillRect(x, y, width, height);
}
else {
Color bg = getContainer().getBackground();
Color bottom, top;
if (bg == null || bg.equals(Color.white)) {
top = Color.darkGray;
bottom = Color.lightGray;
}
else {
top = Color.darkGray;
bottom = Color.white;
}
g.setColor(bottom);
g.drawLine(x + width - 1, y, x + width - 1, y + height - 1);
g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);
g.setColor(top);
g.drawLine(x, y, x + width - 1, y);
g.drawLine(x, y, x, y + height - 1);
}
}
/**
* Calculates the desired shape of the rule... this is
* basically the preferred size of the border.
*
* @param axis may be either X_AXIS or Y_AXIS
* @return the desired span
* @see View#getPreferredSpan
*/
public float getPreferredSpan(int axis) {
switch (axis) {
case View.X_AXIS:
return 1;
case View.Y_AXIS:
if (size > 0) {
return size + SPACE_ABOVE + SPACE_BELOW + topMargin +
bottomMargin;
} else {
if (noshade != null) {
return 2 + SPACE_ABOVE + SPACE_BELOW + topMargin +
bottomMargin;
} else {
return SPACE_ABOVE + SPACE_BELOW + topMargin +bottomMargin;
}
}
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
/**
* Gets the resize weight for the axis.
* The rule is: rigid vertically and flexible horizontally.
*
* @param axis may be either X_AXIS or Y_AXIS
* @return the weight
*/
public int getResizeWeight(int axis) {
if (axis == View.X_AXIS) {
return 1;
} else if (axis == View.Y_AXIS) {
return 0;
} else {
return 0;
}
}
/**
* Determines how attractive a break opportunity in
* this view is. This is implemented to request a forced break.
*
* @param axis may be either View.X_AXIS or View.Y_AXIS
* @param pos the potential location of the start of the
* broken view (greater than or equal to zero).
* This may be useful for calculating tab
* positions.
* @param len specifies the relative length from <em>pos</em>
* where a potential break is desired. The value must be greater
* than or equal to zero.
* @return the weight, which should be a value between
* ForcedBreakWeight and BadBreakWeight.
*/
public int getBreakWeight(int axis, float pos, float len) {
if (axis == X_AXIS) {
return ForcedBreakWeight;
}
return BadBreakWeight;
}
public View breakView(int axis, int offset, float pos, float len) {
return null;
}
/**
* Provides a mapping from the document model coordinate space
* to the coordinate space of the view mapped to it.
*
* @param pos the position to convert
* @param a the allocated region to render into
* @return the bounding box of the given position
* @exception BadLocationException if the given position does not
* represent a valid location in the associated document
* @see View#modelToView
*/
public Shape modelToView(int pos, Shape a, Position.Bias b) throws BadLocationException {
int p0 = getStartOffset();
int p1 = getEndOffset();
if ((pos >= p0) && (pos <= p1)) {
Rectangle r = a.getBounds();
if (pos == p1) {
r.x += r.width;
}
r.width = 0;
return r;
}
return null;
}
/**
* Provides a mapping from the view coordinate space to the logical
* coordinate space of the model.
*
* @param x the X coordinate
* @param y the Y coordinate
* @param a the allocated region to render into
* @return the location within the model that best represents the
* given point of view
* @see View#viewToModel
*/
public int viewToModel(float x, float y, Shape a, Position.Bias[] bias) {
Rectangle alloc = (Rectangle) a;
if (x < alloc.x + (alloc.width / 2)) {
bias[0] = Position.Bias.Forward;
return getStartOffset();
}
bias[0] = Position.Bias.Backward;
return getEndOffset();
}
/**
* Fetches the attributes to use when rendering. This is
* implemented to multiplex the attributes specified in the
* model with a StyleSheet.
*/
public AttributeSet getAttributes() {
return attr;
}
public void changedUpdate(DocumentEvent changes, Shape a, ViewFactory f) {
super.changedUpdate(changes, a, f);
int pos = changes.getOffset();
if (pos <= getStartOffset() && (pos + changes.getLength()) >=
getEndOffset()) {
setPropertiesFromAttributes();
}
}
// --- variables ------------------------------------------------
private float topMargin;
private float bottomMargin;
private float leftMargin;
private float rightMargin;
private int alignment = StyleConstants.ALIGN_CENTER;
private String noshade = null;
private int size = 0;
private CSS.LengthValue widthValue;
private static final int SPACE_ABOVE = 3;
private static final int SPACE_BELOW = 3;
/** View Attributes. */
private AttributeSet attr;
}

View File

@@ -0,0 +1,697 @@
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.io.*;
import java.util.Hashtable;
import javax.swing.text.AttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
/**
* Constants used in the <code>HTMLDocument</code>. These
* are basically tag and attribute definitions.
*
* @author Timothy Prinzing
* @author Sunita Mani
*
*/
public class HTML {
/**
* Typesafe enumeration for an HTML tag. Although the
* set of HTML tags is a closed set, we have left the
* set open so that people can add their own tag types
* to their custom parser and still communicate to the
* reader.
*/
public static class Tag {
/** @since 1.3 */
public Tag() {}
/**
* Creates a new <code>Tag</code> with the specified <code>id</code>,
* and with <code>causesBreak</code> and <code>isBlock</code>
* set to <code>false</code>.
*
* @param id the id of the new tag
*/
protected Tag(String id) {
this(id, false, false);
}
/**
* Creates a new <code>Tag</code> with the specified <code>id</code>;
* <code>causesBreak</code> and <code>isBlock</code> are defined
* by the user.
*
* @param id the id of the new tag
* @param causesBreak <code>true</code> if this tag
* causes a break to the flow of data
* @param isBlock <code>true</code> if the tag is used
* to add structure to a document
*/
protected Tag(String id, boolean causesBreak, boolean isBlock) {
name = id;
this.breakTag = causesBreak;
this.blockTag = isBlock;
}
/**
* Returns <code>true</code> if this tag is a block
* tag, which is a tag used to add structure to a
* document.
*
* @return <code>true</code> if this tag is a block
* tag, otherwise returns <code>false</code>
*/
public boolean isBlock() {
return blockTag;
}
/**
* Returns <code>true</code> if this tag causes a
* line break to the flow of data, otherwise returns
* <code>false</code>.
*
* @return <code>true</code> if this tag causes a
* line break to the flow of data, otherwise returns
* <code>false</code>
*/
public boolean breaksFlow() {
return breakTag;
}
/**
* Returns <code>true</code> if this tag is pre-formatted,
* which is true if the tag is either <code>PRE</code> or
* <code>TEXTAREA</code>.
*
* @return <code>true</code> if this tag is pre-formatted,
* otherwise returns <code>false</code>
*/
public boolean isPreformatted() {
return (this == PRE || this == TEXTAREA);
}
/**
* Returns the string representation of the
* tag.
*
* @return the <code>String</code> representation of the tag
*/
public String toString() {
return name;
}
/**
* Returns <code>true</code> if this tag is considered to be a paragraph
* in the internal HTML model. <code>false</code> - otherwise.
*
* @return <code>true</code> if this tag is considered to be a paragraph
* in the internal HTML model. <code>false</code> - otherwise.
* @see HTMLDocument.HTMLReader.ParagraphAction
*/
boolean isParagraph() {
return (
this == P
|| this == IMPLIED
|| this == DT
|| this == H1
|| this == H2
|| this == H3
|| this == H4
|| this == H5
|| this == H6
);
}
boolean blockTag;
boolean breakTag;
String name;
boolean unknown;
// --- Tag Names -----------------------------------
public static final Tag A = new Tag("a");
public static final Tag ADDRESS = new Tag("address");
public static final Tag APPLET = new Tag("applet");
public static final Tag AREA = new Tag("area");
public static final Tag B = new Tag("b");
public static final Tag BASE = new Tag("base");
public static final Tag BASEFONT = new Tag("basefont");
public static final Tag BIG = new Tag("big");
public static final Tag BLOCKQUOTE = new Tag("blockquote", true, true);
public static final Tag BODY = new Tag("body", true, true);
public static final Tag BR = new Tag("br", true, false);
public static final Tag CAPTION = new Tag("caption");
public static final Tag CENTER = new Tag("center", true, false);
public static final Tag CITE = new Tag("cite");
public static final Tag CODE = new Tag("code");
public static final Tag DD = new Tag("dd", true, true);
public static final Tag DFN = new Tag("dfn");
public static final Tag DIR = new Tag("dir", true, true);
public static final Tag DIV = new Tag("div", true, true);
public static final Tag DL = new Tag("dl", true, true);
public static final Tag DT = new Tag("dt", true, true);
public static final Tag EM = new Tag("em");
public static final Tag FONT = new Tag("font");
public static final Tag FORM = new Tag("form", true, false);
public static final Tag FRAME = new Tag("frame");
public static final Tag FRAMESET = new Tag("frameset");
public static final Tag H1 = new Tag("h1", true, true);
public static final Tag H2 = new Tag("h2", true, true);
public static final Tag H3 = new Tag("h3", true, true);
public static final Tag H4 = new Tag("h4", true, true);
public static final Tag H5 = new Tag("h5", true, true);
public static final Tag H6 = new Tag("h6", true, true);
public static final Tag HEAD = new Tag("head", true, true);
public static final Tag HR = new Tag("hr", true, false);
public static final Tag HTML = new Tag("html", true, false);
public static final Tag I = new Tag("i");
public static final Tag IMG = new Tag("img");
public static final Tag INPUT = new Tag("input");
public static final Tag ISINDEX = new Tag("isindex", true, false);
public static final Tag KBD = new Tag("kbd");
public static final Tag LI = new Tag("li", true, true);
public static final Tag LINK = new Tag("link");
public static final Tag MAP = new Tag("map");
public static final Tag MENU = new Tag("menu", true, true);
public static final Tag META = new Tag("meta");
/*public*/ static final Tag NOBR = new Tag("nobr");
public static final Tag NOFRAMES = new Tag("noframes", true, true);
public static final Tag OBJECT = new Tag("object");
public static final Tag OL = new Tag("ol", true, true);
public static final Tag OPTION = new Tag("option");
public static final Tag P = new Tag("p", true, true);
public static final Tag PARAM = new Tag("param");
public static final Tag PRE = new Tag("pre", true, true);
public static final Tag SAMP = new Tag("samp");
public static final Tag SCRIPT = new Tag("script");
public static final Tag SELECT = new Tag("select");
public static final Tag SMALL = new Tag("small");
public static final Tag SPAN = new Tag("span");
public static final Tag STRIKE = new Tag("strike");
public static final Tag S = new Tag("s");
public static final Tag STRONG = new Tag("strong");
public static final Tag STYLE = new Tag("style");
public static final Tag SUB = new Tag("sub");
public static final Tag SUP = new Tag("sup");
public static final Tag TABLE = new Tag("table", false, true);
public static final Tag TD = new Tag("td", true, true);
public static final Tag TEXTAREA = new Tag("textarea");
public static final Tag TH = new Tag("th", true, true);
public static final Tag TITLE = new Tag("title", true, true);
public static final Tag TR = new Tag("tr", false, true);
public static final Tag TT = new Tag("tt");
public static final Tag U = new Tag("u");
public static final Tag UL = new Tag("ul", true, true);
public static final Tag VAR = new Tag("var");
/**
* All text content must be in a paragraph element.
* If a paragraph didn't exist when content was
* encountered, a paragraph is manufactured.
* <p>
* This is a tag synthesized by the HTML reader.
* Since elements are identified by their tag type,
* we create a some fake tag types to mark the elements
* that were manufactured.
*/
public static final Tag IMPLIED = new Tag("p-implied");
/**
* All text content is labeled with this tag.
* <p>
* This is a tag synthesized by the HTML reader.
* Since elements are identified by their tag type,
* we create a some fake tag types to mark the elements
* that were manufactured.
*/
public static final Tag CONTENT = new Tag("content");
/**
* All comments are labeled with this tag.
* <p>
* This is a tag synthesized by the HTML reader.
* Since elements are identified by their tag type,
* we create a some fake tag types to mark the elements
* that were manufactured.
*/
public static final Tag COMMENT = new Tag("comment");
static final Tag allTags[] = {
A, ADDRESS, APPLET, AREA, B, BASE, BASEFONT, BIG,
BLOCKQUOTE, BODY, BR, CAPTION, CENTER, CITE, CODE,
DD, DFN, DIR, DIV, DL, DT, EM, FONT, FORM, FRAME,
FRAMESET, H1, H2, H3, H4, H5, H6, HEAD, HR, HTML,
I, IMG, INPUT, ISINDEX, KBD, LI, LINK, MAP, MENU,
META, NOBR, NOFRAMES, OBJECT, OL, OPTION, P, PARAM,
PRE, SAMP, SCRIPT, SELECT, SMALL, SPAN, STRIKE, S,
STRONG, STYLE, SUB, SUP, TABLE, TD, TEXTAREA,
TH, TITLE, TR, TT, U, UL, VAR
};
static {
// Force HTMLs static initialize to be loaded.
getTag("html");
}
}
// There is no unique instance of UnknownTag, so we allow it to be
// Serializable.
public static class UnknownTag extends Tag implements Serializable {
/**
* Creates a new <code>UnknownTag</code> with the specified
* <code>id</code>.
* @param id the id of the new tag
*/
public UnknownTag(String id) {
super(id);
}
/**
* Returns the hash code which corresponds to the string
* for this tag.
*/
public int hashCode() {
return toString().hashCode();
}
/**
* Compares this object to the specified object.
* The result is <code>true</code> if and only if the argument is not
* <code>null</code> and is an <code>UnknownTag</code> object
* with the same name.
*
* @param obj the object to compare this tag with
* @return <code>true</code> if the objects are equal;
* <code>false</code> otherwise
*/
public boolean equals(Object obj) {
if (obj instanceof UnknownTag) {
return toString().equals(obj.toString());
}
return false;
}
private void writeObject(java.io.ObjectOutputStream s)
throws IOException {
s.defaultWriteObject();
s.writeBoolean(blockTag);
s.writeBoolean(breakTag);
s.writeBoolean(unknown);
s.writeObject(name);
}
private void readObject(ObjectInputStream s)
throws ClassNotFoundException, IOException {
s.defaultReadObject();
blockTag = s.readBoolean();
breakTag = s.readBoolean();
unknown = s.readBoolean();
name = (String)s.readObject();
}
}
/**
* Typesafe enumeration representing an HTML
* attribute.
*/
public static final class Attribute {
/**
* Creates a new <code>Attribute</code> with the specified
* <code>id</code>.
*
* @param id the id of the new <code>Attribute</code>
*/
Attribute(String id) {
name = id;
}
/**
* Returns the string representation of this attribute.
* @return the string representation of this attribute
*/
public String toString() {
return name;
}
private String name;
public static final Attribute SIZE = new Attribute("size");
public static final Attribute COLOR = new Attribute("color");
public static final Attribute CLEAR = new Attribute("clear");
public static final Attribute BACKGROUND = new Attribute("background");
public static final Attribute BGCOLOR = new Attribute("bgcolor");
public static final Attribute TEXT = new Attribute("text");
public static final Attribute LINK = new Attribute("link");
public static final Attribute VLINK = new Attribute("vlink");
public static final Attribute ALINK = new Attribute("alink");
public static final Attribute WIDTH = new Attribute("width");
public static final Attribute HEIGHT = new Attribute("height");
public static final Attribute ALIGN = new Attribute("align");
public static final Attribute NAME = new Attribute("name");
public static final Attribute HREF = new Attribute("href");
public static final Attribute REL = new Attribute("rel");
public static final Attribute REV = new Attribute("rev");
public static final Attribute TITLE = new Attribute("title");
public static final Attribute TARGET = new Attribute("target");
public static final Attribute SHAPE = new Attribute("shape");
public static final Attribute COORDS = new Attribute("coords");
public static final Attribute ISMAP = new Attribute("ismap");
public static final Attribute NOHREF = new Attribute("nohref");
public static final Attribute ALT = new Attribute("alt");
public static final Attribute ID = new Attribute("id");
public static final Attribute SRC = new Attribute("src");
public static final Attribute HSPACE = new Attribute("hspace");
public static final Attribute VSPACE = new Attribute("vspace");
public static final Attribute USEMAP = new Attribute("usemap");
public static final Attribute LOWSRC = new Attribute("lowsrc");
public static final Attribute CODEBASE = new Attribute("codebase");
public static final Attribute CODE = new Attribute("code");
public static final Attribute ARCHIVE = new Attribute("archive");
public static final Attribute VALUE = new Attribute("value");
public static final Attribute VALUETYPE = new Attribute("valuetype");
public static final Attribute TYPE = new Attribute("type");
public static final Attribute CLASS = new Attribute("class");
public static final Attribute STYLE = new Attribute("style");
public static final Attribute LANG = new Attribute("lang");
public static final Attribute FACE = new Attribute("face");
public static final Attribute DIR = new Attribute("dir");
public static final Attribute DECLARE = new Attribute("declare");
public static final Attribute CLASSID = new Attribute("classid");
public static final Attribute DATA = new Attribute("data");
public static final Attribute CODETYPE = new Attribute("codetype");
public static final Attribute STANDBY = new Attribute("standby");
public static final Attribute BORDER = new Attribute("border");
public static final Attribute SHAPES = new Attribute("shapes");
public static final Attribute NOSHADE = new Attribute("noshade");
public static final Attribute COMPACT = new Attribute("compact");
public static final Attribute START = new Attribute("start");
public static final Attribute ACTION = new Attribute("action");
public static final Attribute METHOD = new Attribute("method");
public static final Attribute ENCTYPE = new Attribute("enctype");
public static final Attribute CHECKED = new Attribute("checked");
public static final Attribute MAXLENGTH = new Attribute("maxlength");
public static final Attribute MULTIPLE = new Attribute("multiple");
public static final Attribute SELECTED = new Attribute("selected");
public static final Attribute ROWS = new Attribute("rows");
public static final Attribute COLS = new Attribute("cols");
public static final Attribute DUMMY = new Attribute("dummy");
public static final Attribute CELLSPACING = new Attribute("cellspacing");
public static final Attribute CELLPADDING = new Attribute("cellpadding");
public static final Attribute VALIGN = new Attribute("valign");
public static final Attribute HALIGN = new Attribute("halign");
public static final Attribute NOWRAP = new Attribute("nowrap");
public static final Attribute ROWSPAN = new Attribute("rowspan");
public static final Attribute COLSPAN = new Attribute("colspan");
public static final Attribute PROMPT = new Attribute("prompt");
public static final Attribute HTTPEQUIV = new Attribute("http-equiv");
public static final Attribute CONTENT = new Attribute("content");
public static final Attribute LANGUAGE = new Attribute("language");
public static final Attribute VERSION = new Attribute("version");
public static final Attribute N = new Attribute("n");
public static final Attribute FRAMEBORDER = new Attribute("frameborder");
public static final Attribute MARGINWIDTH = new Attribute("marginwidth");
public static final Attribute MARGINHEIGHT = new Attribute("marginheight");
public static final Attribute SCROLLING = new Attribute("scrolling");
public static final Attribute NORESIZE = new Attribute("noresize");
public static final Attribute ENDTAG = new Attribute("endtag");
public static final Attribute COMMENT = new Attribute("comment");
static final Attribute MEDIA = new Attribute("media");
static final Attribute allAttributes[] = {
FACE,
COMMENT,
SIZE,
COLOR,
CLEAR,
BACKGROUND,
BGCOLOR,
TEXT,
LINK,
VLINK,
ALINK,
WIDTH,
HEIGHT,
ALIGN,
NAME,
HREF,
REL,
REV,
TITLE,
TARGET,
SHAPE,
COORDS,
ISMAP,
NOHREF,
ALT,
ID,
SRC,
HSPACE,
VSPACE,
USEMAP,
LOWSRC,
CODEBASE,
CODE,
ARCHIVE,
VALUE,
VALUETYPE,
TYPE,
CLASS,
STYLE,
LANG,
DIR,
DECLARE,
CLASSID,
DATA,
CODETYPE,
STANDBY,
BORDER,
SHAPES,
NOSHADE,
COMPACT,
START,
ACTION,
METHOD,
ENCTYPE,
CHECKED,
MAXLENGTH,
MULTIPLE,
SELECTED,
ROWS,
COLS,
DUMMY,
CELLSPACING,
CELLPADDING,
VALIGN,
HALIGN,
NOWRAP,
ROWSPAN,
COLSPAN,
PROMPT,
HTTPEQUIV,
CONTENT,
LANGUAGE,
VERSION,
N,
FRAMEBORDER,
MARGINWIDTH,
MARGINHEIGHT,
SCROLLING,
NORESIZE,
MEDIA,
ENDTAG
};
}
// The secret to 73, is that, given that the Hashtable contents
// never change once the static initialization happens, the initial size
// that the hashtable grew to was determined, and then that very size
// is used.
//
private static final Hashtable<String, Tag> tagHashtable = new Hashtable<String, Tag>(73);
/** Maps from StyleConstant key to HTML.Tag. */
private static final Hashtable<Object, Tag> scMapping = new Hashtable<Object, Tag>(8);
static {
for (int i = 0; i < Tag.allTags.length; i++ ) {
tagHashtable.put(Tag.allTags[i].toString(), Tag.allTags[i]);
StyleContext.registerStaticAttributeKey(Tag.allTags[i]);
}
StyleContext.registerStaticAttributeKey(Tag.IMPLIED);
StyleContext.registerStaticAttributeKey(Tag.CONTENT);
StyleContext.registerStaticAttributeKey(Tag.COMMENT);
for (int i = 0; i < Attribute.allAttributes.length; i++) {
StyleContext.registerStaticAttributeKey(Attribute.
allAttributes[i]);
}
StyleContext.registerStaticAttributeKey(HTML.NULL_ATTRIBUTE_VALUE);
scMapping.put(StyleConstants.Bold, Tag.B);
scMapping.put(StyleConstants.Italic, Tag.I);
scMapping.put(StyleConstants.Underline, Tag.U);
scMapping.put(StyleConstants.StrikeThrough, Tag.STRIKE);
scMapping.put(StyleConstants.Superscript, Tag.SUP);
scMapping.put(StyleConstants.Subscript, Tag.SUB);
scMapping.put(StyleConstants.FontFamily, Tag.FONT);
scMapping.put(StyleConstants.FontSize, Tag.FONT);
}
/**
* Returns the set of actual HTML tags that
* are recognized by the default HTML reader.
* This set does not include tags that are
* manufactured by the reader.
*/
public static Tag[] getAllTags() {
Tag[] tags = new Tag[Tag.allTags.length];
System.arraycopy(Tag.allTags, 0, tags, 0, Tag.allTags.length);
return tags;
}
/**
* Fetches a tag constant for a well-known tag name (i.e. one of
* the tags in the set {A, ADDRESS, APPLET, AREA, B,
* BASE, BASEFONT, BIG,
* BLOCKQUOTE, BODY, BR, CAPTION, CENTER, CITE, CODE,
* DD, DFN, DIR, DIV, DL, DT, EM, FONT, FORM, FRAME,
* FRAMESET, H1, H2, H3, H4, H5, H6, HEAD, HR, HTML,
* I, IMG, INPUT, ISINDEX, KBD, LI, LINK, MAP, MENU,
* META, NOBR, NOFRAMES, OBJECT, OL, OPTION, P, PARAM,
* PRE, SAMP, SCRIPT, SELECT, SMALL, SPAN, STRIKE, S,
* STRONG, STYLE, SUB, SUP, TABLE, TD, TEXTAREA,
* TH, TITLE, TR, TT, U, UL, VAR}. If the given
* name does not represent one of the well-known tags, then
* <code>null</code> will be returned.
*
* @param tagName the <code>String</code> name requested
* @return a tag constant corresponding to the <code>tagName</code>,
* or <code>null</code> if not found
*/
public static Tag getTag(String tagName) {
Tag t = tagHashtable.get(tagName);
return (t == null ? null : t);
}
/**
* Returns the HTML <code>Tag</code> associated with the
* <code>StyleConstants</code> key <code>sc</code>.
* If no matching <code>Tag</code> is found, returns
* <code>null</code>.
*
* @param sc the <code>StyleConstants</code> key
* @return tag which corresponds to <code>sc</code>, or
* <code>null</code> if not found
*/
static Tag getTagForStyleConstantsKey(StyleConstants sc) {
return scMapping.get(sc);
}
/**
* Fetches an integer attribute value. Attribute values
* are stored as a string, and this is a convenience method
* to convert to an actual integer.
*
* @param attr the set of attributes to use to try to fetch a value
* @param key the key to use to fetch the value
* @param def the default value to use if the attribute isn't
* defined or there is an error converting to an integer
*/
public static int getIntegerAttributeValue(AttributeSet attr,
Attribute key, int def) {
int value = def;
String istr = (String) attr.getAttribute(key);
if (istr != null) {
try {
value = Integer.valueOf(istr).intValue();
} catch (NumberFormatException e) {
value = def;
}
}
return value;
}
// This is used in cases where the value for the attribute has not
// been specified.
//
public static final String NULL_ATTRIBUTE_VALUE = "#DEFAULT";
// size determined similar to size of tagHashtable
private static final Hashtable<String, Attribute> attHashtable = new Hashtable<String, Attribute>(77);
static {
for (int i = 0; i < Attribute.allAttributes.length; i++ ) {
attHashtable.put(Attribute.allAttributes[i].toString(), Attribute.allAttributes[i]);
}
}
/**
* Returns the set of HTML attributes recognized.
* @return the set of HTML attributes recognized
*/
public static Attribute[] getAllAttributeKeys() {
Attribute[] attributes = new Attribute[Attribute.allAttributes.length];
System.arraycopy(Attribute.allAttributes, 0,
attributes, 0, Attribute.allAttributes.length);
return attributes;
}
/**
* Fetches an attribute constant for a well-known attribute name
* (i.e. one of the attributes in the set {FACE, COMMENT, SIZE,
* COLOR, CLEAR, BACKGROUND, BGCOLOR, TEXT, LINK, VLINK, ALINK,
* WIDTH, HEIGHT, ALIGN, NAME, HREF, REL, REV, TITLE, TARGET,
* SHAPE, COORDS, ISMAP, NOHREF, ALT, ID, SRC, HSPACE, VSPACE,
* USEMAP, LOWSRC, CODEBASE, CODE, ARCHIVE, VALUE, VALUETYPE,
* TYPE, CLASS, STYLE, LANG, DIR, DECLARE, CLASSID, DATA, CODETYPE,
* STANDBY, BORDER, SHAPES, NOSHADE, COMPACT, START, ACTION, METHOD,
* ENCTYPE, CHECKED, MAXLENGTH, MULTIPLE, SELECTED, ROWS, COLS,
* DUMMY, CELLSPACING, CELLPADDING, VALIGN, HALIGN, NOWRAP, ROWSPAN,
* COLSPAN, PROMPT, HTTPEQUIV, CONTENT, LANGUAGE, VERSION, N,
* FRAMEBORDER, MARGINWIDTH, MARGINHEIGHT, SCROLLING, NORESIZE,
* MEDIA, ENDTAG}).
* If the given name does not represent one of the well-known attributes,
* then <code>null</code> will be returned.
*
* @param attName the <code>String</code> requested
* @return the <code>Attribute</code> corresponding to <code>attName</code>
*/
public static Attribute getAttributeKey(String attName) {
Attribute a = attHashtable.get(attName);
if (a == null) {
return null;
}
return a;
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,134 @@
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.event.InputEvent;
import javax.swing.text.*;
import javax.swing.event.HyperlinkEvent;
import java.net.URL;
/**
* HTMLFrameHyperlinkEvent is used to notify interested
* parties that link was activated in a frame.
*
* @author Sunita Mani
*/
public class HTMLFrameHyperlinkEvent extends HyperlinkEvent {
/**
* Creates a new object representing a html frame
* hypertext link event.
*
* @param source the object responsible for the event
* @param type the event type
* @param targetURL the affected URL
* @param targetFrame the Frame to display the document in
*/
public HTMLFrameHyperlinkEvent(Object source, EventType type, URL targetURL,
String targetFrame) {
super(source, type, targetURL);
this.targetFrame = targetFrame;
}
/**
* Creates a new object representing a hypertext link event.
*
* @param source the object responsible for the event
* @param type the event type
* @param targetURL the affected URL
* @param desc a description
* @param targetFrame the Frame to display the document in
*/
public HTMLFrameHyperlinkEvent(Object source, EventType type, URL targetURL, String desc,
String targetFrame) {
super(source, type, targetURL, desc);
this.targetFrame = targetFrame;
}
/**
* Creates a new object representing a hypertext link event.
*
* @param source the object responsible for the event
* @param type the event type
* @param targetURL the affected URL
* @param sourceElement the element that corresponds to the source
* of the event
* @param targetFrame the Frame to display the document in
*/
public HTMLFrameHyperlinkEvent(Object source, EventType type, URL targetURL,
Element sourceElement, String targetFrame) {
super(source, type, targetURL, null, sourceElement);
this.targetFrame = targetFrame;
}
/**
* Creates a new object representing a hypertext link event.
*
* @param source the object responsible for the event
* @param type the event type
* @param targetURL the affected URL
* @param desc a description
* @param sourceElement the element that corresponds to the source
* of the event
* @param targetFrame the Frame to display the document in
*/
public HTMLFrameHyperlinkEvent(Object source, EventType type, URL targetURL, String desc,
Element sourceElement, String targetFrame) {
super(source, type, targetURL, desc, sourceElement);
this.targetFrame = targetFrame;
}
/**
* Creates a new object representing a hypertext link event.
*
* @param source the object responsible for the event
* @param type the event type
* @param targetURL the affected URL
* @param desc a description
* @param sourceElement the element that corresponds to the source
* of the event
* @param inputEvent InputEvent that triggered the hyperlink event
* @param targetFrame the Frame to display the document in
* @since 1.7
*/
public HTMLFrameHyperlinkEvent(Object source, EventType type, URL targetURL,
String desc, Element sourceElement,
InputEvent inputEvent, String targetFrame) {
super(source, type, targetURL, desc, sourceElement, inputEvent);
this.targetFrame = targetFrame;
}
/**
* returns the target for the link.
*/
public String getTarget() {
return targetFrame;
}
private String targetFrame;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,361 @@
/*
* Copyright (c) 1998, 2006, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.text.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import java.util.*;
/**
* HiddenTagView subclasses EditableView to contain a JTextField showing
* the element name. When the textfield is edited the element name is
* reset. As this inherits from EditableView if the JTextComponent is
* not editable, the textfield will not be visible.
*
* @author Scott Violet
*/
class HiddenTagView extends EditableView implements DocumentListener {
HiddenTagView(Element e) {
super(e);
yAlign = 1;
}
protected Component createComponent() {
JTextField tf = new JTextField(getElement().getName());
Document doc = getDocument();
Font font;
if (doc instanceof StyledDocument) {
font = ((StyledDocument)doc).getFont(getAttributes());
tf.setFont(font);
}
else {
font = tf.getFont();
}
tf.getDocument().addDocumentListener(this);
updateYAlign(font);
// Create a panel to wrap the textfield so that the textfields
// laf border shows through.
JPanel panel = new JPanel(new BorderLayout());
panel.setBackground(null);
if (isEndTag()) {
panel.setBorder(EndBorder);
}
else {
panel.setBorder(StartBorder);
}
panel.add(tf);
return panel;
}
public float getAlignment(int axis) {
if (axis == View.Y_AXIS) {
return yAlign;
}
return 0.5f;
}
public float getMinimumSpan(int axis) {
if (axis == View.X_AXIS && isVisible()) {
// Default to preferred.
return Math.max(30, super.getPreferredSpan(axis));
}
return super.getMinimumSpan(axis);
}
public float getPreferredSpan(int axis) {
if (axis == View.X_AXIS && isVisible()) {
return Math.max(30, super.getPreferredSpan(axis));
}
return super.getPreferredSpan(axis);
}
public float getMaximumSpan(int axis) {
if (axis == View.X_AXIS && isVisible()) {
// Default to preferred.
return Math.max(30, super.getMaximumSpan(axis));
}
return super.getMaximumSpan(axis);
}
// DocumentListener methods
public void insertUpdate(DocumentEvent e) {
updateModelFromText();
}
public void removeUpdate(DocumentEvent e) {
updateModelFromText();
}
public void changedUpdate(DocumentEvent e) {
updateModelFromText();
}
// View method
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
if (!isSettingAttributes) {
setTextFromModel();
}
}
// local methods
void updateYAlign(Font font) {
Container c = getContainer();
FontMetrics fm = (c != null) ? c.getFontMetrics(font) :
Toolkit.getDefaultToolkit().getFontMetrics(font);
float h = fm.getHeight();
float d = fm.getDescent();
yAlign = (h > 0) ? (h - d) / h : 0;
}
void resetBorder() {
Component comp = getComponent();
if (comp != null) {
if (isEndTag()) {
((JPanel)comp).setBorder(EndBorder);
}
else {
((JPanel)comp).setBorder(StartBorder);
}
}
}
/**
* This resets the text on the text component we created to match
* that of the AttributeSet for the Element we represent.
* <p>If this is invoked on the event dispatching thread, this
* directly invokes <code>_setTextFromModel</code>, otherwise
* <code>SwingUtilities.invokeLater</code> is used to schedule execution
* of <code>_setTextFromModel</code>.
*/
void setTextFromModel() {
if (SwingUtilities.isEventDispatchThread()) {
_setTextFromModel();
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_setTextFromModel();
}
});
}
}
/**
* This resets the text on the text component we created to match
* that of the AttributeSet for the Element we represent.
*/
void _setTextFromModel() {
Document doc = getDocument();
try {
isSettingAttributes = true;
if (doc instanceof AbstractDocument) {
((AbstractDocument)doc).readLock();
}
JTextComponent text = getTextComponent();
if (text != null) {
text.setText(getRepresentedText());
resetBorder();
Container host = getContainer();
if (host != null) {
preferenceChanged(this, true, true);
host.repaint();
}
}
}
finally {
isSettingAttributes = false;
if (doc instanceof AbstractDocument) {
((AbstractDocument)doc).readUnlock();
}
}
}
/**
* This copies the text from the text component we've created
* to the Element's AttributeSet we represent.
* <p>If this is invoked on the event dispatching thread, this
* directly invokes <code>_updateModelFromText</code>, otherwise
* <code>SwingUtilities.invokeLater</code> is used to schedule execution
* of <code>_updateModelFromText</code>.
*/
void updateModelFromText() {
if (!isSettingAttributes) {
if (SwingUtilities.isEventDispatchThread()) {
_updateModelFromText();
}
else {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
_updateModelFromText();
}
});
}
}
}
/**
* This copies the text from the text component we've created
* to the Element's AttributeSet we represent.
*/
void _updateModelFromText() {
Document doc = getDocument();
Object name = getElement().getAttributes().getAttribute
(StyleConstants.NameAttribute);
if ((name instanceof HTML.UnknownTag) &&
(doc instanceof StyledDocument)) {
SimpleAttributeSet sas = new SimpleAttributeSet();
JTextComponent textComponent = getTextComponent();
if (textComponent != null) {
String text = textComponent.getText();
isSettingAttributes = true;
try {
sas.addAttribute(StyleConstants.NameAttribute,
new HTML.UnknownTag(text));
((StyledDocument)doc).setCharacterAttributes
(getStartOffset(), getEndOffset() -
getStartOffset(), sas, false);
}
finally {
isSettingAttributes = false;
}
}
}
}
JTextComponent getTextComponent() {
Component comp = getComponent();
return (comp == null) ? null : (JTextComponent)((Container)comp).
getComponent(0);
}
String getRepresentedText() {
String retValue = getElement().getName();
return (retValue == null) ? "" : retValue;
}
boolean isEndTag() {
AttributeSet as = getElement().getAttributes();
if (as != null) {
Object end = as.getAttribute(HTML.Attribute.ENDTAG);
if (end != null && (end instanceof String) &&
((String)end).equals("true")) {
return true;
}
}
return false;
}
/** Alignment along the y axis, based on the font of the textfield. */
float yAlign;
/** Set to true when setting attributes. */
boolean isSettingAttributes;
// Following are for Borders that used for Unknown tags and comments.
//
// Border defines
static final int circleR = 3;
static final int circleD = circleR * 2;
static final int tagSize = 6;
static final int padding = 3;
static final Color UnknownTagBorderColor = Color.black;
static final Border StartBorder = new StartTagBorder();
static final Border EndBorder = new EndTagBorder();
static class StartTagBorder implements Border, Serializable {
public void paintBorder(Component c, Graphics g, int x, int y,
int width, int height) {
g.setColor(UnknownTagBorderColor);
x += padding;
width -= (padding * 2);
g.drawLine(x, y + circleR,
x, y + height - circleR);
g.drawArc(x, y + height - circleD - 1,
circleD, circleD, 180, 90);
g.drawArc(x, y, circleD, circleD, 90, 90);
g.drawLine(x + circleR, y, x + width - tagSize, y);
g.drawLine(x + circleR, y + height - 1,
x + width - tagSize, y + height - 1);
g.drawLine(x + width - tagSize, y,
x + width - 1, y + height / 2);
g.drawLine(x + width - tagSize, y + height,
x + width - 1, y + height / 2);
}
public Insets getBorderInsets(Component c) {
return new Insets(2, 2 + padding, 2, tagSize + 2 + padding);
}
public boolean isBorderOpaque() {
return false;
}
} // End of class HiddenTagView.StartTagBorder
static class EndTagBorder implements Border, Serializable {
public void paintBorder(Component c, Graphics g, int x, int y,
int width, int height) {
g.setColor(UnknownTagBorderColor);
x += padding;
width -= (padding * 2);
g.drawLine(x + width - 1, y + circleR,
x + width - 1, y + height - circleR);
g.drawArc(x + width - circleD - 1, y + height - circleD - 1,
circleD, circleD, 270, 90);
g.drawArc(x + width - circleD - 1, y, circleD, circleD, 0, 90);
g.drawLine(x + tagSize, y, x + width - circleR, y);
g.drawLine(x + tagSize, y + height - 1,
x + width - circleR, y + height - 1);
g.drawLine(x + tagSize, y,
x, y + height / 2);
g.drawLine(x + tagSize, y + height,
x, y + height / 2);
}
public Insets getBorderInsets(Component c) {
return new Insets(2, tagSize + 2 + padding, 2, 2 + padding);
}
public boolean isBorderOpaque() {
return false;
}
} // End of class HiddenTagView.EndTagBorder
} // End of HiddenTagView

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,225 @@
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.*;
import java.text.BreakIterator;
import javax.swing.event.DocumentEvent;
import javax.swing.text.*;
/**
* Displays the <dfn>inline element</dfn> styles
* based upon css attributes.
*
* @author Timothy Prinzing
*/
public class InlineView extends LabelView {
/**
* Constructs a new view wrapped on an element.
*
* @param elem the element
*/
public InlineView(Element elem) {
super(elem);
StyleSheet sheet = getStyleSheet();
attr = sheet.getViewAttributes(this);
}
/**
* Gives notification that something was inserted into
* the document in a location that this view is responsible for.
* If either parameter is <code>null</code>, behavior of this method is
* implementation dependent.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
* @since 1.5
* @see View#insertUpdate
*/
public void insertUpdate(DocumentEvent e, Shape a, ViewFactory f) {
super.insertUpdate(e, a, f);
}
/**
* Gives notification that something was removed from the document
* in a location that this view is responsible for.
* If either parameter is <code>null</code>, behavior of this method is
* implementation dependent.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
* @since 1.5
* @see View#removeUpdate
*/
public void removeUpdate(DocumentEvent e, Shape a, ViewFactory f) {
super.removeUpdate(e, a, f);
}
/**
* Gives notification from the document that attributes were changed
* in a location that this view is responsible for.
*
* @param e the change information from the associated document
* @param a the current allocation of the view
* @param f the factory to use to rebuild if the view has children
* @see View#changedUpdate
*/
public void changedUpdate(DocumentEvent e, Shape a, ViewFactory f) {
super.changedUpdate(e, a, f);
StyleSheet sheet = getStyleSheet();
attr = sheet.getViewAttributes(this);
preferenceChanged(null, true, true);
}
/**
* Fetches the attributes to use when rendering. This is
* implemented to multiplex the attributes specified in the
* model with a StyleSheet.
*/
public AttributeSet getAttributes() {
return attr;
}
/**
* Determines how attractive a break opportunity in
* this view is. This can be used for determining which
* view is the most attractive to call <code>breakView</code>
* on in the process of formatting. A view that represents
* text that has whitespace in it might be more attractive
* than a view that has no whitespace, for example. The
* higher the weight, the more attractive the break. A
* value equal to or lower than <code>BadBreakWeight</code>
* should not be considered for a break. A value greater
* than or equal to <code>ForcedBreakWeight</code> should
* be broken.
* <p>
* This is implemented to provide the default behavior
* of returning <code>BadBreakWeight</code> unless the length
* is greater than the length of the view in which case the
* entire view represents the fragment. Unless a view has
* been written to support breaking behavior, it is not
* attractive to try and break the view. An example of
* a view that does support breaking is <code>LabelView</code>.
* An example of a view that uses break weight is
* <code>ParagraphView</code>.
*
* @param axis may be either View.X_AXIS or View.Y_AXIS
* @param pos the potential location of the start of the
* broken view &gt;= 0. This may be useful for calculating tab
* positions.
* @param len specifies the relative length from <em>pos</em>
* where a potential break is desired &gt;= 0.
* @return the weight, which should be a value between
* ForcedBreakWeight and BadBreakWeight.
* @see LabelView
* @see ParagraphView
* @see javax.swing.text.View#BadBreakWeight
* @see javax.swing.text.View#GoodBreakWeight
* @see javax.swing.text.View#ExcellentBreakWeight
* @see javax.swing.text.View#ForcedBreakWeight
*/
public int getBreakWeight(int axis, float pos, float len) {
if (nowrap) {
return BadBreakWeight;
}
return super.getBreakWeight(axis, pos, len);
}
/**
* Tries to break this view on the given axis. Refer to
* {@link javax.swing.text.View#breakView} for a complete
* description of this method.
* <p>Behavior of this method is unspecified in case <code>axis</code>
* is neither <code>View.X_AXIS</code> nor <code>View.Y_AXIS</code>, and
* in case <code>offset</code>, <code>pos</code>, or <code>len</code>
* is null.
*
* @param axis may be either <code>View.X_AXIS</code> or
* <code>View.Y_AXIS</code>
* @param offset the location in the document model
* that a broken fragment would occupy &gt;= 0. This
* would be the starting offset of the fragment
* returned
* @param pos the position along the axis that the
* broken view would occupy &gt;= 0. This may be useful for
* things like tab calculations
* @param len specifies the distance along the axis
* where a potential break is desired &gt;= 0
* @return the fragment of the view that represents the
* given span.
* @since 1.5
* @see javax.swing.text.View#breakView
*/
public View breakView(int axis, int offset, float pos, float len) {
return super.breakView(axis, offset, pos, len);
}
/**
* Set the cached properties from the attributes.
*/
protected void setPropertiesFromAttributes() {
super.setPropertiesFromAttributes();
AttributeSet a = getAttributes();
Object decor = a.getAttribute(CSS.Attribute.TEXT_DECORATION);
boolean u = (decor != null) ?
(decor.toString().indexOf("underline") >= 0) : false;
setUnderline(u);
boolean s = (decor != null) ?
(decor.toString().indexOf("line-through") >= 0) : false;
setStrikeThrough(s);
Object vAlign = a.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
s = (vAlign != null) ? (vAlign.toString().indexOf("sup") >= 0) : false;
setSuperscript(s);
s = (vAlign != null) ? (vAlign.toString().indexOf("sub") >= 0) : false;
setSubscript(s);
Object whitespace = a.getAttribute(CSS.Attribute.WHITE_SPACE);
if ((whitespace != null) && whitespace.equals("nowrap")) {
nowrap = true;
} else {
nowrap = false;
}
HTMLDocument doc = (HTMLDocument)getDocument();
// fetches background color from stylesheet if specified
Color bg = doc.getBackground(a);
if (bg != null) {
setBackground(bg);
}
}
protected StyleSheet getStyleSheet() {
HTMLDocument doc = (HTMLDocument) getDocument();
return doc.getStyleSheet();
}
private boolean nowrap;
private AttributeSet attr;
}

View File

@@ -0,0 +1,114 @@
/*
* Copyright (c) 1998, 2000, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.*;
import java.awt.event.*;
import java.net.URLEncoder;
import java.net.MalformedURLException;
import java.io.IOException;
import java.net.URL;
import javax.swing.text.*;
import javax.swing.*;
/**
* A view that supports the &lt;ISINDEX&lt; tag. This is implemented
* as a JPanel that contains
*
* @author Sunita Mani
*/
class IsindexView extends ComponentView implements ActionListener {
JTextField textField;
/**
* Creates an IsindexView
*/
public IsindexView(Element elem) {
super(elem);
}
/**
* Creates the components necessary to to implement
* this view. The component returned is a <code>JPanel</code>,
* that contains the PROMPT to the left and <code>JTextField</code>
* to the right.
*/
public Component createComponent() {
AttributeSet attr = getElement().getAttributes();
JPanel panel = new JPanel(new BorderLayout());
panel.setBackground(null);
String prompt = (String)attr.getAttribute(HTML.Attribute.PROMPT);
if (prompt == null) {
prompt = UIManager.getString("IsindexView.prompt");
}
JLabel label = new JLabel(prompt);
textField = new JTextField();
textField.addActionListener(this);
panel.add(label, BorderLayout.WEST);
panel.add(textField, BorderLayout.CENTER);
panel.setAlignmentY(1.0f);
panel.setOpaque(false);
return panel;
}
/**
* Responsible for processing the ActionEvent.
* In this case this is hitting enter/return
* in the text field. This will construct the
* URL from the base URL of the document.
* To the URL is appended a '?' followed by the
* contents of the JTextField. The search
* contents are URLEncoded.
*/
public void actionPerformed(ActionEvent evt) {
String data = textField.getText();
if (data != null) {
data = URLEncoder.encode(data);
}
AttributeSet attr = getElement().getAttributes();
HTMLDocument hdoc = (HTMLDocument)getElement().getDocument();
String action = (String) attr.getAttribute(HTML.Attribute.ACTION);
if (action == null) {
action = hdoc.getBase().toString();
}
try {
URL url = new URL(action+"?"+data);
JEditorPane pane = (JEditorPane)getContainer();
pane.setPage(url);
} catch (MalformedURLException e1) {
} catch (IOException e2) {
}
}
}

View File

@@ -0,0 +1,186 @@
/*
* Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.util.Enumeration;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
import javax.swing.event.*;
import javax.swing.text.*;
/**
* A view implementation to display an unwrapped
* preformatted line.<p>
* This subclasses ParagraphView, but this really only contains one
* Row of text.
*
* @author Timothy Prinzing
*/
class LineView extends ParagraphView {
/** Last place painted at. */
int tabBase;
/**
* Creates a LineView object.
*
* @param elem the element to wrap in a view
*/
public LineView(Element elem) {
super(elem);
}
/**
* Preformatted lines are not suppressed if they
* have only whitespace, so they are always visible.
*/
public boolean isVisible() {
return true;
}
/**
* Determines the minimum span for this view along an
* axis. The preformatted line should refuse to be
* sized less than the preferred size.
*
* @param axis may be either <code>View.X_AXIS</code> or
* <code>View.Y_AXIS</code>
* @return the minimum span the view can be rendered into
* @see View#getPreferredSpan
*/
public float getMinimumSpan(int axis) {
return getPreferredSpan(axis);
}
/**
* Gets the resize weight for the specified axis.
*
* @param axis may be either X_AXIS or Y_AXIS
* @return the weight
*/
public int getResizeWeight(int axis) {
switch (axis) {
case View.X_AXIS:
return 1;
case View.Y_AXIS:
return 0;
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
/**
* Gets the alignment for an axis.
*
* @param axis may be either X_AXIS or Y_AXIS
* @return the alignment
*/
public float getAlignment(int axis) {
if (axis == View.X_AXIS) {
return 0;
}
return super.getAlignment(axis);
}
/**
* Lays out the children. If the layout span has changed,
* the rows are rebuilt. The superclass functionality
* is called after checking and possibly rebuilding the
* rows. If the height has changed, the
* <code>preferenceChanged</code> method is called
* on the parent since the vertical preference is
* rigid.
*
* @param width the width to lay out against >= 0. This is
* the width inside of the inset area.
* @param height the height to lay out against >= 0 (not used
* by paragraph, but used by the superclass). This
* is the height inside of the inset area.
*/
protected void layout(int width, int height) {
super.layout(Integer.MAX_VALUE - 1, height);
}
/**
* Returns the next tab stop position given a reference position.
* This view implements the tab coordinate system, and calls
* <code>getTabbedSpan</code> on the logical children in the process
* of layout to determine the desired span of the children. The
* logical children can delegate their tab expansion upward to
* the paragraph which knows how to expand tabs.
* <code>LabelView</code> is an example of a view that delegates
* its tab expansion needs upward to the paragraph.
* <p>
* This is implemented to try and locate a <code>TabSet</code>
* in the paragraph element's attribute set. If one can be
* found, its settings will be used, otherwise a default expansion
* will be provided. The base location for for tab expansion
* is the left inset from the paragraphs most recent allocation
* (which is what the layout of the children is based upon).
*
* @param x the X reference position
* @param tabOffset the position within the text stream
* that the tab occurred at >= 0.
* @return the trailing end of the tab expansion >= 0
* @see TabSet
* @see TabStop
* @see LabelView
*/
public float nextTabStop(float x, int tabOffset) {
// If the text isn't left justified, offset by 10 pixels!
if (getTabSet() == null &&
StyleConstants.getAlignment(getAttributes()) ==
StyleConstants.ALIGN_LEFT) {
return getPreTab(x, tabOffset);
}
return super.nextTabStop(x, tabOffset);
}
/**
* Returns the location for the tab.
*/
protected float getPreTab(float x, int tabOffset) {
Document d = getDocument();
View v = getViewAtPosition(tabOffset, null);
if ((d instanceof StyledDocument) && v != null) {
// Assume f is fixed point.
Font f = ((StyledDocument)d).getFont(v.getAttributes());
Container c = getContainer();
FontMetrics fm = (c != null) ? c.getFontMetrics(f) :
Toolkit.getDefaultToolkit().getFontMetrics(f);
int width = getCharactersPerTab() * fm.charWidth('W');
int tb = (int)getTabBase();
return (float)((((int)x - tb) / width + 1) * width + tb);
}
return 10.0f + x;
}
/**
* @return number of characters per tab, 8.
*/
protected int getCharactersPerTab() {
return 8;
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright (c) 1997, 1999, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.util.Enumeration;
import java.awt.*;
import javax.swing.text.*;
/**
* A view implementation to display an html list
*
* @author Timothy Prinzing
*/
public class ListView extends BlockView {
/**
* Creates a new view that represents a list element.
*
* @param elem the element to create a view for
*/
public ListView(Element elem) {
super(elem, View.Y_AXIS);
}
/**
* Calculates the desired shape of the list.
*
* @return the desired span
* @see View#getPreferredSpan
*/
public float getAlignment(int axis) {
switch (axis) {
case View.X_AXIS:
return 0.5f;
case View.Y_AXIS:
return 0.5f;
default:
throw new IllegalArgumentException("Invalid axis: " + axis);
}
}
/**
* Renders using the given rendering surface and area on that
* surface.
*
* @param g the rendering surface to use
* @param allocation the allocated region to render into
* @see View#paint
*/
public void paint(Graphics g, Shape allocation) {
super.paint(g, allocation);
Rectangle alloc = allocation.getBounds();
Rectangle clip = g.getClipBounds();
// Since listPainter paints in the insets we have to check for the
// case where the child is not painted because the paint region is
// to the left of the child. This assumes the ListPainter paints in
// the left margin.
if ((clip.x + clip.width) < (alloc.x + getLeftInset())) {
Rectangle childRect = alloc;
alloc = getInsideAllocation(allocation);
int n = getViewCount();
int endY = clip.y + clip.height;
for (int i = 0; i < n; i++) {
childRect.setBounds(alloc);
childAllocation(i, childRect);
if (childRect.y < endY) {
if ((childRect.y + childRect.height) >= clip.y) {
listPainter.paint(g, childRect.x, childRect.y,
childRect.width, childRect.height,
this, i);
}
}
else {
break;
}
}
}
}
/**
* Paints one of the children; called by paint(). By default
* that is all it does, but a subclass can use this to paint
* things relative to the child.
*
* @param g the graphics context
* @param alloc the allocated region to render the child into
* @param index the index of the child
*/
protected void paintChild(Graphics g, Rectangle alloc, int index) {
listPainter.paint(g, alloc.x, alloc.y, alloc.width, alloc.height, this, index);
super.paintChild(g, alloc, index);
}
protected void setPropertiesFromAttributes() {
super.setPropertiesFromAttributes();
listPainter = getStyleSheet().getListPainter(getAttributes());
}
private StyleSheet.ListPainter listPainter;
}

View File

@@ -0,0 +1,503 @@
/*
* Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.Polygon;
import java.io.Serializable;
import java.util.StringTokenizer;
import java.util.Vector;
import javax.swing.text.AttributeSet;
/**
* Map is used to represent a map element that is part of an HTML document.
* Once a Map has been created, and any number of areas have been added,
* you can test if a point falls inside the map via the contains method.
*
* @author Scott Violet
*/
class Map implements Serializable {
/** Name of the Map. */
private String name;
/** An array of AttributeSets. */
private Vector<AttributeSet> areaAttributes;
/** An array of RegionContainments, will slowly grow to match the
* length of areaAttributes as needed. */
private Vector<RegionContainment> areas;
public Map() {
}
public Map(String name) {
this.name = name;
}
/**
* Returns the name of the Map.
*/
public String getName() {
return name;
}
/**
* Defines a region of the Map, based on the passed in AttributeSet.
*/
public void addArea(AttributeSet as) {
if (as == null) {
return;
}
if (areaAttributes == null) {
areaAttributes = new Vector<AttributeSet>(2);
}
areaAttributes.addElement(as.copyAttributes());
}
/**
* Removes the previously created area.
*/
public void removeArea(AttributeSet as) {
if (as != null && areaAttributes != null) {
int numAreas = (areas != null) ? areas.size() : 0;
for (int counter = areaAttributes.size() - 1; counter >= 0;
counter--) {
if (areaAttributes.elementAt(counter).isEqual(as)){
areaAttributes.removeElementAt(counter);
if (counter < numAreas) {
areas.removeElementAt(counter);
}
}
}
}
}
/**
* Returns the AttributeSets representing the differet areas of the Map.
*/
public AttributeSet[] getAreas() {
int numAttributes = (areaAttributes != null) ? areaAttributes.size() :
0;
if (numAttributes != 0) {
AttributeSet[] retValue = new AttributeSet[numAttributes];
areaAttributes.copyInto(retValue);
return retValue;
}
return null;
}
/**
* Returns the AttributeSet that contains the passed in location,
* <code>x</code>, <code>y</code>. <code>width</code>, <code>height</code>
* gives the size of the region the map is defined over. If a matching
* area is found, the AttribueSet for it is returned.
*/
public AttributeSet getArea(int x, int y, int width, int height) {
int numAttributes = (areaAttributes != null) ?
areaAttributes.size() : 0;
if (numAttributes > 0) {
int numAreas = (areas != null) ? areas.size() : 0;
if (areas == null) {
areas = new Vector<RegionContainment>(numAttributes);
}
for (int counter = 0; counter < numAttributes; counter++) {
if (counter >= numAreas) {
areas.addElement(createRegionContainment
(areaAttributes.elementAt(counter)));
}
RegionContainment rc = areas.elementAt(counter);
if (rc != null && rc.contains(x, y, width, height)) {
return areaAttributes.elementAt(counter);
}
}
}
return null;
}
/**
* Creates and returns an instance of RegionContainment that can be
* used to test if a particular point lies inside a region.
*/
protected RegionContainment createRegionContainment
(AttributeSet attributes) {
Object shape = attributes.getAttribute(HTML.Attribute.SHAPE);
if (shape == null) {
shape = "rect";
}
if (shape instanceof String) {
String shapeString = ((String)shape).toLowerCase();
RegionContainment rc = null;
try {
if (shapeString.equals("rect")) {
rc = new RectangleRegionContainment(attributes);
}
else if (shapeString.equals("circle")) {
rc = new CircleRegionContainment(attributes);
}
else if (shapeString.equals("poly")) {
rc = new PolygonRegionContainment(attributes);
}
else if (shapeString.equals("default")) {
rc = DefaultRegionContainment.sharedInstance();
}
} catch (RuntimeException re) {
// Something wrong with attributes.
rc = null;
}
return rc;
}
return null;
}
/**
* Creates and returns an array of integers from the String
* <code>stringCoords</code>. If one of the values represents a
* % the returned value with be negative. If a parse error results
* from trying to parse one of the numbers null is returned.
*/
static protected int[] extractCoords(Object stringCoords) {
if (stringCoords == null || !(stringCoords instanceof String)) {
return null;
}
StringTokenizer st = new StringTokenizer((String)stringCoords,
", \t\n\r");
int[] retValue = null;
int numCoords = 0;
while(st.hasMoreElements()) {
String token = st.nextToken();
int scale;
if (token.endsWith("%")) {
scale = -1;
token = token.substring(0, token.length() - 1);
}
else {
scale = 1;
}
try {
int intValue = Integer.parseInt(token);
if (retValue == null) {
retValue = new int[4];
}
else if(numCoords == retValue.length) {
int[] temp = new int[retValue.length * 2];
System.arraycopy(retValue, 0, temp, 0, retValue.length);
retValue = temp;
}
retValue[numCoords++] = intValue * scale;
} catch (NumberFormatException nfe) {
return null;
}
}
if (numCoords > 0 && numCoords != retValue.length) {
int[] temp = new int[numCoords];
System.arraycopy(retValue, 0, temp, 0, numCoords);
retValue = temp;
}
return retValue;
}
/**
* Defines the interface used for to check if a point is inside a
* region.
*/
interface RegionContainment {
/**
* Returns true if the location <code>x</code>, <code>y</code>
* falls inside the region defined in the receiver.
* <code>width</code>, <code>height</code> is the size of
* the enclosing region.
*/
public boolean contains(int x, int y, int width, int height);
}
/**
* Used to test for containment in a rectangular region.
*/
static class RectangleRegionContainment implements RegionContainment {
/** Will be non-null if one of the values is a percent, and any value
* that is non null indicates it is a percent
* (order is x, y, width, height). */
float[] percents;
/** Last value of width passed in. */
int lastWidth;
/** Last value of height passed in. */
int lastHeight;
/** Top left. */
int x0;
int y0;
/** Bottom right. */
int x1;
int y1;
public RectangleRegionContainment(AttributeSet as) {
int[] coords = Map.extractCoords(as.getAttribute(HTML.
Attribute.COORDS));
percents = null;
if (coords == null || coords.length != 4) {
throw new RuntimeException("Unable to parse rectangular area");
}
else {
x0 = coords[0];
y0 = coords[1];
x1 = coords[2];
y1 = coords[3];
if (x0 < 0 || y0 < 0 || x1 < 0 || y1 < 0) {
percents = new float[4];
lastWidth = lastHeight = -1;
for (int counter = 0; counter < 4; counter++) {
if (coords[counter] < 0) {
percents[counter] = Math.abs
(coords[counter]) / 100.0f;
}
else {
percents[counter] = -1.0f;
}
}
}
}
}
public boolean contains(int x, int y, int width, int height) {
if (percents == null) {
return contains(x, y);
}
if (lastWidth != width || lastHeight != height) {
lastWidth = width;
lastHeight = height;
if (percents[0] != -1.0f) {
x0 = (int)(percents[0] * width);
}
if (percents[1] != -1.0f) {
y0 = (int)(percents[1] * height);
}
if (percents[2] != -1.0f) {
x1 = (int)(percents[2] * width);
}
if (percents[3] != -1.0f) {
y1 = (int)(percents[3] * height);
}
}
return contains(x, y);
}
public boolean contains(int x, int y) {
return ((x >= x0 && x <= x1) &&
(y >= y0 && y <= y1));
}
}
/**
* Used to test for containment in a polygon region.
*/
static class PolygonRegionContainment extends Polygon implements
RegionContainment {
/** If any value is a percent there will be an entry here for the
* percent value. Use percentIndex to find out the index for it. */
float[] percentValues;
int[] percentIndexs;
/** Last value of width passed in. */
int lastWidth;
/** Last value of height passed in. */
int lastHeight;
public PolygonRegionContainment(AttributeSet as) {
int[] coords = Map.extractCoords(as.getAttribute(HTML.Attribute.
COORDS));
if (coords == null || coords.length == 0 ||
coords.length % 2 != 0) {
throw new RuntimeException("Unable to parse polygon area");
}
else {
int numPercents = 0;
lastWidth = lastHeight = -1;
for (int counter = coords.length - 1; counter >= 0;
counter--) {
if (coords[counter] < 0) {
numPercents++;
}
}
if (numPercents > 0) {
percentIndexs = new int[numPercents];
percentValues = new float[numPercents];
for (int counter = coords.length - 1, pCounter = 0;
counter >= 0; counter--) {
if (coords[counter] < 0) {
percentValues[pCounter] = coords[counter] /
-100.0f;
percentIndexs[pCounter] = counter;
pCounter++;
}
}
}
else {
percentIndexs = null;
percentValues = null;
}
npoints = coords.length / 2;
xpoints = new int[npoints];
ypoints = new int[npoints];
for (int counter = 0; counter < npoints; counter++) {
xpoints[counter] = coords[counter + counter];
ypoints[counter] = coords[counter + counter + 1];
}
}
}
public boolean contains(int x, int y, int width, int height) {
if (percentValues == null || (lastWidth == width &&
lastHeight == height)) {
return contains(x, y);
}
// Force the bounding box to be recalced.
bounds = null;
lastWidth = width;
lastHeight = height;
float fWidth = (float)width;
float fHeight = (float)height;
for (int counter = percentValues.length - 1; counter >= 0;
counter--) {
if (percentIndexs[counter] % 2 == 0) {
// x
xpoints[percentIndexs[counter] / 2] =
(int)(percentValues[counter] * fWidth);
}
else {
// y
ypoints[percentIndexs[counter] / 2] =
(int)(percentValues[counter] * fHeight);
}
}
return contains(x, y);
}
}
/**
* Used to test for containment in a circular region.
*/
static class CircleRegionContainment implements RegionContainment {
/** X origin of the circle. */
int x;
/** Y origin of the circle. */
int y;
/** Radius of the circle. */
int radiusSquared;
/** Non-null indicates one of the values represents a percent. */
float[] percentValues;
/** Last value of width passed in. */
int lastWidth;
/** Last value of height passed in. */
int lastHeight;
public CircleRegionContainment(AttributeSet as) {
int[] coords = Map.extractCoords(as.getAttribute(HTML.Attribute.
COORDS));
if (coords == null || coords.length != 3) {
throw new RuntimeException("Unable to parse circular area");
}
x = coords[0];
y = coords[1];
radiusSquared = coords[2] * coords[2];
if (coords[0] < 0 || coords[1] < 0 || coords[2] < 0) {
lastWidth = lastHeight = -1;
percentValues = new float[3];
for (int counter = 0; counter < 3; counter++) {
if (coords[counter] < 0) {
percentValues[counter] = coords[counter] /
-100.0f;
}
else {
percentValues[counter] = -1.0f;
}
}
}
else {
percentValues = null;
}
}
public boolean contains(int x, int y, int width, int height) {
if (percentValues != null && (lastWidth != width ||
lastHeight != height)) {
int newRad = Math.min(width, height) / 2;
lastWidth = width;
lastHeight = height;
if (percentValues[0] != -1.0f) {
this.x = (int)(percentValues[0] * width);
}
if (percentValues[1] != -1.0f) {
this.y = (int)(percentValues[1] * height);
}
if (percentValues[2] != -1.0f) {
radiusSquared = (int)(percentValues[2] *
Math.min(width, height));
radiusSquared *= radiusSquared;
}
}
return (((x - this.x) * (x - this.x) +
(y - this.y) * (y - this.y)) <= radiusSquared);
}
}
/**
* An implementation that will return true if the x, y location is
* inside a rectangle defined by origin 0, 0, and width equal to
* width passed in, and height equal to height passed in.
*/
static class DefaultRegionContainment implements RegionContainment {
/** A global shared instance. */
static DefaultRegionContainment si = null;
public static DefaultRegionContainment sharedInstance() {
if (si == null) {
si = new DefaultRegionContainment();
}
return si;
}
public boolean contains(int x, int y, int width, int height) {
return (x <= width && x >= 0 && y >= 0 && y <= width);
}
}
}

View File

@@ -0,0 +1,726 @@
/*
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.io.Writer;
import java.io.IOException;
import java.util.*;
import java.awt.Color;
import javax.swing.text.*;
/**
* MinimalHTMLWriter is a fallback writer used by the
* HTMLEditorKit to write out HTML for a document that
* is a not produced by the EditorKit.
*
* The format for the document is:
* <pre>
* &lt;html&gt;
* &lt;head&gt;
* &lt;style&gt;
* &lt;!-- list of named styles
* p.normal {
* font-family: SansSerif;
* margin-height: 0;
* font-size: 14
* }
* --&gt;
* &lt;/style&gt;
* &lt;/head&gt;
* &lt;body&gt;
* &lt;p style=normal&gt;
* <b>Bold, italic, and underline attributes
* of the run are emitted as HTML tags.
* The remaining attributes are emitted as
* part of the style attribute of a &lt;span&gt; tag.
* The syntax is similar to inline styles.</b>
* &lt;/p&gt;
* &lt;/body&gt;
* &lt;/html&gt;
* </pre>
*
* @author Sunita Mani
*/
public class MinimalHTMLWriter extends AbstractWriter {
/**
* These static finals are used to
* tweak and query the fontMask about which
* of these tags need to be generated or
* terminated.
*/
private static final int BOLD = 0x01;
private static final int ITALIC = 0x02;
private static final int UNDERLINE = 0x04;
// Used to map StyleConstants to CSS.
private static final CSS css = new CSS();
private int fontMask = 0;
int startOffset = 0;
int endOffset = 0;
/**
* Stores the attributes of the previous run.
* Used to compare with the current run's
* attributeset. If identical, then a
* &lt;span&gt; tag is not emitted.
*/
private AttributeSet fontAttributes;
/**
* Maps from style name as held by the Document, to the archived
* style name (style name written out). These may differ.
*/
private Hashtable<String, String> styleNameMapping;
/**
* Creates a new MinimalHTMLWriter.
*
* @param w Writer
* @param doc StyledDocument
*
*/
public MinimalHTMLWriter(Writer w, StyledDocument doc) {
super(w, doc);
}
/**
* Creates a new MinimalHTMLWriter.
*
* @param w Writer
* @param doc StyledDocument
* @param pos The location in the document to fetch the
* content.
* @param len The amount to write out.
*
*/
public MinimalHTMLWriter(Writer w, StyledDocument doc, int pos, int len) {
super(w, doc, pos, len);
}
/**
* Generates HTML output
* from a StyledDocument.
*
* @exception IOException on any I/O error
* @exception BadLocationException if pos represents an invalid
* location within the document.
*
*/
public void write() throws IOException, BadLocationException {
styleNameMapping = new Hashtable<String, String>();
writeStartTag("<html>");
writeHeader();
writeBody();
writeEndTag("</html>");
}
/**
* Writes out all the attributes for the
* following types:
* StyleConstants.ParagraphConstants,
* StyleConstants.CharacterConstants,
* StyleConstants.FontConstants,
* StyleConstants.ColorConstants.
* The attribute name and value are separated by a colon.
* Each pair is separated by a semicolon.
*
* @exception IOException on any I/O error
*/
protected void writeAttributes(AttributeSet attr) throws IOException {
Enumeration attributeNames = attr.getAttributeNames();
while (attributeNames.hasMoreElements()) {
Object name = attributeNames.nextElement();
if ((name instanceof StyleConstants.ParagraphConstants) ||
(name instanceof StyleConstants.CharacterConstants) ||
(name instanceof StyleConstants.FontConstants) ||
(name instanceof StyleConstants.ColorConstants)) {
indent();
write(name.toString());
write(':');
write(css.styleConstantsValueToCSSValue
((StyleConstants)name, attr.getAttribute(name)).
toString());
write(';');
write(NEWLINE);
}
}
}
/**
* Writes out text.
*
* @exception IOException on any I/O error
*/
protected void text(Element elem) throws IOException, BadLocationException {
String contentStr = getText(elem);
if ((contentStr.length() > 0) &&
(contentStr.charAt(contentStr.length()-1) == NEWLINE)) {
contentStr = contentStr.substring(0, contentStr.length()-1);
}
if (contentStr.length() > 0) {
write(contentStr);
}
}
/**
* Writes out a start tag appropriately
* indented. Also increments the indent level.
*
* @exception IOException on any I/O error
*/
protected void writeStartTag(String tag) throws IOException {
indent();
write(tag);
write(NEWLINE);
incrIndent();
}
/**
* Writes out an end tag appropriately
* indented. Also decrements the indent level.
*
* @exception IOException on any I/O error
*/
protected void writeEndTag(String endTag) throws IOException {
decrIndent();
indent();
write(endTag);
write(NEWLINE);
}
/**
* Writes out the &lt;head&gt; and &lt;style&gt;
* tags, and then invokes writeStyles() to write
* out all the named styles as the content of the
* &lt;style&gt; tag. The content is surrounded by
* valid HTML comment markers to ensure that the
* document is viewable in applications/browsers
* that do not support the tag.
*
* @exception IOException on any I/O error
*/
protected void writeHeader() throws IOException {
writeStartTag("<head>");
writeStartTag("<style>");
writeStartTag("<!--");
writeStyles();
writeEndTag("-->");
writeEndTag("</style>");
writeEndTag("</head>");
}
/**
* Writes out all the named styles as the
* content of the &lt;style&gt; tag.
*
* @exception IOException on any I/O error
*/
protected void writeStyles() throws IOException {
/*
* Access to DefaultStyledDocument done to workaround
* a missing API in styled document to access the
* stylenames.
*/
DefaultStyledDocument styledDoc = ((DefaultStyledDocument)getDocument());
Enumeration styleNames = styledDoc.getStyleNames();
while (styleNames.hasMoreElements()) {
Style s = styledDoc.getStyle((String)styleNames.nextElement());
/** PENDING: Once the name attribute is removed
from the list we check check for 0. **/
if (s.getAttributeCount() == 1 &&
s.isDefined(StyleConstants.NameAttribute)) {
continue;
}
indent();
write("p." + addStyleName(s.getName()));
write(" {\n");
incrIndent();
writeAttributes(s);
decrIndent();
indent();
write("}\n");
}
}
/**
* Iterates over the elements in the document
* and processes elements based on whether they are
* branch elements or leaf elements. This method specially handles
* leaf elements that are text.
*
* @exception IOException on any I/O error
*/
protected void writeBody() throws IOException, BadLocationException {
ElementIterator it = getElementIterator();
/*
This will be a section element for a styled document.
We represent this element in HTML as the body tags.
Therefore we ignore it.
*/
it.current();
Element next;
writeStartTag("<body>");
boolean inContent = false;
while((next = it.next()) != null) {
if (!inRange(next)) {
continue;
}
if (next instanceof AbstractDocument.BranchElement) {
if (inContent) {
writeEndParagraph();
inContent = false;
fontMask = 0;
}
writeStartParagraph(next);
} else if (isText(next)) {
writeContent(next, !inContent);
inContent = true;
} else {
writeLeaf(next);
inContent = true;
}
}
if (inContent) {
writeEndParagraph();
}
writeEndTag("</body>");
}
/**
* Emits an end tag for a &lt;p&gt;
* tag. Before writing out the tag, this method ensures
* that all other tags that have been opened are
* appropriately closed off.
*
* @exception IOException on any I/O error
*/
protected void writeEndParagraph() throws IOException {
writeEndMask(fontMask);
if (inFontTag()) {
endSpanTag();
} else {
write(NEWLINE);
}
writeEndTag("</p>");
}
/**
* Emits the start tag for a paragraph. If
* the paragraph has a named style associated with it,
* then this method also generates a class attribute for the
* &lt;p&gt; tag and sets its value to be the name of the
* style.
*
* @exception IOException on any I/O error
*/
protected void writeStartParagraph(Element elem) throws IOException {
AttributeSet attr = elem.getAttributes();
Object resolveAttr = attr.getAttribute(StyleConstants.ResolveAttribute);
if (resolveAttr instanceof StyleContext.NamedStyle) {
writeStartTag("<p class=" + mapStyleName(((StyleContext.NamedStyle)resolveAttr).getName()) + ">");
} else {
writeStartTag("<p>");
}
}
/**
* Responsible for writing out other non-text leaf
* elements.
*
* @exception IOException on any I/O error
*/
protected void writeLeaf(Element elem) throws IOException {
indent();
if (elem.getName() == StyleConstants.IconElementName) {
writeImage(elem);
} else if (elem.getName() == StyleConstants.ComponentElementName) {
writeComponent(elem);
}
}
/**
* Responsible for handling Icon Elements;
* deliberately unimplemented. How to implement this method is
* an issue of policy. For example, if you're generating
* an &lt;img&gt; tag, how should you
* represent the src attribute (the location of the image)?
* In certain cases it could be a URL, in others it could
* be read from a stream.
*
* @param elem element of type StyleConstants.IconElementName
*/
protected void writeImage(Element elem) throws IOException {
}
/**
* Responsible for handling Component Elements;
* deliberately unimplemented.
* How this method is implemented is a matter of policy.
*/
protected void writeComponent(Element elem) throws IOException {
}
/**
* Returns true if the element is a text element.
*
*/
protected boolean isText(Element elem) {
return (elem.getName() == AbstractDocument.ContentElementName);
}
/**
* Writes out the attribute set
* in an HTML-compliant manner.
*
* @exception IOException on any I/O error
* @exception BadLocationException if pos represents an invalid
* location within the document.
*/
protected void writeContent(Element elem, boolean needsIndenting)
throws IOException, BadLocationException {
AttributeSet attr = elem.getAttributes();
writeNonHTMLAttributes(attr);
if (needsIndenting) {
indent();
}
writeHTMLTags(attr);
text(elem);
}
/**
* Generates
* bold &lt;b&gt;, italic &lt;i&gt;, and &lt;u&gt; tags for the
* text based on its attribute settings.
*
* @exception IOException on any I/O error
*/
protected void writeHTMLTags(AttributeSet attr) throws IOException {
int oldMask = fontMask;
setFontMask(attr);
int endMask = 0;
int startMask = 0;
if ((oldMask & BOLD) != 0) {
if ((fontMask & BOLD) == 0) {
endMask |= BOLD;
}
} else if ((fontMask & BOLD) != 0) {
startMask |= BOLD;
}
if ((oldMask & ITALIC) != 0) {
if ((fontMask & ITALIC) == 0) {
endMask |= ITALIC;
}
} else if ((fontMask & ITALIC) != 0) {
startMask |= ITALIC;
}
if ((oldMask & UNDERLINE) != 0) {
if ((fontMask & UNDERLINE) == 0) {
endMask |= UNDERLINE;
}
} else if ((fontMask & UNDERLINE) != 0) {
startMask |= UNDERLINE;
}
writeEndMask(endMask);
writeStartMask(startMask);
}
/**
* Tweaks the appropriate bits of fontMask
* to reflect whether the text is to be displayed in
* bold, italic, and/or with an underline.
*
*/
private void setFontMask(AttributeSet attr) {
if (StyleConstants.isBold(attr)) {
fontMask |= BOLD;
}
if (StyleConstants.isItalic(attr)) {
fontMask |= ITALIC;
}
if (StyleConstants.isUnderline(attr)) {
fontMask |= UNDERLINE;
}
}
/**
* Writes out start tags &lt;u&gt;, &lt;i&gt;, and &lt;b&gt; based on
* the mask settings.
*
* @exception IOException on any I/O error
*/
private void writeStartMask(int mask) throws IOException {
if (mask != 0) {
if ((mask & UNDERLINE) != 0) {
write("<u>");
}
if ((mask & ITALIC) != 0) {
write("<i>");
}
if ((mask & BOLD) != 0) {
write("<b>");
}
}
}
/**
* Writes out end tags for &lt;u&gt;, &lt;i&gt;, and &lt;b&gt; based on
* the mask settings.
*
* @exception IOException on any I/O error
*/
private void writeEndMask(int mask) throws IOException {
if (mask != 0) {
if ((mask & BOLD) != 0) {
write("</b>");
}
if ((mask & ITALIC) != 0) {
write("</i>");
}
if ((mask & UNDERLINE) != 0) {
write("</u>");
}
}
}
/**
* Writes out the remaining
* character-level attributes (attributes other than bold,
* italic, and underline) in an HTML-compliant way. Given that
* attributes such as font family and font size have no direct
* mapping to HTML tags, a &lt;span&gt; tag is generated and its
* style attribute is set to contain the list of remaining
* attributes just like inline styles.
*
* @exception IOException on any I/O error
*/
protected void writeNonHTMLAttributes(AttributeSet attr) throws IOException {
String style = "";
String separator = "; ";
if (inFontTag() && fontAttributes.isEqual(attr)) {
return;
}
boolean first = true;
Color color = (Color)attr.getAttribute(StyleConstants.Foreground);
if (color != null) {
style += "color: " + css.styleConstantsValueToCSSValue
((StyleConstants)StyleConstants.Foreground,
color);
first = false;
}
Integer size = (Integer)attr.getAttribute(StyleConstants.FontSize);
if (size != null) {
if (!first) {
style += separator;
}
style += "font-size: " + size.intValue() + "pt";
first = false;
}
String family = (String)attr.getAttribute(StyleConstants.FontFamily);
if (family != null) {
if (!first) {
style += separator;
}
style += "font-family: " + family;
first = false;
}
if (style.length() > 0) {
if (fontMask != 0) {
writeEndMask(fontMask);
fontMask = 0;
}
startSpanTag(style);
fontAttributes = attr;
}
else if (fontAttributes != null) {
writeEndMask(fontMask);
fontMask = 0;
endSpanTag();
}
}
/**
* Returns true if we are currently in a &lt;font&gt; tag.
*/
protected boolean inFontTag() {
return (fontAttributes != null);
}
/**
* This is no longer used, instead &lt;span&gt; will be written out.
* <p>
* Writes out an end tag for the &lt;font&gt; tag.
*
* @exception IOException on any I/O error
*/
protected void endFontTag() throws IOException {
write(NEWLINE);
writeEndTag("</font>");
fontAttributes = null;
}
/**
* This is no longer used, instead &lt;span&gt; will be written out.
* <p>
* Writes out a start tag for the &lt;font&gt; tag.
* Because font tags cannot be nested,
* this method closes out
* any enclosing font tag before writing out a
* new start tag.
*
* @exception IOException on any I/O error
*/
protected void startFontTag(String style) throws IOException {
boolean callIndent = false;
if (inFontTag()) {
endFontTag();
callIndent = true;
}
writeStartTag("<font style=\"" + style + "\">");
if (callIndent) {
indent();
}
}
/**
* Writes out a start tag for the &lt;font&gt; tag.
* Because font tags cannot be nested,
* this method closes out
* any enclosing font tag before writing out a
* new start tag.
*
* @exception IOException on any I/O error
*/
private void startSpanTag(String style) throws IOException {
boolean callIndent = false;
if (inFontTag()) {
endSpanTag();
callIndent = true;
}
writeStartTag("<span style=\"" + style + "\">");
if (callIndent) {
indent();
}
}
/**
* Writes out an end tag for the &lt;span&gt; tag.
*
* @exception IOException on any I/O error
*/
private void endSpanTag() throws IOException {
write(NEWLINE);
writeEndTag("</span>");
fontAttributes = null;
}
/**
* Adds the style named <code>style</code> to the style mapping. This
* returns the name that should be used when outputting. CSS does not
* allow the full Unicode set to be used as a style name.
*/
private String addStyleName(String style) {
if (styleNameMapping == null) {
return style;
}
StringBuilder sb = null;
for (int counter = style.length() - 1; counter >= 0; counter--) {
if (!isValidCharacter(style.charAt(counter))) {
if (sb == null) {
sb = new StringBuilder(style);
}
sb.setCharAt(counter, 'a');
}
}
String mappedName = (sb != null) ? sb.toString() : style;
while (styleNameMapping.get(mappedName) != null) {
mappedName = mappedName + 'x';
}
styleNameMapping.put(style, mappedName);
return mappedName;
}
/**
* Returns the mapped style name corresponding to <code>style</code>.
*/
private String mapStyleName(String style) {
if (styleNameMapping == null) {
return style;
}
String retValue = styleNameMapping.get(style);
return (retValue == null) ? style : retValue;
}
private boolean isValidCharacter(char character) {
return ((character >= 'a' && character <= 'z') ||
(character >= 'A' && character <= 'Z'));
}
}

View File

@@ -0,0 +1,311 @@
/*
* Copyright (c) 2001, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import javax.swing.text.*;
import java.io.Serializable;
import java.util.*;
/**
* An implementation of <code>AttributeSet</code> that can multiplex
* across a set of <code>AttributeSet</code>s.
*
*/
class MuxingAttributeSet implements AttributeSet, Serializable {
/**
* Creates a <code>MuxingAttributeSet</code> with the passed in
* attributes.
*/
public MuxingAttributeSet(AttributeSet[] attrs) {
this.attrs = attrs;
}
/**
* Creates an empty <code>MuxingAttributeSet</code>. This is intended for
* use by subclasses only, and it is also intended that subclasses will
* set the constituent <code>AttributeSet</code>s before invoking any
* of the <code>AttributeSet</code> methods.
*/
protected MuxingAttributeSet() {
}
/**
* Directly sets the <code>AttributeSet</code>s that comprise this
* <code>MuxingAttributeSet</code>.
*/
protected synchronized void setAttributes(AttributeSet[] attrs) {
this.attrs = attrs;
}
/**
* Returns the <code>AttributeSet</code>s multiplexing too. When the
* <code>AttributeSet</code>s need to be referenced, this should be called.
*/
protected synchronized AttributeSet[] getAttributes() {
return attrs;
}
/**
* Inserts <code>as</code> at <code>index</code>. This assumes
* the value of <code>index</code> is between 0 and attrs.length,
* inclusive.
*/
protected synchronized void insertAttributeSetAt(AttributeSet as,
int index) {
int numAttrs = attrs.length;
AttributeSet newAttrs[] = new AttributeSet[numAttrs + 1];
if (index < numAttrs) {
if (index > 0) {
System.arraycopy(attrs, 0, newAttrs, 0, index);
System.arraycopy(attrs, index, newAttrs, index + 1,
numAttrs - index);
}
else {
System.arraycopy(attrs, 0, newAttrs, 1, numAttrs);
}
}
else {
System.arraycopy(attrs, 0, newAttrs, 0, numAttrs);
}
newAttrs[index] = as;
attrs = newAttrs;
}
/**
* Removes the AttributeSet at <code>index</code>. This assumes
* the value of <code>index</code> is greater than or equal to 0,
* and less than attrs.length.
*/
protected synchronized void removeAttributeSetAt(int index) {
int numAttrs = attrs.length;
AttributeSet[] newAttrs = new AttributeSet[numAttrs - 1];
if (numAttrs > 0) {
if (index == 0) {
// FIRST
System.arraycopy(attrs, 1, newAttrs, 0, numAttrs - 1);
}
else if (index < (numAttrs - 1)) {
// MIDDLE
System.arraycopy(attrs, 0, newAttrs, 0, index);
System.arraycopy(attrs, index + 1, newAttrs, index,
numAttrs - index - 1);
}
else {
// END
System.arraycopy(attrs, 0, newAttrs, 0, numAttrs - 1);
}
}
attrs = newAttrs;
}
// --- AttributeSet methods ----------------------------
/**
* Gets the number of attributes that are defined.
*
* @return the number of attributes
* @see AttributeSet#getAttributeCount
*/
public int getAttributeCount() {
AttributeSet[] as = getAttributes();
int n = 0;
for (int i = 0; i < as.length; i++) {
n += as[i].getAttributeCount();
}
return n;
}
/**
* Checks whether a given attribute is defined.
* This will convert the key over to CSS if the
* key is a StyleConstants key that has a CSS
* mapping.
*
* @param key the attribute key
* @return true if the attribute is defined
* @see AttributeSet#isDefined
*/
public boolean isDefined(Object key) {
AttributeSet[] as = getAttributes();
for (int i = 0; i < as.length; i++) {
if (as[i].isDefined(key)) {
return true;
}
}
return false;
}
/**
* Checks whether two attribute sets are equal.
*
* @param attr the attribute set to check against
* @return true if the same
* @see AttributeSet#isEqual
*/
public boolean isEqual(AttributeSet attr) {
return ((getAttributeCount() == attr.getAttributeCount()) &&
containsAttributes(attr));
}
/**
* Copies a set of attributes.
*
* @return the copy
* @see AttributeSet#copyAttributes
*/
public AttributeSet copyAttributes() {
AttributeSet[] as = getAttributes();
MutableAttributeSet a = new SimpleAttributeSet();
int n = 0;
for (int i = as.length - 1; i >= 0; i--) {
a.addAttributes(as[i]);
}
return a;
}
/**
* Gets the value of an attribute. If the requested
* attribute is a StyleConstants attribute that has
* a CSS mapping, the request will be converted.
*
* @param key the attribute name
* @return the attribute value
* @see AttributeSet#getAttribute
*/
public Object getAttribute(Object key) {
AttributeSet[] as = getAttributes();
int n = as.length;
for (int i = 0; i < n; i++) {
Object o = as[i].getAttribute(key);
if (o != null) {
return o;
}
}
return null;
}
/**
* Gets the names of all attributes.
*
* @return the attribute names
* @see AttributeSet#getAttributeNames
*/
public Enumeration getAttributeNames() {
return new MuxingAttributeNameEnumeration();
}
/**
* Checks whether a given attribute name/value is defined.
*
* @param name the attribute name
* @param value the attribute value
* @return true if the name/value is defined
* @see AttributeSet#containsAttribute
*/
public boolean containsAttribute(Object name, Object value) {
return value.equals(getAttribute(name));
}
/**
* Checks whether the attribute set contains all of
* the given attributes.
*
* @param attrs the attributes to check
* @return true if the element contains all the attributes
* @see AttributeSet#containsAttributes
*/
public boolean containsAttributes(AttributeSet attrs) {
boolean result = true;
Enumeration names = attrs.getAttributeNames();
while (result && names.hasMoreElements()) {
Object name = names.nextElement();
result = attrs.getAttribute(name).equals(getAttribute(name));
}
return result;
}
/**
* Returns null, subclasses may wish to do something more
* intelligent with this.
*/
public AttributeSet getResolveParent() {
return null;
}
/**
* The <code>AttributeSet</code>s that make up the resulting
* <code>AttributeSet</code>.
*/
private AttributeSet[] attrs;
/**
* An Enumeration of the Attribute names in a MuxingAttributeSet.
* This may return the same name more than once.
*/
private class MuxingAttributeNameEnumeration implements Enumeration {
MuxingAttributeNameEnumeration() {
updateEnum();
}
public boolean hasMoreElements() {
if (currentEnum == null) {
return false;
}
return currentEnum.hasMoreElements();
}
public Object nextElement() {
if (currentEnum == null) {
throw new NoSuchElementException("No more names");
}
Object retObject = currentEnum.nextElement();
if (!currentEnum.hasMoreElements()) {
updateEnum();
}
return retObject;
}
void updateEnum() {
AttributeSet[] as = getAttributes();
currentEnum = null;
while (currentEnum == null && attrIndex < as.length) {
currentEnum = as[attrIndex++].getAttributeNames();
if (!currentEnum.hasMoreElements()) {
currentEnum = null;
}
}
}
/** Index into attrs the current Enumeration came from. */
private int attrIndex;
/** Enumeration from attrs. */
private Enumeration currentEnum;
}
}

View File

@@ -0,0 +1,173 @@
/*
* Copyright (c) 1998, 2000, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import javax.swing.text.*;
import java.awt.*;
/**
* This is the view associated with the html tag NOFRAMES.
* This view has been written to ignore the contents of the
* NOFRAMES tag. The contents of the tag will only be visible
* when the JTextComponent the view is contained in is editable.
*
* @author Sunita Mani
*/
class NoFramesView extends BlockView {
/**
* Creates a new view that represents an
* html box. This can be used for a number
* of elements. By default this view is not
* visible.
*
* @param elem the element to create a view for
* @param axis either View.X_AXIS or View.Y_AXIS
*/
public NoFramesView(Element elem, int axis) {
super(elem, axis);
visible = false;
}
/**
* If this view is not visible, then it returns.
* Otherwise it invokes the superclass.
*
* @param g the rendering surface to use
* @param allocation the allocated region to render into
* @see #isVisible
* @see text.ParagraphView#paint
*/
public void paint(Graphics g, Shape allocation) {
Container host = getContainer();
if (host != null &&
visible != ((JTextComponent)host).isEditable()) {
visible = ((JTextComponent)host).isEditable();
}
if (!isVisible()) {
return;
}
super.paint(g, allocation);
}
/**
* Determines if the JTextComponent that the view
* is contained in is editable. If so, then this
* view and all its child views are visible.
* Once this has been determined, the superclass
* is invoked to continue processing.
*
* @param p the parent View.
* @see BlockView#setParent
*/
public void setParent(View p) {
if (p != null) {
Container host = p.getContainer();
if (host != null) {
visible = ((JTextComponent)host).isEditable();
}
}
super.setParent(p);
}
/**
* Returns a true/false value that represents
* whether the view is visible or not.
*/
public boolean isVisible() {
return visible;
}
/**
* Do nothing if the view is not visible, otherwise
* invoke the superclass to perform layout.
*/
protected void layout(int width, int height) {
if (!isVisible()) {
return;
}
super.layout(width, height);
}
/**
* Determines the preferred span for this view. Returns
* 0 if the view is not visible, otherwise it calls the
* superclass method to get the preferred span.
* axis.
*
* @param axis may be either View.X_AXIS or View.Y_AXIS
* @return the span the view would like to be rendered into;
* typically the view is told to render into the span
* that is returned, although there is no guarantee;
* the parent may choose to resize or break the view
* @see text.ParagraphView#getPreferredSpan
*/
public float getPreferredSpan(int axis) {
if (!visible) {
return 0;
}
return super.getPreferredSpan(axis);
}
/**
* Determines the minimum span for this view along an
* axis. Returns 0 if the view is not visible, otherwise
* it calls the superclass method to get the minimum span.
*
* @param axis may be either <code>View.X_AXIS</code> or
* <code>View.Y_AXIS</code>
* @return the minimum span the view can be rendered into
* @see text.ParagraphView#getMinimumSpan
*/
public float getMinimumSpan(int axis) {
if (!visible) {
return 0;
}
return super.getMinimumSpan(axis);
}
/**
* Determines the maximum span for this view along an
* axis. Returns 0 if the view is not visible, otherwise
* it calls the superclass method ot get the maximum span.
*
* @param axis may be either <code>View.X_AXIS</code> or
* <code>View.Y_AXIS</code>
* @return the maximum span the view can be rendered into
* @see text.ParagraphView#getMaximumSpan
*/
public float getMaximumSpan(int axis) {
if (!visible) {
return 0;
}
return super.getMaximumSpan(axis);
}
boolean visible;
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.util.Enumeration;
import java.awt.*;
import javax.swing.*;
import javax.swing.text.*;
import java.beans.*;
import java.lang.reflect.*;
import sun.reflect.misc.MethodUtil;
import sun.reflect.misc.ReflectUtil;
/**
* Component decorator that implements the view interface
* for &lt;object&gt; elements.
* <p>
* This view will try to load the class specified by the
* <code>classid</code> attribute. If possible, the Classloader
* used to load the associated Document is used.
* This would typically be the same as the ClassLoader
* used to load the EditorKit. If the document's
* ClassLoader is null, <code>Class.forName</code> is used.
* <p>
* If the class can successfully be loaded, an attempt will
* be made to create an instance of it by calling
* <code>Class.newInstance</code>. An attempt will be made
* to narrow the instance to type <code>java.awt.Component</code>
* to display the object.
* <p>
* This view can also manage a set of parameters with limitations.
* The parameters to the &lt;object&gt; element are expected to
* be present on the associated elements attribute set as simple
* strings. Each bean property will be queried as a key on
* the AttributeSet, with the expectation that a non-null value
* (of type String) will be present if there was a parameter
* specification for the property. Reflection is used to
* set the parameter. Currently, this is limited to a very
* simple single parameter of type String.
* <p>
* A simple example HTML invocation is:
* <pre>
* &lt;object classid="javax.swing.JLabel"&gt;
* &lt;param name="text" value="sample text"&gt;
* &lt;/object&gt;
* </pre>
*
* @author Timothy Prinzing
*/
public class ObjectView extends ComponentView {
private boolean createComp = true; // default
/**
* Creates a new ObjectView object.
*
* @param elem the element to decorate
*/
public ObjectView(Element elem) {
super(elem);
}
ObjectView(Element elem, boolean createComp) {
super(elem);
this.createComp = createComp;
}
/**
* Create the component. The classid is used
* as a specification of the classname, which
* we try to load.
*/
protected Component createComponent() {
if (!createComp) {
return getUnloadableRepresentation();
}
AttributeSet attr = getElement().getAttributes();
String classname = (String) attr.getAttribute(HTML.Attribute.CLASSID);
try {
ReflectUtil.checkPackageAccess(classname);
Class c = Class.forName(classname, false,Thread.currentThread().
getContextClassLoader());
if (Component.class.isAssignableFrom(c)) {
Object o = c.newInstance();
if (o instanceof Component) {
Component comp = (Component) o;
setParameters(comp, attr);
return comp;
}
}
} catch (Throwable e) {
// couldn't create a component... fall through to the
// couldn't load representation.
}
return getUnloadableRepresentation();
}
/**
* Fetch a component that can be used to represent the
* object if it can't be created.
*/
Component getUnloadableRepresentation() {
// PENDING(prinz) get some artwork and return something
// interesting here.
Component comp = new JLabel("??");
comp.setForeground(Color.red);
return comp;
}
/**
* Initialize this component according the KEY/VALUEs passed in
* via the &lt;param&gt; elements in the corresponding
* &lt;object&gt; element.
*/
private void setParameters(Component comp, AttributeSet attr) {
Class k = comp.getClass();
BeanInfo bi;
try {
bi = Introspector.getBeanInfo(k);
} catch (IntrospectionException ex) {
System.err.println("introspector failed, ex: "+ex);
return; // quit for now
}
PropertyDescriptor props[] = bi.getPropertyDescriptors();
for (int i=0; i < props.length; i++) {
// System.err.println("checking on props[i]: "+props[i].getName());
Object v = attr.getAttribute(props[i].getName());
if (v instanceof String) {
// found a property parameter
String value = (String) v;
Method writer = props[i].getWriteMethod();
if (writer == null) {
// read-only property. ignore
return; // for now
}
Class[] params = writer.getParameterTypes();
if (params.length != 1) {
// zero or more than one argument, ignore
return; // for now
}
Object [] args = { value };
try {
MethodUtil.invoke(writer, comp, args);
} catch (Exception ex) {
System.err.println("Invocation failed");
// invocation code
}
}
}
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.io.Serializable;
import javax.swing.text.*;
/**
* Value for the ListModel used to represent
* &lt;option&gt; elements. This is the object
* installed as items of the DefaultComboBoxModel
* used to represent the &lt;select&gt; element.
* <p>
* <strong>Warning:</strong>
* Serialized objects of this class will not be compatible with
* future Swing releases. The current serialization support is
* appropriate for short term storage or RMI between applications running
* the same version of Swing. As of 1.4, support for long term storage
* of all JavaBeans&trade;
* has been added to the <code>java.beans</code> package.
* Please see {@link java.beans.XMLEncoder}.
*
* @author Timothy Prinzing
*/
public class Option implements Serializable {
/**
* Creates a new Option object.
*
* @param attr the attributes associated with the
* option element. The attributes are copied to
* ensure they won't change.
*/
public Option(AttributeSet attr) {
this.attr = attr.copyAttributes();
selected = (attr.getAttribute(HTML.Attribute.SELECTED) != null);
}
/**
* Sets the label to be used for the option.
*/
public void setLabel(String label) {
this.label = label;
}
/**
* Fetch the label associated with the option.
*/
public String getLabel() {
return label;
}
/**
* Fetch the attributes associated with this option.
*/
public AttributeSet getAttributes() {
return attr;
}
/**
* String representation is the label.
*/
public String toString() {
return label;
}
/**
* Sets the selected state.
*/
protected void setSelection(boolean state) {
selected = state;
}
/**
* Fetches the selection state associated with this option.
*/
public boolean isSelected() {
return selected;
}
/**
* Convenience method to return the string associated
* with the <code>value</code> attribute. If the
* value has not been specified, the label will be
* returned.
*/
public String getValue() {
String value = (String) attr.getAttribute(HTML.Attribute.VALUE);
if (value == null) {
value = label;
}
return value;
}
private boolean selected;
private String label;
private AttributeSet attr;
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import javax.swing.*;
import java.io.Serializable;
/**
* OptionComboBoxModel extends the capabilities of the DefaultComboBoxModel,
* to store the Option that is initially marked as selected.
* This is stored, in order to enable an accurate reset of the
* ComboBox that represents the SELECT form element when the
* user requests a clear/reset. Given that a combobox only allow
* for one item to be selected, the last OPTION that has the
* attribute set wins.
*
@author Sunita Mani
*/
class OptionComboBoxModel<E> extends DefaultComboBoxModel<E> implements Serializable {
private Option selectedOption = null;
/**
* Stores the Option that has been marked its
* selected attribute set.
*/
public void setInitialSelection(Option option) {
selectedOption = option;
}
/**
* Fetches the Option item that represents that was
* initially set to a selected state.
*/
public Option getInitialSelection() {
return selectedOption;
}
}

View File

@@ -0,0 +1,569 @@
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import javax.swing.*;
import javax.swing.event.*;
import java.util.BitSet;
import java.io.Serializable;
/**
* This class extends DefaultListModel, and also implements
* the ListSelectionModel interface, allowing for it to store state
* relevant to a SELECT form element which is implemented as a List.
* If SELECT has a size attribute whose value is greater than 1,
* or if allows multiple selection then a JList is used to
* represent it and the OptionListModel is used as its model.
* It also stores the initial state of the JList, to ensure an
* accurate reset, if the user requests a reset of the form.
*
@author Sunita Mani
*/
class OptionListModel<E> extends DefaultListModel<E> implements ListSelectionModel, Serializable {
private static final int MIN = -1;
private static final int MAX = Integer.MAX_VALUE;
private int selectionMode = SINGLE_SELECTION;
private int minIndex = MAX;
private int maxIndex = MIN;
private int anchorIndex = -1;
private int leadIndex = -1;
private int firstChangedIndex = MAX;
private int lastChangedIndex = MIN;
private boolean isAdjusting = false;
private BitSet value = new BitSet(32);
private BitSet initialValue = new BitSet(32);
protected EventListenerList listenerList = new EventListenerList();
protected boolean leadAnchorNotificationEnabled = true;
public int getMinSelectionIndex() { return isSelectionEmpty() ? -1 : minIndex; }
public int getMaxSelectionIndex() { return maxIndex; }
public boolean getValueIsAdjusting() { return isAdjusting; }
public int getSelectionMode() { return selectionMode; }
public void setSelectionMode(int selectionMode) {
switch (selectionMode) {
case SINGLE_SELECTION:
case SINGLE_INTERVAL_SELECTION:
case MULTIPLE_INTERVAL_SELECTION:
this.selectionMode = selectionMode;
break;
default:
throw new IllegalArgumentException("invalid selectionMode");
}
}
public boolean isSelectedIndex(int index) {
return ((index < minIndex) || (index > maxIndex)) ? false : value.get(index);
}
public boolean isSelectionEmpty() {
return (minIndex > maxIndex);
}
public void addListSelectionListener(ListSelectionListener l) {
listenerList.add(ListSelectionListener.class, l);
}
public void removeListSelectionListener(ListSelectionListener l) {
listenerList.remove(ListSelectionListener.class, l);
}
/**
* Returns an array of all the <code>ListSelectionListener</code>s added
* to this OptionListModel with addListSelectionListener().
*
* @return all of the <code>ListSelectionListener</code>s added or an empty
* array if no listeners have been added
* @since 1.4
*/
public ListSelectionListener[] getListSelectionListeners() {
return listenerList.getListeners(ListSelectionListener.class);
}
/**
* Notify listeners that we are beginning or ending a
* series of value changes
*/
protected void fireValueChanged(boolean isAdjusting) {
fireValueChanged(getMinSelectionIndex(), getMaxSelectionIndex(), isAdjusting);
}
/**
* Notify ListSelectionListeners that the value of the selection,
* in the closed interval firstIndex,lastIndex, has changed.
*/
protected void fireValueChanged(int firstIndex, int lastIndex) {
fireValueChanged(firstIndex, lastIndex, getValueIsAdjusting());
}
/**
* @param firstIndex The first index in the interval.
* @param lastIndex The last index in the interval.
* @param isAdjusting True if this is the final change in a series of them.
* @see EventListenerList
*/
protected void fireValueChanged(int firstIndex, int lastIndex, boolean isAdjusting)
{
Object[] listeners = listenerList.getListenerList();
ListSelectionEvent e = null;
for (int i = listeners.length - 2; i >= 0; i -= 2) {
if (listeners[i] == ListSelectionListener.class) {
if (e == null) {
e = new ListSelectionEvent(this, firstIndex, lastIndex, isAdjusting);
}
((ListSelectionListener)listeners[i+1]).valueChanged(e);
}
}
}
private void fireValueChanged() {
if (lastChangedIndex == MIN) {
return;
}
/* Change the values before sending the event to the
* listeners in case the event causes a listener to make
* another change to the selection.
*/
int oldFirstChangedIndex = firstChangedIndex;
int oldLastChangedIndex = lastChangedIndex;
firstChangedIndex = MAX;
lastChangedIndex = MIN;
fireValueChanged(oldFirstChangedIndex, oldLastChangedIndex);
}
// Update first and last change indices
private void markAsDirty(int r) {
firstChangedIndex = Math.min(firstChangedIndex, r);
lastChangedIndex = Math.max(lastChangedIndex, r);
}
// Set the state at this index and update all relevant state.
private void set(int r) {
if (value.get(r)) {
return;
}
value.set(r);
Option option = (Option)get(r);
option.setSelection(true);
markAsDirty(r);
// Update minimum and maximum indices
minIndex = Math.min(minIndex, r);
maxIndex = Math.max(maxIndex, r);
}
// Clear the state at this index and update all relevant state.
private void clear(int r) {
if (!value.get(r)) {
return;
}
value.clear(r);
Option option = (Option)get(r);
option.setSelection(false);
markAsDirty(r);
// Update minimum and maximum indices
/*
If (r > minIndex) the minimum has not changed.
The case (r < minIndex) is not possible because r'th value was set.
We only need to check for the case when lowest entry has been cleared,
and in this case we need to search for the first value set above it.
*/
if (r == minIndex) {
for(minIndex = minIndex + 1; minIndex <= maxIndex; minIndex++) {
if (value.get(minIndex)) {
break;
}
}
}
/*
If (r < maxIndex) the maximum has not changed.
The case (r > maxIndex) is not possible because r'th value was set.
We only need to check for the case when highest entry has been cleared,
and in this case we need to search for the first value set below it.
*/
if (r == maxIndex) {
for(maxIndex = maxIndex - 1; minIndex <= maxIndex; maxIndex--) {
if (value.get(maxIndex)) {
break;
}
}
}
/* Performance note: This method is called from inside a loop in
changeSelection() but we will only iterate in the loops
above on the basis of one iteration per deselected cell - in total.
Ie. the next time this method is called the work of the previous
deselection will not be repeated.
We also don't need to worry about the case when the min and max
values are in their unassigned states. This cannot happen because
this method's initial check ensures that the selection was not empty
and therefore that the minIndex and maxIndex had 'real' values.
If we have cleared the whole selection, set the minIndex and maxIndex
to their cannonical values so that the next set command always works
just by using Math.min and Math.max.
*/
if (isSelectionEmpty()) {
minIndex = MAX;
maxIndex = MIN;
}
}
/**
* Sets the value of the leadAnchorNotificationEnabled flag.
* @see #isLeadAnchorNotificationEnabled()
*/
public void setLeadAnchorNotificationEnabled(boolean flag) {
leadAnchorNotificationEnabled = flag;
}
/**
* Returns the value of the leadAnchorNotificationEnabled flag.
* When leadAnchorNotificationEnabled is true the model
* generates notification events with bounds that cover all the changes to
* the selection plus the changes to the lead and anchor indices.
* Setting the flag to false causes a norrowing of the event's bounds to
* include only the elements that have been selected or deselected since
* the last change. Either way, the model continues to maintain the lead
* and anchor variables internally. The default is true.
* @return the value of the leadAnchorNotificationEnabled flag
* @see #setLeadAnchorNotificationEnabled(boolean)
*/
public boolean isLeadAnchorNotificationEnabled() {
return leadAnchorNotificationEnabled;
}
private void updateLeadAnchorIndices(int anchorIndex, int leadIndex) {
if (leadAnchorNotificationEnabled) {
if (this.anchorIndex != anchorIndex) {
if (this.anchorIndex != -1) { // The unassigned state.
markAsDirty(this.anchorIndex);
}
markAsDirty(anchorIndex);
}
if (this.leadIndex != leadIndex) {
if (this.leadIndex != -1) { // The unassigned state.
markAsDirty(this.leadIndex);
}
markAsDirty(leadIndex);
}
}
this.anchorIndex = anchorIndex;
this.leadIndex = leadIndex;
}
private boolean contains(int a, int b, int i) {
return (i >= a) && (i <= b);
}
private void changeSelection(int clearMin, int clearMax,
int setMin, int setMax, boolean clearFirst) {
for(int i = Math.min(setMin, clearMin); i <= Math.max(setMax, clearMax); i++) {
boolean shouldClear = contains(clearMin, clearMax, i);
boolean shouldSet = contains(setMin, setMax, i);
if (shouldSet && shouldClear) {
if (clearFirst) {
shouldClear = false;
}
else {
shouldSet = false;
}
}
if (shouldSet) {
set(i);
}
if (shouldClear) {
clear(i);
}
}
fireValueChanged();
}
/* Change the selection with the effect of first clearing the values
* in the inclusive range [clearMin, clearMax] then setting the values
* in the inclusive range [setMin, setMax]. Do this in one pass so
* that no values are cleared if they would later be set.
*/
private void changeSelection(int clearMin, int clearMax, int setMin, int setMax) {
changeSelection(clearMin, clearMax, setMin, setMax, true);
}
public void clearSelection() {
removeSelectionInterval(minIndex, maxIndex);
}
public void setSelectionInterval(int index0, int index1) {
if (index0 == -1 || index1 == -1) {
return;
}
if (getSelectionMode() == SINGLE_SELECTION) {
index0 = index1;
}
updateLeadAnchorIndices(index0, index1);
int clearMin = minIndex;
int clearMax = maxIndex;
int setMin = Math.min(index0, index1);
int setMax = Math.max(index0, index1);
changeSelection(clearMin, clearMax, setMin, setMax);
}
public void addSelectionInterval(int index0, int index1)
{
if (index0 == -1 || index1 == -1) {
return;
}
if (getSelectionMode() != MULTIPLE_INTERVAL_SELECTION) {
setSelectionInterval(index0, index1);
return;
}
updateLeadAnchorIndices(index0, index1);
int clearMin = MAX;
int clearMax = MIN;
int setMin = Math.min(index0, index1);
int setMax = Math.max(index0, index1);
changeSelection(clearMin, clearMax, setMin, setMax);
}
public void removeSelectionInterval(int index0, int index1)
{
if (index0 == -1 || index1 == -1) {
return;
}
updateLeadAnchorIndices(index0, index1);
int clearMin = Math.min(index0, index1);
int clearMax = Math.max(index0, index1);
int setMin = MAX;
int setMax = MIN;
changeSelection(clearMin, clearMax, setMin, setMax);
}
private void setState(int index, boolean state) {
if (state) {
set(index);
}
else {
clear(index);
}
}
/**
* Insert length indices beginning before/after index. If the value
* at index is itself selected, set all of the newly inserted
* items, otherwise leave them unselected. This method is typically
* called to sync the selection model with a corresponding change
* in the data model.
*/
public void insertIndexInterval(int index, int length, boolean before)
{
/* The first new index will appear at insMinIndex and the last
* one will appear at insMaxIndex
*/
int insMinIndex = (before) ? index : index + 1;
int insMaxIndex = (insMinIndex + length) - 1;
/* Right shift the entire bitset by length, beginning with
* index-1 if before is true, index+1 if it's false (i.e. with
* insMinIndex).
*/
for(int i = maxIndex; i >= insMinIndex; i--) {
setState(i + length, value.get(i));
}
/* Initialize the newly inserted indices.
*/
boolean setInsertedValues = value.get(index);
for(int i = insMinIndex; i <= insMaxIndex; i++) {
setState(i, setInsertedValues);
}
}
/**
* Remove the indices in the interval index0,index1 (inclusive) from
* the selection model. This is typically called to sync the selection
* model width a corresponding change in the data model. Note
* that (as always) index0 can be greater than index1.
*/
public void removeIndexInterval(int index0, int index1)
{
int rmMinIndex = Math.min(index0, index1);
int rmMaxIndex = Math.max(index0, index1);
int gapLength = (rmMaxIndex - rmMinIndex) + 1;
/* Shift the entire bitset to the left to close the index0, index1
* gap.
*/
for(int i = rmMinIndex; i <= maxIndex; i++) {
setState(i, value.get(i + gapLength));
}
}
public void setValueIsAdjusting(boolean isAdjusting) {
if (isAdjusting != this.isAdjusting) {
this.isAdjusting = isAdjusting;
this.fireValueChanged(isAdjusting);
}
}
public String toString() {
String s = ((getValueIsAdjusting()) ? "~" : "=") + value.toString();
return getClass().getName() + " " + Integer.toString(hashCode()) + " " + s;
}
/**
* Returns a clone of the receiver with the same selection.
* <code>listenerLists</code> are not duplicated.
*
* @return a clone of the receiver
* @exception CloneNotSupportedException if the receiver does not
* both (a) implement the <code>Cloneable</code> interface
* and (b) define a <code>clone</code> method
*/
public Object clone() throws CloneNotSupportedException {
OptionListModel clone = (OptionListModel)super.clone();
clone.value = (BitSet)value.clone();
clone.listenerList = new EventListenerList();
return clone;
}
public int getAnchorSelectionIndex() {
return anchorIndex;
}
public int getLeadSelectionIndex() {
return leadIndex;
}
/**
* Set the anchor selection index, leaving all selection values unchanged.
*
* @see #getAnchorSelectionIndex
* @see #setLeadSelectionIndex
*/
public void setAnchorSelectionIndex(int anchorIndex) {
this.anchorIndex = anchorIndex;
}
/**
* Set the lead selection index, ensuring that values between the
* anchor and the new lead are either all selected or all deselected.
* If the value at the anchor index is selected, first clear all the
* values in the range [anchor, oldLeadIndex], then select all the values
* values in the range [anchor, newLeadIndex], where oldLeadIndex is the old
* leadIndex and newLeadIndex is the new one.
* <p>
* If the value at the anchor index is not selected, do the same thing in reverse,
* selecting values in the old range and deselecting values in the new one.
* <p>
* Generate a single event for this change and notify all listeners.
* For the purposes of generating minimal bounds in this event, do the
* operation in a single pass; that way the first and last index inside the
* ListSelectionEvent that is broadcast will refer to cells that actually
* changed value because of this method. If, instead, this operation were
* done in two steps the effect on the selection state would be the same
* but two events would be generated and the bounds around the changed values
* would be wider, including cells that had been first cleared and only
* to later be set.
* <p>
* This method can be used in the mouseDragged() method of a UI class
* to extend a selection.
*
* @see #getLeadSelectionIndex
* @see #setAnchorSelectionIndex
*/
public void setLeadSelectionIndex(int leadIndex) {
int anchorIndex = this.anchorIndex;
if (getSelectionMode() == SINGLE_SELECTION) {
anchorIndex = leadIndex;
}
int oldMin = Math.min(this.anchorIndex, this.leadIndex);
int oldMax = Math.max(this.anchorIndex, this.leadIndex);
int newMin = Math.min(anchorIndex, leadIndex);
int newMax = Math.max(anchorIndex, leadIndex);
if (value.get(this.anchorIndex)) {
changeSelection(oldMin, oldMax, newMin, newMax);
}
else {
changeSelection(newMin, newMax, oldMin, oldMax, false);
}
this.anchorIndex = anchorIndex;
this.leadIndex = leadIndex;
}
/**
* This method is responsible for storing the state
* of the initial selection. If the selectionMode
* is the default, i.e allowing only for SINGLE_SELECTION,
* then the very last OPTION that has the selected
* attribute set wins.
*/
public void setInitialSelection(int i) {
if (initialValue.get(i)) {
return;
}
if (selectionMode == SINGLE_SELECTION) {
// reset to empty
initialValue.and(new BitSet());
}
initialValue.set(i);
}
/**
* Fetches the BitSet that represents the initial
* set of selected items in the list.
*/
public BitSet getInitialSelection() {
return initialValue;
}
}

View File

@@ -0,0 +1,294 @@
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import java.awt.*;
import javax.swing.SizeRequirements;
import javax.swing.event.DocumentEvent;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.AttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.View;
import javax.swing.text.ViewFactory;
import javax.swing.text.BadLocationException;
import javax.swing.text.JTextComponent;
/**
* Displays the a paragraph, and uses css attributes for its
* configuration.
*
* @author Timothy Prinzing
*/
public class ParagraphView extends javax.swing.text.ParagraphView {
/**
* Constructs a ParagraphView for the given element.
*
* @param elem the element that this view is responsible for
*/
public ParagraphView(Element elem) {
super(elem);
}
/**
* Establishes the parent view for this view. This is
* guaranteed to be called before any other methods if the
* parent view is functioning properly.
* <p>
* This is implemented
* to forward to the superclass as well as call the
* {@link #setPropertiesFromAttributes setPropertiesFromAttributes}
* method to set the paragraph properties from the css
* attributes. The call is made at this time to ensure
* the ability to resolve upward through the parents
* view attributes.
*
* @param parent the new parent, or null if the view is
* being removed from a parent it was previously added
* to
*/
public void setParent(View parent) {
super.setParent(parent);
if (parent != null) {
setPropertiesFromAttributes();
}
}
/**
* Fetches the attributes to use when rendering. This is
* implemented to multiplex the attributes specified in the
* model with a StyleSheet.
*/
public AttributeSet getAttributes() {
if (attr == null) {
StyleSheet sheet = getStyleSheet();
attr = sheet.getViewAttributes(this);
}
return attr;
}
/**
* Sets up the paragraph from css attributes instead of
* the values found in StyleConstants (i.e. which are used
* by the superclass). Since
*/
protected void setPropertiesFromAttributes() {
StyleSheet sheet = getStyleSheet();
attr = sheet.getViewAttributes(this);
painter = sheet.getBoxPainter(attr);
if (attr != null) {
super.setPropertiesFromAttributes();
setInsets((short) painter.getInset(TOP, this),
(short) painter.getInset(LEFT, this),
(short) painter.getInset(BOTTOM, this),
(short) painter.getInset(RIGHT, this));
Object o = attr.getAttribute(CSS.Attribute.TEXT_ALIGN);
if (o != null) {
// set horizontal alignment
String ta = o.toString();
if (ta.equals("left")) {
setJustification(StyleConstants.ALIGN_LEFT);
} else if (ta.equals("center")) {
setJustification(StyleConstants.ALIGN_CENTER);
} else if (ta.equals("right")) {
setJustification(StyleConstants.ALIGN_RIGHT);
} else if (ta.equals("justify")) {
setJustification(StyleConstants.ALIGN_JUSTIFIED);
}
}
// Get the width/height
cssWidth = (CSS.LengthValue)attr.getAttribute(
CSS.Attribute.WIDTH);
cssHeight = (CSS.LengthValue)attr.getAttribute(
CSS.Attribute.HEIGHT);
}
}
protected StyleSheet getStyleSheet() {
HTMLDocument doc = (HTMLDocument) getDocument();
return doc.getStyleSheet();
}
/**
* Calculate the needs for the paragraph along the minor axis.
*
* <p>If size requirements are explicitly specified for the paragraph,
* use that requirements. Otherwise, use the requirements of the
* superclass {@link javax.swing.text.ParagraphView}.</p>
*
* <p>If the {@code axis} parameter is neither {@code View.X_AXIS} nor
* {@code View.Y_AXIS}, {@link IllegalArgumentException} is thrown. If the
* {@code r} parameter is {@code null,} a new {@code SizeRequirements}
* object is created, otherwise the supplied {@code SizeRequirements}
* object is returned.</p>
*
* @param axis the minor axis
* @param r the input {@code SizeRequirements} object
* @return the new or adjusted {@code SizeRequirements} object
* @throws IllegalArgumentException if the {@code axis} parameter is invalid
*/
protected SizeRequirements calculateMinorAxisRequirements(
int axis, SizeRequirements r) {
r = super.calculateMinorAxisRequirements(axis, r);
if (BlockView.spanSetFromAttributes(axis, r, cssWidth, cssHeight)) {
// Offset by the margins so that pref/min/max return the
// right value.
int margin = (axis == X_AXIS) ? getLeftInset() + getRightInset() :
getTopInset() + getBottomInset();
r.minimum -= margin;
r.preferred -= margin;
r.maximum -= margin;
}
return r;
}
/**
* Indicates whether or not this view should be
* displayed. If none of the children wish to be
* displayed and the only visible child is the
* break that ends the paragraph, the paragraph
* will not be considered visible. Otherwise,
* it will be considered visible and return true.
*
* @return true if the paragraph should be displayed
*/
public boolean isVisible() {
int n = getLayoutViewCount() - 1;
for (int i = 0; i < n; i++) {
View v = getLayoutView(i);
if (v.isVisible()) {
return true;
}
}
if (n > 0) {
View v = getLayoutView(n);
if ((v.getEndOffset() - v.getStartOffset()) == 1) {
return false;
}
}
// If it's the last paragraph and not editable, it shouldn't
// be visible.
if (getStartOffset() == getDocument().getLength()) {
boolean editable = false;
Component c = getContainer();
if (c instanceof JTextComponent) {
editable = ((JTextComponent)c).isEditable();
}
if (!editable) {
return false;
}
}
return true;
}
/**
* Renders using the given rendering surface and area on that
* surface. This is implemented to delegate to the superclass
* after stashing the base coordinate for tab calculations.
*
* @param g the rendering surface to use
* @param a the allocated region to render into
* @see View#paint
*/
public void paint(Graphics g, Shape a) {
if (a == null) {
return;
}
Rectangle r;
if (a instanceof Rectangle) {
r = (Rectangle) a;
} else {
r = a.getBounds();
}
painter.paint(g, r.x, r.y, r.width, r.height, this);
super.paint(g, a);
}
/**
* Determines the preferred span for this view. Returns
* 0 if the view is not visible, otherwise it calls the
* superclass method to get the preferred span.
* axis.
*
* @param axis may be either View.X_AXIS or View.Y_AXIS
* @return the span the view would like to be rendered into;
* typically the view is told to render into the span
* that is returned, although there is no guarantee;
* the parent may choose to resize or break the view
* @see javax.swing.text.ParagraphView#getPreferredSpan
*/
public float getPreferredSpan(int axis) {
if (!isVisible()) {
return 0;
}
return super.getPreferredSpan(axis);
}
/**
* Determines the minimum span for this view along an
* axis. Returns 0 if the view is not visible, otherwise
* it calls the superclass method to get the minimum span.
*
* @param axis may be either <code>View.X_AXIS</code> or
* <code>View.Y_AXIS</code>
* @return the minimum span the view can be rendered into
* @see javax.swing.text.ParagraphView#getMinimumSpan
*/
public float getMinimumSpan(int axis) {
if (!isVisible()) {
return 0;
}
return super.getMinimumSpan(axis);
}
/**
* Determines the maximum span for this view along an
* axis. Returns 0 if the view is not visible, otherwise
* it calls the superclass method ot get the maximum span.
*
* @param axis may be either <code>View.X_AXIS</code> or
* <code>View.Y_AXIS</code>
* @return the maximum span the view can be rendered into
* @see javax.swing.text.ParagraphView#getMaximumSpan
*/
public float getMaximumSpan(int axis) {
if (!isVisible()) {
return 0;
}
return super.getMaximumSpan(axis);
}
private AttributeSet attr;
private StyleSheet.BoxPainter painter;
private CSS.LengthValue cssWidth;
private CSS.LengthValue cssHeight;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
/*
* Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html;
import javax.swing.text.*;
/**
* TextAreaDocument extends the capabilities of the PlainDocument
* to store the data that is initially set in the Document.
* This is stored in order to enable an accurate reset of the
* state when a reset is requested.
*
* @author Sunita Mani
*/
class TextAreaDocument extends PlainDocument {
String initialText;
/**
* Resets the model by removing all the data,
* and restoring it to its initial state.
*/
void reset() {
try {
remove(0, getLength());
if (initialText != null) {
insertString(0, initialText, null);
}
} catch (BadLocationException e) {
}
}
/**
* Stores the data that the model is initially
* loaded with.
*/
void storeInitialText() {
try {
initialText = getText(0, getLength());
} catch (BadLocationException e) {
}
}
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html.parser;
import java.util.Vector;
import java.util.Hashtable;
import java.util.Enumeration;
import java.io.*;
/**
* This class defines the attributes of an SGML element
* as described in a DTD using the ATTLIST construct.
* An AttributeList can be obtained from the Element
* class using the getAttributes() method.
* <p>
* It is actually an element in a linked list. Use the
* getNext() method repeatedly to enumerate all the attributes
* of an element.
*
* @see Element
* @author Arthur Van Hoff
*
*/
public final
class AttributeList implements DTDConstants, Serializable {
public String name;
public int type;
public Vector<?> values;
public int modifier;
public String value;
public AttributeList next;
AttributeList() {
}
/**
* Create an attribute list element.
*/
public AttributeList(String name) {
this.name = name;
}
/**
* Create an attribute list element.
*/
public AttributeList(String name, int type, int modifier, String value, Vector<?> values, AttributeList next) {
this.name = name;
this.type = type;
this.modifier = modifier;
this.value = value;
this.values = values;
this.next = next;
}
/**
* @return attribute name
*/
public String getName() {
return name;
}
/**
* @return attribute type
* @see DTDConstants
*/
public int getType() {
return type;
}
/**
* @return attribute modifier
* @see DTDConstants
*/
public int getModifier() {
return modifier;
}
/**
* @return possible attribute values
*/
public Enumeration<?> getValues() {
return (values != null) ? values.elements() : null;
}
/**
* @return default attribute value
*/
public String getValue() {
return value;
}
/**
* @return the next attribute in the list
*/
public AttributeList getNext() {
return next;
}
/**
* @return string representation
*/
public String toString() {
return name;
}
/**
* Create a hashtable of attribute types.
*/
static Hashtable<Object, Object> attributeTypes = new Hashtable<Object, Object>();
static void defineAttributeType(String nm, int val) {
Integer num = Integer.valueOf(val);
attributeTypes.put(nm, num);
attributeTypes.put(num, nm);
}
static {
defineAttributeType("CDATA", CDATA);
defineAttributeType("ENTITY", ENTITY);
defineAttributeType("ENTITIES", ENTITIES);
defineAttributeType("ID", ID);
defineAttributeType("IDREF", IDREF);
defineAttributeType("IDREFS", IDREFS);
defineAttributeType("NAME", NAME);
defineAttributeType("NAMES", NAMES);
defineAttributeType("NMTOKEN", NMTOKEN);
defineAttributeType("NMTOKENS", NMTOKENS);
defineAttributeType("NOTATION", NOTATION);
defineAttributeType("NUMBER", NUMBER);
defineAttributeType("NUMBERS", NUMBERS);
defineAttributeType("NUTOKEN", NUTOKEN);
defineAttributeType("NUTOKENS", NUTOKENS);
attributeTypes.put("fixed", Integer.valueOf(FIXED));
attributeTypes.put("required", Integer.valueOf(REQUIRED));
attributeTypes.put("current", Integer.valueOf(CURRENT));
attributeTypes.put("conref", Integer.valueOf(CONREF));
attributeTypes.put("implied", Integer.valueOf(IMPLIED));
}
public static int name2type(String nm) {
Integer i = (Integer)attributeTypes.get(nm);
return (i == null) ? CDATA : i.intValue();
}
public static String type2name(int tp) {
return (String)attributeTypes.get(Integer.valueOf(tp));
}
}

View File

@@ -0,0 +1,254 @@
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html.parser;
import java.util.Vector;
import java.util.Enumeration;
import java.io.*;
/**
* A representation of a content model. A content model is
* basically a restricted BNF expression. It is restricted in
* the sense that it must be deterministic. This means that you
* don't have to represent it as a finite state automaton.<p>
* See Annex H on page 556 of the SGML handbook for more information.
*
* @author Arthur van Hoff
*
*/
public final class ContentModel implements Serializable {
/**
* Type. Either '*', '?', '+', ',', '|', '&amp;'.
*/
public int type;
/**
* The content. Either an Element or a ContentModel.
*/
public Object content;
/**
* The next content model (in a ',', '|' or '&amp;' expression).
*/
public ContentModel next;
public ContentModel() {
}
/**
* Create a content model for an element.
*/
public ContentModel(Element content) {
this(0, content, null);
}
/**
* Create a content model of a particular type.
*/
public ContentModel(int type, ContentModel content) {
this(type, content, null);
}
/**
* Create a content model of a particular type.
*/
public ContentModel(int type, Object content, ContentModel next) {
this.type = type;
this.content = content;
this.next = next;
}
/**
* Return true if the content model could
* match an empty input stream.
*/
public boolean empty() {
switch (type) {
case '*':
case '?':
return true;
case '+':
case '|':
for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
if (m.empty()) {
return true;
}
}
return false;
case ',':
case '&':
for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
if (!m.empty()) {
return false;
}
}
return true;
default:
return false;
}
}
/**
* Update elemVec with the list of elements that are
* part of the this contentModel.
*/
public void getElements(Vector<Element> elemVec) {
switch (type) {
case '*':
case '?':
case '+':
((ContentModel)content).getElements(elemVec);
break;
case ',':
case '|':
case '&':
for (ContentModel m=(ContentModel)content; m != null; m=m.next){
m.getElements(elemVec);
}
break;
default:
elemVec.addElement((Element)content);
}
}
private boolean valSet[];
private boolean val[];
// A cache used by first(). This cache was found to speed parsing
// by about 10% (based on measurements of the 4-12 code base after
// buffering was fixed).
/**
* Return true if the token could potentially be the
* first token in the input stream.
*/
public boolean first(Object token) {
switch (type) {
case '*':
case '?':
case '+':
return ((ContentModel)content).first(token);
case ',':
for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
if (m.first(token)) {
return true;
}
if (!m.empty()) {
return false;
}
}
return false;
case '|':
case '&': {
Element e = (Element) token;
if (valSet == null || valSet.length <= Element.getMaxIndex()) {
valSet = new boolean[Element.getMaxIndex() + 1];
val = new boolean[valSet.length];
}
if (valSet[e.index]) {
return val[e.index];
}
for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
if (m.first(token)) {
val[e.index] = true;
break;
}
}
valSet[e.index] = true;
return val[e.index];
}
default:
return (content == token);
// PENDING: refer to comment in ContentModelState
/*
if (content == token) {
return true;
}
Element e = (Element)content;
if (e.omitStart() && e.content != null) {
return e.content.first(token);
}
return false;
*/
}
}
/**
* Return the element that must be next.
*/
public Element first() {
switch (type) {
case '&':
case '|':
case '*':
case '?':
return null;
case '+':
case ',':
return ((ContentModel)content).first();
default:
return (Element)content;
}
}
/**
* Convert to a string.
*/
public String toString() {
switch (type) {
case '*':
return content + "*";
case '?':
return content + "?";
case '+':
return content + "+";
case ',':
case '|':
case '&':
char data[] = {' ', (char)type, ' '};
String str = "";
for (ContentModel m = (ContentModel)content ; m != null ; m = m.next) {
str = str + m;
if (m.next != null) {
str += new String(data);
}
}
return "(" + str + ")";
default:
return content.toString();
}
}
}

View File

@@ -0,0 +1,295 @@
/*
* Copyright (c) 1998, 2000, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html.parser;
/**
* A content model state. This is basically a list of pointers to
* the BNF expression representing the model (the ContentModel).
* Each element in a DTD has a content model which describes the
* elements that may occur inside, and the order in which they can
* occur.
* <p>
* Each time a token is reduced a new state is created.
* <p>
* See Annex H on page 556 of the SGML handbook for more information.
*
* @see Parser
* @see DTD
* @see Element
* @see ContentModel
* @author Arthur van Hoff
*/
class ContentModelState {
ContentModel model;
long value;
ContentModelState next;
/**
* Create a content model state for a content model.
*/
public ContentModelState(ContentModel model) {
this(model, null, 0);
}
/**
* Create a content model state for a content model given the
* remaining state that needs to be reduce.
*/
ContentModelState(Object content, ContentModelState next) {
this(content, next, 0);
}
/**
* Create a content model state for a content model given the
* remaining state that needs to be reduce.
*/
ContentModelState(Object content, ContentModelState next, long value) {
this.model = (ContentModel)content;
this.next = next;
this.value = value;
}
/**
* Return the content model that is relevant to the current state.
*/
public ContentModel getModel() {
ContentModel m = model;
for (int i = 0; i < value; i++) {
if (m.next != null) {
m = m.next;
} else {
return null;
}
}
return m;
}
/**
* Check if the state can be terminated. That is there are no more
* tokens required in the input stream.
* @return true if the model can terminate without further input
*/
public boolean terminate() {
switch (model.type) {
case '+':
if ((value == 0) && !(model).empty()) {
return false;
}
case '*':
case '?':
return (next == null) || next.terminate();
case '|':
for (ContentModel m = (ContentModel)model.content ; m != null ; m = m.next) {
if (m.empty()) {
return (next == null) || next.terminate();
}
}
return false;
case '&': {
ContentModel m = (ContentModel)model.content;
for (int i = 0 ; m != null ; i++, m = m.next) {
if ((value & (1L << i)) == 0) {
if (!m.empty()) {
return false;
}
}
}
return (next == null) || next.terminate();
}
case ',': {
ContentModel m = (ContentModel)model.content;
for (int i = 0 ; i < value ; i++, m = m.next);
for (; (m != null) && m.empty() ; m = m.next);
if (m != null) {
return false;
}
return (next == null) || next.terminate();
}
default:
return false;
}
}
/**
* Check if the state can be terminated. That is there are no more
* tokens required in the input stream.
* @return the only possible element that can occur next
*/
public Element first() {
switch (model.type) {
case '*':
case '?':
case '|':
case '&':
return null;
case '+':
return model.first();
case ',': {
ContentModel m = (ContentModel)model.content;
for (int i = 0 ; i < value ; i++, m = m.next);
return m.first();
}
default:
return model.first();
}
}
/**
* Advance this state to a new state. An exception is thrown if the
* token is illegal at this point in the content model.
* @return next state after reducing a token
*/
public ContentModelState advance(Object token) {
switch (model.type) {
case '+':
if (model.first(token)) {
return new ContentModelState(model.content,
new ContentModelState(model, next, value + 1)).advance(token);
}
if (value != 0) {
if (next != null) {
return next.advance(token);
} else {
return null;
}
}
break;
case '*':
if (model.first(token)) {
return new ContentModelState(model.content, this).advance(token);
}
if (next != null) {
return next.advance(token);
} else {
return null;
}
case '?':
if (model.first(token)) {
return new ContentModelState(model.content, next).advance(token);
}
if (next != null) {
return next.advance(token);
} else {
return null;
}
case '|':
for (ContentModel m = (ContentModel)model.content ; m != null ; m = m.next) {
if (m.first(token)) {
return new ContentModelState(m, next).advance(token);
}
}
break;
case ',': {
ContentModel m = (ContentModel)model.content;
for (int i = 0 ; i < value ; i++, m = m.next);
if (m.first(token) || m.empty()) {
if (m.next == null) {
return new ContentModelState(m, next).advance(token);
} else {
return new ContentModelState(m,
new ContentModelState(model, next, value + 1)).advance(token);
}
}
break;
}
case '&': {
ContentModel m = (ContentModel)model.content;
boolean complete = true;
for (int i = 0 ; m != null ; i++, m = m.next) {
if ((value & (1L << i)) == 0) {
if (m.first(token)) {
return new ContentModelState(m,
new ContentModelState(model, next, value | (1L << i))).advance(token);
}
if (!m.empty()) {
complete = false;
}
}
}
if (complete) {
if (next != null) {
return next.advance(token);
} else {
return null;
}
}
break;
}
default:
if (model.content == token) {
if (next == null && (token instanceof Element) &&
((Element)token).content != null) {
return new ContentModelState(((Element)token).content);
}
return next;
}
// PENDING: Currently we don't correctly deal with optional start
// tags. This can most notably be seen with the 4.01 spec where
// TBODY's start and end tags are optional.
// Uncommenting this and the PENDING in ContentModel will
// correctly skip the omit tags, but the delegate is not notified.
// Some additional API needs to be added to track skipped tags,
// and this can then be added back.
/*
if ((model.content instanceof Element)) {
Element e = (Element)model.content;
if (e.omitStart() && e.content != null) {
return new ContentModelState(e.content, next).advance(
token);
}
}
*/
}
// We used to throw this exception at this point. However, it
// was determined that throwing this exception was more expensive
// than returning null, and we could not justify to ourselves why
// it was necessary to throw an exception, rather than simply
// returning null. I'm leaving it in a commented out state so
// that it can be easily restored if the situation ever arises.
//
// throw new IllegalArgumentException("invalid token: " + token);
return null;
}
}

View File

@@ -0,0 +1,466 @@
/*
* Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html.parser;
import sun.awt.AppContext;
import java.io.PrintStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.FileNotFoundException;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.util.Hashtable;
import java.util.Vector;
import java.util.BitSet;
import java.util.StringTokenizer;
import java.util.Enumeration;
import java.util.Properties;
import java.net.URL;
/**
* The representation of an SGML DTD. DTD describes a document
* syntax and is used in parsing of HTML documents. It contains
* a list of elements and their attributes as well as a list of
* entities defined in the DTD.
*
* @see Element
* @see AttributeList
* @see ContentModel
* @see Parser
* @author Arthur van Hoff
*/
public
class DTD implements DTDConstants {
public String name;
public Vector<Element> elements = new Vector<Element>();
public Hashtable<String,Element> elementHash
= new Hashtable<String,Element>();
public Hashtable<Object,Entity> entityHash
= new Hashtable<Object,Entity>();
public final Element pcdata = getElement("#pcdata");
public final Element html = getElement("html");
public final Element meta = getElement("meta");
public final Element base = getElement("base");
public final Element isindex = getElement("isindex");
public final Element head = getElement("head");
public final Element body = getElement("body");
public final Element applet = getElement("applet");
public final Element param = getElement("param");
public final Element p = getElement("p");
public final Element title = getElement("title");
final Element style = getElement("style");
final Element link = getElement("link");
final Element script = getElement("script");
public static final int FILE_VERSION = 1;
/**
* Creates a new DTD with the specified name.
* @param name the name, as a <code>String</code> of the new DTD
*/
protected DTD(String name) {
this.name = name;
defEntity("#RE", GENERAL, '\r');
defEntity("#RS", GENERAL, '\n');
defEntity("#SPACE", GENERAL, ' ');
defineElement("unknown", EMPTY, false, true, null, null, null, null);
}
/**
* Gets the name of the DTD.
* @return the name of the DTD
*/
public String getName() {
return name;
}
/**
* Gets an entity by name.
* @return the <code>Entity</code> corresponding to the
* <code>name</code> <code>String</code>
*/
public Entity getEntity(String name) {
return entityHash.get(name);
}
/**
* Gets a character entity.
* @return the <code>Entity</code> corresponding to the
* <code>ch</code> character
*/
public Entity getEntity(int ch) {
return entityHash.get(Integer.valueOf(ch));
}
/**
* Returns <code>true</code> if the element is part of the DTD,
* otherwise returns <code>false</code>.
*
* @param name the requested <code>String</code>
* @return <code>true</code> if <code>name</code> exists as
* part of the DTD, otherwise returns <code>false</code>
*/
boolean elementExists(String name) {
return !"unknown".equals(name) && (elementHash.get(name) != null);
}
/**
* Gets an element by name. A new element is
* created if the element doesn't exist.
*
* @param name the requested <code>String</code>
* @return the <code>Element</code> corresponding to
* <code>name</code>, which may be newly created
*/
public Element getElement(String name) {
Element e = elementHash.get(name);
if (e == null) {
e = new Element(name, elements.size());
elements.addElement(e);
elementHash.put(name, e);
}
return e;
}
/**
* Gets an element by index.
*
* @param index the requested index
* @return the <code>Element</code> corresponding to
* <code>index</code>
*/
public Element getElement(int index) {
return elements.elementAt(index);
}
/**
* Defines an entity. If the <code>Entity</code> specified
* by <code>name</code>, <code>type</code>, and <code>data</code>
* exists, it is returned; otherwise a new <code>Entity</code>
* is created and is returned.
*
* @param name the name of the <code>Entity</code> as a <code>String</code>
* @param type the type of the <code>Entity</code>
* @param data the <code>Entity</code>'s data
* @return the <code>Entity</code> requested or a new <code>Entity</code>
* if not found
*/
public Entity defineEntity(String name, int type, char data[]) {
Entity ent = entityHash.get(name);
if (ent == null) {
ent = new Entity(name, type, data);
entityHash.put(name, ent);
if (((type & GENERAL) != 0) && (data.length == 1)) {
switch (type & ~GENERAL) {
case CDATA:
case SDATA:
entityHash.put(Integer.valueOf(data[0]), ent);
break;
}
}
}
return ent;
}
/**
* Returns the <code>Element</code> which matches the
* specified parameters. If one doesn't exist, a new
* one is created and returned.
*
* @param name the name of the <code>Element</code>
* @param type the type of the <code>Element</code>
* @param omitStart <code>true</code> if start should be omitted
* @param omitEnd <code>true</code> if end should be omitted
* @param content the <code>ContentModel</code>
* @param atts the <code>AttributeList</code> specifying the
* <code>Element</code>
* @return the <code>Element</code> specified
*/
public Element defineElement(String name, int type,
boolean omitStart, boolean omitEnd, ContentModel content,
BitSet exclusions, BitSet inclusions, AttributeList atts) {
Element e = getElement(name);
e.type = type;
e.oStart = omitStart;
e.oEnd = omitEnd;
e.content = content;
e.exclusions = exclusions;
e.inclusions = inclusions;
e.atts = atts;
return e;
}
/**
* Defines attributes for an {@code Element}.
*
* @param name the name of the <code>Element</code>
* @param atts the <code>AttributeList</code> specifying the
* <code>Element</code>
*/
public void defineAttributes(String name, AttributeList atts) {
Element e = getElement(name);
e.atts = atts;
}
/**
* Creates and returns a character <code>Entity</code>.
* @param name the entity's name
* @return the new character <code>Entity</code>
*/
public Entity defEntity(String name, int type, int ch) {
char data[] = {(char)ch};
return defineEntity(name, type, data);
}
/**
* Creates and returns an <code>Entity</code>.
* @param name the entity's name
* @return the new <code>Entity</code>
*/
protected Entity defEntity(String name, int type, String str) {
int len = str.length();
char data[] = new char[len];
str.getChars(0, len, data, 0);
return defineEntity(name, type, data);
}
/**
* Creates and returns an <code>Element</code>.
* @param name the element's name
* @return the new <code>Element</code>
*/
protected Element defElement(String name, int type,
boolean omitStart, boolean omitEnd, ContentModel content,
String[] exclusions, String[] inclusions, AttributeList atts) {
BitSet excl = null;
if (exclusions != null && exclusions.length > 0) {
excl = new BitSet();
for (String str : exclusions) {
if (str.length() > 0) {
excl.set(getElement(str).getIndex());
}
}
}
BitSet incl = null;
if (inclusions != null && inclusions.length > 0) {
incl = new BitSet();
for (String str : inclusions) {
if (str.length() > 0) {
incl.set(getElement(str).getIndex());
}
}
}
return defineElement(name, type, omitStart, omitEnd, content, excl, incl, atts);
}
/**
* Creates and returns an <code>AttributeList</code>.
* @param name the attribute list's name
* @return the new <code>AttributeList</code>
*/
protected AttributeList defAttributeList(String name, int type, int modifier, String value, String values, AttributeList atts) {
Vector<String> vals = null;
if (values != null) {
vals = new Vector<String>();
for (StringTokenizer s = new StringTokenizer(values, "|") ; s.hasMoreTokens() ;) {
String str = s.nextToken();
if (str.length() > 0) {
vals.addElement(str);
}
}
}
return new AttributeList(name, type, modifier, value, vals, atts);
}
/**
* Creates and returns a new content model.
* @param type the type of the new content model
* @return the new <code>ContentModel</code>
*/
protected ContentModel defContentModel(int type, Object obj, ContentModel next) {
return new ContentModel(type, obj, next);
}
/**
* Returns a string representation of this DTD.
* @return the string representation of this DTD
*/
public String toString() {
return name;
}
/**
* The hashtable key of DTDs in AppContext.
*/
private static final Object DTD_HASH_KEY = new Object();
public static void putDTDHash(String name, DTD dtd) {
getDtdHash().put(name, dtd);
}
/**
* Returns a DTD with the specified <code>name</code>. If
* a DTD with that name doesn't exist, one is created
* and returned. Any uppercase characters in the name
* are converted to lowercase.
*
* @param name the name of the DTD
* @return the DTD which corresponds to <code>name</code>
*/
public static DTD getDTD(String name) throws IOException {
name = name.toLowerCase();
DTD dtd = getDtdHash().get(name);
if (dtd == null)
dtd = new DTD(name);
return dtd;
}
private static Hashtable<String, DTD> getDtdHash() {
AppContext appContext = AppContext.getAppContext();
Hashtable<String, DTD> result = (Hashtable<String, DTD>) appContext.get(DTD_HASH_KEY);
if (result == null) {
result = new Hashtable<String, DTD>();
appContext.put(DTD_HASH_KEY, result);
}
return result;
}
/**
* Recreates a DTD from an archived format.
* @param in the <code>DataInputStream</code> to read from
*/
public void read(DataInputStream in) throws IOException {
if (in.readInt() != FILE_VERSION) {
}
//
// Read the list of names
//
String[] names = new String[in.readShort()];
for (int i = 0; i < names.length; i++) {
names[i] = in.readUTF();
}
//
// Read the entities
//
int num = in.readShort();
for (int i = 0; i < num; i++) {
short nameId = in.readShort();
int type = in.readByte();
String name = in.readUTF();
defEntity(names[nameId], type | GENERAL, name);
}
// Read the elements
//
num = in.readShort();
for (int i = 0; i < num; i++) {
short nameId = in.readShort();
int type = in.readByte();
byte flags = in.readByte();
ContentModel m = readContentModel(in, names);
String[] exclusions = readNameArray(in, names);
String[] inclusions = readNameArray(in, names);
AttributeList atts = readAttributeList(in, names);
defElement(names[nameId], type,
((flags & 0x01) != 0), ((flags & 0x02) != 0),
m, exclusions, inclusions, atts);
}
}
private ContentModel readContentModel(DataInputStream in, String[] names)
throws IOException {
byte flag = in.readByte();
switch(flag) {
case 0: // null
return null;
case 1: { // content_c
int type = in.readByte();
ContentModel m = readContentModel(in, names);
ContentModel next = readContentModel(in, names);
return defContentModel(type, m, next);
}
case 2: { // content_e
int type = in.readByte();
Element el = getElement(names[in.readShort()]);
ContentModel next = readContentModel(in, names);
return defContentModel(type, el, next);
}
default:
throw new IOException("bad bdtd");
}
}
private String[] readNameArray(DataInputStream in, String[] names)
throws IOException {
int num = in.readShort();
if (num == 0) {
return null;
}
String[] result = new String[num];
for (int i = 0; i < num; i++) {
result[i] = names[in.readShort()];
}
return result;
}
private AttributeList readAttributeList(DataInputStream in, String[] names)
throws IOException {
AttributeList result = null;
for (int num = in.readByte(); num > 0; --num) {
short nameId = in.readShort();
int type = in.readByte();
int modifier = in.readByte();
short valueId = in.readShort();
String value = (valueId == -1) ? null : names[valueId];
Vector<String> values = null;
short numValues = in.readShort();
if (numValues > 0) {
values = new Vector<String>(numValues);
for (int i = 0; i < numValues; i++) {
values.addElement(names[in.readShort()]);
}
}
result = new AttributeList(names[nameId], type, modifier, value,
values, result);
// We reverse the order of the linked list by doing this, but
// that order isn't important.
}
return result;
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) 1998, 1999, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html.parser;
/**
* SGML constants used in a DTD. The names of the
* constants correspond the the equivalent SGML constructs
* as described in "The SGML Handbook" by Charles F. Goldfarb.
*
* @see DTD
* @see Element
* @author Arthur van Hoff
*/
public
interface DTDConstants {
// Attribute value types
int CDATA = 1;
int ENTITY = 2;
int ENTITIES = 3;
int ID = 4;
int IDREF = 5;
int IDREFS = 6;
int NAME = 7;
int NAMES = 8;
int NMTOKEN = 9;
int NMTOKENS = 10;
int NOTATION = 11;
int NUMBER = 12;
int NUMBERS = 13;
int NUTOKEN = 14;
int NUTOKENS = 15;
// Content model types
int RCDATA = 16;
int EMPTY = 17;
int MODEL = 18;
int ANY = 19;
// Attribute value modifiers
int FIXED = 1;
int REQUIRED = 2;
int CURRENT = 3;
int CONREF = 4;
int IMPLIED = 5;
// Entity types
int PUBLIC = 10;
int SDATA = 11;
int PI = 12;
int STARTTAG = 13;
int ENDTAG = 14;
int MS = 15;
int MD = 16;
int SYSTEM = 17;
int GENERAL = 1<<16;
int DEFAULT = 1<<17;
int PARAMETER = 1<<18;
}

View File

@@ -0,0 +1,281 @@
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html.parser;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.html.HTMLEditorKit;
import javax.swing.text.html.HTML;
import javax.swing.text.ChangedCharSetException;
import java.util.*;
import java.io.*;
import java.net.*;
/**
* A Parser for HTML Documents (actually, you can specify a DTD, but
* you should really only use this class with the html dtd in swing).
* Reads an InputStream of HTML and
* invokes the appropriate methods in the ParserCallback class. This
* is the default parser used by HTMLEditorKit to parse HTML url's.
* <p>This will message the callback for all valid tags, as well as
* tags that are implied but not explicitly specified. For example, the
* html string (&lt;p&gt;blah) only has a p tag defined. The callback
* will see the following methods:
* <ol><li><i>handleStartTag(html, ...)</i></li>
* <li><i>handleStartTag(head, ...)</i></li>
* <li><i>handleEndTag(head)</i></li>
* <li><i>handleStartTag(body, ...)</i></li>
* <li><i>handleStartTag(p, ...)</i></li>
* <li><i>handleText(...)</i></li>
* <li><i>handleEndTag(p)</i></li>
* <li><i>handleEndTag(body)</i></li>
* <li><i>handleEndTag(html)</i></li>
* </ol>
* The items in <i>italic</i> are implied, that is, although they were not
* explicitly specified, to be correct html they should have been present
* (head isn't necessary, but it is still generated). For tags that
* are implied, the AttributeSet argument will have a value of
* <code>Boolean.TRUE</code> for the key
* <code>HTMLEditorKit.ParserCallback.IMPLIED</code>.
* <p>HTML.Attributes defines a type safe enumeration of html attributes.
* If an attribute key of a tag is defined in HTML.Attribute, the
* HTML.Attribute will be used as the key, otherwise a String will be used.
* For example &lt;p foo=bar class=neat&gt; has two attributes. foo is
* not defined in HTML.Attribute, where as class is, therefore the
* AttributeSet will have two values in it, HTML.Attribute.CLASS with
* a String value of 'neat' and the String key 'foo' with a String value of
* 'bar'.
* <p>The position argument will indicate the start of the tag, comment
* or text. Similar to arrays, the first character in the stream has a
* position of 0. For tags that are
* implied the position will indicate
* the location of the next encountered tag. In the first example,
* the implied start body and html tags will have the same position as the
* p tag, and the implied end p, html and body tags will all have the same
* position.
* <p>As html skips whitespace the position for text will be the position
* of the first valid character, eg in the string '\n\n\nblah'
* the text 'blah' will have a position of 3, the newlines are skipped.
* <p>
* For attributes that do not have a value, eg in the html
* string <code>&lt;foo blah&gt;</code> the attribute <code>blah</code>
* does not have a value, there are two possible values that will be
* placed in the AttributeSet's value:
* <ul>
* <li>If the DTD does not contain an definition for the element, or the
* definition does not have an explicit value then the value in the
* AttributeSet will be <code>HTML.NULL_ATTRIBUTE_VALUE</code>.
* <li>If the DTD contains an explicit value, as in:
* <code>&lt;!ATTLIST OPTION selected (selected) #IMPLIED&gt;</code>
* this value from the dtd (in this case selected) will be used.
* </ul>
* <p>
* Once the stream has been parsed, the callback is notified of the most
* likely end of line string. The end of line string will be one of
* \n, \r or \r\n, which ever is encountered the most in parsing the
* stream.
*
* @author Sunita Mani
*/
public class DocumentParser extends javax.swing.text.html.parser.Parser {
private int inbody;
private int intitle;
private int inhead;
private int instyle;
private int inscript;
private boolean seentitle;
private HTMLEditorKit.ParserCallback callback = null;
private boolean ignoreCharSet = false;
private static final boolean debugFlag = false;
public DocumentParser(DTD dtd) {
super(dtd);
}
public void parse(Reader in, HTMLEditorKit.ParserCallback callback, boolean ignoreCharSet) throws IOException {
this.ignoreCharSet = ignoreCharSet;
this.callback = callback;
parse(in);
// end of line
callback.handleEndOfLineString(getEndOfLineString());
}
/**
* Handle Start Tag.
*/
protected void handleStartTag(TagElement tag) {
Element elem = tag.getElement();
if (elem == dtd.body) {
inbody++;
} else if (elem == dtd.html) {
} else if (elem == dtd.head) {
inhead++;
} else if (elem == dtd.title) {
intitle++;
} else if (elem == dtd.style) {
instyle++;
} else if (elem == dtd.script) {
inscript++;
}
if (debugFlag) {
if (tag.fictional()) {
debug("Start Tag: " + tag.getHTMLTag() + " pos: " + getCurrentPos());
} else {
debug("Start Tag: " + tag.getHTMLTag() + " attributes: " +
getAttributes() + " pos: " + getCurrentPos());
}
}
if (tag.fictional()) {
SimpleAttributeSet attrs = new SimpleAttributeSet();
attrs.addAttribute(HTMLEditorKit.ParserCallback.IMPLIED,
Boolean.TRUE);
callback.handleStartTag(tag.getHTMLTag(), attrs,
getBlockStartPosition());
} else {
callback.handleStartTag(tag.getHTMLTag(), getAttributes(),
getBlockStartPosition());
flushAttributes();
}
}
protected void handleComment(char text[]) {
if (debugFlag) {
debug("comment: ->" + new String(text) + "<-"
+ " pos: " + getCurrentPos());
}
callback.handleComment(text, getBlockStartPosition());
}
/**
* Handle Empty Tag.
*/
protected void handleEmptyTag(TagElement tag) throws ChangedCharSetException {
Element elem = tag.getElement();
if (elem == dtd.meta && !ignoreCharSet) {
SimpleAttributeSet atts = getAttributes();
if (atts != null) {
String content = (String)atts.getAttribute(HTML.Attribute.CONTENT);
if (content != null) {
if ("content-type".equalsIgnoreCase((String)atts.getAttribute(HTML.Attribute.HTTPEQUIV))) {
if (!content.equalsIgnoreCase("text/html") &&
!content.equalsIgnoreCase("text/plain")) {
throw new ChangedCharSetException(content, false);
}
} else if ("charset" .equalsIgnoreCase((String)atts.getAttribute(HTML.Attribute.HTTPEQUIV))) {
throw new ChangedCharSetException(content, true);
}
}
}
}
if (inbody != 0 || elem == dtd.meta || elem == dtd.base || elem == dtd.isindex || elem == dtd.style || elem == dtd.link) {
if (debugFlag) {
if (tag.fictional()) {
debug("Empty Tag: " + tag.getHTMLTag() + " pos: " + getCurrentPos());
} else {
debug("Empty Tag: " + tag.getHTMLTag() + " attributes: "
+ getAttributes() + " pos: " + getCurrentPos());
}
}
if (tag.fictional()) {
SimpleAttributeSet attrs = new SimpleAttributeSet();
attrs.addAttribute(HTMLEditorKit.ParserCallback.IMPLIED,
Boolean.TRUE);
callback.handleSimpleTag(tag.getHTMLTag(), attrs,
getBlockStartPosition());
} else {
callback.handleSimpleTag(tag.getHTMLTag(), getAttributes(),
getBlockStartPosition());
flushAttributes();
}
}
}
/**
* Handle End Tag.
*/
protected void handleEndTag(TagElement tag) {
Element elem = tag.getElement();
if (elem == dtd.body) {
inbody--;
} else if (elem == dtd.title) {
intitle--;
seentitle = true;
} else if (elem == dtd.head) {
inhead--;
} else if (elem == dtd.style) {
instyle--;
} else if (elem == dtd.script) {
inscript--;
}
if (debugFlag) {
debug("End Tag: " + tag.getHTMLTag() + " pos: " + getCurrentPos());
}
callback.handleEndTag(tag.getHTMLTag(), getBlockStartPosition());
}
/**
* Handle Text.
*/
protected void handleText(char data[]) {
if (data != null) {
if (inscript != 0) {
callback.handleComment(data, getBlockStartPosition());
return;
}
if (inbody != 0 || ((instyle != 0) ||
((intitle != 0) && !seentitle))) {
if (debugFlag) {
debug("text: ->" + new String(data) + "<-" + " pos: " + getCurrentPos());
}
callback.handleText(data, getBlockStartPosition());
}
}
}
/*
* Error handling.
*/
protected void handleError(int ln, String errorMsg) {
if (debugFlag) {
debug("Error: ->" + errorMsg + "<-" + " pos: " + getCurrentPos());
}
/* PENDING: need to improve the error string. */
callback.handleError(errorMsg, getCurrentPos());
}
/*
* debug messages
*/
private void debug(String msg) {
System.out.println(msg);
}
}

View File

@@ -0,0 +1,185 @@
/*
* Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html.parser;
import java.util.Hashtable;
import java.util.BitSet;
import java.io.*;
import sun.awt.AppContext;
/**
* An element as described in a DTD using the ELEMENT construct.
* This is essential the description of a tag. It describes the
* type, content model, attributes, attribute types etc. It is used
* to correctly parse a document by the Parser.
*
* @see DTD
* @see AttributeList
* @author Arthur van Hoff
*/
public final
class Element implements DTDConstants, Serializable {
public int index;
public String name;
public boolean oStart;
public boolean oEnd;
public BitSet inclusions;
public BitSet exclusions;
public int type = ANY;
public ContentModel content;
public AttributeList atts;
/**
* A field to store user data. Mostly used to store
* style sheets.
*/
public Object data;
Element() {
}
/**
* Create a new element.
*/
Element(String name, int index) {
this.name = name;
this.index = index;
if (index > getMaxIndex()) {
AppContext.getAppContext().put(MAX_INDEX_KEY, index);
}
}
private static final Object MAX_INDEX_KEY = new Object();
static int getMaxIndex() {
Integer value = (Integer) AppContext.getAppContext().get(MAX_INDEX_KEY);
return (value != null)
? value.intValue()
: 0;
}
/**
* Get the name of the element.
*/
public String getName() {
return name;
}
/**
* Return true if the start tag can be omitted.
*/
public boolean omitStart() {
return oStart;
}
/**
* Return true if the end tag can be omitted.
*/
public boolean omitEnd() {
return oEnd;
}
/**
* Get type.
*/
public int getType() {
return type;
}
/**
* Get content model
*/
public ContentModel getContent() {
return content;
}
/**
* Get the attributes.
*/
public AttributeList getAttributes() {
return atts;
}
/**
* Get index.
*/
public int getIndex() {
return index;
}
/**
* Check if empty
*/
public boolean isEmpty() {
return type == EMPTY;
}
/**
* Convert to a string.
*/
public String toString() {
return name;
}
/**
* Get an attribute by name.
*/
public AttributeList getAttribute(String name) {
for (AttributeList a = atts ; a != null ; a = a.next) {
if (a.name.equals(name)) {
return a;
}
}
return null;
}
/**
* Get an attribute by value.
*/
public AttributeList getAttributeByValue(String name) {
for (AttributeList a = atts ; a != null ; a = a.next) {
if ((a.values != null) && a.values.contains(name)) {
return a;
}
}
return null;
}
static Hashtable<String, Integer> contentTypes = new Hashtable<String, Integer>();
static {
contentTypes.put("CDATA", Integer.valueOf(CDATA));
contentTypes.put("RCDATA", Integer.valueOf(RCDATA));
contentTypes.put("EMPTY", Integer.valueOf(EMPTY));
contentTypes.put("ANY", Integer.valueOf(ANY));
}
public static int name2type(String nm) {
Integer val = contentTypes.get(nm);
return (val != null) ? val.intValue() : 0;
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html.parser;
import java.util.Hashtable;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.CharArrayReader;
import java.net.URL;
/**
* An entity is described in a DTD using the ENTITY construct.
* It defines the type and value of the the entity.
*
* @see DTD
* @author Arthur van Hoff
*/
public final
class Entity implements DTDConstants {
public String name;
public int type;
public char data[];
/**
* Creates an entity.
* @param name the name of the entity
* @param type the type of the entity
* @param data the char array of data
*/
public Entity(String name, int type, char data[]) {
this.name = name;
this.type = type;
this.data = data;
}
/**
* Gets the name of the entity.
* @return the name of the entity, as a <code>String</code>
*/
public String getName() {
return name;
}
/**
* Gets the type of the entity.
* @return the type of the entity
*/
public int getType() {
return type & 0xFFFF;
}
/**
* Returns <code>true</code> if it is a parameter entity.
* @return <code>true</code> if it is a parameter entity
*/
public boolean isParameter() {
return (type & PARAMETER) != 0;
}
/**
* Returns <code>true</code> if it is a general entity.
* @return <code>true</code> if it is a general entity
*/
public boolean isGeneral() {
return (type & GENERAL) != 0;
}
/**
* Returns the <code>data</code>.
* @return the <code>data</code>
*/
public char getData()[] {
return data;
}
/**
* Returns the data as a <code>String</code>.
* @return the data as a <code>String</code>
*/
public String getString() {
return new String(data, 0, data.length);
}
static Hashtable<String, Integer> entityTypes = new Hashtable<String, Integer>();
static {
entityTypes.put("PUBLIC", Integer.valueOf(PUBLIC));
entityTypes.put("CDATA", Integer.valueOf(CDATA));
entityTypes.put("SDATA", Integer.valueOf(SDATA));
entityTypes.put("PI", Integer.valueOf(PI));
entityTypes.put("STARTTAG", Integer.valueOf(STARTTAG));
entityTypes.put("ENDTAG", Integer.valueOf(ENDTAG));
entityTypes.put("MS", Integer.valueOf(MS));
entityTypes.put("MD", Integer.valueOf(MD));
entityTypes.put("SYSTEM", Integer.valueOf(SYSTEM));
}
/**
* Converts <code>nm</code> string to the corresponding
* entity type. If the string does not have a corresponding
* entity type, returns the type corresponding to "CDATA".
* Valid entity types are: "PUBLIC", "CDATA", "SDATA", "PI",
* "STARTTAG", "ENDTAG", "MS", "MD", "SYSTEM".
*
* @param nm the string to be converted
* @return the corresponding entity type, or the type corresponding
* to "CDATA", if none exists
*/
public static int name2type(String nm) {
Integer i = entityTypes.get(nm);
return (i == null) ? CDATA : i.intValue();
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,129 @@
/*
* Copyright (c) 1998, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html.parser;
import sun.awt.AppContext;
import javax.swing.text.html.HTMLEditorKit;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.DataInputStream;
import java.io.ObjectInputStream;
import java.io.Reader;
import java.io.Serializable;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* Responsible for starting up a new DocumentParser
* each time its parse method is invoked. Stores a
* reference to the dtd.
*
* @author Sunita Mani
*/
public class ParserDelegator extends HTMLEditorKit.Parser implements Serializable {
private static final Object DTD_KEY = new Object();
protected static void setDefaultDTD() {
getDefaultDTD();
}
private static synchronized DTD getDefaultDTD() {
AppContext appContext = AppContext.getAppContext();
DTD dtd = (DTD) appContext.get(DTD_KEY);
if (dtd == null) {
DTD _dtd = null;
// (PENDING) Hate having to hard code!
String nm = "html32";
try {
_dtd = DTD.getDTD(nm);
} catch (IOException e) {
// (PENDING) UGLY!
System.out.println("Throw an exception: could not get default dtd: " + nm);
}
dtd = createDTD(_dtd, nm);
appContext.put(DTD_KEY, dtd);
}
return dtd;
}
protected static DTD createDTD(DTD dtd, String name) {
InputStream in = null;
boolean debug = true;
try {
String path = name + ".bdtd";
in = getResourceAsStream(path);
if (in != null) {
dtd.read(new DataInputStream(new BufferedInputStream(in)));
dtd.putDTDHash(name, dtd);
}
} catch (Exception e) {
System.out.println(e);
}
return dtd;
}
public ParserDelegator() {
setDefaultDTD();
}
public void parse(Reader r, HTMLEditorKit.ParserCallback cb, boolean ignoreCharSet) throws IOException {
new DocumentParser(getDefaultDTD()).parse(r, cb, ignoreCharSet);
}
/**
* Fetch a resource relative to the ParserDelegator classfile.
* If this is called on 1.2 the loading will occur under the
* protection of a doPrivileged call to allow the ParserDelegator
* to function when used in an applet.
*
* @param name the name of the resource, relative to the
* ParserDelegator class.
* @returns a stream representing the resource
*/
static InputStream getResourceAsStream(final String name) {
return AccessController.doPrivileged(
new PrivilegedAction<InputStream>() {
public InputStream run() {
return ParserDelegator.class.getResourceAsStream(name);
}
});
}
private void readObject(ObjectInputStream s)
throws ClassNotFoundException, IOException {
s.defaultReadObject();
setDefaultDTD();
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright (c) 1998, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html.parser;
import javax.swing.text.html.HTML;
/**
* A generic HTML TagElement class. The methods define how white
* space is interpreted around the tag.
*
* @author Sunita Mani
*/
public class TagElement {
Element elem;
HTML.Tag htmlTag;
boolean insertedByErrorRecovery;
public TagElement ( Element elem ) {
this(elem, false);
}
public TagElement (Element elem, boolean fictional) {
this.elem = elem;
htmlTag = HTML.getTag(elem.getName());
if (htmlTag == null) {
htmlTag = new HTML.UnknownTag(elem.getName());
}
insertedByErrorRecovery = fictional;
}
public boolean breaksFlow() {
return htmlTag.breaksFlow();
}
public boolean isPreformatted() {
return htmlTag.isPreformatted();
}
public Element getElement() {
return elem;
}
public HTML.Tag getHTMLTag() {
return htmlTag;
}
public boolean fictional() {
return insertedByErrorRecovery;
}
}

View File

@@ -0,0 +1,204 @@
/*
* Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.text.html.parser;
import java.util.BitSet;
import java.util.Vector;
import java.io.*;
/**
* A stack of tags. Used while parsing an HTML document.
* It, together with the ContentModelStates, defines the
* complete state of the parser while reading a document.
* When a start tag is encountered an element is pushed onto
* the stack, when an end tag is enountered an element is popped
* of the stack.
*
* @see Parser
* @see DTD
* @see ContentModelState
* @author Arthur van Hoff
*/
final
class TagStack implements DTDConstants {
TagElement tag;
Element elem;
ContentModelState state;
TagStack next;
BitSet inclusions;
BitSet exclusions;
boolean net;
boolean pre;
/**
* Construct a stack element.
*/
TagStack(TagElement tag, TagStack next) {
this.tag = tag;
this.elem = tag.getElement();
this.next = next;
Element elem = tag.getElement();
if (elem.getContent() != null) {
this.state = new ContentModelState(elem.getContent());
}
if (next != null) {
inclusions = next.inclusions;
exclusions = next.exclusions;
pre = next.pre;
}
if (tag.isPreformatted()) {
pre = true;
}
if (elem.inclusions != null) {
if (inclusions != null) {
inclusions = (BitSet)inclusions.clone();
inclusions.or(elem.inclusions);
} else {
inclusions = elem.inclusions;
}
}
if (elem.exclusions != null) {
if (exclusions != null) {
exclusions = (BitSet)exclusions.clone();
exclusions.or(elem.exclusions);
} else {
exclusions = elem.exclusions;
}
}
}
/**
* Return the element that must come next in the
* input stream.
*/
public Element first() {
return (state != null) ? state.first() : null;
}
/**
* Return the ContentModel that must be satisfied by
* what comes next in the input stream.
*/
public ContentModel contentModel() {
if (state == null) {
return null;
} else {
return state.getModel();
}
}
/**
* Return true if the element that is contained at
* the index specified by the parameter is part of
* the exclusions specified in the DTD for the element
* currently on the TagStack.
*/
boolean excluded(int elemIndex) {
return (exclusions != null) && exclusions.get(elem.getIndex());
}
/**
* Advance the state by reducing the given element.
* Returns false if the element is not legal and the
* state is not advanced.
*/
boolean advance(Element elem) {
if ((exclusions != null) && exclusions.get(elem.getIndex())) {
return false;
}
if (state != null) {
ContentModelState newState = state.advance(elem);
if (newState != null) {
state = newState;
return true;
}
} else if (this.elem.getType() == ANY) {
return true;
}
return (inclusions != null) && inclusions.get(elem.getIndex());
}
/**
* Return true if the current state can be terminated.
*/
boolean terminate() {
return (state == null) || state.terminate();
}
/**
* Convert to a string.
*/
public String toString() {
return (next == null) ?
"<" + tag.getElement().getName() + ">" :
next + " <" + tag.getElement().getName() + ">";
}
}
class NPrintWriter extends PrintWriter {
private int numLines = 5;
private int numPrinted = 0;
public NPrintWriter (int numberOfLines) {
super(System.out);
numLines = numberOfLines;
}
public void println(char[] array) {
if (numPrinted >= numLines) {
return;
}
char[] partialArray = null;
for (int i = 0; i < array.length; i++) {
if (array[i] == '\n') {
numPrinted++;
}
if (numPrinted == numLines) {
System.arraycopy(array, 0, partialArray, 0, i);
}
}
if (partialArray != null) {
super.print(partialArray);
}
if (numPrinted == numLines) {
return;
}
super.println(array);
numPrinted++;
}
}