0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: /**
0018: * @author Alexander T. Simbirtsev
0019: * @version $Revision$
0020: */package javax.swing.text.html;
0021:
0022: import java.io.BufferedReader;
0023: import java.io.IOException;
0024: import java.io.InputStreamReader;
0025: import java.io.StringReader;
0026: import java.net.URL;
0027: import java.util.HashMap;
0028: import java.util.HashSet;
0029: import java.util.Set;
0030: import java.util.Stack;
0031: import java.util.Vector;
0032:
0033: import javax.swing.ButtonGroup;
0034: import javax.swing.event.DocumentEvent.EventType;
0035: import javax.swing.text.AttributeSet;
0036: import javax.swing.text.BadLocationException;
0037: import javax.swing.text.DefaultEditorKit;
0038: import javax.swing.text.DefaultStyledDocument;
0039: import javax.swing.text.Element;
0040: import javax.swing.text.ElementIterator;
0041: import javax.swing.text.MutableAttributeSet;
0042: import javax.swing.text.SimpleAttributeSet;
0043: import javax.swing.text.StyleConstants;
0044: import javax.swing.text.html.HTML.Tag;
0045: import javax.swing.text.html.HTMLEditorKit.Parser;
0046: import javax.swing.text.html.parser.ParserDelegator;
0047:
0048: import org.apache.harmony.x.swing.text.html.form.Form;
0049: import org.apache.harmony.x.swing.text.html.form.FormAttributes;
0050: import org.apache.harmony.x.swing.text.html.form.FormButtonModel;
0051: import org.apache.harmony.x.swing.text.html.form.FormElement;
0052: import org.apache.harmony.x.swing.text.html.form.FormFieldsetModel;
0053: import org.apache.harmony.x.swing.text.html.form.FormOption;
0054: import org.apache.harmony.x.swing.text.html.form.FormOptionGroup;
0055: import org.apache.harmony.x.swing.text.html.form.FormSelectComboBoxModel;
0056: import org.apache.harmony.x.swing.text.html.form.FormSelectListModel;
0057: import org.apache.harmony.x.swing.text.html.form.FormSelectModel;
0058: import org.apache.harmony.x.swing.text.html.form.FormTextModel;
0059: import org.apache.harmony.x.swing.text.html.form.FormToggleButtonModel;
0060:
0061: import org.apache.harmony.x.swing.internal.nls.Messages;
0062:
0063: public class HTMLDocument extends DefaultStyledDocument {
0064:
0065: public class BlockElement extends BranchElement {
0066: public BlockElement(final Element parent,
0067: final AttributeSet attr) {
0068: super (parent, attr);
0069: }
0070:
0071: public String getName() {
0072: final Object tag = getAttribute(StyleConstants.NameAttribute);
0073: return tag != null ? tag.toString() : super .getName();
0074: }
0075:
0076: public AttributeSet getResolveParent() {
0077: return null;
0078: }
0079: }
0080:
0081: public class RunElement extends LeafElement {
0082: public RunElement(final Element parent, final AttributeSet a,
0083: final int start, final int end) {
0084: super (parent, a, start, end);
0085: }
0086:
0087: public String getName() {
0088: final Object tag = getAttribute(StyleConstants.NameAttribute);
0089: return tag != null ? tag.toString() : super .getName();
0090: }
0091:
0092: public AttributeSet getResolveParent() {
0093: return null;
0094: }
0095: }
0096:
0097: public class HTMLReader extends HTMLEditorKit.ParserCallback {
0098:
0099: private boolean anchorReferenceEncountered = false;
0100:
0101: public class TagAction {
0102: public void start(final Tag tag,
0103: final MutableAttributeSet attr) {
0104: }
0105:
0106: public void end(final Tag tag) {
0107: }
0108: }
0109:
0110: public class BlockAction extends TagAction {
0111: public void start(final Tag tag,
0112: final MutableAttributeSet attr) {
0113: addJoinPreviousSpec = true;
0114: checkInsertTag(tag);
0115: skipAddingBlockSpec = false;
0116: blockOpen(tag, attr);
0117: }
0118:
0119: public void end(final Tag tag) {
0120: blockClose(tag);
0121: }
0122: }
0123:
0124: public class CharacterAction extends TagAction {
0125: public void start(final Tag tag,
0126: final MutableAttributeSet attr) {
0127: addJoinPreviousSpec = true;
0128: checkInsertTag(tag);
0129: pushCharacterStyle();
0130: charAttr.addAttribute(tag, attr.copyAttributes());
0131: }
0132:
0133: public void end(final Tag tag) {
0134: popCharacterStyle();
0135: }
0136: }
0137:
0138: public class FormAction extends SpecialAction {
0139: private static final String NO_NAME_ATTRIBUTE = "___no_name___";
0140:
0141: private Form currentForm;
0142: private HashMap radioGroupped = new HashMap();
0143:
0144: public void start(final Tag tag,
0145: final MutableAttributeSet attr) {
0146: if (Tag.OPTION.equals(tag) || Tag.OPTGROUP.equals(tag)) {
0147: checkInsertTag(tag);
0148: handleOption(tag, attr);
0149: return;
0150: }
0151: super .start(tag, attr);
0152:
0153: final ElementSpec spec = getLastSpec();
0154: assert spec != null : "we've just created a spec in super.start()";
0155: FormElement model = null;
0156: final MutableAttributeSet specAttr = (MutableAttributeSet) spec
0157: .getAttributes();
0158: if (Tag.INPUT.equals(tag)) {
0159: model = handleInput(attr, specAttr);
0160: } else if (Tag.TEXTAREA.equals(tag)) {
0161: model = new FormTextModel(getCurrentForm(), attr);
0162: openedBlocks.add(Tag.TEXTAREA);
0163: } else if (Tag.BUTTON.equals(tag)) {
0164: model = new FormButtonModel(getCurrentForm(), attr);
0165: openedBlocks.add(Tag.BUTTON);
0166: } else if (Tag.LEGEND.equals(tag)) {
0167: openedBlocks.add(Tag.LEGEND);
0168: if (openedBlocks.contains(Tag.FIELDSET)) {
0169: handleLegend(null, specAttr);
0170: }
0171: } else if (Tag.FIELDSET.equals(tag)) {
0172: model = new FormFieldsetModel(getCurrentForm(),
0173: attr);
0174: openedBlocks.add(Tag.FIELDSET);
0175: } else if (Tag.SELECT.equals(tag)) {
0176: if (FormAttributes.isListSelect(specAttr)) {
0177: selectModel = new FormSelectListModel(
0178: getCurrentForm(), attr);
0179: } else {
0180: selectModel = new FormSelectComboBoxModel(
0181: getCurrentForm(), attr);
0182: }
0183: model = selectModel;
0184: openedBlocks.add(Tag.SELECT);
0185: }
0186: if (model != null) {
0187: specAttr.addAttribute(
0188: StyleConstants.ModelAttribute, model);
0189: assert currentForm != null : "creating model with getCurrentForm() in constructor assures this";
0190: currentForm.addElement(model);
0191: }
0192: }
0193:
0194: public void end(final Tag tag) {
0195: openedBlocks.remove(tag);
0196: if (Tag.SELECT.equals(tag)) {
0197: selectModel = null;
0198: } else if (Tag.OPTGROUP.equals(tag)) {
0199: if (selectModel != null) {
0200: selectModel.getRootOptionGroup().popGroup();
0201: }
0202: }
0203: }
0204:
0205: void openForm(final AttributeSet attr) {
0206: currentForm = new Form(attr);
0207: }
0208:
0209: void closeForm() {
0210: currentForm = null;
0211: selectModel = null;
0212: radioGroupped.clear();
0213: }
0214:
0215: private FormElement handleInput(final AttributeSet attr,
0216: final MutableAttributeSet specAttr) {
0217: FormElement result = null;
0218: String inputType = (String) attr
0219: .getAttribute(HTML.Attribute.TYPE);
0220: if (inputType == null) {
0221: inputType = FormAttributes.INPUT_TYPE_TEXT;
0222: specAttr.addAttribute(HTML.Attribute.TYPE,
0223: inputType);
0224: }
0225: int inputTypeIndex = FormAttributes
0226: .getTypeAttributeIndex((String) inputType);
0227:
0228: switch (inputTypeIndex) {
0229: case FormAttributes.INPUT_TYPE_TEXT_INDEX:
0230: case FormAttributes.INPUT_TYPE_PASSWORD_INDEX:
0231: result = new FormTextModel(getCurrentForm(), attr,
0232: FormTextModel.ENABLE_MAX_LENGTH_BOUND);
0233: break;
0234: case FormAttributes.INPUT_TYPE_FILE_INDEX:
0235: result = new FormTextModel(getCurrentForm(), attr);
0236: break;
0237: case FormAttributes.INPUT_TYPE_SUBMIT_INDEX:
0238: case FormAttributes.INPUT_TYPE_RESET_INDEX:
0239: case FormAttributes.INPUT_TYPE_BUTTON_INDEX:
0240: case FormAttributes.INPUT_TYPE_IMAGE_INDEX:
0241: result = new FormButtonModel(getCurrentForm(), attr);
0242: break;
0243: case FormAttributes.INPUT_TYPE_RADIO_INDEX:
0244: FormToggleButtonModel buttonModel = new FormToggleButtonModel(
0245: getCurrentForm(), attr);
0246: manageRadioGroup(attr, buttonModel);
0247: result = buttonModel;
0248: break;
0249: case FormAttributes.INPUT_TYPE_CHECKBOX_INDEX:
0250: result = new FormToggleButtonModel(
0251: getCurrentForm(), attr);
0252: break;
0253: default:
0254: break;
0255: }
0256: return result;
0257: }
0258:
0259: private void manageRadioGroup(final AttributeSet attr,
0260: final FormToggleButtonModel buttonModel) {
0261: String name = (String) attr
0262: .getAttribute(HTML.Attribute.NAME);
0263: if (name == null) {
0264: name = NO_NAME_ATTRIBUTE;
0265: }
0266: Object groupped = radioGroupped.get(name);
0267: if (groupped instanceof FormToggleButtonModel) {
0268: FormToggleButtonModel grouppedModel = (FormToggleButtonModel) groupped;
0269: ButtonGroup buttonGroup;
0270: if (grouppedModel.getGroup() != null) {
0271: buttonGroup = grouppedModel.getGroup();
0272: } else {
0273: buttonGroup = new ButtonGroup();
0274: grouppedModel.setGroup(buttonGroup);
0275: }
0276: buttonModel.setGroup(buttonGroup);
0277: } else {
0278: radioGroupped.put(name, buttonModel);
0279: }
0280: }
0281:
0282: private void handleOption(final Tag tag,
0283: final AttributeSet attr) {
0284: FormOption option = null;
0285: FormOptionGroup currentGroup = (selectModel != null) ? selectModel
0286: .getRootOptionGroup().getCurrentGroup()
0287: : null;
0288:
0289: if (Tag.OPTION.equals(tag)) {
0290: option = new FormOption(currentGroup, attr);
0291: openedBlocks.add(Tag.OPTION);
0292: } else if (Tag.OPTGROUP.equals(tag)) {
0293: currentGroup = new FormOptionGroup(currentGroup,
0294: attr);
0295: option = currentGroup;
0296: }
0297:
0298: if (option != null && selectModel != null) {
0299: selectModel.addOption(option);
0300: selectModel.getRootOptionGroup().pushGroup(
0301: currentGroup);
0302: }
0303: }
0304:
0305: private ElementSpec getLastSpec() {
0306: return !parseBuffer.isEmpty() ? (ElementSpec) parseBuffer
0307: .get(parseBuffer.size() - 1)
0308: : null;
0309: }
0310:
0311: private Form getCurrentForm() {
0312: return (currentForm != null) ? currentForm
0313: : (currentForm = new Form(
0314: SimpleAttributeSet.EMPTY));
0315: }
0316: }
0317:
0318: public class HiddenAction extends TagAction {
0319: public void start(final Tag tag,
0320: final MutableAttributeSet attr) {
0321: addSpecialElement(tag, attr);
0322: }
0323:
0324: public void end(final Tag tag) {
0325: addSpecialElement(tag, createMutableSet(
0326: HTML.Attribute.ENDTAG, Boolean.TRUE));
0327: }
0328: }
0329:
0330: public class IsindexAction extends TagAction {
0331: public void start(final Tag tag,
0332: final MutableAttributeSet attr) {
0333: addJoinPreviousSpec = true;
0334: checkInsertTag(tag);
0335: blockOpen(Tag.IMPLIED, new SimpleAttributeSet());
0336: addSpecialElement(tag, attr);
0337: blockClose(Tag.IMPLIED);
0338: }
0339:
0340: }
0341:
0342: public class ParagraphAction extends BlockAction {
0343: public void start(final Tag tag,
0344: final MutableAttributeSet attr) {
0345: super .start(tag, attr);
0346: openedBlocks.add(PARAGRAPH_TAG);
0347: }
0348:
0349: public void end(final Tag tag) {
0350: super .end(tag);
0351: openedBlocks.remove(PARAGRAPH_TAG);
0352: }
0353: }
0354:
0355: public class PreAction extends BlockAction {
0356: public void start(final Tag tag,
0357: final MutableAttributeSet attr) {
0358: super .start(tag, attr);
0359: MutableAttributeSet blockAttr = new SimpleAttributeSet(
0360: attr);
0361: SimpleAttributeSet defaultAttr = getDefaultCSSAttributes(tag);
0362: if (defaultAttr != null) {
0363: blockAttr.addAttributes(defaultAttr);
0364: }
0365: blockOpen(Tag.IMPLIED, blockAttr);
0366: impliedBlockOpen = true;
0367: needImpliedNewLine = true;
0368: }
0369:
0370: public void end(final Tag tag) {
0371: blockClose(Tag.IMPLIED);
0372: super .end(tag);
0373: }
0374: }
0375:
0376: public class SpecialAction extends TagAction {
0377: public void start(final Tag tag,
0378: final MutableAttributeSet attr) {
0379: addJoinPreviousSpec = true;
0380: addSpecialElement(tag, attr);
0381: }
0382: }
0383:
0384: class AdvancedCharacterAction extends CharacterAction {
0385: public void start(final Tag tag,
0386: final MutableAttributeSet attr) {
0387: super .start(tag, attr);
0388: final AttributeSet attrs = getDefaultCSSAttributes(tag);
0389: if (attrs != null) {
0390: charAttr.addAttributes(attrs);
0391: }
0392: }
0393: }
0394:
0395: class LabelAction extends BlockAction {
0396: // TODO
0397: }
0398:
0399: class AnchorAction extends CharacterAction {
0400: public void start(final Tag tag,
0401: final MutableAttributeSet attr) {
0402: anchorReferenceEncountered = attr
0403: .isDefined(HTML.Attribute.HREF);
0404: //anchorReferenceEncountered verification added according to H4606
0405: if (anchorReferenceEncountered) {
0406: super .start(tag, attr);
0407: openedBlocks.add(Tag.A);
0408: }
0409: }
0410:
0411: public void end(final Tag tag) {
0412: // According to H4574 Empty AncorTextEncoured verification has
0413: // been removed, but according H4606 anchorReferenceEncountered
0414: // has been added
0415: if (anchorReferenceEncountered) {
0416: super .end(tag);
0417: openedBlocks.remove(Tag.A);
0418: anchorReferenceEncountered = false;
0419: }
0420: }
0421: }
0422:
0423: class FontAction extends CharacterAction {
0424: final HTML.Attribute[] specialHTMLAttributes = new HTML.Attribute[] {
0425: HTML.Attribute.FACE, HTML.Attribute.COLOR,
0426: HTML.Attribute.SIZE };
0427:
0428: final CSS.Attribute[] specialCSSAttributes = new CSS.Attribute[] {
0429: CSS.Attribute.FONT_FAMILY, CSS.Attribute.COLOR,
0430: CSS.Attribute.FONT_SIZE };
0431:
0432: public void start(final Tag tag,
0433: final MutableAttributeSet attr) {
0434: super .start(tag, attr);
0435:
0436: final StyleSheet styleSheet = getStyleSheet();
0437: for (int i = 0; i < specialHTMLAttributes.length; i++) {
0438: final String value = (String) attr
0439: .getAttribute(specialHTMLAttributes[i]);
0440: if (value != null) {
0441: styleSheet.addCSSAttributeFromHTML(charAttr,
0442: specialCSSAttributes[i], value);
0443: }
0444: }
0445: }
0446: }
0447:
0448: class FormTagAction extends BlockAction {
0449: private Tag[] formActionTags = new Tag[] { Tag.TEXTAREA,
0450: Tag.SELECT, Tag.INPUT, Tag.OPTION, Tag.OPTGROUP,
0451: Tag.BUTTON, Tag.LEGEND, Tag.FIELDSET };
0452:
0453: public void start(final Tag tag,
0454: final MutableAttributeSet attr) {
0455: super .start(tag, attr);
0456: FormAction formAction = getFormAction();
0457: if (formAction != null) {
0458: formAction.openForm(attr);
0459: }
0460: }
0461:
0462: public void end(final Tag tag) {
0463: super .end(tag);
0464: FormAction formAction = getFormAction();
0465: if (formAction != null) {
0466: formAction.closeForm();
0467: }
0468: }
0469:
0470: private FormAction getFormAction() {
0471: for (int i = 0; i < formActionTags.length; i++) {
0472: final Tag tag = formActionTags[i];
0473: TagAction action = getAction(tag);
0474: if (action instanceof FormAction) {
0475: return (FormAction) action;
0476: }
0477: }
0478: return null;
0479: }
0480: }
0481:
0482: class ImageAction extends SpecialAction {
0483:
0484: @Override
0485: public void start(Tag tag, MutableAttributeSet attr) {
0486:
0487: if (anchorReferenceEncountered) {
0488:
0489: attr.addAttributes(charAttr.copyAttributes());
0490: }
0491:
0492: super .start(tag, attr);
0493: }
0494: }
0495:
0496: class BaseAction extends TagAction {
0497: public void start(final Tag tag,
0498: final MutableAttributeSet attr) {
0499: checkInsertTag(tag);
0500: if (attr == null) {
0501: return;
0502: }
0503: final String href = (String) attr
0504: .getAttribute(HTML.Attribute.HREF);
0505: if (href == null) {
0506: return;
0507: }
0508:
0509: final URL url = HTML.resolveURL(href, getBase());
0510: setBase(url);
0511: putProperty(INITIAL_BASE_PROPERTY, url);
0512: }
0513: }
0514:
0515: class HeadAction extends BlockAction {
0516: public void end(final Tag tag) {
0517: super .end(tag);
0518: if (styleRule != null) {
0519: getStyleSheet().addRule(styleRule);
0520: styleRule = null;
0521: }
0522: }
0523: }
0524:
0525: class TitleAction extends BlockAction {
0526: public void start(final Tag tag,
0527: final MutableAttributeSet attr) {
0528: addJoinPreviousSpec = true;
0529: checkInsertTag(tag);
0530: addSpecialElement(tag, attr);
0531: openedBlocks.add(Tag.TITLE);
0532: }
0533:
0534: public void end(final Tag tag) {
0535: addSpecialElement(tag, createMutableSet(
0536: HTML.Attribute.ENDTAG, Boolean.TRUE));
0537: openedBlocks.remove(Tag.TITLE);
0538: }
0539: }
0540:
0541: class AppletAction extends HiddenAction {
0542: // TODO: implement
0543: public void start(final Tag tag,
0544: final MutableAttributeSet attr) {
0545: addJoinPreviousSpec = true;
0546: super .start(tag, attr);
0547: }
0548: }
0549:
0550: class AreaAction extends HiddenAction {
0551: // TODO: implement
0552: }
0553:
0554: class MapAction extends HiddenAction {
0555: // TODO: implement
0556: }
0557:
0558: class ScriptAction extends HiddenAction {
0559: // TODO: implement
0560: public void start(final Tag tag,
0561: final MutableAttributeSet attr) {
0562: addJoinPreviousSpec = true;
0563: super .start(tag, attr);
0564: }
0565: }
0566:
0567: class LinkAction extends HiddenAction {
0568: public void start(final Tag tag,
0569: final MutableAttributeSet attr) {
0570: addJoinPreviousSpec = true;
0571: super .start(tag, attr);
0572: if (attr.containsAttribute(HTML.Attribute.TYPE,
0573: "text/css")) {
0574: loadCSS(attr);
0575: }
0576: }
0577:
0578: public void end(final Tag tag) {
0579: }
0580:
0581: private void loadCSS(final AttributeSet attr) {
0582: String href = (String) attr
0583: .getAttribute(HTML.Attribute.HREF);
0584: final URL url = HTML.resolveURL(href, getBase());
0585: try {
0586: getStyleSheet().loadRules(
0587: new BufferedReader(new InputStreamReader(
0588: url.openStream())), url);
0589: } catch (IOException e) {
0590: }
0591: }
0592: }
0593:
0594: class MetaAction extends SpecialAction {
0595: public void end(final Tag tag) {
0596: }
0597: }
0598:
0599: class StyleAction extends TagAction {
0600: public void start(Tag tag, MutableAttributeSet attr) {
0601: checkInsertTag(tag);
0602: openedBlocks.add(Tag.STYLE);
0603: }
0604:
0605: public void end(Tag tag) {
0606: openedBlocks.remove(Tag.STYLE);
0607: }
0608: }
0609:
0610: protected MutableAttributeSet charAttr = new SimpleAttributeSet();
0611:
0612: protected Vector<DefaultStyledDocument.ElementSpec> parseBuffer = new Vector<DefaultStyledDocument.ElementSpec>();
0613:
0614: private static final String PARAGRAPH_TAG = "_paragraph_tag_";
0615: private static final int IMPLIED_HTML_DOCUMENT_START_SPECS_NUMBER = 8;
0616: private static final int TOKEN_THRESHOLD_MULTIPLIER = 5;
0617:
0618: private final HashMap tagActionMap = new HashMap();
0619: private final TagAction emptyAction = new TagAction();
0620: private final Stack attrStack = new Stack();
0621: private final Set openedBlocks = new HashSet();
0622: private boolean impliedBlockOpen;
0623: private int numBlocksOpen;
0624:
0625: private boolean needImpliedNewLine;
0626: private String styleRule;
0627: private FormSelectModel selectModel;
0628: private int tokenThreshold;
0629:
0630: private int offset;
0631: private int popDepth;
0632: private int pushDepth;
0633: private Tag insertTag;
0634: private boolean insertTagFound;
0635: private boolean implicitSpecsRemove;
0636: private boolean skipAddingBlockSpec;
0637: private boolean addJoinPreviousSpec;
0638: private int specsCount;
0639:
0640: public HTMLReader(final int offset) {
0641: this (offset, 0, 0, null);
0642: }
0643:
0644: public HTMLReader(final int offset, final int popDepth,
0645: final int pushDepth, final Tag insertTag) {
0646: this .offset = offset;
0647: this .popDepth = popDepth;
0648: this .pushDepth = pushDepth;
0649: this .insertTag = insertTag;
0650: insertTagFound = (insertTag == null);
0651: tokenThreshold = getTokenThreshold();
0652: fillTagActionMap();
0653: }
0654:
0655: public void handleComment(final char[] data, final int pos) {
0656: final String comment = new String(data);
0657: if (openedBlocks.contains(Tag.P)) {
0658: addSpecialElement(Tag.COMMENT, createMutableSet(
0659: HTML.Attribute.COMMENT, comment));
0660: } else {
0661: Vector comments = (Vector) getProperty(AdditionalComments);
0662: if (comments == null) {
0663: comments = new Vector();
0664: putProperty(AdditionalComments, comments);
0665: }
0666: comments.add(comment);
0667: }
0668: }
0669:
0670: public void handleEndOfLineString(final String eol) {
0671: putProperty(DefaultEditorKit.EndOfLineStringProperty, eol);
0672: }
0673:
0674: public void handleSimpleTag(final Tag tag,
0675: final MutableAttributeSet attr, final int pos) {
0676: final TagAction action = getAction(tag);
0677: MutableAttributeSet tagAttr = handleStyleAttribute(attr);
0678: if (action != emptyAction || !getPreservesUnknownTags()) {
0679: action.start(tag, tagAttr);
0680: action.end(tag);
0681: } else {
0682: addSpecialElement(tag, tagAttr);
0683: }
0684: }
0685:
0686: public void handleStartTag(final Tag tag,
0687: final MutableAttributeSet attr, final int pos) {
0688: final TagAction action = getAction(tag);
0689: action.start(tag, handleStyleAttribute(attr));
0690: }
0691:
0692: public void handleEndTag(final Tag tag, final int pos) {
0693: final TagAction action = getAction(tag);
0694: action.end(tag);
0695: }
0696:
0697: public void handleText(final char[] data, final int pos) {
0698: if (openedBlocks.contains(Tag.TITLE)) {
0699: putProperty(TitleProperty, new String(data));
0700: return;
0701: }
0702: if (openedBlocks.contains(Tag.STYLE)
0703: && openedBlocks.contains(Tag.HEAD)) {
0704: final String newStyle = new String(data);
0705: if (styleRule == null) {
0706: styleRule = newStyle;
0707: } else {
0708: styleRule += newStyle;
0709: }
0710: return;
0711: }
0712: if (openedBlocks.contains(Tag.TEXTAREA)) {
0713: textAreaContent(data);
0714: return;
0715: }
0716: if (openedBlocks.contains(Tag.PRE)) {
0717: preContent(data);
0718: return;
0719: }
0720: if (openedBlocks.contains(Tag.OPTION)
0721: && openedBlocks.contains(Tag.SELECT)) {
0722: final Option option = selectModel.getLastOption();
0723: if (option != null
0724: && !(option instanceof FormOptionGroup)) {
0725: option.setLabel(new String(data));
0726: }
0727: }
0728: if (openedBlocks.contains(Tag.LEGEND)
0729: && openedBlocks.contains(Tag.FIELDSET)) {
0730: if (handleLegend(new String(data), null)) {
0731: return;
0732: }
0733: }
0734: if (numBlocksOpen > 0) {
0735: addContent(data, 0, data.length);
0736: }
0737: }
0738:
0739: public void flush() throws BadLocationException {
0740: flushImpl(true);
0741: }
0742:
0743: protected void registerTag(final Tag tag, final TagAction action) {
0744: tagActionMap.put(tag, action);
0745: }
0746:
0747: protected void pushCharacterStyle() {
0748: if (charAttr == null) {
0749: throw new NullPointerException();
0750: }
0751: attrStack.push(charAttr.copyAttributes());
0752: }
0753:
0754: protected void popCharacterStyle() {
0755: if (!attrStack.empty()) {
0756: charAttr = (MutableAttributeSet) attrStack.pop();
0757: }
0758: }
0759:
0760: protected void preContent(char[] data) {
0761: int offset = 0;
0762:
0763: for (int i = 0; i < data.length; i++) {
0764: if ((data[i] == '\n') || (data[i] == '\r')) {
0765: addContent(data, offset, i - offset);
0766: blockClose(HTML.Tag.IMPLIED);
0767:
0768: blockOpen(HTML.Tag.IMPLIED,
0769: new SimpleAttributeSet());
0770: offset = i + 1;
0771: }
0772: }
0773:
0774: if (offset < data.length) {
0775: addContent(data, offset, data.length - offset);
0776: }
0777: }
0778:
0779: protected void addContent(final char[] data, final int offset,
0780: final int length) {
0781: addContent(data, offset, length, true);
0782: }
0783:
0784: protected void addContent(final char[] data, final int offset,
0785: final int length,
0786: final boolean createImpliedPIfNecessary) {
0787: addContentSpec(Tag.CONTENT, data, offset, length, charAttr,
0788: createImpliedPIfNecessary);
0789:
0790: if (parseBuffer.size() > tokenThreshold) {
0791: try {
0792: flushImpl(false);
0793: } catch (BadLocationException e) {
0794: }
0795: tokenThreshold *= TOKEN_THRESHOLD_MULTIPLIER;
0796: }
0797: }
0798:
0799: protected void addSpecialElement(final Tag tag,
0800: final MutableAttributeSet attr) {
0801: final boolean needImpliedBlock = !Tag.FRAME.equals(tag);
0802: if (!needImpliedBlock) {
0803: needImpliedNewLine = false;
0804: }
0805: addContentSpec(tag, new char[] { ' ' }, 0, 1, attr,
0806: needImpliedBlock);
0807: }
0808:
0809: protected void textAreaContent(final char[] data) {
0810: final ElementSpec textareaSpec = findLastSpec(Tag.TEXTAREA);
0811: if (textareaSpec == null) {
0812: return;
0813: }
0814: FormTextModel doc = (FormTextModel) getModel(textareaSpec);
0815: if (doc != null) {
0816: doc.setInitialContent(new String(data));
0817: }
0818: }
0819:
0820: protected void blockOpen(final Tag tag,
0821: final MutableAttributeSet attr) {
0822: if (impliedBlockOpen && !Tag.IMPLIED.equals(tag)) {
0823: blockClose(Tag.IMPLIED);
0824: impliedBlockOpen = false;
0825: }
0826: if (!skipAddingBlockSpec) {
0827: ElementSpec blockSpec = new ElementSpec(
0828: deriveSpecAttributes(tag, attr),
0829: ElementSpec.StartTagType);
0830: addSpec(blockSpec);
0831: }
0832: skipAddingBlockSpec = false;
0833: needImpliedNewLine = true;
0834: openedBlocks.add(tag);
0835: numBlocksOpen++;
0836: }
0837:
0838: protected void blockClose(final Tag tag) {
0839: if (needImpliedNewLine) {
0840: addImpliedNewLine();
0841: if (impliedBlockOpen && !Tag.IMPLIED.equals(tag)) {
0842: blockClose(Tag.IMPLIED);
0843: impliedBlockOpen = false;
0844: }
0845: }
0846: numBlocksOpen--;
0847: openedBlocks.remove(tag);
0848: addSpec(new ElementSpec(null, ElementSpec.EndTagType));
0849: }
0850:
0851: private Object getModel(final ElementSpec spec) {
0852: return spec.getAttributes().getAttribute(
0853: StyleConstants.ModelAttribute);
0854: }
0855:
0856: private void addImpliedNewLine() {
0857: pushCharacterStyle();
0858: charAttr.addAttribute(HTML.Attribute.IMPLIED_NEW_LINE,
0859: Boolean.TRUE);
0860: addContent(new char[] { '\n' }, 0, 1, true);
0861: popCharacterStyle();
0862: needImpliedNewLine = false;
0863: }
0864:
0865: private MutableAttributeSet handleStyleAttribute(
0866: final MutableAttributeSet attr) {
0867: final String style = (String) attr
0868: .getAttribute(HTML.Attribute.STYLE);
0869: if (style != null) {
0870: attr.addAttributes(getStyleSheet()
0871: .getDeclaration(style));
0872: attr.removeAttribute(HTML.Attribute.STYLE);
0873: }
0874: return attr;
0875: }
0876:
0877: private void createImpliedBlock() {
0878: if (paragraphOpened()) {
0879: return;
0880: }
0881: blockOpen(Tag.IMPLIED, new SimpleAttributeSet());
0882: impliedBlockOpen = true;
0883: }
0884:
0885: private MutableAttributeSet createMutableSet(final Object key,
0886: final Object value) {
0887: MutableAttributeSet specAttr = new SimpleAttributeSet();
0888: specAttr.addAttribute(key, value);
0889: return specAttr;
0890: }
0891:
0892: private boolean paragraphOpened() {
0893: return openedBlocks.contains(PARAGRAPH_TAG)
0894: || openedBlocks.contains(Tag.IMPLIED)
0895: && impliedBlockOpen;
0896: }
0897:
0898: private boolean handleLegend(final String legend,
0899: final MutableAttributeSet legendAttr) {
0900: final ElementSpec fieldSetSpec = findLastSpec(Tag.FIELDSET);
0901: FormFieldsetModel fieldSet = (FormFieldsetModel) getModel(fieldSetSpec);
0902: if (fieldSet == null || fieldSet.getLegend() != null) {
0903: return false;
0904: }
0905: if (legend != null) {
0906: fieldSet.setLegend(legend);
0907: }
0908: if (legendAttr != null) {
0909: fieldSet.setLegendAttributes(legendAttr);
0910: }
0911: return true;
0912: }
0913:
0914: private void setRemoveImplicitSpecs(
0915: final boolean implicitSpecsRemove) {
0916: this .implicitSpecsRemove = implicitSpecsRemove;
0917: }
0918:
0919: private void flushImpl(final boolean isFinal)
0920: throws BadLocationException {
0921: if (parseBuffer.isEmpty()) {
0922: return;
0923: }
0924: if (isCreate()) {
0925: ElementSpec[] specs = vectorToArray(parseBuffer);
0926: create(specs);
0927: removeDefaultBody();
0928: } else {
0929: if (needAddingPopPushSpecs()) {
0930: addPopPushSpecs(parseBuffer);
0931: }
0932: ElementSpec[] specs = isFinal ? trimEndSpecs(parseBuffer)
0933: : vectorToArray(parseBuffer);
0934: insert(offset, specs);
0935: }
0936:
0937: parseBuffer.clear();
0938: }
0939:
0940: private void removeDefaultBody() {
0941: Element impliedLF = getCharacterElement(getLength() - 1);
0942: try {
0943: remove(getLength() - 1, 1);
0944: } catch (BadLocationException e) {
0945: }
0946: BranchElement root = (BranchElement) getDefaultRootElement();
0947: final int oddBodyIndex = root.getElementCount() - 1;
0948: Element oddBody = root.getElement(oddBodyIndex);
0949: final Element[] emptyArray = new Element[0];
0950: root.replace(oddBodyIndex, 1, emptyArray);
0951:
0952: Element lf = getCharacterElement(getLength());
0953: writeLock();
0954: try {
0955: ((MutableAttributeSet) lf).removeAttributes(lf
0956: .getAttributes().getAttributeNames());
0957: ((MutableAttributeSet) lf).addAttributes(impliedLF
0958: .getAttributes());
0959: } finally {
0960: writeUnlock();
0961: }
0962:
0963: final DefaultDocumentEvent removeEvent = new DefaultDocumentEvent(
0964: oddBody.getStartOffset(), oddBody.getEndOffset()
0965: - oddBody.getStartOffset(),
0966: EventType.REMOVE);
0967: removeEvent.addEdit(new ElementEdit(root, oddBodyIndex,
0968: new Element[] { oddBody }, emptyArray));
0969: fireRemoveUpdate(removeEvent);
0970: }
0971:
0972: private boolean isCreate() {
0973: return offset == 0 && insertTag == null && getLength() == 0
0974: && !implicitSpecsRemove;
0975: }
0976:
0977: private ElementSpec[] trimEndSpecs(final Vector buffer) {
0978: if ((implicitSpecsRemove || insertTag != null
0979: && insertTagFound)) {
0980: for (int i = 0; i < 4; i++) {
0981: buffer.remove(buffer.size() - 1);
0982: }
0983: }
0984: return vectorToArray(buffer);
0985: }
0986:
0987: private ElementSpec[] vectorToArray(final Vector buffer) {
0988: return (ElementSpec[]) buffer
0989: .toArray(new ElementSpec[buffer.size()]);
0990: }
0991:
0992: private boolean needAddingPopPushSpecs() {
0993: return (insertTag != null && insertTagFound || insertTag == null)
0994: && (popDepth != 0 || pushDepth != 0);
0995: }
0996:
0997: private void addContentSpec(final Tag tag, final char[] data,
0998: final int offset, final int length,
0999: final MutableAttributeSet attr,
1000: final boolean createImpliedBlock) {
1001: if (createImpliedBlock) {
1002: createImpliedBlock();
1003: }
1004: checkInsertTag(tag);
1005: addSpec(new ElementSpec(deriveSpecAttributes(tag, attr),
1006: ElementSpec.ContentType, data, offset, length));
1007: }
1008:
1009: private SimpleAttributeSet deriveSpecAttributes(final Tag tag,
1010: final MutableAttributeSet attr) {
1011: attr.removeAttribute(IMPLIED);
1012: attr.addAttribute(StyleConstants.NameAttribute, tag);
1013: return new SimpleAttributeSet(attr);
1014: }
1015:
1016: private void addSpec(final ElementSpec spec) {
1017: if (insertTagFound
1018: && (!implicitSpecsRemove || specsCount >= IMPLIED_HTML_DOCUMENT_START_SPECS_NUMBER)) {
1019: parseBuffer.add(spec);
1020: }
1021: specsCount++;
1022: }
1023:
1024: private void fillTagActionMap() {
1025: HiddenAction hiddenAction = new HiddenAction();
1026: CharacterAction characterAction = new CharacterAction();
1027: AdvancedCharacterAction advancedCharacterAction = new AdvancedCharacterAction();
1028: BlockAction blockAction = new BlockAction();
1029: ParagraphAction paragraphAction = new ParagraphAction();
1030: SpecialAction specialAction = new SpecialAction();
1031: FormAction formAction = new FormAction();
1032:
1033: tagActionMap.put(Tag.A, new AnchorAction());
1034: tagActionMap.put(Tag.ABBR, characterAction);
1035: tagActionMap.put(Tag.ACRONYM, characterAction);
1036: tagActionMap.put(Tag.ADDRESS, characterAction);
1037: tagActionMap.put(Tag.APPLET, new AppletAction());
1038: tagActionMap.put(Tag.AREA, new AreaAction());
1039: tagActionMap.put(Tag.B, advancedCharacterAction);
1040: tagActionMap.put(Tag.BASE, new BaseAction());
1041: tagActionMap.put(Tag.BASEFONT, characterAction);
1042: tagActionMap.put(Tag.BIG, characterAction);
1043: tagActionMap.put(Tag.BDO, characterAction);
1044: tagActionMap.put(Tag.BLOCKQUOTE, blockAction);
1045: tagActionMap.put(Tag.BODY, blockAction);
1046: tagActionMap.put(Tag.BR, specialAction);
1047: tagActionMap.put(Tag.BUTTON, formAction);
1048: tagActionMap.put(Tag.CAPTION, blockAction);
1049: tagActionMap.put(Tag.CENTER, blockAction);
1050: tagActionMap.put(Tag.CITE, characterAction);
1051: tagActionMap.put(Tag.CODE, characterAction);
1052: tagActionMap.put(Tag.COL, hiddenAction);
1053: tagActionMap.put(Tag.COLGROUP, hiddenAction);
1054: tagActionMap.put(Tag.DD, blockAction);
1055: tagActionMap.put(Tag.DFN, characterAction);
1056: tagActionMap.put(Tag.DEL, characterAction);
1057: tagActionMap.put(Tag.DIR, blockAction);
1058: tagActionMap.put(Tag.DIV, blockAction);
1059: tagActionMap.put(Tag.DL, blockAction);
1060: tagActionMap.put(Tag.DT, paragraphAction);
1061: tagActionMap.put(Tag.EM, characterAction);
1062: tagActionMap.put(Tag.FIELDSET, formAction);
1063: tagActionMap.put(Tag.FONT, new FontAction());
1064: tagActionMap.put(Tag.FORM, new FormTagAction());
1065: tagActionMap.put(Tag.FRAME, specialAction);
1066: tagActionMap.put(Tag.FRAMESET, blockAction);
1067: tagActionMap.put(Tag.H1, paragraphAction);
1068: tagActionMap.put(Tag.H2, paragraphAction);
1069: tagActionMap.put(Tag.H3, paragraphAction);
1070: tagActionMap.put(Tag.H4, paragraphAction);
1071: tagActionMap.put(Tag.H5, paragraphAction);
1072: tagActionMap.put(Tag.H6, paragraphAction);
1073: tagActionMap.put(Tag.HEAD, new HeadAction());
1074: tagActionMap.put(Tag.HR, specialAction);
1075: tagActionMap.put(Tag.HTML, blockAction);
1076: tagActionMap.put(Tag.I, advancedCharacterAction);
1077: tagActionMap.put(Tag.IFRAME, hiddenAction);
1078: tagActionMap.put(Tag.IMG, new ImageAction());
1079: tagActionMap.put(Tag.INPUT, formAction);
1080: tagActionMap.put(Tag.INS, characterAction);
1081: tagActionMap.put(Tag.ISINDEX, new IsindexAction());
1082: tagActionMap.put(Tag.KBD, characterAction);
1083: tagActionMap.put(Tag.LABEL, new LabelAction());
1084: tagActionMap.put(Tag.LEGEND, formAction);
1085: tagActionMap.put(Tag.LI, blockAction);
1086: tagActionMap.put(Tag.LINK, new LinkAction());
1087: tagActionMap.put(Tag.MAP, new MapAction());
1088: tagActionMap.put(Tag.MENU, blockAction);
1089: tagActionMap.put(Tag.META, new MetaAction());
1090: tagActionMap.put(Tag.NOFRAMES, blockAction);
1091: tagActionMap.put(Tag.NOSCRIPT, blockAction);
1092: tagActionMap.put(Tag.OBJECT, specialAction);
1093: tagActionMap.put(Tag.OL, blockAction);
1094: tagActionMap.put(Tag.OPTION, formAction);
1095: tagActionMap.put(Tag.OPTGROUP, formAction);
1096: tagActionMap.put(Tag.P, paragraphAction);
1097: tagActionMap.put(Tag.PARAM, hiddenAction);
1098: tagActionMap.put(Tag.PRE, new PreAction());
1099: tagActionMap.put(Tag.Q, characterAction);
1100: tagActionMap.put(Tag.SAMP, characterAction);
1101: tagActionMap.put(Tag.SCRIPT, new ScriptAction());
1102: tagActionMap.put(Tag.SELECT, formAction);
1103: tagActionMap.put(Tag.SMALL, characterAction);
1104: tagActionMap.put(Tag.STRIKE, advancedCharacterAction);
1105: tagActionMap.put(Tag.S, characterAction);
1106: tagActionMap.put(Tag.SPAN, characterAction);
1107: tagActionMap.put(Tag.STRONG, characterAction);
1108: tagActionMap.put(Tag.STYLE, new StyleAction());
1109: tagActionMap.put(Tag.SUB, advancedCharacterAction);
1110: tagActionMap.put(Tag.SUP, advancedCharacterAction);
1111: tagActionMap.put(Tag.TABLE, blockAction);
1112: tagActionMap.put(Tag.TBODY, blockAction);
1113: tagActionMap.put(Tag.TD, blockAction);
1114: tagActionMap.put(Tag.TEXTAREA, formAction);
1115: tagActionMap.put(Tag.TFOOT, blockAction);
1116: tagActionMap.put(Tag.THEAD, blockAction);
1117: tagActionMap.put(Tag.TH, blockAction);
1118: tagActionMap.put(Tag.TITLE, new TitleAction());
1119: tagActionMap.put(Tag.TR, blockAction);
1120: tagActionMap.put(Tag.TT, characterAction);
1121: tagActionMap.put(Tag.U, advancedCharacterAction);
1122: tagActionMap.put(Tag.UL, blockAction);
1123: tagActionMap.put(Tag.VAR, characterAction);
1124: }
1125:
1126: private TagAction getAction(final Tag tag) {
1127: TagAction action = (TagAction) tagActionMap.get(tag);
1128: if (action == null) {
1129: action = emptyAction;
1130: }
1131: return action;
1132: }
1133:
1134: private ElementSpec findLastSpec(final Tag tag) {
1135: for (int i = parseBuffer.size() - 1; i >= 0; i--) {
1136: ElementSpec spec = (ElementSpec) parseBuffer.get(i);
1137: final AttributeSet specAttr = spec.getAttributes();
1138: if (specAttr != null
1139: && specAttr.containsAttribute(
1140: StyleConstants.NameAttribute, tag)) {
1141: return spec;
1142: }
1143: }
1144: return null;
1145: }
1146:
1147: private boolean checkInsertTag(final Tag tag) {
1148: if (!insertTagFound && insertTag.equals(tag)) {
1149: insertTagFound = true;
1150: skipAddingBlockSpec = true;
1151: addPopPushSpecs(parseBuffer);
1152: }
1153:
1154: return insertTagFound;
1155: }
1156:
1157: private void addPopPushSpecs(final Vector buffer) {
1158: addJoinPreviousSpec &= (pushDepth > 0 || popDepth > 0);
1159: // boolean addNewLineTag = (pushDepth == 0 && popDepth > 0);
1160: // if (addNewLineTag && !implicitSpecsRemove && offset == 0) {
1161: // parseBuffer.add(0, new ElementSpec(null, ElementSpec.ContentType, new char[] {'\n'}, 0, 1));
1162: // }
1163: for (int i = 0; i < pushDepth; i++) {
1164: final ElementSpec pushSpec = new ElementSpec(null,
1165: ElementSpec.StartTagType);
1166: pushSpec.setDirection(ElementSpec.JoinNextDirection);
1167: buffer.add(0, pushSpec);
1168: }
1169: pushDepth = 0;
1170: for (int i = 0; i < popDepth; i++) {
1171: final ElementSpec popSpec = new ElementSpec(null,
1172: ElementSpec.EndTagType);
1173: buffer.add(0, popSpec);
1174: }
1175: popDepth = 0;
1176: if (addJoinPreviousSpec && !implicitSpecsRemove
1177: && offset == 0) {
1178: ElementSpec joinPreviousSpec = new ElementSpec(null,
1179: ElementSpec.ContentType, new char[] { '\n' },
1180: 0, 1);
1181: joinPreviousSpec
1182: .setDirection(ElementSpec.JoinPreviousDirection);
1183: parseBuffer.add(0, joinPreviousSpec);
1184: addJoinPreviousSpec = false;
1185: }
1186: }
1187: }
1188:
1189: public abstract static class Iterator {
1190: public abstract AttributeSet getAttributes();
1191:
1192: public abstract int getEndOffset();
1193:
1194: public abstract int getStartOffset();
1195:
1196: public abstract Tag getTag();
1197:
1198: public abstract boolean isValid();
1199:
1200: public abstract void next();
1201: }
1202:
1203: public static final String AdditionalComments = "AdditionalComments";
1204: static final String INITIAL_BASE_PROPERTY = "_initialBase_";
1205:
1206: private static final String TARGET_TOP = "_top";
1207: private static final String TARGET_SELF = "_self";
1208: private static final String TARGET_PARENT = "_parent";
1209:
1210: private static final SimpleAttributeSet DEFAULT_CHARACTER_ATTRIBUTES = new SimpleAttributeSet();
1211:
1212: private URL base;
1213: private Parser parser;
1214: private boolean preservesUnknownTags = true;
1215: private int threshold = Integer.MAX_VALUE;
1216:
1217: static {
1218: initDefaultCharacterAttributes();
1219: }
1220:
1221: public HTMLDocument(final Content c, final StyleSheet styles) {
1222: super (c, styles);
1223: }
1224:
1225: public HTMLDocument(final StyleSheet styles) {
1226: super (styles);
1227: }
1228:
1229: public HTMLDocument() {
1230: super (new StyleSheet());
1231: }
1232:
1233: public void setBase(final URL base) {
1234: this .base = base;
1235: getStyleSheet().setBase(base);
1236: }
1237:
1238: public URL getBase() {
1239: return base;
1240: }
1241:
1242: public void setParser(final HTMLEditorKit.Parser parser) {
1243: this .parser = parser;
1244: }
1245:
1246: public HTMLEditorKit.Parser getParser() {
1247: return parser;
1248: }
1249:
1250: public void setPreservesUnknownTags(final boolean preservesTags) {
1251: preservesUnknownTags = preservesTags;
1252: }
1253:
1254: public boolean getPreservesUnknownTags() {
1255: return preservesUnknownTags;
1256: }
1257:
1258: public void setTokenThreshold(final int threshold) {
1259: this .threshold = threshold;
1260: }
1261:
1262: public int getTokenThreshold() {
1263: return threshold;
1264: }
1265:
1266: public Element getElement(final Element e, final Object attribute,
1267: final Object value) {
1268: final ElementIterator it = new ElementIterator(e);
1269: while (it.next() != null) {
1270: final Element current = it.current();
1271: if (current.getAttributes().containsAttribute(attribute,
1272: value)) {
1273: return current;
1274: }
1275: }
1276: return null;
1277: }
1278:
1279: public Element getElement(final String id) {
1280: return getElement(getDefaultRootElement(), HTML.Attribute.ID,
1281: id);
1282: }
1283:
1284: public Iterator getIterator(final Tag tag) {
1285: return new TagIterator(tag, this );
1286: }
1287:
1288: public HTMLEditorKit.ParserCallback getReader(final int pos,
1289: final int popDepth, final int pushDepth, final Tag insertTag) {
1290: return new HTMLReader(pos, popDepth, pushDepth, insertTag);
1291: }
1292:
1293: public HTMLEditorKit.ParserCallback getReader(final int pos) {
1294: return new HTMLReader(pos);
1295: }
1296:
1297: public StyleSheet getStyleSheet() {
1298: return (StyleSheet) getAttributeContext();
1299: }
1300:
1301: public void insertBeforeEnd(final Element elem,
1302: final String htmlText) throws BadLocationException,
1303: IOException {
1304: checkParser();
1305: checkLeaf(elem);
1306: int offset = isParagraph(elem) ? elem.getEndOffset() - 1 : elem
1307: .getEndOffset();
1308: insertHTMLText(elem, offset, htmlText);
1309: }
1310:
1311: private boolean isParagraph(final Element elem) {
1312: final AttributeSet attr = elem.getAttributes();
1313: return attr != null
1314: && Tag.P.equals(attr
1315: .getAttribute(StyleConstants.NameAttribute));
1316: }
1317:
1318: public void insertAfterEnd(final Element elem, final String htmlText)
1319: throws BadLocationException, IOException {
1320: checkParser();
1321: if (elem == null) {
1322: return;
1323: }
1324: int offset = elem.getEndOffset() > getLength() ? getLength()
1325: : elem.getEndOffset();
1326: insertHTMLText(elem.getParentElement(), offset, htmlText);
1327: }
1328:
1329: public void insertBeforeStart(final Element elem,
1330: final String htmlText) throws BadLocationException,
1331: IOException {
1332: checkParser();
1333: if (elem == null) {
1334: return;
1335: }
1336: insertHTMLText(elem.getParentElement(), elem.getStartOffset(),
1337: htmlText);
1338: }
1339:
1340: public void insertAfterStart(final Element elem,
1341: final String htmlText) throws BadLocationException,
1342: IOException {
1343: checkParser();
1344: checkLeaf(elem);
1345: insertHTMLText(elem, elem.getStartOffset(), htmlText);
1346: }
1347:
1348: public void setInnerHTML(final Element elem, final String htmlText)
1349: throws BadLocationException, IOException {
1350: checkParser();
1351: if (elem == null) {
1352: return;
1353: }
1354: checkLeaf(elem);
1355:
1356: int numRemovingElements = elem.getElementCount();
1357: insertHTMLText(elem, elem.getStartOffset(), htmlText);
1358: removeElements(elem, elem.getElementCount()
1359: - numRemovingElements, numRemovingElements);
1360: throw new UnsupportedOperationException(Messages
1361: .getString("swing.27")); //$NON-NLS-1$
1362: }
1363:
1364: public void setOuterHTML(final Element elem, final String htmlText)
1365: throws BadLocationException, IOException {
1366: checkParser();
1367: if (elem == null) {
1368: return;
1369: }
1370:
1371: int length = elem.getEndOffset() - elem.getStartOffset() - 1;
1372: final BranchElement parent = (BranchElement) elem
1373: .getParentElement();
1374: final int indexBefore = getElementIndex(parent, elem);
1375: final int numElementsBefore = parent.getElementCount();
1376: insertHTMLText(elem.getParentElement(), elem.getStartOffset(),
1377: htmlText);
1378: removeElements(parent, indexBefore
1379: + (parent.getElementCount() - numElementsBefore), 1);
1380: throw new UnsupportedOperationException(Messages
1381: .getString("swing.27")); //$NON-NLS-1$
1382: }
1383:
1384: public void processHTMLFrameHyperlinkEvent(
1385: final HTMLFrameHyperlinkEvent event) {
1386: final String target = event.getTarget();
1387:
1388: if (TARGET_TOP.equals(target)) {
1389: return;
1390: }
1391:
1392: final Element source = event.getSourceElement();
1393: final String src = event.getURL().toString();
1394: if (source != null && TARGET_SELF.equals(target)) {
1395: processTarget(source, src);
1396: } else if (source != null && TARGET_PARENT.equals(target)) {
1397: final Element parent = source.getParentElement();
1398: if (getParser() == null) {
1399: setParser(new ParserDelegator());
1400: }
1401: try {
1402: setOuterHTML(parent, "<frame src=\"" + src + "\">");
1403: } catch (BadLocationException e) {
1404: } catch (IOException e) {
1405: }
1406: } else {
1407: final ElementIterator frameIterator = new ElementIterator(
1408: getDefaultRootElement());
1409: if (frameIterator != null) {
1410: while (frameIterator.next() != null) {
1411: final Element element = frameIterator.current();
1412: if (Tag.FRAME.equals(element.getName())
1413: && element
1414: .getAttributes()
1415: .containsAttribute(
1416: HTML.Attribute.NAME, target)) {
1417:
1418: processTarget(element, src);
1419: }
1420: }
1421: }
1422: }
1423: }
1424:
1425: protected AbstractElement createDefaultRoot() {
1426: final BlockElement root = new BlockElement(null, null);
1427: writeLock();
1428: try {
1429: root.addAttribute(StyleConstants.NameAttribute, Tag.HTML);
1430: AttributeSet attr = getAttributeContext().getEmptySet();
1431: final BranchElement body = (BranchElement) createBranchElement(
1432: root, null);
1433: body.addAttribute(StyleConstants.NameAttribute, Tag.BODY);
1434:
1435: final BranchElement p = (BranchElement) createBranchElement(
1436: body, null);
1437: p.addAttribute(StyleConstants.NameAttribute, Tag.P);
1438: p.addAttribute(CSS.Attribute.MARGIN_TOP,
1439: CSS.Attribute.MARGIN_TOP.getDefaultValue());
1440:
1441: final LeafElement content = (LeafElement) createLeafElement(
1442: p, null, getStartPosition().getOffset(),
1443: getEndPosition().getOffset());
1444: content.addAttribute(StyleConstants.NameAttribute,
1445: Tag.CONTENT);
1446: p.replace(0, 0, new Element[] { content });
1447: body.replace(0, 0, new Element[] { p });
1448: root.replace(0, 0, new Element[] { body });
1449: } finally {
1450: writeUnlock();
1451: }
1452: return root;
1453: }
1454:
1455: protected Element createLeafElement(final Element parent,
1456: final AttributeSet a, final int start, final int end) {
1457: return new RunElement(parent, a, start, end);
1458: }
1459:
1460: protected Element createBranchElement(final Element parent,
1461: final AttributeSet attr) {
1462: return new BlockElement(parent, attr);
1463: }
1464:
1465: protected void insertUpdate(final DefaultDocumentEvent event,
1466: final AttributeSet attrs) {
1467: AttributeSet contentAttr = attrs;
1468: if (contentAttr == null) {
1469: contentAttr = new SimpleAttributeSet();
1470: ((SimpleAttributeSet) contentAttr).addAttribute(
1471: StyleConstants.NameAttribute, Tag.CONTENT);
1472: }
1473: super .insertUpdate(event, contentAttr);
1474: }
1475:
1476: private void processTarget(final Element target, final String src) {
1477: final MutableAttributeSet targetAttr = (MutableAttributeSet) target
1478: .getAttributes();
1479: writeLock();
1480: try {
1481: targetAttr.addAttribute(HTML.Attribute.SRC, src);
1482: } finally {
1483: writeUnlock();
1484: }
1485: fireChangedUpdate(new DefaultDocumentEvent(target
1486: .getStartOffset(), target.getEndOffset()
1487: - target.getStartOffset(), EventType.CHANGE));
1488: }
1489:
1490: private void removeElements(final Element parent,
1491: final int startRemoveIndex, final int numRemoved) {
1492: final Element[] emptyArray = new Element[0];
1493: final Element[] removedArray = new Element[numRemoved];
1494: final BranchElement branch = (BranchElement) parent;
1495: final int numElements = branch.getElementCount();
1496: for (int i = 0; i < numRemoved; i++) {
1497: removedArray[i] = branch.getElement(startRemoveIndex + i);
1498: }
1499: branch.replace(startRemoveIndex, numRemoved, emptyArray);
1500: final int eventLength = removedArray[numRemoved - 1]
1501: .getEndOffset()
1502: - removedArray[0].getStartOffset();
1503: DefaultDocumentEvent removeEvent = new DefaultDocumentEvent(
1504: removedArray[0].getStartOffset(), eventLength,
1505: EventType.REMOVE);
1506: removeEvent.addEdit(new ElementEdit(parent, startRemoveIndex,
1507: removedArray, emptyArray));
1508: fireRemoveUpdate(removeEvent);
1509: }
1510:
1511: private void insertHTMLText(final Element elem, final int offset,
1512: final String htmlText) throws IOException {
1513: Element preceedBranch = getInsertionStartElement(offset - 1);
1514: int pop = -1;
1515: int push = -1;
1516: do {
1517: pop++;
1518: preceedBranch = preceedBranch.getParentElement();
1519: push = getAncestorDepth(preceedBranch, elem);
1520: } while (preceedBranch != null && push < 0);
1521: if (push == -1) {
1522: return;
1523: }
1524:
1525: final HTMLReader reader = new HTMLReader(offset, pop, push,
1526: null);
1527: reader.setRemoveImplicitSpecs(true);
1528: parser.parse(new StringReader(htmlText), reader, true);
1529: try {
1530: reader.flush();
1531: } catch (BadLocationException e) {
1532: e.printStackTrace();
1533: }
1534: }
1535:
1536: private void checkParser() {
1537: if (parser == null) {
1538: throw new IllegalStateException(Messages
1539: .getString("swing.9D")); //$NON-NLS-1$
1540: }
1541: }
1542:
1543: private void checkLeaf(final Element elem) {
1544: if (elem.isLeaf()) {
1545: throw new IllegalArgumentException(Messages
1546: .getString("swing.9E")); //$NON-NLS-1$
1547: }
1548: }
1549:
1550: private int getElementIndex(final BranchElement branch,
1551: final Element child) {
1552: final int numChildren = branch.getElementCount();
1553: for (int i = 0; i < numChildren; i++) {
1554: if (branch.getElement(i) == child) {
1555: return i;
1556: }
1557: }
1558: return -1;
1559: }
1560:
1561: private Element getInsertionStartElement(final int offset) {
1562: Element result = getDefaultRootElement();
1563: do {
1564: result = result.getElement(result.getElementIndex(offset));
1565: } while (!result.isLeaf());
1566:
1567: return result;
1568: }
1569:
1570: private int getAncestorDepth(final Element elem, final Element child) {
1571: int depth = 0;
1572: Element parent = child;
1573: while (parent != null) {
1574: if (elem == parent) {
1575: return depth;
1576: }
1577: depth++;
1578: parent = parent.getParentElement();
1579: }
1580: return -1;
1581: }
1582:
1583: private static void initDefaultCharacterAttributes() {
1584: addDefaultCSSAttribute(Tag.B, CSS.Attribute.FONT_WEIGHT, "bold");
1585: addDefaultCSSAttribute(Tag.I, CSS.Attribute.FONT_STYLE,
1586: "italic");
1587: addDefaultCSSAttribute(Tag.STRIKE,
1588: CSS.Attribute.TEXT_DECORATION, "line-through");
1589: addDefaultCSSAttribute(Tag.SUB, CSS.Attribute.VERTICAL_ALIGN,
1590: "sub");
1591: addDefaultCSSAttribute(Tag.SUP, CSS.Attribute.VERTICAL_ALIGN,
1592: "super");
1593: addDefaultCSSAttribute(Tag.U, CSS.Attribute.TEXT_DECORATION,
1594: "underline");
1595: addDefaultCSSAttribute(Tag.PRE, CSS.Attribute.WHITE_SPACE,
1596: "pre");
1597: }
1598:
1599: private static void addDefaultCSSAttribute(final Tag tag,
1600: final CSS.Attribute attr, final String value) {
1601: SimpleAttributeSet attrSet = new SimpleAttributeSet();
1602: attrSet.addAttribute(attr, attr.getConverter().toCSS(value));
1603: DEFAULT_CHARACTER_ATTRIBUTES.addAttribute(tag, attrSet);
1604: }
1605:
1606: private static SimpleAttributeSet getDefaultCSSAttributes(
1607: final Tag tag) {
1608: return (SimpleAttributeSet) DEFAULT_CHARACTER_ATTRIBUTES
1609: .getAttribute(tag);
1610: }
1611: }
|