summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/text/html/StyleSheet.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/text/html/StyleSheet.java')
-rw-r--r--libjava/classpath/javax/swing/text/html/StyleSheet.java1455
1 files changed, 1455 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/text/html/StyleSheet.java b/libjava/classpath/javax/swing/text/html/StyleSheet.java
new file mode 100644
index 000000000..5cf015bc5
--- /dev/null
+++ b/libjava/classpath/javax/swing/text/html/StyleSheet.java
@@ -0,0 +1,1455 @@
+/* StyleSheet.java --
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+This file is part of GNU Classpath.
+
+GNU Classpath is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2, or (at your option)
+any later version.
+
+GNU Classpath is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GNU Classpath; see the file COPYING. If not, write to the
+Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version. */
+
+
+package javax.swing.text.html;
+
+import gnu.javax.swing.text.html.css.BorderWidth;
+import gnu.javax.swing.text.html.css.CSSColor;
+import gnu.javax.swing.text.html.css.CSSParser;
+import gnu.javax.swing.text.html.css.CSSParserCallback;
+import gnu.javax.swing.text.html.css.FontSize;
+import gnu.javax.swing.text.html.css.FontStyle;
+import gnu.javax.swing.text.html.css.FontWeight;
+import gnu.javax.swing.text.html.css.Length;
+import gnu.javax.swing.text.html.css.Selector;
+
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.Shape;
+import java.awt.font.FontRenderContext;
+import java.awt.geom.Rectangle2D;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Serializable;
+import java.io.StringReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.border.Border;
+import javax.swing.event.ChangeListener;
+import javax.swing.text.AttributeSet;
+import javax.swing.text.Element;
+import javax.swing.text.MutableAttributeSet;
+import javax.swing.text.SimpleAttributeSet;
+import javax.swing.text.Style;
+import javax.swing.text.StyleConstants;
+import javax.swing.text.StyleContext;
+import javax.swing.text.View;
+
+
+/**
+ * This class adds support for defining the visual characteristics of HTML views
+ * being rendered. This enables views to be customized by a look-and-feel, mulitple
+ * views over the same model can be rendered differently. Each EditorPane has its
+ * own StyleSheet, but by default one sheet will be shared by all of the HTMLEditorKit
+ * instances. An HTMLDocument can also have a StyleSheet, which holds specific CSS
+ * specs.
+ *
+ * In order for Views to store less state and therefore be more lightweight,
+ * the StyleSheet can act as a factory for painters that handle some of the
+ * rendering tasks. Since the StyleSheet may be used by views over multiple
+ * documents the HTML attributes don't effect the selector being used.
+ *
+ * The rules are stored as named styles, and other information is stored to
+ * translate the context of an element to a rule.
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ */
+public class StyleSheet extends StyleContext
+{
+
+ /**
+ * Parses CSS stylesheets using the parser in gnu/javax/swing/html/css.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ class CSSStyleSheetParserCallback
+ implements CSSParserCallback
+ {
+ /**
+ * The current styles.
+ */
+ private CSSStyle[] styles;
+
+ /**
+ * The precedence of the stylesheet to be parsed.
+ */
+ private int precedence;
+
+ /**
+ * Creates a new CSS parser. This parser parses a CSS stylesheet with
+ * the specified precedence.
+ *
+ * @param prec the precedence, according to the constants defined in
+ * CSSStyle
+ */
+ CSSStyleSheetParserCallback(int prec)
+ {
+ precedence = prec;
+ }
+
+ /**
+ * Called at the beginning of a statement.
+ *
+ * @param sel the selector
+ */
+ public void startStatement(Selector[] sel)
+ {
+ styles = new CSSStyle[sel.length];
+ for (int i = 0; i < sel.length; i++)
+ styles[i] = new CSSStyle(precedence, sel[i]);
+ }
+
+ /**
+ * Called at the end of a statement.
+ */
+ public void endStatement()
+ {
+ for (int i = 0; i < styles.length; i++)
+ css.add(styles[i]);
+ styles = null;
+ }
+
+ /**
+ * Called when a declaration is parsed.
+ *
+ * @param property the property
+ * @param value the value
+ */
+ public void declaration(String property, String value)
+ {
+ CSS.Attribute cssAtt = CSS.getAttribute(property);
+ Object val = CSS.getValue(cssAtt, value);
+ for (int i = 0; i < styles.length; i++)
+ {
+ CSSStyle style = styles[i];
+ CSS.addInternal(style, cssAtt, value);
+ if (cssAtt != null)
+ style.addAttribute(cssAtt, val);
+ }
+ }
+
+ }
+
+ /**
+ * Represents a style that is defined by a CSS rule.
+ */
+ private class CSSStyle
+ extends SimpleAttributeSet
+ implements Style, Comparable<CSSStyle>
+ {
+
+ static final int PREC_UA = 0;
+ static final int PREC_NORM = 100000;
+ static final int PREC_AUTHOR_NORMAL = 200000;
+ static final int PREC_AUTHOR_IMPORTANT = 300000;
+ static final int PREC_USER_IMPORTANT = 400000;
+
+ /**
+ * The priority of this style when matching CSS selectors.
+ */
+ private int precedence;
+
+ /**
+ * The selector for this rule.
+ *
+ * This is package private to avoid accessor methods.
+ */
+ Selector selector;
+
+ CSSStyle(int prec, Selector sel)
+ {
+ precedence = prec;
+ selector = sel;
+ }
+
+ public String getName()
+ {
+ // TODO: Implement this for correctness.
+ return null;
+ }
+
+ public void addChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement this for correctness.
+ }
+
+ public void removeChangeListener(ChangeListener listener)
+ {
+ // TODO: Implement this for correctness.
+ }
+
+ /**
+ * Sorts the rule according to the style's precedence and the
+ * selectors specificity.
+ */
+ public int compareTo(CSSStyle other)
+ {
+ return other.precedence + other.selector.getSpecificity()
+ - precedence - selector.getSpecificity();
+ }
+
+ }
+
+ /** The base URL */
+ URL base;
+
+ /** Base font size (int) */
+ int baseFontSize;
+
+ /**
+ * The linked style sheets stored.
+ */
+ private ArrayList<StyleSheet> linked;
+
+ /**
+ * Maps element names (selectors) to AttributSet (the corresponding style
+ * information).
+ */
+ ArrayList<CSSStyle> css = new ArrayList<CSSStyle>();
+
+ /**
+ * Maps selectors to their resolved styles.
+ */
+ private HashMap<String,Style> resolvedStyles;
+
+ /**
+ * Constructs a StyleSheet.
+ */
+ public StyleSheet()
+ {
+ super();
+ baseFontSize = 4; // Default font size from CSS
+ resolvedStyles = new HashMap<String,Style>();
+ }
+
+ /**
+ * Gets the style used to render the given tag. The element represents the tag
+ * and can be used to determine the nesting, where the attributes will differ
+ * if there is nesting inside of elements.
+ *
+ * @param t - the tag to translate to visual attributes
+ * @param e - the element representing the tag
+ * @return the set of CSS attributes to use to render the tag.
+ */
+ public Style getRule(HTML.Tag t, Element e)
+ {
+ // Create list of the element and all of its parents, starting
+ // with the bottommost element.
+ ArrayList<Element> path = new ArrayList<Element>();
+ Element el;
+ AttributeSet atts;
+ for (el = e; el != null; el = el.getParentElement())
+ path.add(el);
+
+ // Create fully qualified selector.
+ StringBuilder selector = new StringBuilder();
+ int count = path.size();
+ // We append the actual element after this loop.
+ for (int i = count - 1; i > 0; i--)
+ {
+ el = path.get(i);
+ atts = el.getAttributes();
+ Object name = atts.getAttribute(StyleConstants.NameAttribute);
+ selector.append(name.toString());
+ if (atts.isDefined(HTML.Attribute.ID))
+ {
+ selector.append('#');
+ selector.append(atts.getAttribute(HTML.Attribute.ID));
+ }
+ if (atts.isDefined(HTML.Attribute.CLASS))
+ {
+ selector.append('.');
+ selector.append(atts.getAttribute(HTML.Attribute.CLASS));
+ }
+ if (atts.isDefined(HTML.Attribute.DYNAMIC_CLASS))
+ {
+ selector.append(':');
+ selector.append(atts.getAttribute(HTML.Attribute.DYNAMIC_CLASS));
+ }
+ if (atts.isDefined(HTML.Attribute.PSEUDO_CLASS))
+ {
+ selector.append(':');
+ selector.append(atts.getAttribute(HTML.Attribute.PSEUDO_CLASS));
+ }
+ selector.append(' ');
+ }
+ selector.append(t.toString());
+ el = path.get(0);
+ atts = el.getAttributes();
+ // For leaf elements, we have to fetch the tag specific attributes.
+ if (el.isLeaf())
+ {
+ Object o = atts.getAttribute(t);
+ if (o instanceof AttributeSet)
+ atts = (AttributeSet) o;
+ else
+ atts = null;
+ }
+ if (atts != null)
+ {
+ if (atts.isDefined(HTML.Attribute.ID))
+ {
+ selector.append('#');
+ selector.append(atts.getAttribute(HTML.Attribute.ID));
+ }
+ if (atts.isDefined(HTML.Attribute.CLASS))
+ {
+ selector.append('.');
+ selector.append(atts.getAttribute(HTML.Attribute.CLASS));
+ }
+ if (atts.isDefined(HTML.Attribute.DYNAMIC_CLASS))
+ {
+ selector.append(':');
+ selector.append(atts.getAttribute(HTML.Attribute.DYNAMIC_CLASS));
+ }
+ if (atts.isDefined(HTML.Attribute.PSEUDO_CLASS))
+ {
+ selector.append(':');
+ selector.append(atts.getAttribute(HTML.Attribute.PSEUDO_CLASS));
+ }
+ }
+ return getResolvedStyle(selector.toString(), path, t);
+ }
+
+ /**
+ * Fetches a resolved style. If there is no resolved style for the
+ * specified selector, the resolve the style using
+ * {@link #resolveStyle(String, List, HTML.Tag)}.
+ *
+ * @param selector the selector for which to resolve the style
+ * @param path the Element path, used in the resolving algorithm
+ * @param tag the tag for which to resolve
+ *
+ * @return the resolved style
+ */
+ private Style getResolvedStyle(String selector, List<Element> path, HTML.Tag tag)
+ {
+ Style style = resolvedStyles.get(selector);
+ if (style == null)
+ style = resolveStyle(selector, path, tag);
+ return style;
+ }
+
+ /**
+ * Resolves a style. This creates arrays that hold the tag names,
+ * class and id attributes and delegates the work to
+ * {@link #resolveStyle(String, String[], List<Map<String,String>>)}.
+ *
+ * @param selector the selector
+ * @param path the Element path
+ * @param tag the tag
+ *
+ * @return the resolved style
+ */
+ private Style resolveStyle(String selector, List<Element> path, HTML.Tag tag)
+ {
+ int count = path.size();
+ String[] tags = new String[count];
+ List<Map<String,String>> attributes =
+ new ArrayList<Map<String,String>>(count);
+ for (int i = 0; i < count; i++)
+ {
+ Element el = path.get(i);
+ AttributeSet atts = el.getAttributes();
+ if (i == 0 && el.isLeaf())
+ {
+ Object o = atts.getAttribute(tag);
+ if (o instanceof AttributeSet)
+ atts = (AttributeSet) o;
+ else
+ atts = null;
+ }
+ if (atts != null)
+ {
+ HTML.Tag t =
+ (HTML.Tag) atts.getAttribute(StyleConstants.NameAttribute);
+ if (t != null)
+ tags[i] = t.toString();
+ else
+ tags[i] = null;
+ attributes.set(i, attributeSetToMap(atts));
+ }
+ else
+ {
+ tags[i] = null;
+ }
+ }
+ tags[0] = tag.toString();
+ return resolveStyle(selector, tags, attributes);
+ }
+
+ /**
+ * Performs style resolving.
+ *
+ * @param selector the selector
+ * @param tags the tags
+ * @param attributes the attributes of the tags
+ *
+ * @return the resolved style
+ */
+ private Style resolveStyle(String selector, String[] tags,
+ List<Map<String,String>> attributes)
+ {
+ // FIXME: This style resolver is not correct. But it works good enough for
+ // the default.css.
+ ArrayList<CSSStyle> styles = new ArrayList<CSSStyle>();
+ for (CSSStyle style : css)
+ {
+ if (style.selector.matches(tags, attributes))
+ styles.add(style);
+ }
+
+ // Add styles from linked stylesheets.
+ if (linked != null)
+ {
+ for (int i = linked.size() - 1; i >= 0; i--)
+ {
+ StyleSheet ss = linked.get(i);
+ for (int j = ss.css.size() - 1; j >= 0; j--)
+ {
+ CSSStyle style = ss.css.get(j);
+ if (style.selector.matches(tags, attributes))
+ styles.add(style);
+ }
+ }
+ }
+
+ // Sort selectors.
+ Collections.sort(styles);
+ Style[] styleArray = styles.toArray(new Style[styles.size()]);
+ Style resolved = new MultiStyle(selector, styleArray);
+ resolvedStyles.put(selector, resolved);
+ return resolved;
+ }
+
+ /**
+ * Gets the rule that best matches the selector. selector is a space
+ * separated String of element names. The attributes of the returned
+ * Style will change as rules are added and removed.
+ *
+ * @param selector - the element names separated by spaces
+ * @return the set of CSS attributes to use to render
+ */
+ public Style getRule(String selector)
+ {
+ CSSStyle best = null;
+ for (Iterator<CSSStyle> i = css.iterator(); i.hasNext();)
+ {
+ CSSStyle style = i.next();
+ if (style.compareTo(best) < 0)
+ best = style;
+ }
+ return best;
+ }
+
+ /**
+ * Adds a set of rules to the sheet. The rules are expected to be in valid
+ * CSS format. This is called as a result of parsing a <style> tag
+ *
+ * @param rule - the rule to add to the sheet
+ */
+ public void addRule(String rule)
+ {
+ CSSStyleSheetParserCallback cb =
+ new CSSStyleSheetParserCallback(CSSStyle.PREC_AUTHOR_NORMAL);
+ // FIXME: Handle ref.
+ StringReader in = new StringReader(rule);
+ CSSParser parser = new CSSParser(in, cb);
+ try
+ {
+ parser.parse();
+ }
+ catch (IOException ex)
+ {
+ // Shouldn't happen. And if, then don't let it bork the outside code.
+ }
+ // Clean up resolved styles cache so that the new styles are recognized
+ // on next stylesheet request.
+ resolvedStyles.clear();
+ }
+
+ /**
+ * Translates a CSS declaration into an AttributeSet. This is called
+ * as a result of encountering an HTML style attribute.
+ *
+ * @param decl - the declaration to get
+ * @return the AttributeSet representing the declaration
+ */
+ public AttributeSet getDeclaration(String decl)
+ {
+ if (decl == null)
+ return SimpleAttributeSet.EMPTY;
+ // FIXME: Not implemented.
+ return null;
+ }
+
+ /**
+ * Loads a set of rules that have been specified in terms of CSS grammar.
+ * If there are any conflicts with existing rules, the new rule is added.
+ *
+ * @param in - the stream to read the CSS grammar from.
+ * @param ref - the reference URL. It is the location of the stream, it may
+ * be null. All relative URLs specified in the stream will be based upon this
+ * parameter.
+ * @throws IOException - For any IO error while reading
+ */
+ public void loadRules(Reader in, URL ref)
+ throws IOException
+ {
+ CSSStyleSheetParserCallback cb =
+ new CSSStyleSheetParserCallback(CSSStyle.PREC_UA);
+ // FIXME: Handle ref.
+ CSSParser parser = new CSSParser(in, cb);
+ parser.parse();
+ }
+
+ /**
+ * Gets a set of attributes to use in the view. This is a set of
+ * attributes that can be used for View.getAttributes
+ *
+ * @param v - the view to get the set for
+ * @return the AttributeSet to use in the view.
+ */
+ public AttributeSet getViewAttributes(View v)
+ {
+ return new ViewAttributeSet(v, this);
+ }
+
+ /**
+ * Removes a style previously added.
+ *
+ * @param nm - the name of the style to remove
+ */
+ public void removeStyle(String nm)
+ {
+ // FIXME: Not implemented.
+ super.removeStyle(nm);
+ }
+
+ /**
+ * Adds the rules from ss to those of the receiver. ss's rules will
+ * override the old rules. An added StyleSheet will never override the rules
+ * of the receiving style sheet.
+ *
+ * @param ss - the new StyleSheet.
+ */
+ public void addStyleSheet(StyleSheet ss)
+ {
+ if (linked == null)
+ linked = new ArrayList<StyleSheet>();
+ linked.add(ss);
+ }
+
+ /**
+ * Removes ss from those of the receiver
+ *
+ * @param ss - the StyleSheet to remove.
+ */
+ public void removeStyleSheet(StyleSheet ss)
+ {
+ if (linked != null)
+ {
+ linked.remove(ss);
+ }
+ }
+
+ /**
+ * Returns an array of the linked StyleSheets. May return null.
+ *
+ * @return - An array of the linked StyleSheets.
+ */
+ public StyleSheet[] getStyleSheets()
+ {
+ StyleSheet[] linkedSS;
+ if (linked != null)
+ {
+ linkedSS = new StyleSheet[linked.size()];
+ linkedSS = linked.toArray(linkedSS);
+ }
+ else
+ {
+ linkedSS = null;
+ }
+ return linkedSS;
+ }
+
+ /**
+ * Imports a style sheet from the url. The rules are directly added to the
+ * receiver. This is usually called when a <link> tag is resolved in an
+ * HTML document.
+ *
+ * @param url the URL to import the StyleSheet from
+ */
+ public void importStyleSheet(URL url)
+ {
+ try
+ {
+ InputStream in = url.openStream();
+ Reader r = new BufferedReader(new InputStreamReader(in));
+ CSSStyleSheetParserCallback cb =
+ new CSSStyleSheetParserCallback(CSSStyle.PREC_AUTHOR_NORMAL);
+ CSSParser parser = new CSSParser(r, cb);
+ parser.parse();
+ }
+ catch (IOException ex)
+ {
+ // We can't do anything about it I guess.
+ }
+ }
+
+ /**
+ * Sets the base url. All import statements that are relative, will be
+ * relative to base.
+ *
+ * @param base -
+ * the base URL.
+ */
+ public void setBase(URL base)
+ {
+ this.base = base;
+ }
+
+ /**
+ * Gets the base url.
+ *
+ * @return - the base
+ */
+ public URL getBase()
+ {
+ return base;
+ }
+
+ /**
+ * Adds a CSS attribute to the given set.
+ *
+ * @param attr - the attribute set
+ * @param key - the attribute to add
+ * @param value - the value of the key
+ */
+ public void addCSSAttribute(MutableAttributeSet attr, CSS.Attribute key,
+ String value)
+ {
+ Object val = CSS.getValue(key, value);
+ CSS.addInternal(attr, key, value);
+ attr.addAttribute(key, val);
+ }
+
+ /**
+ * Adds a CSS attribute to the given set.
+ * This method parses the value argument from HTML based on key.
+ * Returns true if it finds a valid value for the given key,
+ * and false otherwise.
+ *
+ * @param attr - the attribute set
+ * @param key - the attribute to add
+ * @param value - the value of the key
+ * @return true if a valid value was found.
+ */
+ public boolean addCSSAttributeFromHTML(MutableAttributeSet attr, CSS.Attribute key,
+ String value)
+ {
+ // FIXME: Need to parse value from HTML based on key.
+ attr.addAttribute(key, value);
+ return attr.containsAttribute(key, value);
+ }
+
+ /**
+ * Converts a set of HTML attributes to an equivalent set of CSS attributes.
+ *
+ * @param htmlAttrSet - the set containing the HTML attributes.
+ * @return the set of CSS attributes
+ */
+ public AttributeSet translateHTMLToCSS(AttributeSet htmlAttrSet)
+ {
+ AttributeSet cssAttr = htmlAttrSet.copyAttributes();
+
+ // The HTML align attribute maps directly to the CSS text-align attribute.
+ Object o = htmlAttrSet.getAttribute(HTML.Attribute.ALIGN);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.TEXT_ALIGN, o);
+
+ // The HTML width attribute maps directly to CSS width.
+ o = htmlAttrSet.getAttribute(HTML.Attribute.WIDTH);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.WIDTH,
+ new Length(o.toString()));
+
+ // The HTML height attribute maps directly to CSS height.
+ o = htmlAttrSet.getAttribute(HTML.Attribute.HEIGHT);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.HEIGHT,
+ new Length(o.toString()));
+
+ o = htmlAttrSet.getAttribute(HTML.Attribute.NOWRAP);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.WHITE_SPACE, "nowrap");
+
+ // Map cellspacing attr of tables to CSS border-spacing.
+ o = htmlAttrSet.getAttribute(HTML.Attribute.CELLSPACING);
+ if (o != null)
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_SPACING,
+ new Length(o.toString()));
+
+ // For table cells and headers, fetch the cellpadding value from the
+ // parent table and set it as CSS padding attribute.
+ HTML.Tag tag = (HTML.Tag)
+ htmlAttrSet.getAttribute(StyleConstants.NameAttribute);
+ if ((tag == HTML.Tag.TD || tag == HTML.Tag.TH)
+ && htmlAttrSet instanceof Element)
+ {
+ Element el = (Element) htmlAttrSet;
+ AttributeSet tableAttrs = el.getParentElement().getParentElement()
+ .getAttributes();
+ o = tableAttrs.getAttribute(HTML.Attribute.CELLPADDING);
+ if (o != null)
+ {
+ Length l = new Length(o.toString());
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_BOTTOM, l);
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_LEFT, l);
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_RIGHT, l);
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.PADDING_TOP, l);
+ }
+ o = tableAttrs.getAttribute(HTML.Attribute.BORDER);
+ cssAttr = translateBorder(cssAttr, o);
+ }
+
+ // Translate border attribute.
+ o = cssAttr.getAttribute(HTML.Attribute.BORDER);
+ cssAttr = translateBorder(cssAttr, o);
+
+ // TODO: Add more mappings.
+ return cssAttr;
+ }
+
+ /**
+ * Translates a HTML border attribute to a corresponding set of CSS
+ * attributes.
+ *
+ * @param cssAttr the original set of CSS attributes to add to
+ * @param o the value of the border attribute
+ *
+ * @return the new set of CSS attributes
+ */
+ private AttributeSet translateBorder(AttributeSet cssAttr, Object o)
+ {
+ if (o != null)
+ {
+ BorderWidth l = new BorderWidth(o.toString());
+ if (l.getValue() > 0)
+ {
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_WIDTH, l);
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_STYLE,
+ "solid");
+ cssAttr = addAttribute(cssAttr, CSS.Attribute.BORDER_COLOR,
+ new CSSColor("black"));
+ }
+ }
+ return cssAttr;
+ }
+
+ /**
+ * Adds an attribute to the given set and returns a new set. This is implemented
+ * to convert StyleConstants attributes to CSS before forwarding them to the superclass.
+ * The StyleConstants attribute do not have corresponding CSS entry, the attribute
+ * is stored (but will likely not be used).
+ *
+ * @param old - the old set
+ * @param key - the non-null attribute key
+ * @param value - the attribute value
+ * @return the updated set
+ */
+ public AttributeSet addAttribute(AttributeSet old, Object key,
+ Object value)
+ {
+ // FIXME: Not implemented.
+ return super.addAttribute(old, key, value);
+ }
+
+ /**
+ * Adds a set of attributes to the element. If any of these attributes are
+ * StyleConstants, they will be converted to CSS before forwarding to the
+ * superclass.
+ *
+ * @param old - the old set
+ * @param attr - the attributes to add
+ * @return the updated attribute set
+ */
+ public AttributeSet addAttributes(AttributeSet old, AttributeSet attr)
+ {
+ // FIXME: Not implemented.
+ return super.addAttributes(old, attr);
+ }
+
+ /**
+ * Removes an attribute from the set. If the attribute is a
+ * StyleConstants, it will be converted to CSS before forwarding to the
+ * superclass.
+ *
+ * @param old - the old set
+ * @param key - the non-null attribute key
+ * @return the updated set
+ */
+ public AttributeSet removeAttribute(AttributeSet old, Object key)
+ {
+ // FIXME: Not implemented.
+ return super.removeAttribute(old, key);
+ }
+
+ /**
+ * Removes an attribute from the set. If any of the attributes are
+ * StyleConstants, they will be converted to CSS before forwarding to the
+ * superclass.
+ *
+ * @param old - the old set
+ * @param attrs - the attributes to remove
+ * @return the updated set
+ */
+ public AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs)
+ {
+ // FIXME: Not implemented.
+ return super.removeAttributes(old, attrs);
+ }
+
+ /**
+ * Removes a set of attributes for the element. If any of the attributes is a
+ * StyleConstants, they will be converted to CSS before forwarding to the
+ * superclass.
+ *
+ * @param old - the old attribute set
+ * @param names - the attribute names
+ * @return the update attribute set
+ */
+ public AttributeSet removeAttributes(AttributeSet old, Enumeration<?> names)
+ {
+ // FIXME: Not implemented.
+ return super.removeAttributes(old, names);
+ }
+
+ /**
+ * Creates a compact set of attributes that might be shared. This is a hook
+ * for subclasses that want to change the behaviour of SmallAttributeSet.
+ *
+ * @param a - the set of attributes to be represented in the compact form.
+ * @return the set of attributes created
+ */
+ protected StyleContext.SmallAttributeSet createSmallAttributeSet(AttributeSet a)
+ {
+ return super.createSmallAttributeSet(a);
+ }
+
+ /**
+ * Creates a large set of attributes. This set is not shared. This is a hook
+ * for subclasses that want to change the behaviour of the larger attribute
+ * storage format.
+ *
+ * @param a - the set of attributes to be represented in the larger form.
+ * @return the large set of attributes.
+ */
+ protected MutableAttributeSet createLargeAttributeSet(AttributeSet a)
+ {
+ return super.createLargeAttributeSet(a);
+ }
+
+ /**
+ * Gets the font to use for the given set.
+ *
+ * @param a - the set to get the font for.
+ * @return the font for the set
+ */
+ public Font getFont(AttributeSet a)
+ {
+ int realSize = getFontSize(a);
+
+ // Decrement size for subscript and superscript.
+ Object valign = a.getAttribute(CSS.Attribute.VERTICAL_ALIGN);
+ if (valign != null)
+ {
+ String v = valign.toString();
+ if (v.contains("sup") || v.contains("sub"))
+ realSize -= 2;
+ }
+
+ // TODO: Convert font family.
+ String family = "SansSerif";
+
+ int style = Font.PLAIN;
+ FontWeight weight = (FontWeight) a.getAttribute(CSS.Attribute.FONT_WEIGHT);
+ if (weight != null)
+ style |= weight.getValue();
+ FontStyle fStyle = (FontStyle) a.getAttribute(CSS.Attribute.FONT_STYLE);
+ if (fStyle != null)
+ style |= fStyle.getValue();
+ return new Font(family, style, realSize);
+ }
+
+ /**
+ * Determines the EM base value based on the specified attributes.
+ *
+ * @param atts the attibutes
+ *
+ * @return the EM base value
+ */
+ float getEMBase(AttributeSet atts)
+ {
+ Font font = getFont(atts);
+ FontRenderContext ctx = new FontRenderContext(null, false, false);
+ Rectangle2D bounds = font.getStringBounds("M", ctx);
+ return (float) bounds.getWidth();
+ }
+
+ /**
+ * Determines the EX base value based on the specified attributes.
+ *
+ * @param atts the attibutes
+ *
+ * @return the EX base value
+ */
+ float getEXBase(AttributeSet atts)
+ {
+ Font font = getFont(atts);
+ FontRenderContext ctx = new FontRenderContext(null, false, false);
+ Rectangle2D bounds = font.getStringBounds("x", ctx);
+ return (float) bounds.getHeight();
+ }
+
+ /**
+ * Resolves the fontsize for a given set of attributes.
+ *
+ * @param atts the attributes
+ *
+ * @return the resolved font size
+ */
+ private int getFontSize(AttributeSet atts)
+ {
+ int size = 12;
+ if (atts.isDefined(CSS.Attribute.FONT_SIZE))
+ {
+ FontSize fs = (FontSize) atts.getAttribute(CSS.Attribute.FONT_SIZE);
+ if (fs.isRelative())
+ {
+ int parSize = 12;
+ AttributeSet resolver = atts.getResolveParent();
+ if (resolver != null)
+ parSize = getFontSize(resolver);
+ size = fs.getValue(parSize);
+ }
+ else
+ {
+ size = fs.getValue();
+ }
+ }
+ else
+ {
+ AttributeSet resolver = atts.getResolveParent();
+ if (resolver != null)
+ size = getFontSize(resolver);
+ }
+ return size;
+ }
+
+ /**
+ * Takes a set of attributes and turns it into a foreground
+ * color specification. This is used to specify things like, brigher, more hue
+ * etc.
+ *
+ * @param a - the set to get the foreground color for
+ * @return the foreground color for the set
+ */
+ public Color getForeground(AttributeSet a)
+ {
+ CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.COLOR);
+ Color color = null;
+ if (c != null)
+ color = c.getValue();
+ return color;
+ }
+
+ /**
+ * Takes a set of attributes and turns it into a background
+ * color specification. This is used to specify things like, brigher, more hue
+ * etc.
+ *
+ * @param a - the set to get the background color for
+ * @return the background color for the set
+ */
+ public Color getBackground(AttributeSet a)
+ {
+ CSSColor c = (CSSColor) a.getAttribute(CSS.Attribute.BACKGROUND_COLOR);
+ Color color = null;
+ if (c != null)
+ color = c.getValue();
+ return color;
+ }
+
+ /**
+ * Gets the box formatter to use for the given set of CSS attributes.
+ *
+ * @param a - the given set
+ * @return the box formatter
+ */
+ public BoxPainter getBoxPainter(AttributeSet a)
+ {
+ return new BoxPainter(a, this);
+ }
+
+ /**
+ * Gets the list formatter to use for the given set of CSS attributes.
+ *
+ * @param a - the given set
+ * @return the list formatter
+ */
+ public ListPainter getListPainter(AttributeSet a)
+ {
+ return new ListPainter(a, this);
+ }
+
+ /**
+ * Sets the base font size between 1 and 7.
+ *
+ * @param sz - the new font size for the base.
+ */
+ public void setBaseFontSize(int sz)
+ {
+ if (sz <= 7 && sz >= 1)
+ baseFontSize = sz;
+ }
+
+ /**
+ * Sets the base font size from the String. It can either identify
+ * a specific font size (between 1 and 7) or identify a relative
+ * font size such as +1 or -2.
+ *
+ * @param size - the new font size as a String.
+ */
+ public void setBaseFontSize(String size)
+ {
+ size = size.trim();
+ int temp = 0;
+ try
+ {
+ if (size.length() == 2)
+ {
+ int i = new Integer(size.substring(1)).intValue();
+ if (size.startsWith("+"))
+ temp = baseFontSize + i;
+ else if (size.startsWith("-"))
+ temp = baseFontSize - i;
+ }
+ else if (size.length() == 1)
+ temp = new Integer(size.substring(0)).intValue();
+
+ if (temp <= 7 && temp >= 1)
+ baseFontSize = temp;
+ }
+ catch (NumberFormatException nfe)
+ {
+ // Do nothing here
+ }
+ }
+
+ /**
+ * TODO
+ *
+ * @param pt - TODO
+ * @return TODO
+ */
+ public static int getIndexOfSize(float pt)
+ {
+ // FIXME: Not implemented.
+ return 0;
+ }
+
+ /**
+ * Gets the point size, given a size index.
+ *
+ * @param index - the size index
+ * @return the point size.
+ */
+ public float getPointSize(int index)
+ {
+ // FIXME: Not implemented.
+ return 0;
+ }
+
+ /**
+ * Given the string of the size, returns the point size value.
+ *
+ * @param size - the string representation of the size.
+ * @return - the point size value.
+ */
+ public float getPointSize(String size)
+ {
+ // FIXME: Not implemented.
+ return 0;
+ }
+
+ /**
+ * Convert the color string represenation into java.awt.Color. The valid
+ * values are like "aqua" , "#00FFFF" or "rgb(1,6,44)".
+ *
+ * @param colorName the color to convert.
+ * @return the matching java.awt.color
+ */
+ public Color stringToColor(String colorName)
+ {
+ return CSSColor.convertValue(colorName);
+ }
+
+ /**
+ * This class carries out some of the duties of CSS formatting. This enables views
+ * to present the CSS formatting while not knowing how the CSS values are cached.
+ *
+ * This object is reponsible for the insets of a View and making sure
+ * the background is maintained according to the CSS attributes.
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ */
+ public static class BoxPainter extends Object implements Serializable
+ {
+
+ /**
+ * The left inset.
+ */
+ private float leftInset;
+
+ /**
+ * The right inset.
+ */
+ private float rightInset;
+
+ /**
+ * The top inset.
+ */
+ private float topInset;
+
+ /**
+ * The bottom inset.
+ */
+ private float bottomInset;
+
+ /**
+ * The border of the box.
+ */
+ private Border border;
+
+ private float leftPadding;
+ private float rightPadding;
+ private float topPadding;
+ private float bottomPadding;
+
+ /**
+ * The background color.
+ */
+ private Color background;
+
+ /**
+ * Package-private constructor.
+ *
+ * @param as - AttributeSet for painter
+ */
+ BoxPainter(AttributeSet as, StyleSheet ss)
+ {
+ float emBase = ss.getEMBase(as);
+ float exBase = ss.getEXBase(as);
+ // Fetch margins.
+ Length l = (Length) as.getAttribute(CSS.Attribute.MARGIN_LEFT);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ leftInset = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.MARGIN_RIGHT);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ rightInset = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.MARGIN_TOP);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ topInset = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.MARGIN_BOTTOM);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ bottomInset = l.getValue();
+ }
+
+ // Fetch padding.
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_LEFT);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ leftPadding = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_RIGHT);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ rightPadding = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_TOP);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ topPadding = l.getValue();
+ }
+ l = (Length) as.getAttribute(CSS.Attribute.PADDING_BOTTOM);
+ if (l != null)
+ {
+ l.setFontBases(emBase, exBase);
+ bottomPadding = l.getValue();
+ }
+
+ // Determine border.
+ border = new CSSBorder(as, ss);
+
+ // Determine background.
+ background = ss.getBackground(as);
+
+ }
+
+
+ /**
+ * Gets the inset needed on a given side to account for the margin, border
+ * and padding.
+ *
+ * @param size - the size of the box to get the inset for. View.TOP, View.LEFT,
+ * View.BOTTOM or View.RIGHT.
+ * @param v - the view making the request. This is used to get the AttributeSet,
+ * amd may be used to resolve percentage arguments.
+ * @return the inset
+ * @throws IllegalArgumentException - for an invalid direction.
+ */
+ public float getInset(int size, View v)
+ {
+ float inset;
+ switch (size)
+ {
+ case View.TOP:
+ inset = topInset;
+ if (border != null)
+ inset += border.getBorderInsets(null).top;
+ inset += topPadding;
+ break;
+ case View.BOTTOM:
+ inset = bottomInset;
+ if (border != null)
+ inset += border.getBorderInsets(null).bottom;
+ inset += bottomPadding;
+ break;
+ case View.LEFT:
+ inset = leftInset;
+ if (border != null)
+ inset += border.getBorderInsets(null).left;
+ inset += leftPadding;
+ break;
+ case View.RIGHT:
+ inset = rightInset;
+ if (border != null)
+ inset += border.getBorderInsets(null).right;
+ inset += rightPadding;
+ break;
+ default:
+ inset = 0.0F;
+ }
+ return inset;
+ }
+
+ /**
+ * Paints the CSS box according to the attributes given. This should
+ * paint the border, padding and background.
+ *
+ * @param g - the graphics configuration
+ * @param x - the x coordinate
+ * @param y - the y coordinate
+ * @param w - the width of the allocated area
+ * @param h - the height of the allocated area
+ * @param v - the view making the request
+ */
+ public void paint(Graphics g, float x, float y, float w, float h, View v)
+ {
+ int inX = (int) (x + leftInset);
+ int inY = (int) (y + topInset);
+ int inW = (int) (w - leftInset - rightInset);
+ int inH = (int) (h - topInset - bottomInset);
+ if (background != null)
+ {
+ g.setColor(background);
+ g.fillRect(inX, inY, inW, inH);
+ }
+ if (border != null)
+ {
+ border.paintBorder(null, g, inX, inY, inW, inH);
+ }
+ }
+ }
+
+ /**
+ * This class carries out some of the CSS list formatting duties. Implementations
+ * of this class enable views to present the CSS formatting while not knowing anything
+ * about how the CSS values are being cached.
+ *
+ * @author Lillian Angel (langel@redhat.com)
+ */
+ public static class ListPainter implements Serializable
+ {
+
+ /**
+ * Attribute set for painter
+ */
+ private AttributeSet attributes;
+
+ /**
+ * The associated style sheet.
+ */
+ private StyleSheet styleSheet;
+
+ /**
+ * The bullet type.
+ */
+ private String type;
+
+ /**
+ * Package-private constructor.
+ *
+ * @param as - AttributeSet for painter
+ */
+ ListPainter(AttributeSet as, StyleSheet ss)
+ {
+ attributes = as;
+ styleSheet = ss;
+ type = (String) as.getAttribute(CSS.Attribute.LIST_STYLE_TYPE);
+ }
+
+ /**
+ * Cached rectangle re-used in the paint method below.
+ */
+ private final Rectangle tmpRect = new Rectangle();
+
+ /**
+ * Paints the CSS list decoration according to the attributes given.
+ *
+ * @param g - the graphics configuration
+ * @param x - the x coordinate
+ * @param y - the y coordinate
+ * @param w - the width of the allocated area
+ * @param h - the height of the allocated area
+ * @param v - the view making the request
+ * @param item - the list item to be painted >=0.
+ */
+ public void paint(Graphics g, float x, float y, float w, float h, View v,
+ int item)
+ {
+ // FIXME: This is a very simplistic list rendering. We still need
+ // to implement different bullet types (see type field) and custom
+ // bullets via images.
+ View itemView = v.getView(item);
+ AttributeSet viewAtts = itemView.getAttributes();
+ Object tag = viewAtts.getAttribute(StyleConstants.NameAttribute);
+ // Only paint something here when the child view is an LI tag
+ // and the calling view is some of the list tags then).
+ if (tag != null && tag == HTML.Tag.LI)
+ {
+ g.setColor(Color.BLACK);
+ int centerX = (int) (x - 12);
+ int centerY = -1;
+ // For paragraphs (almost all cases) center bullet vertically
+ // in the middle of the first line.
+ tmpRect.setBounds((int) x, (int) y, (int) w, (int) h);
+ if (itemView.getViewCount() > 0)
+ {
+ View v1 = itemView.getView(0);
+ if (v1 instanceof ParagraphView && v1.getViewCount() > 0)
+ {
+ Shape a1 = itemView.getChildAllocation(0, tmpRect);
+ Rectangle r1 = a1 instanceof Rectangle ? (Rectangle) a1
+ : a1.getBounds();
+ ParagraphView par = (ParagraphView) v1;
+ Shape a = par.getChildAllocation(0, r1);
+ if (a != null)
+ {
+ Rectangle r = a instanceof Rectangle ? (Rectangle) a
+ : a.getBounds();
+ centerY = (int) (r.height / 2 + r.y);
+ }
+ }
+ }
+ if (centerY == -1)
+ {
+ centerY =(int) (h / 2 + y);
+ }
+ g.fillOval(centerX - 3, centerY - 3, 6, 6);
+ }
+ }
+ }
+
+ /**
+ * Converts an AttributeSet to a Map. This is used for CSS resolving.
+ *
+ * @param atts the attributes to convert
+ *
+ * @return the converted map
+ */
+ private Map<String,String> attributeSetToMap(AttributeSet atts)
+ {
+ HashMap<String,String> map = new HashMap<String,String>();
+ Enumeration<?> keys = atts.getAttributeNames();
+ while (keys.hasMoreElements())
+ {
+ Object key = keys.nextElement();
+ Object value = atts.getAttribute(key);
+ map.put(key.toString(), value.toString());
+ }
+ return map;
+ }
+}