| 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 */  | 
 | 
package javax.swing.text.html;  | 
 | 
 | 
 | 
import javax.swing.text.*;  | 
 | 
import java.io.Writer;  | 
 | 
import java.util.Stack;  | 
 | 
import java.util.Enumeration;  | 
 | 
import java.util.Vector;  | 
 | 
import java.io.IOException;  | 
 | 
import java.util.StringTokenizer;  | 
 | 
import java.util.NoSuchElementException;  | 
 | 
import java.net.URL;  | 
 | 
 | 
 | 
/**  | 
 | 
 * This is a writer for HTMLDocuments.  | 
 | 
 *  | 
 | 
 * @author  Sunita Mani  | 
 | 
 */  | 
 | 
 | 
 | 
 | 
 | 
public class HTMLWriter extends AbstractWriter { | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private Stack<Element> blockElementStack = new Stack<Element>();  | 
 | 
    private boolean inContent = false;  | 
 | 
    private boolean inPre = false;  | 
 | 
      | 
 | 
     * element. */  | 
 | 
    private int preEndOffset;  | 
 | 
    private boolean inTextArea = false;  | 
 | 
    private boolean newlineOutputed = false;  | 
 | 
    private boolean completeDoc;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private Vector<HTML.Tag> tags = new Vector<HTML.Tag>(10);  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private Vector<Object> tagValues = new Vector<Object>(10);  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private Segment segment;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private Vector<HTML.Tag> tagsToRemove = new Vector<HTML.Tag>(10);  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private boolean wroteHead;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private boolean replaceEntities;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private char[] tempChars;  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public HTMLWriter(Writer w, HTMLDocument doc) { | 
 | 
        this(w, doc, 0, doc.getLength());  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public HTMLWriter(Writer w, HTMLDocument doc, int pos, int len) { | 
 | 
        super(w, doc, pos, len);  | 
 | 
        completeDoc = (pos == 0 && len == doc.getLength());  | 
 | 
        setLineLength(80);  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    public void write() throws IOException, BadLocationException { | 
 | 
        ElementIterator it = getElementIterator();  | 
 | 
        Element current = null;  | 
 | 
        Element next;  | 
 | 
 | 
 | 
        wroteHead = false;  | 
 | 
        setCurrentLineLength(0);  | 
 | 
        replaceEntities = false;  | 
 | 
        setCanWrapLines(false);  | 
 | 
        if (segment == null) { | 
 | 
            segment = new Segment();  | 
 | 
        }  | 
 | 
        inPre = false;  | 
 | 
        boolean forcedBody = false;  | 
 | 
        while ((next = it.next()) != null) { | 
 | 
            if (!inRange(next)) { | 
 | 
                if (completeDoc && next.getAttributes().getAttribute(  | 
 | 
                        StyleConstants.NameAttribute) == HTML.Tag.BODY) { | 
 | 
                    forcedBody = true;  | 
 | 
                }  | 
 | 
                else { | 
 | 
                    continue;  | 
 | 
                }  | 
 | 
            }  | 
 | 
            if (current != null) { | 
 | 
 | 
 | 
                /*  | 
 | 
                  if next is child of current increment indent  | 
 | 
                */  | 
 | 
 | 
 | 
                if (indentNeedsIncrementing(current, next)) { | 
 | 
                    incrIndent();  | 
 | 
                } else if (current.getParentElement() != next.getParentElement()) { | 
 | 
                      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
                    */  | 
 | 
                    Element top = blockElementStack.peek();  | 
 | 
                    while (top != next.getParentElement()) { | 
 | 
                          | 
 | 
 | 
 | 
                        */  | 
 | 
                        blockElementStack.pop();  | 
 | 
                        if (!synthesizedElement(top)) { | 
 | 
                            AttributeSet attrs = top.getAttributes();  | 
 | 
                            if (!matchNameAttribute(attrs, HTML.Tag.PRE) &&  | 
 | 
                                !isFormElementWithContent(attrs)) { | 
 | 
                                decrIndent();  | 
 | 
                            }  | 
 | 
                            endTag(top);  | 
 | 
                        }  | 
 | 
                        top = blockElementStack.peek();  | 
 | 
                    }  | 
 | 
                } else if (current.getParentElement() == next.getParentElement()) { | 
 | 
                      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
                    */  | 
 | 
                    Element top = blockElementStack.peek();  | 
 | 
                    if (top == current) { | 
 | 
                        blockElementStack.pop();  | 
 | 
                        endTag(top);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
            if (!next.isLeaf() || isFormElementWithContent(next.getAttributes())) { | 
 | 
                blockElementStack.push(next);  | 
 | 
                startTag(next);  | 
 | 
            } else { | 
 | 
                emptyTag(next);  | 
 | 
            }  | 
 | 
            current = next;  | 
 | 
        }  | 
 | 
        /* Emit all remaining end tags */  | 
 | 
 | 
 | 
          | 
 | 
 | 
 | 
 | 
 | 
        */  | 
 | 
        closeOutUnwantedEmbeddedTags(null);  | 
 | 
 | 
 | 
        if (forcedBody) { | 
 | 
            blockElementStack.pop();  | 
 | 
            endTag(current);  | 
 | 
        }  | 
 | 
        while (!blockElementStack.empty()) { | 
 | 
            current = blockElementStack.pop();  | 
 | 
            if (!synthesizedElement(current)) { | 
 | 
                AttributeSet attrs = current.getAttributes();  | 
 | 
                if (!matchNameAttribute(attrs, HTML.Tag.PRE) &&  | 
 | 
                              !isFormElementWithContent(attrs)) { | 
 | 
                    decrIndent();  | 
 | 
                }  | 
 | 
                endTag(current);  | 
 | 
            }  | 
 | 
        }  | 
 | 
 | 
 | 
        if (completeDoc) { | 
 | 
            writeAdditionalComments();  | 
 | 
        }  | 
 | 
 | 
 | 
        segment.array = null;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected void writeAttributes(AttributeSet attr) throws IOException { | 
 | 
          | 
 | 
        convAttr.removeAttributes(convAttr);  | 
 | 
        convertToHTML32(attr, convAttr);  | 
 | 
 | 
 | 
        Enumeration names = convAttr.getAttributeNames();  | 
 | 
        while (names.hasMoreElements()) { | 
 | 
            Object name = names.nextElement();  | 
 | 
            if (name instanceof HTML.Tag ||  | 
 | 
                name instanceof StyleConstants ||  | 
 | 
                name == HTML.Attribute.ENDTAG) { | 
 | 
                continue;  | 
 | 
            }  | 
 | 
            write(" " + name + "=\"" + convAttr.getAttribute(name) + "\""); | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected void emptyTag(Element elem) throws BadLocationException, IOException { | 
 | 
 | 
 | 
        if (!inContent && !inPre) { | 
 | 
            indentSmart();  | 
 | 
        }  | 
 | 
 | 
 | 
        AttributeSet attr = elem.getAttributes();  | 
 | 
        closeOutUnwantedEmbeddedTags(attr);  | 
 | 
        writeEmbeddedTags(attr);  | 
 | 
 | 
 | 
        if (matchNameAttribute(attr, HTML.Tag.CONTENT)) { | 
 | 
            inContent = true;  | 
 | 
            text(elem);  | 
 | 
        } else if (matchNameAttribute(attr, HTML.Tag.COMMENT)) { | 
 | 
            comment(elem);  | 
 | 
        }  else { | 
 | 
            boolean isBlock = isBlockTag(elem.getAttributes());  | 
 | 
            if (inContent && isBlock ) { | 
 | 
                writeLineSeparator();  | 
 | 
                indentSmart();  | 
 | 
            }  | 
 | 
 | 
 | 
            Object nameTag = (attr != null) ? attr.getAttribute  | 
 | 
                              (StyleConstants.NameAttribute) : null;  | 
 | 
            Object endTag = (attr != null) ? attr.getAttribute  | 
 | 
                              (HTML.Attribute.ENDTAG) : null;  | 
 | 
 | 
 | 
            boolean outputEndTag = false;  | 
 | 
            // If an instance of an UNKNOWN Tag, or an instance of a  | 
 | 
            // tag that is only visible during editing  | 
 | 
              | 
 | 
            if (nameTag != null && endTag != null &&  | 
 | 
                (endTag instanceof String) &&  | 
 | 
                endTag.equals("true")) { | 
 | 
                outputEndTag = true;  | 
 | 
            }  | 
 | 
 | 
 | 
            if (completeDoc && matchNameAttribute(attr, HTML.Tag.HEAD)) { | 
 | 
                if (outputEndTag) { | 
 | 
                      | 
 | 
                    writeStyles(((HTMLDocument)getDocument()).getStyleSheet());  | 
 | 
                }  | 
 | 
                wroteHead = true;  | 
 | 
            }  | 
 | 
 | 
 | 
            write('<'); | 
 | 
            if (outputEndTag) { | 
 | 
                write('/'); | 
 | 
            }  | 
 | 
            write(elem.getName());  | 
 | 
            writeAttributes(attr);  | 
 | 
            write('>'); | 
 | 
            if (matchNameAttribute(attr, HTML.Tag.TITLE) && !outputEndTag) { | 
 | 
                Document doc = elem.getDocument();  | 
 | 
                String title = (String)doc.getProperty(Document.TitleProperty);  | 
 | 
                write(title);  | 
 | 
            } else if (!inContent || isBlock) { | 
 | 
                writeLineSeparator();  | 
 | 
                if (isBlock && inContent) { | 
 | 
                    indentSmart();  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected boolean isBlockTag(AttributeSet attr) { | 
 | 
        Object o = attr.getAttribute(StyleConstants.NameAttribute);  | 
 | 
        if (o instanceof HTML.Tag) { | 
 | 
            HTML.Tag name = (HTML.Tag) o;  | 
 | 
            return name.isBlock();  | 
 | 
        }  | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected void startTag(Element elem) throws IOException, BadLocationException { | 
 | 
 | 
 | 
        if (synthesizedElement(elem)) { | 
 | 
            return;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        AttributeSet attr = elem.getAttributes();  | 
 | 
        Object nameAttribute = attr.getAttribute(StyleConstants.NameAttribute);  | 
 | 
        HTML.Tag name;  | 
 | 
        if (nameAttribute instanceof HTML.Tag) { | 
 | 
            name = (HTML.Tag)nameAttribute;  | 
 | 
        }  | 
 | 
        else { | 
 | 
            name = null;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (name == HTML.Tag.PRE) { | 
 | 
            inPre = true;  | 
 | 
            preEndOffset = elem.getEndOffset();  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        closeOutUnwantedEmbeddedTags(attr);  | 
 | 
 | 
 | 
        if (inContent) { | 
 | 
            writeLineSeparator();  | 
 | 
            inContent = false;  | 
 | 
            newlineOutputed = false;  | 
 | 
        }  | 
 | 
 | 
 | 
        if (completeDoc && name == HTML.Tag.BODY && !wroteHead) { | 
 | 
              | 
 | 
            wroteHead = true;  | 
 | 
            indentSmart();  | 
 | 
            write("<head>"); | 
 | 
            writeLineSeparator();  | 
 | 
            incrIndent();  | 
 | 
            writeStyles(((HTMLDocument)getDocument()).getStyleSheet());  | 
 | 
            decrIndent();  | 
 | 
            writeLineSeparator();  | 
 | 
            indentSmart();  | 
 | 
            write("</head>"); | 
 | 
            writeLineSeparator();  | 
 | 
        }  | 
 | 
 | 
 | 
        indentSmart();  | 
 | 
        write('<'); | 
 | 
        write(elem.getName());  | 
 | 
        writeAttributes(attr);  | 
 | 
        write('>'); | 
 | 
        if (name != HTML.Tag.PRE) { | 
 | 
            writeLineSeparator();  | 
 | 
        }  | 
 | 
 | 
 | 
        if (name == HTML.Tag.TEXTAREA) { | 
 | 
            textAreaContent(elem.getAttributes());  | 
 | 
        } else if (name == HTML.Tag.SELECT) { | 
 | 
            selectContent(elem.getAttributes());  | 
 | 
        } else if (completeDoc && name == HTML.Tag.BODY) { | 
 | 
            // Write out the maps, which is not stored as Elements in  | 
 | 
              | 
 | 
            writeMaps(((HTMLDocument)getDocument()).getMaps());  | 
 | 
        }  | 
 | 
        else if (name == HTML.Tag.HEAD) { | 
 | 
            HTMLDocument document = (HTMLDocument)getDocument();  | 
 | 
            wroteHead = true;  | 
 | 
            incrIndent();  | 
 | 
            writeStyles(document.getStyleSheet());  | 
 | 
            if (document.hasBaseTag()) { | 
 | 
                indentSmart();  | 
 | 
                write("<base href=\"" + document.getBase() + "\">"); | 
 | 
                writeLineSeparator();  | 
 | 
            }  | 
 | 
            decrIndent();  | 
 | 
        }  | 
 | 
 | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected void textAreaContent(AttributeSet attr) throws BadLocationException, IOException { | 
 | 
        Document doc = (Document)attr.getAttribute(StyleConstants.ModelAttribute);  | 
 | 
        if (doc != null && doc.getLength() > 0) { | 
 | 
            if (segment == null) { | 
 | 
                segment = new Segment();  | 
 | 
            }  | 
 | 
            doc.getText(0, doc.getLength(), segment);  | 
 | 
            if (segment.count > 0) { | 
 | 
                inTextArea = true;  | 
 | 
                incrIndent();  | 
 | 
                indentSmart();  | 
 | 
                setCanWrapLines(true);  | 
 | 
                replaceEntities = true;  | 
 | 
                write(segment.array, segment.offset, segment.count);  | 
 | 
                replaceEntities = false;  | 
 | 
                setCanWrapLines(false);  | 
 | 
                writeLineSeparator();  | 
 | 
                inTextArea = false;  | 
 | 
                decrIndent();  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected void text(Element elem) throws BadLocationException, IOException { | 
 | 
        int start = Math.max(getStartOffset(), elem.getStartOffset());  | 
 | 
        int end = Math.min(getEndOffset(), elem.getEndOffset());  | 
 | 
        if (start < end) { | 
 | 
            if (segment == null) { | 
 | 
                segment = new Segment();  | 
 | 
            }  | 
 | 
            getDocument().getText(start, end - start, segment);  | 
 | 
            newlineOutputed = false;  | 
 | 
            if (segment.count > 0) { | 
 | 
                if (segment.array[segment.offset + segment.count - 1] == '\n'){ | 
 | 
                    newlineOutputed = true;  | 
 | 
                }  | 
 | 
                if (inPre && end == preEndOffset) { | 
 | 
                    if (segment.count > 1) { | 
 | 
                        segment.count--;  | 
 | 
                    }  | 
 | 
                    else { | 
 | 
                        return;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                replaceEntities = true;  | 
 | 
                setCanWrapLines(!inPre);  | 
 | 
                write(segment.array, segment.offset, segment.count);  | 
 | 
                setCanWrapLines(false);  | 
 | 
                replaceEntities = false;  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected void selectContent(AttributeSet attr) throws IOException { | 
 | 
        Object model = attr.getAttribute(StyleConstants.ModelAttribute);  | 
 | 
        incrIndent();  | 
 | 
        if (model instanceof OptionListModel) { | 
 | 
            OptionListModel<Option> listModel = (OptionListModel<Option>) model;  | 
 | 
            int size = listModel.getSize();  | 
 | 
            for (int i = 0; i < size; i++) { | 
 | 
                Option option = listModel.getElementAt(i);  | 
 | 
                writeOption(option);  | 
 | 
            }  | 
 | 
        } else if (model instanceof OptionComboBoxModel) { | 
 | 
            OptionComboBoxModel<Option> comboBoxModel = (OptionComboBoxModel<Option>) model;  | 
 | 
            int size = comboBoxModel.getSize();  | 
 | 
            for (int i = 0; i < size; i++) { | 
 | 
                Option option = comboBoxModel.getElementAt(i);  | 
 | 
                writeOption(option);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        decrIndent();  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected void writeOption(Option option) throws IOException { | 
 | 
 | 
 | 
        indentSmart();  | 
 | 
        write('<'); | 
 | 
        write("option"); | 
 | 
          | 
 | 
        Object value = option.getAttributes().getAttribute  | 
 | 
                              (HTML.Attribute.VALUE);  | 
 | 
        if (value != null) { | 
 | 
            write(" value="+ value); | 
 | 
        }  | 
 | 
        if (option.isSelected()) { | 
 | 
            write(" selected"); | 
 | 
        }  | 
 | 
        write('>'); | 
 | 
        if (option.getLabel() != null) { | 
 | 
            write(option.getLabel());  | 
 | 
        }  | 
 | 
        writeLineSeparator();  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected void endTag(Element elem) throws IOException { | 
 | 
        if (synthesizedElement(elem)) { | 
 | 
            return;  | 
 | 
        }  | 
 | 
 | 
 | 
          | 
 | 
        closeOutUnwantedEmbeddedTags(elem.getAttributes());  | 
 | 
        if (inContent) { | 
 | 
            if (!newlineOutputed && !inPre) { | 
 | 
                writeLineSeparator();  | 
 | 
            }  | 
 | 
            newlineOutputed = false;  | 
 | 
            inContent = false;  | 
 | 
        }  | 
 | 
        if (!inPre) { | 
 | 
            indentSmart();  | 
 | 
        }  | 
 | 
        if (matchNameAttribute(elem.getAttributes(), HTML.Tag.PRE)) { | 
 | 
            inPre = false;  | 
 | 
        }  | 
 | 
        write('<'); | 
 | 
        write('/'); | 
 | 
        write(elem.getName());  | 
 | 
        write('>'); | 
 | 
        writeLineSeparator();  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected void comment(Element elem) throws BadLocationException, IOException { | 
 | 
        AttributeSet as = elem.getAttributes();  | 
 | 
        if (matchNameAttribute(as, HTML.Tag.COMMENT)) { | 
 | 
            Object comment = as.getAttribute(HTML.Attribute.COMMENT);  | 
 | 
            if (comment instanceof String) { | 
 | 
                writeComment((String)comment);  | 
 | 
            }  | 
 | 
            else { | 
 | 
                writeComment(null);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    void writeComment(String string) throws IOException { | 
 | 
        write("<!--"); | 
 | 
        if (string != null) { | 
 | 
            write(string);  | 
 | 
        }  | 
 | 
        write("-->"); | 
 | 
        writeLineSeparator();  | 
 | 
        indentSmart();  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    void writeAdditionalComments() throws IOException { | 
 | 
        Object comments = getDocument().getProperty  | 
 | 
                                        (HTMLDocument.AdditionalComments);  | 
 | 
 | 
 | 
        if (comments instanceof Vector) { | 
 | 
            Vector v = (Vector)comments;  | 
 | 
            for (int counter = 0, maxCounter = v.size(); counter < maxCounter;  | 
 | 
                 counter++) { | 
 | 
                writeComment(v.elementAt(counter).toString());  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected boolean synthesizedElement(Element elem) { | 
 | 
        if (matchNameAttribute(elem.getAttributes(), HTML.Tag.IMPLIED)) { | 
 | 
            return true;  | 
 | 
        }  | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected boolean matchNameAttribute(AttributeSet attr, HTML.Tag tag) { | 
 | 
        Object o = attr.getAttribute(StyleConstants.NameAttribute);  | 
 | 
        if (o instanceof HTML.Tag) { | 
 | 
            HTML.Tag name = (HTML.Tag) o;  | 
 | 
            if (name == tag) { | 
 | 
                return true;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected void writeEmbeddedTags(AttributeSet attr) throws IOException { | 
 | 
 | 
 | 
          | 
 | 
        attr = convertToHTML(attr, oConvAttr);  | 
 | 
 | 
 | 
        Enumeration names = attr.getAttributeNames();  | 
 | 
        while (names.hasMoreElements()) { | 
 | 
            Object name = names.nextElement();  | 
 | 
            if (name instanceof HTML.Tag) { | 
 | 
                HTML.Tag tag = (HTML.Tag)name;  | 
 | 
                if (tag == HTML.Tag.FORM || tags.contains(tag)) { | 
 | 
                    continue;  | 
 | 
                }  | 
 | 
                write('<'); | 
 | 
                write(tag.toString());  | 
 | 
                Object o = attr.getAttribute(tag);  | 
 | 
                if (o != null && o instanceof AttributeSet) { | 
 | 
                    writeAttributes((AttributeSet)o);  | 
 | 
                }  | 
 | 
                write('>'); | 
 | 
                tags.addElement(tag);  | 
 | 
                tagValues.addElement(o);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private boolean noMatchForTagInAttributes(AttributeSet attr, HTML.Tag t,  | 
 | 
                                              Object tagValue) { | 
 | 
        if (attr != null && attr.isDefined(t)) { | 
 | 
            Object newValue = attr.getAttribute(t);  | 
 | 
 | 
 | 
            if ((tagValue == null) ? (newValue == null) :  | 
 | 
                (newValue != null && tagValue.equals(newValue))) { | 
 | 
                return false;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return true;  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected void closeOutUnwantedEmbeddedTags(AttributeSet attr) throws IOException { | 
 | 
 | 
 | 
        tagsToRemove.removeAllElements();  | 
 | 
 | 
 | 
          | 
 | 
        attr = convertToHTML(attr, null);  | 
 | 
 | 
 | 
        HTML.Tag t;  | 
 | 
        Object tValue;  | 
 | 
        int firstIndex = -1;  | 
 | 
        int size = tags.size();  | 
 | 
          | 
 | 
        for (int i = size - 1; i >= 0; i--) { | 
 | 
            t = tags.elementAt(i);  | 
 | 
            tValue = tagValues.elementAt(i);  | 
 | 
            if ((attr == null) || noMatchForTagInAttributes(attr, t, tValue)) { | 
 | 
                firstIndex = i;  | 
 | 
                tagsToRemove.addElement(t);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (firstIndex != -1) { | 
 | 
              | 
 | 
            boolean removeAll = ((size - firstIndex) == tagsToRemove.size());  | 
 | 
            for (int i = size - 1; i >= firstIndex; i--) { | 
 | 
                t = tags.elementAt(i);  | 
 | 
                if (removeAll || tagsToRemove.contains(t)) { | 
 | 
                    tags.removeElementAt(i);  | 
 | 
                    tagValues.removeElementAt(i);  | 
 | 
                }  | 
 | 
                write('<'); | 
 | 
                write('/'); | 
 | 
                write(t.toString());  | 
 | 
                write('>'); | 
 | 
            }  | 
 | 
            // Have to output any tags after firstIndex that still remaing,  | 
 | 
              | 
 | 
            size = tags.size();  | 
 | 
            for (int i = firstIndex; i < size; i++) { | 
 | 
                t = tags.elementAt(i);  | 
 | 
                write('<'); | 
 | 
                write(t.toString());  | 
 | 
                Object o = tagValues.elementAt(i);  | 
 | 
                if (o != null && o instanceof AttributeSet) { | 
 | 
                    writeAttributes((AttributeSet)o);  | 
 | 
                }  | 
 | 
                write('>'); | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private boolean isFormElementWithContent(AttributeSet attr) { | 
 | 
        return matchNameAttribute(attr, HTML.Tag.TEXTAREA) ||  | 
 | 
                matchNameAttribute(attr, HTML.Tag.SELECT);  | 
 | 
    }  | 
 | 
 | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private boolean indentNext = false;  | 
 | 
    private boolean indentNeedsIncrementing(Element current, Element next) { | 
 | 
        if ((next.getParentElement() == current) && !inPre) { | 
 | 
            if (indentNext) { | 
 | 
                indentNext = false;  | 
 | 
                return true;  | 
 | 
            } else if (synthesizedElement(next)) { | 
 | 
                indentNext = true;  | 
 | 
            } else if (!synthesizedElement(current)){ | 
 | 
                return true;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        return false;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    void writeMaps(Enumeration maps) throws IOException { | 
 | 
        if (maps != null) { | 
 | 
            while(maps.hasMoreElements()) { | 
 | 
                Map map = (Map)maps.nextElement();  | 
 | 
                String name = map.getName();  | 
 | 
 | 
 | 
                incrIndent();  | 
 | 
                indentSmart();  | 
 | 
                write("<map"); | 
 | 
                if (name != null) { | 
 | 
                    write(" name=\""); | 
 | 
                    write(name);  | 
 | 
                    write("\">"); | 
 | 
                }  | 
 | 
                else { | 
 | 
                    write('>'); | 
 | 
                }  | 
 | 
                writeLineSeparator();  | 
 | 
                incrIndent();  | 
 | 
 | 
 | 
                  | 
 | 
                AttributeSet[] areas = map.getAreas();  | 
 | 
                if (areas != null) { | 
 | 
                    for (int counter = 0, maxCounter = areas.length;  | 
 | 
                         counter < maxCounter; counter++) { | 
 | 
                        indentSmart();  | 
 | 
                        write("<area"); | 
 | 
                        writeAttributes(areas[counter]);  | 
 | 
                        write("></area>"); | 
 | 
                        writeLineSeparator();  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                decrIndent();  | 
 | 
                indentSmart();  | 
 | 
                write("</map>"); | 
 | 
                writeLineSeparator();  | 
 | 
                decrIndent();  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    void writeStyles(StyleSheet sheet) throws IOException { | 
 | 
        if (sheet != null) { | 
 | 
            Enumeration styles = sheet.getStyleNames();  | 
 | 
            if (styles != null) { | 
 | 
                boolean outputStyle = false;  | 
 | 
                while (styles.hasMoreElements()) { | 
 | 
                    String name = (String)styles.nextElement();  | 
 | 
                      | 
 | 
                    if (!StyleContext.DEFAULT_STYLE.equals(name) &&  | 
 | 
                        writeStyle(name, sheet.getStyle(name), outputStyle)) { | 
 | 
                        outputStyle = true;  | 
 | 
                    }  | 
 | 
                }  | 
 | 
                if (outputStyle) { | 
 | 
                    writeStyleEndTag();  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    boolean writeStyle(String name, Style style, boolean outputStyle)  | 
 | 
                 throws IOException{ | 
 | 
        boolean didOutputStyle = false;  | 
 | 
        Enumeration attributes = style.getAttributeNames();  | 
 | 
        if (attributes != null) { | 
 | 
            while (attributes.hasMoreElements()) { | 
 | 
                Object attribute = attributes.nextElement();  | 
 | 
                if (attribute instanceof CSS.Attribute) { | 
 | 
                    String value = style.getAttribute(attribute).toString();  | 
 | 
                    if (value != null) { | 
 | 
                        if (!outputStyle) { | 
 | 
                            writeStyleStartTag();  | 
 | 
                            outputStyle = true;  | 
 | 
                        }  | 
 | 
                        if (!didOutputStyle) { | 
 | 
                            didOutputStyle = true;  | 
 | 
                            indentSmart();  | 
 | 
                            write(name);  | 
 | 
                            write(" {"); | 
 | 
                        }  | 
 | 
                        else { | 
 | 
                            write(";"); | 
 | 
                        }  | 
 | 
                        write(' '); | 
 | 
                        write(attribute.toString());  | 
 | 
                        write(": "); | 
 | 
                        write(value);  | 
 | 
                    }  | 
 | 
                }  | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (didOutputStyle) { | 
 | 
            write(" }"); | 
 | 
            writeLineSeparator();  | 
 | 
        }  | 
 | 
        return didOutputStyle;  | 
 | 
    }  | 
 | 
 | 
 | 
    void writeStyleStartTag() throws IOException { | 
 | 
        indentSmart();  | 
 | 
        write("<style type=\"text/css\">"); | 
 | 
        incrIndent();  | 
 | 
        writeLineSeparator();  | 
 | 
        indentSmart();  | 
 | 
        write("<!--"); | 
 | 
        incrIndent();  | 
 | 
        writeLineSeparator();  | 
 | 
    }  | 
 | 
 | 
 | 
    void writeStyleEndTag() throws IOException { | 
 | 
        decrIndent();  | 
 | 
        indentSmart();  | 
 | 
        write("-->"); | 
 | 
        writeLineSeparator();  | 
 | 
        decrIndent();  | 
 | 
        indentSmart();  | 
 | 
        write("</style>"); | 
 | 
        writeLineSeparator();  | 
 | 
        indentSmart();  | 
 | 
    }  | 
 | 
 | 
 | 
    // --- conversion support ---------------------------  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    AttributeSet convertToHTML(AttributeSet from, MutableAttributeSet to) { | 
 | 
        if (to == null) { | 
 | 
            to = convAttr;  | 
 | 
        }  | 
 | 
        to.removeAttributes(to);  | 
 | 
        if (writeCSS) { | 
 | 
            convertToHTML40(from, to);  | 
 | 
        } else { | 
 | 
            convertToHTML32(from, to);  | 
 | 
        }  | 
 | 
        return to;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private boolean writeCSS = false;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private MutableAttributeSet convAttr = new SimpleAttributeSet();  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private MutableAttributeSet oConvAttr = new SimpleAttributeSet();  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static void convertToHTML32(AttributeSet from, MutableAttributeSet to) { | 
 | 
        if (from == null) { | 
 | 
            return;  | 
 | 
        }  | 
 | 
        Enumeration keys = from.getAttributeNames();  | 
 | 
        String value = "";  | 
 | 
        while (keys.hasMoreElements()) { | 
 | 
            Object key = keys.nextElement();  | 
 | 
            if (key instanceof CSS.Attribute) { | 
 | 
                if ((key == CSS.Attribute.FONT_FAMILY) ||  | 
 | 
                    (key == CSS.Attribute.FONT_SIZE) ||  | 
 | 
                    (key == CSS.Attribute.COLOR)) { | 
 | 
 | 
 | 
                    createFontAttribute((CSS.Attribute)key, from, to);  | 
 | 
                } else if (key == CSS.Attribute.FONT_WEIGHT) { | 
 | 
                      | 
 | 
                    CSS.FontWeight weightValue = (CSS.FontWeight)  | 
 | 
                        from.getAttribute(CSS.Attribute.FONT_WEIGHT);  | 
 | 
                    if ((weightValue != null) && (weightValue.getValue() > 400)) { | 
 | 
                        addAttribute(to, HTML.Tag.B, SimpleAttributeSet.EMPTY);  | 
 | 
                    }  | 
 | 
                } else if (key == CSS.Attribute.FONT_STYLE) { | 
 | 
                    String s = from.getAttribute(key).toString();  | 
 | 
                    if (s.indexOf("italic") >= 0) { | 
 | 
                        addAttribute(to, HTML.Tag.I, SimpleAttributeSet.EMPTY);  | 
 | 
                    }  | 
 | 
                } else if (key == CSS.Attribute.TEXT_DECORATION) { | 
 | 
                    String decor = from.getAttribute(key).toString();  | 
 | 
                    if (decor.indexOf("underline") >= 0) { | 
 | 
                        addAttribute(to, HTML.Tag.U, SimpleAttributeSet.EMPTY);  | 
 | 
                    }  | 
 | 
                    if (decor.indexOf("line-through") >= 0) { | 
 | 
                        addAttribute(to, HTML.Tag.STRIKE, SimpleAttributeSet.EMPTY);  | 
 | 
                    }  | 
 | 
                } else if (key == CSS.Attribute.VERTICAL_ALIGN) { | 
 | 
                    String vAlign = from.getAttribute(key).toString();  | 
 | 
                    if (vAlign.indexOf("sup") >= 0) { | 
 | 
                        addAttribute(to, HTML.Tag.SUP, SimpleAttributeSet.EMPTY);  | 
 | 
                    }  | 
 | 
                    if (vAlign.indexOf("sub") >= 0) { | 
 | 
                        addAttribute(to, HTML.Tag.SUB, SimpleAttributeSet.EMPTY);  | 
 | 
                    }  | 
 | 
                } else if (key == CSS.Attribute.TEXT_ALIGN) { | 
 | 
                    addAttribute(to, HTML.Attribute.ALIGN,  | 
 | 
                                    from.getAttribute(key).toString());  | 
 | 
                } else { | 
 | 
                      | 
 | 
                    if (value.length() > 0) { | 
 | 
                        value = value + "; ";  | 
 | 
                    }  | 
 | 
                    value = value + key + ": " + from.getAttribute(key);  | 
 | 
                }  | 
 | 
            } else { | 
 | 
                Object attr = from.getAttribute(key);  | 
 | 
                if (attr instanceof AttributeSet) { | 
 | 
                    attr = ((AttributeSet)attr).copyAttributes();  | 
 | 
                }  | 
 | 
                addAttribute(to, key, attr);  | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (value.length() > 0) { | 
 | 
            to.addAttribute(HTML.Attribute.STYLE, value);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static void addAttribute(MutableAttributeSet to, Object key, Object value) { | 
 | 
        Object attr = to.getAttribute(key);  | 
 | 
        if (attr == null || attr == SimpleAttributeSet.EMPTY) { | 
 | 
            to.addAttribute(key, value);  | 
 | 
        } else { | 
 | 
            if (attr instanceof MutableAttributeSet &&  | 
 | 
                value instanceof AttributeSet) { | 
 | 
                ((MutableAttributeSet)attr).addAttributes((AttributeSet)value);  | 
 | 
            }  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static void createFontAttribute(CSS.Attribute a, AttributeSet from,  | 
 | 
                                    MutableAttributeSet to) { | 
 | 
        MutableAttributeSet fontAttr = (MutableAttributeSet)  | 
 | 
            to.getAttribute(HTML.Tag.FONT);  | 
 | 
        if (fontAttr == null) { | 
 | 
            fontAttr = new SimpleAttributeSet();  | 
 | 
            to.addAttribute(HTML.Tag.FONT, fontAttr);  | 
 | 
        }  | 
 | 
          | 
 | 
        String htmlValue = from.getAttribute(a).toString();  | 
 | 
        if (a == CSS.Attribute.FONT_FAMILY) { | 
 | 
            fontAttr.addAttribute(HTML.Attribute.FACE, htmlValue);  | 
 | 
        } else if (a == CSS.Attribute.FONT_SIZE) { | 
 | 
            fontAttr.addAttribute(HTML.Attribute.SIZE, htmlValue);  | 
 | 
        } else if (a == CSS.Attribute.COLOR) { | 
 | 
            fontAttr.addAttribute(HTML.Attribute.COLOR, htmlValue);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private static void convertToHTML40(AttributeSet from, MutableAttributeSet to) { | 
 | 
        Enumeration keys = from.getAttributeNames();  | 
 | 
        String value = "";  | 
 | 
        while (keys.hasMoreElements()) { | 
 | 
            Object key = keys.nextElement();  | 
 | 
            if (key instanceof CSS.Attribute) { | 
 | 
                value = value + " " + key + "=" + from.getAttribute(key) + ";";  | 
 | 
            } else { | 
 | 
                to.addAttribute(key, from.getAttribute(key));  | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (value.length() > 0) { | 
 | 
            to.addAttribute(HTML.Attribute.STYLE, value);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
    //  | 
 | 
    // Overrides the writing methods to only break a string when  | 
 | 
    // canBreakString is true.  | 
 | 
    // In a future release it is likely AbstractWriter will get this  | 
 | 
    // functionality.  | 
 | 
    //  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected void writeLineSeparator() throws IOException { | 
 | 
        boolean oldReplace = replaceEntities;  | 
 | 
        replaceEntities = false;  | 
 | 
        super.writeLineSeparator();  | 
 | 
        replaceEntities = oldReplace;  | 
 | 
        indented = false;  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    protected void output(char[] chars, int start, int length)  | 
 | 
                   throws IOException { | 
 | 
        if (!replaceEntities) { | 
 | 
            super.output(chars, start, length);  | 
 | 
            return;  | 
 | 
        }  | 
 | 
        int last = start;  | 
 | 
        length += start;  | 
 | 
        for (int counter = start; counter < length; counter++) { | 
 | 
            // This will change, we need better support character level  | 
 | 
              | 
 | 
            switch(chars[counter]) { | 
 | 
                  | 
 | 
            case '<':  | 
 | 
                if (counter > last) { | 
 | 
                    super.output(chars, last, counter - last);  | 
 | 
                }  | 
 | 
                last = counter + 1;  | 
 | 
                output("<"); | 
 | 
                break;  | 
 | 
            case '>':  | 
 | 
                if (counter > last) { | 
 | 
                    super.output(chars, last, counter - last);  | 
 | 
                }  | 
 | 
                last = counter + 1;  | 
 | 
                output(">"); | 
 | 
                break;  | 
 | 
            case '&':  | 
 | 
                if (counter > last) { | 
 | 
                    super.output(chars, last, counter - last);  | 
 | 
                }  | 
 | 
                last = counter + 1;  | 
 | 
                output("&"); | 
 | 
                break;  | 
 | 
            case '"':  | 
 | 
                if (counter > last) { | 
 | 
                    super.output(chars, last, counter - last);  | 
 | 
                }  | 
 | 
                last = counter + 1;  | 
 | 
                output("""); | 
 | 
                break;  | 
 | 
                  | 
 | 
            case '\n':  | 
 | 
            case '\t':  | 
 | 
            case '\r':  | 
 | 
                break;  | 
 | 
            default:  | 
 | 
                if (chars[counter] < ' ' || chars[counter] > 127) { | 
 | 
                    if (counter > last) { | 
 | 
                        super.output(chars, last, counter - last);  | 
 | 
                    }  | 
 | 
                    last = counter + 1;  | 
 | 
                    // If the character is outside of ascii, write the  | 
 | 
                      | 
 | 
                    output("&#"); | 
 | 
                    output(String.valueOf((int)chars[counter]));  | 
 | 
                    output(";"); | 
 | 
                }  | 
 | 
                break;  | 
 | 
            }  | 
 | 
        }  | 
 | 
        if (last < length) { | 
 | 
            super.output(chars, last, length - last);  | 
 | 
        }  | 
 | 
    }  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
 | 
 | 
     */  | 
 | 
    private void output(String string) throws IOException { | 
 | 
        int length = string.length();  | 
 | 
        if (tempChars == null || tempChars.length < length) { | 
 | 
            tempChars = new char[length];  | 
 | 
        }  | 
 | 
        string.getChars(0, length, tempChars, 0);  | 
 | 
        super.output(tempChars, 0, length);  | 
 | 
    }  | 
 | 
 | 
 | 
    private boolean indented = false;  | 
 | 
 | 
 | 
      | 
 | 
 | 
 | 
     */  | 
 | 
    private void indentSmart() throws IOException { | 
 | 
        if (!indented) { | 
 | 
            indent();  | 
 | 
            indented = true;  | 
 | 
        }  | 
 | 
    }  | 
 | 
}  |