/*
* Created on Jun 16, 2005
*
*/
package net.atlanticbb.tantlinger.ui.text;
import java.awt.Color;
import java.io.ByteArrayInputStream;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import javax.swing.JEditorPane;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Element;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyledDocument;
import javax.swing.text.StyledEditorKit;
import javax.swing.text.html.CSS;
import javax.swing.text.html.HTML;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.tidy.Tidy;
/**
* A collection of static convenience methods for working with HTML,
* HTMLDocuments, AttributeSets and Elements from HTML documents.
*
* @author Bob Tantlinger
*
*/
public class HTMLUtils
{
private static final org.w3c.tidy.Tidy tidy = new Tidy();
static
{
tidy.setQuiet(true);
tidy.setShowWarnings(false);
tidy.setForceOutput(true);
tidy.setFixComments(true);
tidy.setFixUri(true);
tidy.setDropEmptyParas(true);
tidy.setLiteralAttribs(true);
tidy.setTrimEmptyElements(true);
tidy.setXHTML(true);
//tidy.setInputEncoding("UTF-16");
//tidy.setOutputEncoding("UTF-16");
}
/**
* Tests if an element is an implied paragraph (p-implied)
*
* @param el The element
* @return true if the elements name equals "p-implied", false otherwise
*/
public static boolean isImplied(final Element el)
{
return el.getName().equals("p-implied");
}
/**
* Incloses a chunk of HTML text in the specified tag
* @param enclTag the tag to enclose the HTML in
* @param innerHTML the HTML to be inclosed
* @return
*/
public static String createTag(final HTML.Tag enclTag, final String innerHTML)
{
return createTag(enclTag, new SimpleAttributeSet(), innerHTML);
}
/**
* Incloses a chunk of HTML text in the specified tag
* with the specified attribs
*
* @param enclTag
* @param set
* @param innerHTML
* @return
*/
public static String createTag(final HTML.Tag enclTag, final AttributeSet set, final String innerHTML)
{
final String t = tagOpen(enclTag, set) + innerHTML + tagClose(enclTag);
return t;
}
private static String tagOpen(final HTML.Tag enclTag, final AttributeSet set)
{
String t = "<" + enclTag;
for(final Enumeration e = set.getAttributeNames(); e.hasMoreElements();)
{
final Object name = e.nextElement();
if(!name.toString().equals("name"))
{
final Object val = set.getAttribute(name);
t += " " + name + "=\"" + val + "\"";
}
}
return t + ">";
}
private static String tagClose(final HTML.Tag t)
{
return "</" + t + ">";
}
public static List getParagraphElements(final JEditorPane editor)
{
final List elems = new LinkedList();
try
{
final HTMLDocument doc = (HTMLDocument)editor.getDocument();
Element curE = getParaElement(doc, editor.getSelectionStart());
final Element endE = getParaElement(doc, editor.getSelectionEnd());
while(curE.getEndOffset() <= endE.getEndOffset())
{
elems.add(curE);
curE = getParaElement(doc, curE.getEndOffset() + 1);
if(curE.getEndOffset() >= doc.getLength())
break;
}
}
catch(final ClassCastException cce){}
return elems;
}
private static Element getParaElement(final HTMLDocument doc, final int pos)
{
Element curE = doc.getParagraphElement(pos);
while(isImplied(curE))
{
curE = curE.getParentElement();
}
final Element lp = getListParent(curE);
if(lp != null)
curE = lp;
return curE;
}
/**
* Searches upward for the specified parent for the element.
* @param curElem
* @param parentTag
* @return The parent element, or null if the parent wasnt found
*/
public static Element getParent(final Element curElem, final HTML.Tag parentTag)
{
Element parent = curElem;
while(parent != null)
{
if(parent.getName().equals(parentTag.toString()))
return parent;
parent = parent.getParentElement();
}
return null;
}
/**
* Tests if the element is empty
* @param el
* @return
*/
public static boolean isElementEmpty(final Element el)
{
final String s = getElementHTML(el, false).trim();
return s.length() == 0;
}
/**
* Searches for a list {@link Element} that is the parent of the specified {@link Element}.
*
* @param elem
* @return A list element (UL, OL, DIR, MENU, or DL) if found, null otherwise
*/
public static Element getListParent(final Element elem)
{
Element parent = elem;
while(parent != null)
{
if(parent.getName().toUpperCase().equals("UL") ||
parent.getName().toUpperCase().equals("OL") ||
parent.getName().equals("dl") || parent.getName().equals("menu") ||
parent.getName().equals("dir"))
return parent;
parent = parent.getParentElement();
}
return null;
}
/**
* Gets the element one position less than the start of the specified element
* @param doc
* @param el
* @return
*/
public static Element getPreviousElement(final HTMLDocument doc, final Element el)
{
if(el.getStartOffset() > 0)
return doc.getParagraphElement(el.getStartOffset() - 1);
return el;
}
/**
* Gets the element one position greater than the end of the specified element
* @param doc
* @param el
* @return
*/
public static Element getNextElement(final HTMLDocument doc, final Element el)
{
if(el.getEndOffset() < doc.getLength())
return doc.getParagraphElement(el.getEndOffset() + 1);
return el;
}
/**
* Removes the enclosing tags from a chunk of HTML text
* @param elem
* @param txt
* @return
*/
public static String removeEnclosingTags(final Element elem, final String txt)
{
final HTML.Tag t = HTML.getTag(elem.getName());
return removeEnclosingTags(t, txt);
}
/**
* Removes the enclosing tags from a chunk of HTML text
* @param t
* @param txt
* @return
*/
public static String removeEnclosingTags(final HTML.Tag t, String txt)
{
final String openStart = "<" + t;
final String closeTag = "</" + t + ">";
txt = txt.trim();
if(txt.startsWith(openStart))
{
final int n = txt.indexOf(">");
if(n != -1)
{
txt = txt.substring(n + 1, txt.length());
}
}
if(txt.endsWith(closeTag))
{
txt = txt.substring(0, txt.length() - closeTag.length());
}
return txt;
}
/**
* Gets the html of the specified {@link Element}
*
* @param el
* @param includeEnclosingTags true, if the enclosing tags should be included
* @return
*/
public static String getElementHTML(final Element el, final boolean includeEnclosingTags)
{
String txt = "";
try
{
final StringWriter out = new StringWriter();
final ElementWriter w = new ElementWriter(out, el);
w.write();
txt = out.toString();
}
catch (final Exception ex)
{
ex.printStackTrace();
}
if(includeEnclosingTags)
return txt;
return removeEnclosingTags(el, txt);
}
/**
* Removes an element from the document that contains it
*
* @param el
* @throws BadLocationException
*/
public static void removeElement(final Element el) throws BadLocationException
{
final HTMLDocument document = (HTMLDocument)el.getDocument();
final int start = el.getStartOffset();
int len = el.getEndOffset() - start;
final Element tdEle = HTMLUtils.getParent(el, HTML.Tag.TD);
if(tdEle != null && el.getEndOffset() == tdEle.getEndOffset())
{
document.remove(start, len - 1);
}
else
{
if(el.getEndOffset() > document.getLength())
len = document.getLength() - start;
document.remove(start, len);
}
}
public static HTML.Tag getStartTag(final String text)
{
final String html = text.trim();
final int s = html.indexOf('<');
if(s != 0)//doesn't start with a tag.
return null;
final int e = html.indexOf('>');
if(e == -1)
return null; //not any kind of tag
String tagName = html.substring(1, e).trim();
if(tagName.indexOf(' ') != -1)
tagName = tagName.split("\\s")[0];
return HTML.getTag(tagName);
}
private static int depthFromRoot(final Element curElem)
{
Element parent = curElem;
int depth = 0;
while(parent != null)
{
if(parent.getName().equals("body") || /*parent.getName().equals("blockquote") ||*/ parent.getName().equals("td"))
break;
parent = parent.getParentElement();
depth++;
}
return depth;
}
/**
* Inserts an arbitrary chunk of HTML into the JEditorPane at the current
* caret position.
*
* @param rawHtml
* @param editor
*/
public static void insertArbitraryHTML(final String rawHtml, final JEditorPane editor)
{
tidy.setOutputEncoding("UTF-8");
tidy.setInputEncoding("UTF-8");
try
{
final ByteArrayInputStream bin = new ByteArrayInputStream(rawHtml.getBytes("UTF-8"));
final Document doc = tidy.parseDOM(bin, null);
final NodeList nodelist = doc.getElementsByTagName("body");
if(nodelist != null)
{
final Node body = nodelist.item(0);
final NodeList bodyChildren = body.getChildNodes();
//for(int i = bodyChildren.getLength() - 1; i >= 0; i--)
final int len = bodyChildren.getLength();
for(int i = 0; i < len; i++)
{
String ml = xmlToString(bodyChildren.item(i));
if(ml != null)
{
//System.out.println(ml);
HTML.Tag tag = getStartTag(ml);
if(tag == null)
{
tag = HTML.Tag.SPAN;
ml = "<span>" + ml + "</span>";
}
insertHTML(ml, tag, editor);
}
}
}
}
catch (final UnsupportedEncodingException e)
{
e.printStackTrace();
}
}
private static String xmlToString(final Node node)
{
try
{
final Source source = new DOMSource(node);
final StringWriter stringWriter = new StringWriter();
final Result result = new StreamResult(stringWriter);
final TransformerFactory factory = TransformerFactory.newInstance();
final Transformer transformer = factory.newTransformer();
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
transformer.transform(source, result);
return stringWriter.getBuffer().toString();
}
catch(final TransformerConfigurationException e)
{
e.printStackTrace();
}
catch(final TransformerException e)
{
e.printStackTrace();
}
return null;
}
/**
* Inserts a string of html into the {@link JEditorPane}'s {@link HTMLDocument}
* at the current caret position.
*
* @param html
* @param tag
* @param editor
*/
public static void insertHTML(String html, final HTML.Tag tag, final JEditorPane editor)
{
HTMLEditorKit editorKit;
HTMLDocument document;
try
{
editorKit = (HTMLEditorKit)editor.getEditorKit();
document = (HTMLDocument)editor.getDocument();
}
catch(final ClassCastException ex)
{
return;
}
final int caret = editor.getCaretPosition();
final Element pElem = document.getParagraphElement(caret);
final boolean breakParagraph = tag.breaksFlow() || tag.isBlock();
final boolean beginParagraph = caret == pElem.getStartOffset();
html = jEditorPaneizeHTML(html);
//System.out.println(html);
try
{
if(breakParagraph && beginParagraph)
{
//System.out.println("breakParagraph && beginParagraph");
document.insertBeforeStart(pElem, "<p></p>");
final Element nextEl = document.getParagraphElement(caret + 1);
editorKit.insertHTML(document, caret + 1, html, depthFromRoot(nextEl)/*1*/, 0, tag);
document.remove(caret, 1);
}
else if(breakParagraph && !beginParagraph)
{
//System.out.println("breakParagraph && !beginParagraph");
editorKit.insertHTML(document, caret, html, depthFromRoot(pElem)/*1*/, 0, tag);
}
else if(!breakParagraph && beginParagraph)
{
//System.out.println("!breakParagraph && beginParagraph");
/* Trick: insert a non-breaking space after start, so that we're inserting into the middle of a line.
* Then, remove the space. This works around a bug when using insertHTML near the beginning of a
* paragraph.*/
document.insertAfterStart(pElem, " ");
editorKit.insertHTML(document, caret + 1, html, 0, 0, tag);
document.remove(caret, 1);
}
else if(!breakParagraph && !beginParagraph)
{
//System.out.println("!breakParagraph && !beginParagraph");
editorKit.insertHTML(document, caret, html, 0, 0, tag);
}
}
catch(final Exception ex)
{
ex.printStackTrace();
}
}
/**
* Gets the character attributes at the {@link JEditorPane}'s caret position
* <p>
* If there is no selection, the character attributes at caretPos - 1 are retuned.
* If there is a slection, the attributes at selectionEnd - 1 are returned
* </p>
*
* @param editor
* @return An {@link AttributeSet} or null, if the editor doesn't have a {@link StyledDocument}
*/
public static AttributeSet getCharacterAttributes(final JEditorPane editor)
{
int p;
if(editor.getSelectedText() != null)
{
p = editor.getSelectionEnd() - 1;
}
else
{
p = (editor.getCaretPosition() > 0) ? (editor.getCaretPosition() - 1) : 0;
}
try
{
final StyledDocument doc = (StyledDocument)editor.getDocument();
return (doc.getCharacterElement(p).getAttributes());
}
catch(final ClassCastException cce){}
return null;
}
/**
* Gets the font family name at the {@link JEditorPane}'s current caret position
*
* @param editor
* @return The font family name, or null if no font is set
*/
public static String getFontFamily(final JEditorPane editor)
{
final AttributeSet attr = getCharacterAttributes(editor);
if(attr != null)
{
Object val = attr.getAttribute(StyleConstants.FontFamily);
if(val != null)
return val.toString();
val = attr.getAttribute(CSS.Attribute.FONT_FAMILY);
if(val != null)
return val.toString();
val = attr.getAttribute(HTML.Tag.FONT);
if(val != null && val instanceof AttributeSet)
{
final AttributeSet set = (AttributeSet)val;
val = set.getAttribute(HTML.Attribute.FACE);
if(val != null)
return val.toString();
}
}
return null; //no font family was defined
}
/**
* Set's the font family at the {@link JEditorPane}'s current caret positon, or
* for the current selection (if there is one).
* <p>
* If the fontName parameter is null, any currently set font family is removed.
* </p>
*
* @param editor
* @param fontName
*/
public static void setFontFamily(final JEditorPane editor, final String fontName)
{
final AttributeSet attr = getCharacterAttributes(editor);
if(attr == null)
return;
/*try
{
HTMLDocument doc = (HTMLDocument)editor.getDocument();
attr = doc.getCharacterElement(editor.getCaretPosition()).getAttributes();
}
catch(ClassCastException cce)
{
return;
}*/
printAttribs(attr);
if(fontName == null) //we're removing the font
{
//the font might be defined as a font tag
Object val = attr.getAttribute(HTML.Tag.FONT);
if(val != null && val instanceof AttributeSet)
{
final MutableAttributeSet set = new SimpleAttributeSet((AttributeSet)val);
val = set.getAttribute(HTML.Attribute.FACE); //does it have a FACE attrib?
if(val != null)
{
set.removeAttribute(HTML.Attribute.FACE);
removeCharacterAttribute(editor, HTML.Tag.FONT); //remove the current font tag
if(set.getAttributeCount() > 0)
{
//it's not empty so replace the other font attribs
final SimpleAttributeSet fontSet = new SimpleAttributeSet();
fontSet.addAttribute(HTML.Tag.FONT, set);
setCharacterAttributes(editor, set);
}
}
}
//also remove these for good measure
removeCharacterAttribute(editor, StyleConstants.FontFamily);
removeCharacterAttribute(editor, CSS.Attribute.FONT_FAMILY);
}
else //adding the font family
{
final MutableAttributeSet tagAttrs = new SimpleAttributeSet();
tagAttrs.addAttribute(StyleConstants.FontFamily, fontName);
setCharacterAttributes(editor, tagAttrs);
}
printAttribs(attr);
}
/**
* Removes a CSS character attribute that has the specified value
* from the {@link JEditorPane}'s current caret position
* or selection.
* <p>
* The val parameter is a {@link String} even though the actual attribute value is not.
* This is because the actual attribute values are not public. Thus, this method checks
* the value via the toString() method</p>
*
* @param editor
* @param atr
* @param val
*/
public static void removeCharacterAttribute(final JEditorPane editor, final CSS.Attribute atr, final String val)
{
HTMLDocument doc;
MutableAttributeSet attr;
try
{
doc = (HTMLDocument)editor.getDocument();
attr = ((HTMLEditorKit)editor.getEditorKit()).getInputAttributes();
}
catch(final ClassCastException cce)
{
return;
}
final List tokens = tokenizeCharAttribs(doc, editor.getSelectionStart(), editor.getSelectionEnd());
for(final Iterator it = tokens.iterator(); it.hasNext();)
{
final CharStyleToken t = (CharStyleToken)it.next();
if(t.attrs.isDefined(atr) && t.attrs.getAttribute(atr).toString().equals(val))
{
final SimpleAttributeSet sas = new SimpleAttributeSet();
sas.addAttributes(t.attrs);
sas.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT);
sas.removeAttribute(atr);
doc.setCharacterAttributes(t.offs, t.len, sas, true);
}
}
final int pos = editor.getCaretPosition();
attr.addAttributes(doc.getCharacterElement(pos).getAttributes());
attr.removeAttribute(atr);
}
/**
* Removes a single character attribute from the editor's current position/selection.
*
* <p>Removes from the editor kit's input attribtues and/or document at the caret position.
* If there is a selction the attribute is removed from the selected text</p>
*
* @param editor
* @param atr
*/
public static void removeCharacterAttribute(final JEditorPane editor, final Object atr)
{
HTMLDocument doc;
MutableAttributeSet attr;
try
{
doc = (HTMLDocument)editor.getDocument();
attr = ((HTMLEditorKit)editor.getEditorKit()).getInputAttributes();
}
catch(final ClassCastException cce)
{
return;
}
final List tokens = tokenizeCharAttribs(doc, editor.getSelectionStart(), editor.getSelectionEnd());
for(final Iterator it = tokens.iterator(); it.hasNext();)
{
final CharStyleToken t = (CharStyleToken)it.next();
if(t.attrs.isDefined(atr))
{
final SimpleAttributeSet sas = new SimpleAttributeSet();
sas.addAttributes(t.attrs);
sas.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT);
sas.removeAttribute(atr);
doc.setCharacterAttributes(t.offs, t.len, sas, true);
}
}
final int pos = editor.getCaretPosition();
attr.addAttributes(doc.getCharacterElement(pos).getAttributes());
attr.removeAttribute(atr);
}
/**
* Tokenizes character attrbutes.
* @param doc
* @param s
* @param e
* @return
*/
private static List tokenizeCharAttribs(final HTMLDocument doc, int s, final int e)
{
final LinkedList tokens = new LinkedList();
CharStyleToken tok = new CharStyleToken();
for(; s <= e; s++ )
{
//if(s == doc.getLength())
// break;
final AttributeSet as = doc.getCharacterElement(s).getAttributes();
if(tok.attrs == null || (s + 1 <= e && !as.isEqual(tok.attrs)))
{
tok = new CharStyleToken();
tok.offs = s;
tokens.add(tok);
tok.attrs = as;
}
if(s+1 <= e)
tok.len++;
}
return tokens;
}
/**
* Sets the character attributes for selection of the specified editor
*
* @param editor
* @param attrs
* @param replace if true, replaces the attrubutes
*/
public static void setCharacterAttributes(final JEditorPane editor, AttributeSet attr, final boolean replace)
{
HTMLDocument doc;
StyledEditorKit k;
try
{
doc = (HTMLDocument)editor.getDocument();
k = (StyledEditorKit)editor.getEditorKit();
}
catch(final ClassCastException ex)
{
return;
}
//TODO figure out what the "CR" attribute is.
//Somewhere along the line the attribute CR (String key) with a value of Boolean.TRUE
//gets inserted. If it is in the attributes, something gets screwed up
//and the text gets all jumbled up and doesn't render correctly.
//Is it yet another JEditorPane bug?
final MutableAttributeSet inputAttributes = k.getInputAttributes();
final SimpleAttributeSet sas = new SimpleAttributeSet(attr);
sas.removeAttribute("CR");
attr = sas;
final int p0 = editor.getSelectionStart();
final int p1 = editor.getSelectionEnd();
if(p0 != p1)
{
doc.setCharacterAttributes(p0, p1 - p0, attr, replace);
}
else
{
//No selection, so we have to update the input attributes
//otherwise they apparently get reread from the document...
//not sure if this is a bug or what, but the following works
//so just go with it.
if(replace)
{
attr = attr.copyAttributes();
inputAttributes.removeAttributes(inputAttributes);
inputAttributes.addAttribute(StyleConstants.NameAttribute, HTML.Tag.CONTENT);
}
inputAttributes.addAttributes(attr);
//System.err.println("inputAttr: " + inputAttributes);
}
}
/**
* Sets the character attributes for selection of the specified editor
*
* @param editor
* @param attrs
*/
public static void setCharacterAttributes(final JEditorPane editor, final AttributeSet attrs)
{
setCharacterAttributes(editor, attrs, false);
}
/**
* Converts an html tag attribute list to a {@link Map}.
* For example, the String 'href="http://blah.com" target="_self"' becomes
* name-value pairs:<br>
* href > http://blah.com<br>
* target > _self
* @param atts
* @return
*/
public static Map tagAttribsToMap(final String atts)
{
final Map attribs = new HashMap();
final StringTokenizer st = new StringTokenizer(atts.trim(), " ");
String lastAtt = null;
while(st.hasMoreTokens())
{
final String atVal = st.nextToken().trim();
final int equalPos = atVal.indexOf('=');
if(equalPos == -1)
{
if(lastAtt == null)
break;//no equals char in this string
final String lastVal = attribs.get(lastAtt).toString();
attribs.put(lastAtt, lastVal + " " + atVal);
continue;
}
final String at = atVal.substring(0, equalPos);
String val = atVal.substring(atVal.indexOf('=') + 1, atVal.length());
if(val.startsWith("\""))
val = val.substring(1, val.length());
if(val.endsWith("\""))
val = val.substring(0, val.length() - 1);
attribs.put(at, val);
lastAtt = at;
}
return attribs;
}
/**
* Converts a Color to a hex string
* in the format "#RRGGBB"
*/
public static String colorToHex(final Color color)
{
String colorstr = new String("#");
// Red
String str = Integer.toHexString(color.getRed());
if (str.length() > 2)
str = str.substring(0, 2);
else if (str.length() < 2)
colorstr += "0" + str;
else
colorstr += str;
// Green
str = Integer.toHexString(color.getGreen());
if (str.length() > 2)
str = str.substring(0, 2);
else if (str.length() < 2)
colorstr += "0" + str;
else
colorstr += str;
// Blue
str = Integer.toHexString(color.getBlue());
if (str.length() > 2)
str = str.substring(0, 2);
else if (str.length() < 2)
colorstr += "0" + str;
else
colorstr += str;
return colorstr;
}
/**
* Convert a "#FFFFFF" hex string to a Color.
* If the color specification is bad, an attempt
* will be made to fix it up.
*/
public static Color hexToColor(final String value)
{
String digits;
//int n = value.length();
if(value.startsWith("#"))
digits = value.substring(1, Math.min(value.length(), 7));
else
digits = value;
final String hstr = "0x" + digits;
Color c;
try
{
c = Color.decode(hstr);
}
catch(final NumberFormatException nfe)
{
c = Color.BLACK; // just return black
}
return c;
}
/**
* Convert a color string such as "RED" or "#NNNNNN" or "rgb(r, g, b)"
* to a Color.
*/
public static Color stringToColor(final String str)
{
Color color = null;
if (str.length() == 0)
color = Color.black;
else if (str.charAt(0) == '#')
color = hexToColor(str);
else if (str.equalsIgnoreCase("Black"))
color = hexToColor("#000000");
else if(str.equalsIgnoreCase("Silver"))
color = hexToColor("#C0C0C0");
else if(str.equalsIgnoreCase("Gray"))
color = hexToColor("#808080");
else if(str.equalsIgnoreCase("White"))
color = hexToColor("#FFFFFF");
else if(str.equalsIgnoreCase("Maroon"))
color = hexToColor("#800000");
else if(str.equalsIgnoreCase("Red"))
color = hexToColor("#FF0000");
else if(str.equalsIgnoreCase("Purple"))
color = hexToColor("#800080");
else if(str.equalsIgnoreCase("Fuchsia"))
color = hexToColor("#FF00FF");
else if(str.equalsIgnoreCase("Green"))
color = hexToColor("#008000");
else if(str.equalsIgnoreCase("Lime"))
color = hexToColor("#00FF00");
else if(str.equalsIgnoreCase("Olive"))
color = hexToColor("#808000");
else if(str.equalsIgnoreCase("Yellow"))
color = hexToColor("#FFFF00");
else if(str.equalsIgnoreCase("Navy"))
color = hexToColor("#000080");
else if(str.equalsIgnoreCase("Blue"))
color = hexToColor("#0000FF");
else if(str.equalsIgnoreCase("Teal"))
color = hexToColor("#008080");
else if(str.equalsIgnoreCase("Aqua"))
color = hexToColor("#00FFFF");
else
color = hexToColor(str); // sometimes get specified without leading #
return color;
}
/**
* Removes self-closing tags from xhtml for the benifit of {@link JEditorPane}
*
* <p>JEditorpane can't handle empty xhtml containers like <br /> or <img />, so this method
* replaces them without the "/" as in <br></p>
*
* @param html
* @return JEditorpane friendly html
*/
public static String jEditorPaneizeHTML(final String html)
{
return html.replaceAll("(<\\s*\\w+\\b[^>]*)/(\\s*>)", "$1$2");
}
/**
* Helper method that prints out the contents of an {@link AttributeSet} to System.err
* for debugging
* @param attr
*/
public static void printAttribs(final AttributeSet attr)
{
System.err.println("----------------------------------------------------------------");
System.err.println(attr);
final Enumeration ee = attr.getAttributeNames();
while(ee.hasMoreElements())
{
final Object name = ee.nextElement();
final Object atr = attr.getAttribute(name);
System.err.println(name + " " + name.getClass().getName() + " | " + atr + " " + atr.getClass().getName());
}
System.err.println("----------------------------------------------------------------");
}
private static class CharStyleToken
{
int offs;
int len;
AttributeSet attrs;
}
}