Package javax.swing.text.html

Source Code of javax.swing.text.html.HTMLDocument$HTMLReader$LabelAction

/*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You under the Apache License, Version 2.0
*  (the "License"); you may not use this file except in compliance with
*  the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
/**
* @author Alexander T. Simbirtsev
*/
package javax.swing.text.html;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import java.util.Vector;

import javax.swing.ButtonGroup;
import javax.swing.event.DocumentEvent.EventType;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Element;
import javax.swing.text.ElementIterator;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;
import javax.swing.text.html.HTML.Tag;
import javax.swing.text.html.HTMLEditorKit.Parser;
import javax.swing.text.html.parser.ParserDelegator;

import org.apache.harmony.x.swing.text.html.form.Form;
import org.apache.harmony.x.swing.text.html.form.FormAttributes;
import org.apache.harmony.x.swing.text.html.form.FormButtonModel;
import org.apache.harmony.x.swing.text.html.form.FormElement;
import org.apache.harmony.x.swing.text.html.form.FormFieldsetModel;
import org.apache.harmony.x.swing.text.html.form.FormOption;
import org.apache.harmony.x.swing.text.html.form.FormOptionGroup;
import org.apache.harmony.x.swing.text.html.form.FormSelectComboBoxModel;
import org.apache.harmony.x.swing.text.html.form.FormSelectListModel;
import org.apache.harmony.x.swing.text.html.form.FormSelectModel;
import org.apache.harmony.x.swing.text.html.form.FormTextModel;
import org.apache.harmony.x.swing.text.html.form.FormToggleButtonModel;

import org.apache.harmony.x.swing.internal.nls.Messages;

public class HTMLDocument extends DefaultStyledDocument {

    public class BlockElement extends BranchElement {
        public BlockElement(final Element parent, final AttributeSet attr) {
            super(parent, attr);
        }

        public String getName() {
            final Object tag = getAttribute(StyleConstants.NameAttribute);
            return tag != null ? tag.toString() : super.getName();
        }

        public AttributeSet getResolveParent() {
            return null;
        }
    }

    public class RunElement extends LeafElement {
        public RunElement(final Element parent, final AttributeSet a,
                          final int start, final int end) {
            super(parent, a, start, end);
        }

        public String getName() {
            final Object tag = getAttribute(StyleConstants.NameAttribute);
            return tag != null ? tag.toString() : super.getName();
        }

        public AttributeSet getResolveParent() {
            return null;
        }
    }

    public class HTMLReader extends HTMLEditorKit.ParserCallback {
      
        private boolean anchorReferenceEncountered = false;
       
        public class TagAction {
            public void start(final Tag tag, final MutableAttributeSet attr) {
            }

            public void end(final Tag tag) {
            }
        }

        public class BlockAction extends TagAction {
            public void start(final Tag tag, final MutableAttributeSet attr) {
                addJoinPreviousSpec = true;
                checkInsertTag(tag);
                skipAddingBlockSpec = false;
                blockOpen(tag, attr);
            }

            public void end(final Tag tag) {
                blockClose(tag);
            }
        }

        public class CharacterAction extends TagAction {
            public void start(final Tag tag, final MutableAttributeSet attr) {
                addJoinPreviousSpec = true;
                checkInsertTag(tag);
                pushCharacterStyle();
                charAttr.addAttribute(tag, attr.copyAttributes());
            }

            public void end(final Tag tag) {
                popCharacterStyle();
            }
        }

        public class FormAction extends SpecialAction {
            private static final String NO_NAME_ATTRIBUTE = "___no_name___";
           
            private Form currentForm;
            private HashMap radioGroupped = new HashMap();
           
            public void start(final Tag tag, final MutableAttributeSet attr) {
                if (Tag.OPTION.equals(tag) || Tag.OPTGROUP.equals(tag)) {
                    checkInsertTag(tag);
                    handleOption(tag, attr);
                    return;
                }
                super.start(tag, attr);
               
                final ElementSpec spec = getLastSpec();
                assert spec != null : "we've just created a spec in super.start()";
                FormElement model = null;
                final MutableAttributeSet specAttr = (MutableAttributeSet)spec.getAttributes();
                if (Tag.INPUT.equals(tag)) {
                    model = handleInput(attr, specAttr);
                } else if (Tag.TEXTAREA.equals(tag)) {
                    model = new FormTextModel(getCurrentForm(), attr);
                    openedBlocks.add(Tag.TEXTAREA);
                } else if (Tag.BUTTON.equals(tag)) {
                    model = new FormButtonModel(getCurrentForm(), attr);
                    openedBlocks.add(Tag.BUTTON);
                } else if (Tag.LEGEND.equals(tag)) {
                    openedBlocks.add(Tag.LEGEND);
                    if (openedBlocks.contains(Tag.FIELDSET)) {
                        handleLegend(null, specAttr);
                    }
                } else if (Tag.FIELDSET.equals(tag)) {
                    model = new FormFieldsetModel(getCurrentForm(), attr);
                    openedBlocks.add(Tag.FIELDSET);
                } else if (Tag.SELECT.equals(tag)) {
                    if (FormAttributes.isListSelect(specAttr)) {
                        selectModel = new FormSelectListModel(getCurrentForm(), attr);
                    } else {
                        selectModel = new FormSelectComboBoxModel(getCurrentForm(), attr);
                    }
                    model = selectModel;
                    openedBlocks.add(Tag.SELECT);
                }
                if (model != null) {
                    specAttr.addAttribute(StyleConstants.ModelAttribute, model);
                    assert currentForm != null : "creating model with getCurrentForm() in constructor assures this";
                    currentForm.addElement(model);
                }
            }

            public void end(final Tag tag) {
                openedBlocks.remove(tag);
                if (Tag.SELECT.equals(tag)) {
                    selectModel = null;
                } else if (Tag.OPTGROUP.equals(tag)) {
                    if (selectModel != null) {
                        selectModel.getRootOptionGroup().popGroup();
                    }
                }
            }
           
            void openForm(final AttributeSet attr) {
                currentForm = new Form(attr);
            }
           
            void closeForm() {
                currentForm = null;
                selectModel = null;
                radioGroupped.clear();
            }
           
            private FormElement handleInput(final AttributeSet attr, final MutableAttributeSet specAttr) {
                FormElement result = null;
                String inputType = (String)attr.getAttribute(HTML.Attribute.TYPE);
                if (inputType == null) {
                    inputType = FormAttributes.INPUT_TYPE_TEXT;
                    specAttr.addAttribute(HTML.Attribute.TYPE, inputType);
                }
                int inputTypeIndex = FormAttributes.getTypeAttributeIndex((String)inputType);
               
                switch (inputTypeIndex) {
                case FormAttributes.INPUT_TYPE_TEXT_INDEX:
                case FormAttributes.INPUT_TYPE_PASSWORD_INDEX:
                    result = new FormTextModel(getCurrentForm(), attr, FormTextModel.ENABLE_MAX_LENGTH_BOUND);
                    break;
                case FormAttributes.INPUT_TYPE_FILE_INDEX:
                    result = new FormTextModel(getCurrentForm(), attr);
                    break;
                case FormAttributes.INPUT_TYPE_SUBMIT_INDEX:
                case FormAttributes.INPUT_TYPE_RESET_INDEX:
                case FormAttributes.INPUT_TYPE_BUTTON_INDEX:
                case FormAttributes.INPUT_TYPE_IMAGE_INDEX:
                    result = new FormButtonModel(getCurrentForm(), attr);
                    break;
                case FormAttributes.INPUT_TYPE_RADIO_INDEX:
                    FormToggleButtonModel buttonModel = new FormToggleButtonModel(getCurrentForm(), attr);
                    manageRadioGroup(attr, buttonModel);
                    result = buttonModel;
                    break;
                case FormAttributes.INPUT_TYPE_CHECKBOX_INDEX:
                    result = new FormToggleButtonModel(getCurrentForm(), attr);
                    break;
                default:
                    break;
                }
                return result;
            }

            private void manageRadioGroup(final AttributeSet attr,
                                          final FormToggleButtonModel buttonModel) {
                String name = (String)attr.getAttribute(HTML.Attribute.NAME);
                if (name == null) {
                    name = NO_NAME_ATTRIBUTE;
                }
                Object groupped = radioGroupped.get(name);
                if (groupped instanceof FormToggleButtonModel) {
                    FormToggleButtonModel grouppedModel = (FormToggleButtonModel)groupped;
                    ButtonGroup buttonGroup;
                    if (grouppedModel.getGroup() != null) {
                        buttonGroup = grouppedModel.getGroup();
                    } else {
                        buttonGroup = new ButtonGroup();
                        grouppedModel.setGroup(buttonGroup);
                    }
                    buttonModel.setGroup(buttonGroup);
                } else {
                    radioGroupped.put(name, buttonModel);
                }
            }

            private void handleOption(final Tag tag, final AttributeSet attr) {
                FormOption option = null;
                FormOptionGroup currentGroup = (selectModel != null) ? selectModel
                        .getRootOptionGroup().getCurrentGroup() : null;
                       
                if (Tag.OPTION.equals(tag)) {
                    option = new FormOption(currentGroup, attr);
                    openedBlocks.add(Tag.OPTION);
                } else if (Tag.OPTGROUP.equals(tag)) {
                    currentGroup = new FormOptionGroup(currentGroup, attr);
                    option = currentGroup;
                }
               
                if (option != null && selectModel != null) {
                    selectModel.addOption(option);
                    selectModel.getRootOptionGroup().pushGroup(currentGroup);
                }
            }
           
            private ElementSpec getLastSpec() {
                return !parseBuffer.isEmpty() ? (ElementSpec)parseBuffer.get(parseBuffer.size() - 1) : null;
            }

            private Form getCurrentForm() {
                return (currentForm != null) ? currentForm : (currentForm = new Form(SimpleAttributeSet.EMPTY));
            }
        }

        public class HiddenAction extends TagAction {
            public void start(final Tag tag, final MutableAttributeSet attr) {
                addSpecialElement(tag, attr);
            }

            public void end(final Tag tag) {
                addSpecialElement(tag, createMutableSet(HTML.Attribute.ENDTAG, Boolean.TRUE));
            }
        }

        public class IsindexAction extends TagAction {
            public void start(final Tag tag, final MutableAttributeSet attr) {
                addJoinPreviousSpec = true;
                checkInsertTag(tag);
                blockOpen(Tag.IMPLIED, new SimpleAttributeSet());
                addSpecialElement(tag, attr);
                blockClose(Tag.IMPLIED);
            }

        }

        public class ParagraphAction extends BlockAction {
            public void start(final Tag tag, final MutableAttributeSet attr) {
                super.start(tag, attr);
                openedBlocks.add(PARAGRAPH_TAG);
            }
       
            public void end(final Tag tag) {
                super.end(tag);
                openedBlocks.remove(PARAGRAPH_TAG);
            }
        }

        public class PreAction extends BlockAction {
            public void start(final Tag tag, final MutableAttributeSet attr) {
                super.start(tag, attr);
                MutableAttributeSet blockAttr = new SimpleAttributeSet(attr);
                SimpleAttributeSet defaultAttr = getDefaultCSSAttributes(tag);
                if (defaultAttr != null) {
                    blockAttr.addAttributes(defaultAttr);
                }
                blockOpen(Tag.IMPLIED, blockAttr);
                impliedBlockOpen = true;
                needImpliedNewLine = true;
            }
       
            public void end(final Tag tag) {
                blockClose(Tag.IMPLIED);
                super.end(tag);
            }
        }

        public class SpecialAction extends TagAction {
            public void start(final Tag tag, final MutableAttributeSet attr) {
                addJoinPreviousSpec = true;
                addSpecialElement(tag, attr);
            }
        }

        class AdvancedCharacterAction extends CharacterAction {
            public void start(final Tag tag, final MutableAttributeSet attr) {
                super.start(tag, attr);
                final AttributeSet attrs = getDefaultCSSAttributes(tag);
                if (attrs != null) {
                    charAttr.addAttributes(attrs);
                }
            }
        }

        class LabelAction extends BlockAction {
            // TODO
        }
       
        class AnchorAction extends CharacterAction {
            public void start(final Tag tag, final MutableAttributeSet attr) {
                anchorReferenceEncountered = attr.isDefined(HTML.Attribute.HREF);
                //anchorReferenceEncountered verification added according to H4606
                if (anchorReferenceEncountered) {
                    super.start(tag, attr);
                    openedBlocks.add(Tag.A);
                }
            }
           
            public void end(final Tag tag) {
                // According to H4574 Empty AncorTextEncoured verification has
                // been removed, but according H4606 anchorReferenceEncountered
                // has been added
                if (anchorReferenceEncountered) {
                    super.end(tag);
                    openedBlocks.remove(Tag.A);
                    anchorReferenceEncountered = false;
                }
            }
        }
       
        class FontAction extends CharacterAction {
            final HTML.Attribute[] specialHTMLAttributes = new HTML.Attribute[] {
                                                             HTML.Attribute.FACE,
                                                             HTML.Attribute.COLOR,
                                                             HTML.Attribute.SIZE};

            final CSS.Attribute[] specialCSSAttributes = new CSS.Attribute[] {
                                                             CSS.Attribute.FONT_FAMILY,
                                                             CSS.Attribute.COLOR,
                                                             CSS.Attribute.FONT_SIZE};
           
            public void start(final Tag tag, final MutableAttributeSet attr) {
                super.start(tag, attr);
               
                final StyleSheet styleSheet = getStyleSheet();
                for (int i = 0; i < specialHTMLAttributes.length; i++) {
                    final String value = (String)attr.getAttribute(specialHTMLAttributes[i]);
                    if (value != null) {
                        styleSheet.addCSSAttributeFromHTML(charAttr, specialCSSAttributes[i], value);
                    }
                }
            }
        }
       
        class FormTagAction extends BlockAction {
            private Tag[] formActionTags = new Tag[] {Tag.TEXTAREA, Tag.SELECT,
                                                 Tag.INPUT, Tag.OPTION,
                                                 Tag.OPTGROUP, Tag.BUTTON,
                                                 Tag.LEGEND, Tag.FIELDSET};
           
            public void start(final Tag tag, final MutableAttributeSet attr) {
                super.start(tag, attr);
                FormAction formAction = getFormAction();
                if (formAction != null) {
                    formAction.openForm(attr);
                }
            }

            public void end(final Tag tag) {
                super.end(tag);
                FormAction formAction = getFormAction();
                if (formAction != null) {
                    formAction.closeForm();
                }
            }

            private FormAction getFormAction() {
                for (int i = 0; i < formActionTags.length; i++) {
                    final Tag tag = formActionTags[i];
                    TagAction action = getAction(tag);
                    if (action instanceof FormAction) {
                        return (FormAction)action;
                    }
                }
                return null;
            }
        }
       
        class ImageAction extends SpecialAction {

            @Override
            public void start(Tag tag, MutableAttributeSet attr) {

                if (anchorReferenceEncountered) {

                    attr.addAttributes(charAttr.copyAttributes());
                }

                super.start(tag, attr);
            }
        }
       
        class BaseAction extends TagAction {
            public void start(final Tag tag, final MutableAttributeSet attr) {
                checkInsertTag(tag);
                if (attr == null) {
                    return;
                }
                final String href = (String)attr.getAttribute(HTML.Attribute.HREF);
                if (href == null) {
                    return;
                }

                final URL url = HTML.resolveURL(href, getBase());
                setBase(url);
                putProperty(INITIAL_BASE_PROPERTY, url);
            }
        }

        class HeadAction extends BlockAction {
            public void end(final Tag tag) {
                super.end(tag);
                if (styleRule != null) {
                    getStyleSheet().addRule(styleRule);
                    styleRule = null;
                }
            }
        }

        class TitleAction extends BlockAction {
            public void start(final Tag tag, final MutableAttributeSet attr) {
                addJoinPreviousSpec = true;
                checkInsertTag(tag);
                addSpecialElement(tag, attr);
                openedBlocks.add(Tag.TITLE);
            }

            public void end(final Tag tag) {
                addSpecialElement(tag, createMutableSet(HTML.Attribute.ENDTAG, Boolean.TRUE));
                openedBlocks.remove(Tag.TITLE);
            }
        }

        class AppletAction extends HiddenAction {
            // TODO: implement
            public void start(final Tag tag, final MutableAttributeSet attr) {
                addJoinPreviousSpec = true;
                super.start(tag, attr);
            }
        }

        class AreaAction extends HiddenAction {
            // TODO: implement
        }

        class MapAction extends HiddenAction {
            // TODO: implement
        }

        class ScriptAction extends HiddenAction {
            // TODO: implement
            public void start(final Tag tag, final MutableAttributeSet attr) {
                addJoinPreviousSpec = true;
                super.start(tag, attr);
            }
        }

        class LinkAction extends HiddenAction {
            public void start(final Tag tag, final MutableAttributeSet attr) {
                addJoinPreviousSpec = true;
                super.start(tag, attr);
                if (attr.containsAttribute(HTML.Attribute.TYPE, "text/css")) {
                    loadCSS(attr);
                }
            }
           
            public void end(final Tag tag) {
            }
           
            private void loadCSS(final AttributeSet attr) {
                String href = (String)attr.getAttribute(HTML.Attribute.HREF);
                final URL url = HTML.resolveURL(href, getBase());
                try {
                    getStyleSheet().loadRules(new BufferedReader(new InputStreamReader(url.openStream())), url);
                } catch (IOException e) {
                }
            }
        }

        class MetaAction extends SpecialAction {
            public void end(final Tag tag) {
            }
        }

        class StyleAction extends TagAction {
            public void start(Tag tag, MutableAttributeSet attr) {
                checkInsertTag(tag);
                openedBlocks.add(Tag.STYLE);
            }
           
            public void end(Tag tag) {
                openedBlocks.remove(Tag.STYLE);
            }
        }

        protected MutableAttributeSet charAttr = new SimpleAttributeSet();

        protected Vector<DefaultStyledDocument.ElementSpec> parseBuffer =
                new Vector<DefaultStyledDocument.ElementSpec>();

        private static final String PARAGRAPH_TAG = "_paragraph_tag_";
        private static final int IMPLIED_HTML_DOCUMENT_START_SPECS_NUMBER = 8;
        private static final int TOKEN_THRESHOLD_MULTIPLIER = 5;

        private final HashMap tagActionMap = new HashMap();
        private final TagAction emptyAction = new TagAction();
        private final Stack attrStack = new Stack();
        private final Set openedBlocks = new HashSet();
        private boolean impliedBlockOpen;
        private int numBlocksOpen;

        private boolean needImpliedNewLine;
        private String styleRule;
        private FormSelectModel selectModel;
        private int tokenThreshold;
       
        private int offset;
        private int popDepth;
        private int pushDepth;
        private Tag insertTag;
        private boolean insertTagFound;
        private boolean implicitSpecsRemove;
        private boolean skipAddingBlockSpec;
        private boolean addJoinPreviousSpec;
        private int specsCount;

        public HTMLReader(final int offset) {
            this(offset, 0, 0, null);
        }

        public HTMLReader(final int offset, final int popDepth,
                          final int pushDepth, final Tag insertTag) {
            this.offset = offset;
            this.popDepth = popDepth;
            this.pushDepth = pushDepth;
            this.insertTag = insertTag;
            insertTagFound = (insertTag == null);
            tokenThreshold = getTokenThreshold();
            fillTagActionMap();
        }

        public void handleComment(final char[] data, final int pos) {
            final String comment = new String(data);
            if (openedBlocks.contains(Tag.P)) {
                addSpecialElement(Tag.COMMENT, createMutableSet(HTML.Attribute.COMMENT, comment));
            } else {
                Vector comments = (Vector)getProperty(AdditionalComments);
                if (comments == null) {
                    comments = new Vector();
                    putProperty(AdditionalComments, comments);
                }
                comments.add(comment);
            }
        }

        public void handleEndOfLineString(final String eol) {
            putProperty(DefaultEditorKit.EndOfLineStringProperty, eol);
        }

        public void handleSimpleTag(final Tag tag,
                                    final MutableAttributeSet attr,
                                    final int pos) {
            final TagAction action = getAction(tag);
            MutableAttributeSet tagAttr = handleStyleAttribute(attr);
            if (action != emptyAction || !getPreservesUnknownTags()) {
                action.start(tag, tagAttr);
                action.end(tag);
            } else {
                addSpecialElement(tag, tagAttr);
            }
        }

        public void handleStartTag(final Tag tag,
                                   final MutableAttributeSet attr, final int pos) {
            final TagAction action = getAction(tag);
            action.start(tag, handleStyleAttribute(attr));
        }

        public void handleEndTag(final Tag tag, final int pos) {
            final TagAction action = getAction(tag);
            action.end(tag);
        }

        public void handleText(final char[] data, final int pos) {
            if (openedBlocks.contains(Tag.TITLE)) {
                putProperty(TitleProperty, new String(data));
                return;
            }
            if (openedBlocks.contains(Tag.STYLE) && openedBlocks.contains(Tag.HEAD)) {
                final String newStyle = new String(data);
                if (styleRule == null) {
                    styleRule = newStyle;
                } else {
                    styleRule += newStyle;
                }
                return;
            }
            if (openedBlocks.contains(Tag.TEXTAREA)) {
                textAreaContent(data);
                return;
            }
            if (openedBlocks.contains(Tag.PRE)) {
                preContent(data);
                return;
            }
            if (openedBlocks.contains(Tag.OPTION) && openedBlocks.contains(Tag.SELECT)) {
                final Option option = selectModel.getLastOption();
                if (option != null && !(option instanceof FormOptionGroup)) {
                    option.setLabel(new String(data));
                }
            }
            if (openedBlocks.contains(Tag.LEGEND) && openedBlocks.contains(Tag.FIELDSET)) {
                if (handleLegend(new String(data), null)) {
                    return;
                }
            }
            if (numBlocksOpen > 0) {
                addContent(data, 0, data.length);
            }
        }

        public void flush() throws BadLocationException {
            flushImpl(true);
        }

        protected void registerTag(final Tag tag,
                                   final TagAction action) {
            tagActionMap.put(tag, action);
        }

        protected void pushCharacterStyle() {
            if (charAttr == null) {
                throw new NullPointerException();
            }
            attrStack.push(charAttr.copyAttributes());
        }

        protected void popCharacterStyle() {
            if (!attrStack.empty()) {
                charAttr = (MutableAttributeSet)attrStack.pop();
            }
        }

        protected void preContent(char[] data) {
            int offset = 0;

            for (int i = 0; i < data.length; i++) {
                if ((data[i] == '\n') || (data[i] == '\r')) {
                    addContent(data, offset, i - offset);
                    blockClose(HTML.Tag.IMPLIED);

                    blockOpen(HTML.Tag.IMPLIED, new SimpleAttributeSet());
                    offset = i + 1;
                }
            }

            if (offset < data.length) {
                addContent(data, offset, data.length - offset);
            }
        }

        protected void addContent(final char[] data, final int offset,
                                  final int length) {
            addContent(data, offset, length, true);
        }

        protected void addContent(final char[] data, final int offset,
                                  final int length,
                                  final boolean createImpliedPIfNecessary) {
            addContentSpec(Tag.CONTENT, data, offset, length, charAttr, createImpliedPIfNecessary);
           
            if (parseBuffer.size() > tokenThreshold) {
                try {
                    flushImpl(false);
                } catch (BadLocationException e) {
                }
                tokenThreshold *= TOKEN_THRESHOLD_MULTIPLIER;
            }
        }

        protected void addSpecialElement(final Tag tag,
                                         final MutableAttributeSet attr) {
            final boolean needImpliedBlock = !Tag.FRAME.equals(tag);
            if (!needImpliedBlock) {
                needImpliedNewLine = false;
            }
            addContentSpec(tag, new char[] {' '}, 0, 1, attr, needImpliedBlock);
        }
       
        protected void textAreaContent(final char[] data) {
            final ElementSpec textareaSpec = findLastSpec(Tag.TEXTAREA);
            if (textareaSpec == null) {
                return;
            }
            FormTextModel doc = (FormTextModel)getModel(textareaSpec);
            if (doc != null) {
                doc.setInitialContent(new String(data));
            }
        }

        protected void blockOpen(final Tag tag,
                                 final MutableAttributeSet attr) {
            if (impliedBlockOpen && !Tag.IMPLIED.equals(tag)) {
                blockClose(Tag.IMPLIED);
                impliedBlockOpen = false;
            }
            if (!skipAddingBlockSpec) {
                ElementSpec blockSpec = new ElementSpec(deriveSpecAttributes(tag, attr), ElementSpec.StartTagType);
                addSpec(blockSpec);
            }
            skipAddingBlockSpec = false;
            needImpliedNewLine = true;
            openedBlocks.add(tag);
            numBlocksOpen++;
        }

        protected void blockClose(final Tag tag) {
            if (needImpliedNewLine) {
                addImpliedNewLine();
                if (impliedBlockOpen && !Tag.IMPLIED.equals(tag)) {
                    blockClose(Tag.IMPLIED);
                    impliedBlockOpen = false;
                }
            }
            numBlocksOpen--;
            openedBlocks.remove(tag);
            addSpec(new ElementSpec(null, ElementSpec.EndTagType));
        }

        private Object getModel(final ElementSpec spec) {
            return spec.getAttributes().getAttribute(StyleConstants.ModelAttribute);
        }

        private void addImpliedNewLine() {
            pushCharacterStyle();
            charAttr.addAttribute(HTML.Attribute.IMPLIED_NEW_LINE, Boolean.TRUE);
            addContent(new char[] {'\n'}, 0, 1, true);
            popCharacterStyle();
            needImpliedNewLine = false;
        }

        private MutableAttributeSet handleStyleAttribute(final MutableAttributeSet attr) {
            final String style = (String)attr.getAttribute(HTML.Attribute.STYLE);
            if (style != null) {
                attr.addAttributes(getStyleSheet().getDeclaration(style));
                attr.removeAttribute(HTML.Attribute.STYLE);
            }
            return attr;
        }

        private void createImpliedBlock() {
            if (paragraphOpened()) {
                return;
            }
            blockOpen(Tag.IMPLIED, new SimpleAttributeSet());
            impliedBlockOpen = true;
        }

        private MutableAttributeSet createMutableSet(final Object key, final Object value) {
            MutableAttributeSet specAttr = new SimpleAttributeSet();
            specAttr.addAttribute(key, value);
            return specAttr;
        }

        private boolean paragraphOpened() {
            return openedBlocks.contains(PARAGRAPH_TAG) || openedBlocks.contains(Tag.IMPLIED) && impliedBlockOpen;
        }

        private boolean handleLegend(final String legend, final MutableAttributeSet legendAttr) {
            final ElementSpec fieldSetSpec = findLastSpec(Tag.FIELDSET);
            FormFieldsetModel fieldSet = (FormFieldsetModel)getModel(fieldSetSpec);
            if (fieldSet == null || fieldSet.getLegend() != null) {
                return false;
            }               
            if (legend != null) {
                fieldSet.setLegend(legend);
            }
            if (legendAttr != null) {
                fieldSet.setLegendAttributes(legendAttr);
            }
            return true;
        }

        private void setRemoveImplicitSpecs(final boolean implicitSpecsRemove) {
            this.implicitSpecsRemove = implicitSpecsRemove;
        }

        private void flushImpl(final boolean isFinal) throws BadLocationException {
            if (parseBuffer.isEmpty()) {
                return;
            }
            if (isCreate()) {
                ElementSpec[] specs = vectorToArray(parseBuffer);
                create(specs);
                removeDefaultBody();
            } else {
                if (needAddingPopPushSpecs()) {
                    addPopPushSpecs(parseBuffer);
                }
                ElementSpec[] specs = isFinal ? trimEndSpecs(parseBuffer)
                        : vectorToArray(parseBuffer);
                insert(offset, specs);
            }
           
            parseBuffer.clear();
        }

        private void removeDefaultBody() {
            Element impliedLF = getCharacterElement(getLength() - 1);
            try {
                remove(getLength() - 1, 1);
            } catch (BadLocationException e) {
            }
            BranchElement root = (BranchElement)getDefaultRootElement();
            final int oddBodyIndex = root.getElementCount() - 1;
            Element oddBody = root.getElement(oddBodyIndex);
            final Element[] emptyArray = new Element[0];
            root.replace(oddBodyIndex, 1, emptyArray);
           
            Element lf = getCharacterElement(getLength());
            writeLock();
            try {
                ((MutableAttributeSet)lf).removeAttributes(lf.getAttributes().getAttributeNames());
                ((MutableAttributeSet)lf).addAttributes(impliedLF.getAttributes());
            } finally {
                writeUnlock();
            }
           
            final DefaultDocumentEvent removeEvent = new DefaultDocumentEvent(oddBody.getStartOffset(), oddBody.getEndOffset() - oddBody.getStartOffset(), EventType.REMOVE);
            removeEvent.addEdit(new ElementEdit(root, oddBodyIndex, new Element[] {oddBody}, emptyArray));
            fireRemoveUpdate(removeEvent);
        }

        private boolean isCreate() {
            return offset == 0 && insertTag == null && getLength() == 0 && !implicitSpecsRemove;
        }

        private ElementSpec[] trimEndSpecs(final Vector buffer) {
            if ((implicitSpecsRemove || insertTag != null && insertTagFound)) {
                for (int i = 0; i < 4; i++) {
                    buffer.remove(buffer.size() - 1);
                }
            }
            return vectorToArray(buffer);
        }

        private ElementSpec[] vectorToArray(final Vector buffer) {
            return (ElementSpec[])buffer.toArray(new ElementSpec[buffer.size()]);
        }

        private boolean needAddingPopPushSpecs() {
            return (insertTag != null && insertTagFound || insertTag == null) && (popDepth != 0 || pushDepth != 0);
        }

        private void addContentSpec(final Tag tag, final char[] data,
                                    final int offset, final int length,
                                    final MutableAttributeSet attr,
                                    final boolean createImpliedBlock) {
            if (createImpliedBlock) {
                createImpliedBlock();
            }
            checkInsertTag(tag);
            addSpec(new ElementSpec(deriveSpecAttributes(tag, attr),
                                    ElementSpec.ContentType, data, offset,
                                    length));
        }

        private SimpleAttributeSet deriveSpecAttributes(final Tag tag, final MutableAttributeSet attr) {
            attr.removeAttribute(IMPLIED);
            attr.addAttribute(StyleConstants.NameAttribute, tag);
            return new SimpleAttributeSet(attr);
        }

        private void addSpec(final ElementSpec spec) {
            if (insertTagFound && (!implicitSpecsRemove ||
                    specsCount >= IMPLIED_HTML_DOCUMENT_START_SPECS_NUMBER)) {
                parseBuffer.add(spec);
            }
            specsCount++;
        }

        private void fillTagActionMap() {
            HiddenAction hiddenAction = new HiddenAction();
            CharacterAction characterAction = new CharacterAction();
            AdvancedCharacterAction advancedCharacterAction = new AdvancedCharacterAction();
            BlockAction blockAction = new BlockAction();
            ParagraphAction paragraphAction = new ParagraphAction();
            SpecialAction specialAction = new SpecialAction();
            FormAction formAction = new FormAction();
           
            tagActionMap.put(Tag.A,  new AnchorAction());
            tagActionMap.put(Tag.ABBR, characterAction);
            tagActionMap.put(Tag.ACRONYM, characterAction);
            tagActionMap.put(Tag.ADDRESS, characterAction);
            tagActionMap.put(Tag.APPLET, new AppletAction());
            tagActionMap.put(Tag.AREA, new AreaAction());
            tagActionMap.put(Tag.B, advancedCharacterAction);
            tagActionMap.put(Tag.BASE, new BaseAction());
            tagActionMap.put(Tag.BASEFONT, characterAction);
            tagActionMap.put(Tag.BIG, characterAction);
            tagActionMap.put(Tag.BDO, characterAction);
            tagActionMap.put(Tag.BLOCKQUOTE, blockAction);
            tagActionMap.put(Tag.BODY, blockAction);
            tagActionMap.put(Tag.BR, specialAction);
            tagActionMap.put(Tag.BUTTON, formAction);
            tagActionMap.put(Tag.CAPTION, blockAction);
            tagActionMap.put(Tag.CENTER, blockAction);
            tagActionMap.put(Tag.CITE, characterAction);
            tagActionMap.put(Tag.CODE, characterAction);
            tagActionMap.put(Tag.COL, hiddenAction);
            tagActionMap.put(Tag.COLGROUP, hiddenAction);
            tagActionMap.put(Tag.DD, blockAction);
            tagActionMap.put(Tag.DFN, characterAction);
            tagActionMap.put(Tag.DEL, characterAction);
            tagActionMap.put(Tag.DIR, blockAction);
            tagActionMap.put(Tag.DIV, blockAction);
            tagActionMap.put(Tag.DL, blockAction);
            tagActionMap.put(Tag.DT, paragraphAction);
            tagActionMap.put(Tag.EM, characterAction);
            tagActionMap.put(Tag.FIELDSET, formAction);
            tagActionMap.put(Tag.FONT, new FontAction());
            tagActionMap.put(Tag.FORM, new FormTagAction());
            tagActionMap.put(Tag.FRAME, specialAction);
            tagActionMap.put(Tag.FRAMESET, blockAction);
            tagActionMap.put(Tag.H1, paragraphAction);
            tagActionMap.put(Tag.H2, paragraphAction);
            tagActionMap.put(Tag.H3, paragraphAction);
            tagActionMap.put(Tag.H4, paragraphAction);
            tagActionMap.put(Tag.H5, paragraphAction);
            tagActionMap.put(Tag.H6, paragraphAction);
            tagActionMap.put(Tag.HEAD, new HeadAction());
            tagActionMap.put(Tag.HR, specialAction);
            tagActionMap.put(Tag.HTML, blockAction);
            tagActionMap.put(Tag.I, advancedCharacterAction);
            tagActionMap.put(Tag.IFRAME, hiddenAction);
            tagActionMap.put(Tag.IMG, new ImageAction());
            tagActionMap.put(Tag.INPUT, formAction);
            tagActionMap.put(Tag.INS, characterAction);
            tagActionMap.put(Tag.ISINDEX, new IsindexAction());
            tagActionMap.put(Tag.KBD, characterAction);
            tagActionMap.put(Tag.LABEL, new LabelAction());
            tagActionMap.put(Tag.LEGEND, formAction);
            tagActionMap.put(Tag.LI, blockAction);
            tagActionMap.put(Tag.LINK, new LinkAction());
            tagActionMap.put(Tag.MAP, new MapAction());
            tagActionMap.put(Tag.MENU, blockAction);
            tagActionMap.put(Tag.META, new MetaAction());
            tagActionMap.put(Tag.NOFRAMES, blockAction);
            tagActionMap.put(Tag.NOSCRIPT, blockAction);
            tagActionMap.put(Tag.OBJECT, specialAction);
            tagActionMap.put(Tag.OL, blockAction);
            tagActionMap.put(Tag.OPTION, formAction);
            tagActionMap.put(Tag.OPTGROUP, formAction);
            tagActionMap.put(Tag.P, paragraphAction);
            tagActionMap.put(Tag.PARAM, hiddenAction);
            tagActionMap.put(Tag.PRE, new PreAction());
            tagActionMap.put(Tag.Q, characterAction);
            tagActionMap.put(Tag.SAMP, characterAction);
            tagActionMap.put(Tag.SCRIPT, new ScriptAction());
            tagActionMap.put(Tag.SELECT, formAction);
            tagActionMap.put(Tag.SMALL, characterAction);
            tagActionMap.put(Tag.STRIKE, advancedCharacterAction);
            tagActionMap.put(Tag.S, characterAction);
            tagActionMap.put(Tag.SPAN, characterAction);
            tagActionMap.put(Tag.STRONG, characterAction);
            tagActionMap.put(Tag.STYLE, new StyleAction());
            tagActionMap.put(Tag.SUB, advancedCharacterAction);
            tagActionMap.put(Tag.SUP, advancedCharacterAction);
            tagActionMap.put(Tag.TABLE, blockAction);
            tagActionMap.put(Tag.TBODY, blockAction);
            tagActionMap.put(Tag.TD, blockAction);
            tagActionMap.put(Tag.TEXTAREA, formAction);
            tagActionMap.put(Tag.TFOOT, blockAction);
            tagActionMap.put(Tag.THEAD, blockAction);
            tagActionMap.put(Tag.TH, blockAction);
            tagActionMap.put(Tag.TITLE, new TitleAction());
            tagActionMap.put(Tag.TR, blockAction);
            tagActionMap.put(Tag.TT, characterAction);
            tagActionMap.put(Tag.U, advancedCharacterAction);
            tagActionMap.put(Tag.UL, blockAction);
            tagActionMap.put(Tag.VAR, characterAction);
        }

        private TagAction getAction(final Tag tag) {
            TagAction action = (TagAction)tagActionMap.get(tag);
            if (action == null) {
                action = emptyAction;
            }
            return action;
        }

        private ElementSpec findLastSpec(final Tag tag) {
            for (int i = parseBuffer.size() - 1; i >= 0; i--) {
                ElementSpec spec = (ElementSpec)parseBuffer.get(i);
                final AttributeSet specAttr = spec.getAttributes();
                if (specAttr != null && specAttr.containsAttribute(StyleConstants.NameAttribute, tag)) {
                    return spec;
                }
            }
            return null;
        }
       
        private boolean checkInsertTag(final Tag tag) {
            if (!insertTagFound && insertTag.equals(tag)) {
                insertTagFound = true;
                skipAddingBlockSpec = true
                addPopPushSpecs(parseBuffer);
            }
           
            return insertTagFound;
        }

        private void addPopPushSpecs(final Vector buffer) {
            addJoinPreviousSpec &= (pushDepth > 0 || popDepth > 0);
//            boolean addNewLineTag = (pushDepth == 0 && popDepth > 0);
//            if (addNewLineTag && !implicitSpecsRemove && offset == 0) {
//                parseBuffer.add(0, new ElementSpec(null, ElementSpec.ContentType, new char[] {'\n'}, 0, 1));
//            }
            for (int i = 0; i < pushDepth; i++) {
                final ElementSpec pushSpec = new ElementSpec(null, ElementSpec.StartTagType);
                pushSpec.setDirection(ElementSpec.JoinNextDirection);
                buffer.add(0, pushSpec);
            }
            pushDepth = 0;
            for (int i = 0; i < popDepth; i++) {
                final ElementSpec popSpec = new ElementSpec(null, ElementSpec.EndTagType);
                buffer.add(0, popSpec);
            }
            popDepth = 0;
            if (addJoinPreviousSpec && !implicitSpecsRemove && offset == 0) {
                ElementSpec joinPreviousSpec = new ElementSpec(null, ElementSpec.ContentType, new char[] {'\n'}, 0, 1);
                joinPreviousSpec.setDirection(ElementSpec.JoinPreviousDirection);
                parseBuffer.add(0, joinPreviousSpec);
                addJoinPreviousSpec = false;
            }
        }
    }

    public abstract static class Iterator {
        public abstract AttributeSet getAttributes();
        public abstract int getEndOffset();
        public abstract int getStartOffset();
        public abstract Tag getTag();
        public abstract boolean isValid();
        public abstract void next();
    }

    public static final String AdditionalComments = "AdditionalComments";
    static final String INITIAL_BASE_PROPERTY = "_initialBase_";
   
    private static final String TARGET_TOP = "_top";
    private static final String TARGET_SELF = "_self";
    private static final String TARGET_PARENT = "_parent";

    private static final SimpleAttributeSet DEFAULT_CHARACTER_ATTRIBUTES = new SimpleAttributeSet()

    private URL base;
    private Parser parser;
    private boolean preservesUnknownTags = true;
    private int threshold = Integer.MAX_VALUE;
   
    static {
        initDefaultCharacterAttributes();
    }

    public HTMLDocument(final Content c, final StyleSheet styles) {
        super(c, styles);
    }

    public HTMLDocument(final StyleSheet styles) {
        super(styles);
    }

    public HTMLDocument() {
        super(new StyleSheet());
    }

    public void setBase(final URL base) {
        this.base = base;
        getStyleSheet().setBase(base);
    }

    public URL getBase() {
        return base;
    }

    public void setParser(final HTMLEditorKit.Parser parser) {
        this.parser = parser;
    }

    public HTMLEditorKit.Parser getParser() {
        return parser;
    }

    public void setPreservesUnknownTags(final boolean preservesTags) {
        preservesUnknownTags = preservesTags;
    }

    public boolean getPreservesUnknownTags() {
        return preservesUnknownTags;
    }

    public void setTokenThreshold(final int threshold) {
        this.threshold = threshold;
    }

    public int getTokenThreshold() {
        return threshold;
    }

    public Element getElement(final Element e, final Object attribute,
                              final Object value) {
        final ElementIterator it = new ElementIterator(e);
        while (it.next() != null) {
            final Element current = it.current();
            if (current.getAttributes().containsAttribute(attribute, value)) {
                return current;
            }
        }
        return null;
    }

    public Element getElement(final String id) {
        return getElement(getDefaultRootElement(), HTML.Attribute.ID, id);
    }

    public Iterator getIterator(final Tag tag) {
        return new TagIterator(tag, this);
    }

    public HTMLEditorKit.ParserCallback getReader(final int pos,
                                                  final int popDepth,
                                                  final int pushDepth,
                                                  final Tag insertTag) {
        return new HTMLReader(pos, popDepth, pushDepth, insertTag);
    }

    public HTMLEditorKit.ParserCallback getReader(final int pos) {
        return new HTMLReader(pos);
    }

    public StyleSheet getStyleSheet() {
        return (StyleSheet)getAttributeContext();
    }

    public void insertBeforeEnd(final Element elem, final String htmlText)
            throws BadLocationException, IOException {
        checkParser();
        checkLeaf(elem);
        int offset = isParagraph(elem) ? elem.getEndOffset() - 1 : elem.getEndOffset();
        insertHTMLText(elem, offset, htmlText);
    }
   
    private boolean isParagraph(final Element elem) {
        final AttributeSet attr = elem.getAttributes();
        return attr != null && Tag.P.equals(attr.getAttribute(StyleConstants.NameAttribute));
    }

    public void insertAfterEnd(final Element elem, final String htmlText)
            throws BadLocationException, IOException {
        checkParser();
        if (elem == null) {
            return;
        }
        int offset = elem.getEndOffset() > getLength() ? getLength() : elem.getEndOffset();
        insertHTMLText(elem.getParentElement(), offset, htmlText);
    }

    public void insertBeforeStart(final Element elem, final String htmlText)
            throws BadLocationException, IOException {
        checkParser();
        if (elem == null) {
            return;
        }
        insertHTMLText(elem.getParentElement(), elem.getStartOffset(), htmlText);
    }
   
    public void insertAfterStart(final Element elem, final String htmlText)
            throws BadLocationException, IOException {
        checkParser();
        checkLeaf(elem);
        insertHTMLText(elem, elem.getStartOffset(), htmlText);
    }

    public void setInnerHTML(final Element elem, final String htmlText)
            throws BadLocationException, IOException {
        checkParser();
        if (elem == null) {
            return;
        }
        checkLeaf(elem);

        int numRemovingElements = elem.getElementCount();
        insertHTMLText(elem, elem.getStartOffset(), htmlText);
        removeElements(elem, elem.getElementCount() - numRemovingElements, numRemovingElements);
        throw new UnsupportedOperationException(Messages.getString("swing.27")); //$NON-NLS-1$
    }
   
    public void setOuterHTML(final Element elem, final String htmlText)
            throws BadLocationException, IOException {
        checkParser();
        if (elem == null) {
            return;
        }
       
        int length = elem.getEndOffset() - elem.getStartOffset() - 1;
        final BranchElement parent = (BranchElement)elem.getParentElement();
        final int indexBefore = getElementIndex(parent, elem);
        final int numElementsBefore = parent.getElementCount();
        insertHTMLText(elem.getParentElement(), elem.getStartOffset(), htmlText);
        removeElements(parent, indexBefore + (parent.getElementCount() - numElementsBefore), 1);
        throw new UnsupportedOperationException(Messages.getString("swing.27")); //$NON-NLS-1$
    }
   
    public void processHTMLFrameHyperlinkEvent(final HTMLFrameHyperlinkEvent event) {
        final String target = event.getTarget();

        if (TARGET_TOP.equals(target)){
            return;
        }

        final Element source = event.getSourceElement();
        final String src = event.getURL().toString();
        if (source != null && TARGET_SELF.equals(target)) {
            processTarget(source, src);
        } else if (source != null && TARGET_PARENT.equals(target)) {
            final Element parent = source.getParentElement();
            if (getParser() == null) {
                setParser(new ParserDelegator());
            }
            try {
                setOuterHTML(parent, "<frame src=\"" + src + "\">");
            } catch (BadLocationException e) {
            } catch (IOException e) {
            }
        } else {
            final ElementIterator frameIterator = new ElementIterator(getDefaultRootElement());
            if (frameIterator != null) {
                while (frameIterator.next() != null) {
                    final Element element = frameIterator.current();
                    if (Tag.FRAME.equals(element.getName())
                        && element.getAttributes().containsAttribute(HTML.Attribute.NAME, target)) {
                       
                        processTarget(element, src);
                    }
                }
            }
        }
    }
   
    protected AbstractElement createDefaultRoot() {
        final BlockElement root = new BlockElement(null, null);
        writeLock();
        try {
            root.addAttribute(StyleConstants.NameAttribute, Tag.HTML);
            AttributeSet attr = getAttributeContext().getEmptySet();
            final BranchElement body = (BranchElement)createBranchElement(root,
                                                                          null);
            body.addAttribute(StyleConstants.NameAttribute, Tag.BODY);
           
            final BranchElement p = (BranchElement)createBranchElement(body, null);
            p.addAttribute(StyleConstants.NameAttribute, Tag.P);
            p.addAttribute(CSS.Attribute.MARGIN_TOP,
                           CSS.Attribute.MARGIN_TOP.getDefaultValue());
           
            final LeafElement content =
                (LeafElement)createLeafElement(p, null, getStartPosition().getOffset(),
                                              getEndPosition().getOffset());
            content.addAttribute(StyleConstants.NameAttribute, Tag.CONTENT);
            p.replace(0, 0, new Element[] {content});
            body.replace(0, 0, new Element[] {p});
            root.replace(0, 0, new Element[] {body});
        } finally {
            writeUnlock();
        }
        return root;
    }

    protected Element createLeafElement(final Element parent,
                                        final AttributeSet a,
                                        final int start, final int end) {
        return new RunElement(parent, a, start, end);
    }

    protected Element createBranchElement(final Element parent,
                                          final AttributeSet attr) {
        return new BlockElement(parent, attr);
    }
   
    protected void insertUpdate(final DefaultDocumentEvent event, final AttributeSet attrs) {
        AttributeSet contentAttr = attrs;
        if (contentAttr == null) {
            contentAttr = new SimpleAttributeSet();
            ((SimpleAttributeSet)contentAttr).addAttribute(StyleConstants.NameAttribute, Tag.CONTENT);
        }
        super.insertUpdate(event, contentAttr);
    }

    private void processTarget(final Element target, final String src) {
        final MutableAttributeSet targetAttr = (MutableAttributeSet)target.getAttributes();
        writeLock();
        try {
            targetAttr.addAttribute(HTML.Attribute.SRC, src);
        } finally {
            writeUnlock();
        }
        fireChangedUpdate(new DefaultDocumentEvent(target.getStartOffset(), target.getEndOffset() - target.getStartOffset(), EventType.CHANGE));
    }

    private void removeElements(final Element parent, final int startRemoveIndex, final int numRemoved) {
        final Element[] emptyArray = new Element[0];
        final Element[] removedArray = new Element[numRemoved];
        final BranchElement branch = (BranchElement)parent;
        final int numElements = branch.getElementCount();
        for (int i = 0; i < numRemoved; i++) {
            removedArray[i] = branch.getElement(startRemoveIndex + i);
        }
        branch.replace(startRemoveIndex, numRemoved, emptyArray);
        final int eventLength = removedArray[numRemoved - 1].getEndOffset() - removedArray[0].getStartOffset();
        DefaultDocumentEvent removeEvent = new DefaultDocumentEvent(removedArray[0].getStartOffset(),
                                                                    eventLength,
                                                                    EventType.REMOVE);
        removeEvent.addEdit(new ElementEdit(parent, startRemoveIndex, removedArray, emptyArray));
        fireRemoveUpdate(removeEvent);
    }

    private void insertHTMLText(final Element elem, final int offset,
                                final String htmlText) throws IOException {
        Element preceedBranch = getInsertionStartElement(offset - 1);
        int pop = -1;
        int push = -1;
        do {
            pop++;
            preceedBranch = preceedBranch.getParentElement();
            push = getAncestorDepth(preceedBranch, elem);
        } while (preceedBranch != null && push < 0);
        if (push == -1) {
            return;
        }
       
        final HTMLReader reader = new HTMLReader(offset, pop, push, null);
        reader.setRemoveImplicitSpecs(true);
        parser.parse(new StringReader(htmlText), reader, true);
        try {
            reader.flush();
        } catch (BadLocationException e) {
            e.printStackTrace();
        }
    }

    private void checkParser() {
        if (parser == null) {
            throw new IllegalStateException(Messages.getString("swing.9D")); //$NON-NLS-1$
        }
    }

    private void checkLeaf(final Element elem) {
        if (elem.isLeaf()) {
            throw new IllegalArgumentException(Messages.getString("swing.9E")); //$NON-NLS-1$
        }
    }
   
    private int getElementIndex(final BranchElement branch, final Element child) {
        final int numChildren = branch.getElementCount();
        for (int i = 0; i < numChildren; i++) {
            if (branch.getElement(i) == child) {
                return i;
            }
        }
        return -1;
    }
   
    private Element getInsertionStartElement(final int offset) {
        Element result = getDefaultRootElement();
        do {
            result = result.getElement(result.getElementIndex(offset));
        } while (!result.isLeaf());
       
        return result;
    }

    private int getAncestorDepth(final Element elem, final Element child) {
        int depth = 0;
        Element parent = child;
        while (parent != null) {
            if (elem == parent) {
                return depth;
            }
            depth++;
            parent = parent.getParentElement();
        }
        return -1;
    }
   
    private static void initDefaultCharacterAttributes() {
        addDefaultCSSAttribute(Tag.B, CSS.Attribute.FONT_WEIGHT, "bold");
        addDefaultCSSAttribute(Tag.I, CSS.Attribute.FONT_STYLE, "italic");
        addDefaultCSSAttribute(Tag.STRIKE, CSS.Attribute.TEXT_DECORATION, "line-through");
        addDefaultCSSAttribute(Tag.SUB, CSS.Attribute.VERTICAL_ALIGN, "sub");
        addDefaultCSSAttribute(Tag.SUP, CSS.Attribute.VERTICAL_ALIGN, "super");
        addDefaultCSSAttribute(Tag.U, CSS.Attribute.TEXT_DECORATION, "underline");
        addDefaultCSSAttribute(Tag.PRE, CSS.Attribute.WHITE_SPACE, "pre");
    }

    private static void addDefaultCSSAttribute(final Tag tag, final CSS.Attribute attr, final String value) {
        SimpleAttributeSet attrSet = new SimpleAttributeSet();
        attrSet.addAttribute(attr, attr.getConverter().toCSS(value));
        DEFAULT_CHARACTER_ATTRIBUTES.addAttribute(tag, attrSet);
    }

    private static SimpleAttributeSet getDefaultCSSAttributes(final Tag tag) {
        return (SimpleAttributeSet)DEFAULT_CHARACTER_ATTRIBUTES.getAttribute(tag);
    }
}
TOP

Related Classes of javax.swing.text.html.HTMLDocument$HTMLReader$LabelAction

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.