package jp.aonir.fuzzyxml.internal; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import jp.aonir.fuzzyxml.FuzzyXMLAttribute; import jp.aonir.fuzzyxml.FuzzyXMLElement; import jp.aonir.fuzzyxml.FuzzyXMLException; import jp.aonir.fuzzyxml.FuzzyXMLNode; import jp.aonir.fuzzyxml.FuzzyXMLParser; import jp.aonir.fuzzyxml.FuzzyXMLText; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Region; import org.objectstyle.wolips.baseforplugins.util.ComparisonUtils; public class FuzzyXMLElementImpl extends AbstractFuzzyXMLNode implements FuzzyXMLElement { private static final Set<String> FORBIDDEN_SELF_CLOSING = new HashSet<String>( Arrays.asList("a", "div", "span", "script", "pre", "td")); private static final Set<String> FORBIDDEN_CLOSE_TAG = new HashSet<String>( Arrays.asList("meta", "link", "base", "basefont", "br", "frame", "hr", "area", "img", "param", "input", "isindex", "col")); private List<FuzzyXMLNode> _children = new ArrayList<FuzzyXMLNode>(); private List<FuzzyXMLAttribute> _attributes = new ArrayList<FuzzyXMLAttribute>(); private String _name; private int _nameOffset; private int _openTagLength; private int _closeTagOffset; private int _closeTagLength; private int _closeNameOffset; private Boolean _isNonBreaking; private boolean _synthetic; // private HashMap namespace = new HashMap(); public FuzzyXMLElementImpl(String name) { this(null, name, -1, -1, -1); } public FuzzyXMLElementImpl(FuzzyXMLNode parent, String name, int offset, int length, int nameOffset) { super(parent, offset, length); this._name = name; _nameOffset = nameOffset; _closeTagOffset = -1; _closeNameOffset = -1; _openTagLength = length - 2; } public int getOpenTagLength() { return _openTagLength; } public int getNameOffset() { return _nameOffset; } public int getNameLength() { return _name != null ? _name.length() : 0; } public boolean hasCloseTag() { return _closeTagOffset != -1 && _closeTagLength > 0; } public void setCloseTagOffset(int closeTagOffset) { _closeTagOffset = closeTagOffset; } public int getCloseTagOffset() { return _closeTagOffset; } public void setCloseTagLength(int closeTagLength) { _closeTagLength = closeTagLength; } public int getCloseTagLength() { return _closeTagLength; } public void setCloseNameOffset(int closeNameOffset) { _closeNameOffset = closeNameOffset; } public int getCloseNameOffset() { return _closeNameOffset; } public int getCloseNameLength() { return getNameLength(); } public String getName() { return _name; } /** * XML�̒f�Ѓe�L�X�g����q�m�[�h�Q��lj����܂��B * <p> * �ʏ��<code>appendChild()</code>�Ŏq�m�[�h��lj������ꍇ�A * ���X�i�ɂ�<code>FuzzyXMLNode#toXMLString()</code>�̌��ʂ��V�����e�L�X�g�Ƃ��Ēʒm����܂����A * ���̃��\�b�h��p���Ďq�m�[�h��lj������ꍇ�A��œn�����e�L�X�g���V�����e�L�X�g�Ƃ��ēn����܂��B * �s����XML���p�[�X���A���̃e�L�X�g����ێ�����K�v������ꍇ�Ɏg�p���Ă��������B * </p> * @param text �lj�����q�v�f���܂�XML�̒f�ЁB */ public void appendChildrenFromText(String text, boolean wo54) { if (text.length() == 0) { return; } // ��x�G�������g��}��ăI�t�Z�b�g���擾 FuzzyXMLElement test = new FuzzyXMLElementImpl("test"); appendChild(test); int offset = test.getOffset(); // �I�t�Z�b�g���擾�����炷���폜 removeChild(test); String parseText = "<root>" + text + "</root>"; FuzzyXMLElement root = new FuzzyXMLParser(wo54).parse(parseText).getDocumentElement(); ((AbstractFuzzyXMLNode) root).appendOffset(root, 0, -6); ((AbstractFuzzyXMLNode) root).appendOffset(root, 0, offset); FuzzyXMLNode[] nodes = ((FuzzyXMLElement) root.getChildren()[0]).getChildren(); appendOffset(this, offset, text.length()); for (int i = 0; i < nodes.length; i++) { appendChild(nodes[i], false, false); } fireModifyEvent(text, offset, 0); } /** * ���̃G�������g�Ɏq�m�[�h��lj����܂��B * �ȉ��̏ꍇ�̓m�[�h��lj����邱�Ƃ͂ł��܂���iFuzzyXMLException���������܂��j�B * * <ul> * <li>�G�������g�����̃c���[�ɑ����Ă���ꍇ�i�e�G�������g����remove����Βlj��ł��܂��j</li> * <li>�G�������g���q�m�[�h�������Ă���ꍇ</li> * </ul> * * @param node �lj�����m�[�h�B * �G�������g�̏ꍇ�A�q�������Ȃ��G�������g���w�肵�Ă��������B * ���łɎq�v�f���\�z�ς݂̃G�������g��n���Ɠ����ŕێ����Ă���ʒu��񂪓����܂���B * * @exception jp.aonir.fuzzyxml.FuzzyXMLException �m�[�h��lj��ł��Ȃ��ꍇ */ public void appendChild(FuzzyXMLNode node) { appendChild(node, true, true); } /** * �p�[�X����<code>appendChild()</code>���\�b�h�̑���Ɏg�p���܂��B */ public void appendChildWithNoCheck(FuzzyXMLNode node) { appendChild(node, true, false); } /** * ���̃G�������g�Ɏq�m�[�h��lj��B * * @param node �lj�����m�[�h�B * �G�������g�̏ꍇ�A�q�������Ȃ��G�������g���w�肵�Ă��������B * ���łɎq�v�f���\�z�ς݂̃G�������g��n���Ɠ����ŕێ����Ă���ʒu��񂪓����܂���B * @param fireEvent �C�x���g�𔭉΂��邩�ǂ����B * false���w�肵���ꍇ�A�m�[�h�������Ă���ʒu���̓������s���܂���B * @param check �lj�����m�[�h�̌��؂��s�����ǂ����B * true���w�肵���ꍇ�A�ȉ��̂ɊY������ꍇFuzzyXMLException��throw���܂��B * <ul> * <li>�m�[�h�����̃c���[�ɑ����Ă���ꍇ</li> * <li>�G�������g�����łɎq���������Ă���ꍇ</li> * </ul> * �p�[�X���ȂǁA���؂��s�������Ȃ��ꍇ��false���w�肵�܂��B * * @exception jp.aonir.fuzzyxml.FuzzyXMLException �m�[�h��lj��ł��Ȃ��ꍇ */ private void appendChild(FuzzyXMLNode node, boolean fireEvent, boolean check) { if (check) { if (((AbstractFuzzyXMLNode) node).getDocument() != null) { throw new FuzzyXMLException("Appended node already has a parent."); } if (node instanceof FuzzyXMLElement) { if (((FuzzyXMLElement) node).getChildren().length != 0) { throw new FuzzyXMLException("Appended node has children."); } } } AbstractFuzzyXMLNode nodeImpl = (AbstractFuzzyXMLNode) node; nodeImpl.setParentNode(this); nodeImpl.setDocument(getDocument()); if (node instanceof FuzzyXMLAttribute) { setAttribute((FuzzyXMLAttribute) node); } else { if (_children.contains(node)) { return; } if (getDocument() == null) { _children.add(node); return; } // �lj�����m�[�h�̈ʒu(�Ō�)���v�Z FuzzyXMLNode[] nodes = getChildren(); int offset = 0; if (nodes.length == 0) { int length = getLength(); FuzzyXMLAttribute[] attrs = getAttributes(); offset = getOffset() + getName().length(); for (int i = 0; i < attrs.length; i++) { offset = offset + attrs[i].toXMLString(new RenderContext(getDocument().isHTML())).length(); } // ���������H offset = offset + 2; nodeImpl.setOffset(offset); if (fireEvent) { nodeImpl.setLength(node.toXMLString(new RenderContext(getDocument().isHTML())).length()); } _children.add(node); String xml = toXMLString(new RenderContext(getDocument().isHTML())); _children.remove(node); // �C�x���g�̔��� if (fireEvent) { fireModifyEvent(xml, getOffset(), getLength()); // �ʒu���̍X�V appendOffset(this, offset, xml.length() - length); } _children.add(node); } else { for (int i = 0; i < nodes.length; i++) { offset = nodes[i].getOffset() + nodes[i].getLength(); } // �C�x���g�̔��� if (fireEvent) { fireModifyEvent(nodeImpl.toXMLString(new RenderContext(getDocument().isHTML())), offset, 0); // �ʒu���̍X�V appendOffset(this, offset, node.toXMLString(new RenderContext(getDocument().isHTML())).length()); } // �Ō�ɒlj� nodeImpl.setOffset(offset); if (fireEvent) { nodeImpl.setLength(node.toXMLString(new RenderContext(getDocument().isHTML())).length()); } _children.add(node); } } } public FuzzyXMLAttribute[] getAttributes() { return _attributes.toArray(new FuzzyXMLAttribute[_attributes.size()]); } public FuzzyXMLNode getChild(int index) { return _children.get(index); } public FuzzyXMLElement getChildElement(int index) { return (FuzzyXMLElement) _children.get(index); } public FuzzyXMLNode[] getChildren() { // �A�g���r���[�g�͊܂܂Ȃ��H return _children.toArray(new FuzzyXMLNode[_children.size()]); } public boolean hasChildren() { return _children.size() > 0; } public boolean isEmpty() { boolean empty = !hasChildren(); if (!empty) { empty = true; for (FuzzyXMLNode child : _children) { if (child instanceof FuzzyXMLText) { FuzzyXMLText text = (FuzzyXMLText) child; String textValue = text.getValue(); if (textValue != null && textValue.trim().length() > 0) { empty = false; break; } } else { empty = false; break; } } } return empty; } public void insertAfter(FuzzyXMLNode newChild, FuzzyXMLNode refChild) { // �A�g���r���[�g�̏ꍇ�͂Ȃɂ����Ȃ� if (newChild instanceof FuzzyXMLAttribute || refChild instanceof FuzzyXMLAttribute) { return; } // �}���ʒu��T�� FuzzyXMLNode[] children = getChildren(); FuzzyXMLNode targetNode = null; boolean flag = false; for (int i = 0; i < children.length; i++) { if (flag) { targetNode = children[i]; } if (children[i] == refChild) { flag = true; } } if (targetNode == null && flag) { appendChild(newChild); } else { insertBefore(newChild, targetNode); } } public void insertBefore(FuzzyXMLNode newChild, FuzzyXMLNode refChild) { // �A�g���r���[�g�̏ꍇ�͂Ȃɂ����Ȃ� if (newChild instanceof FuzzyXMLAttribute || refChild instanceof FuzzyXMLAttribute) { return; } // �}���ʒu��T�� FuzzyXMLNode target = null; int index = -1; FuzzyXMLNode[] children = getChildren(); for (int i = 0; i < children.length; i++) { if (children[i] == refChild) { target = children[i]; index = i; break; } } if (target == null) { return; } int offset = target.getOffset(); // �C�x���g�̔��� fireModifyEvent(newChild.toXMLString(new RenderContext(getDocument().isHTML())), offset, 0); AbstractFuzzyXMLNode nodeImpl = (AbstractFuzzyXMLNode) newChild; nodeImpl.setParentNode(this); nodeImpl.setDocument(getDocument()); nodeImpl.setOffset(offset); nodeImpl.setLength(newChild.toXMLString(new RenderContext(getDocument().isHTML())).length()); // �ʒu���̍X�V appendOffset(this, offset, nodeImpl.toXMLString(new RenderContext(getDocument().isHTML())).length()); // �Ō�ɒlj� this._children.add(index, nodeImpl); } public void replaceChild(FuzzyXMLNode newChild, FuzzyXMLNode refChild) { // �A�g���r���[�g�̏ꍇ�͂Ȃɂ����Ȃ� if (newChild instanceof FuzzyXMLAttribute || refChild instanceof FuzzyXMLAttribute) { return; } // �u������m�[�h�̃C���f�b�N�X���擾 int index = -1; for (int i = 0; i < _children.size(); i++) { if (refChild == _children.get(i)) { index = i; break; } } // �m�[�h�����‚���Ȃ�������Ȃɂ����Ȃ� if (index == -1) { return; } _children.remove(index); AbstractFuzzyXMLNode nodeImpl = (AbstractFuzzyXMLNode) newChild; nodeImpl.setParentNode(this); nodeImpl.setDocument(getDocument()); nodeImpl.setOffset(refChild.getOffset()); nodeImpl.setLength(newChild.toXMLString(new RenderContext(getDocument().isHTML())).length()); // �C�x���g�̔��� fireModifyEvent(newChild.toXMLString(new RenderContext(getDocument().isHTML())), refChild.getOffset(), refChild.getLength()); // �ʒu���̍X�V appendOffset(this, refChild.getOffset(), newChild.getLength() - refChild.getLength()); _children.add(index, newChild); } public void removeChild(FuzzyXMLNode oldChild) { if (oldChild instanceof FuzzyXMLAttribute) { removeAttributeNode((FuzzyXMLAttribute) oldChild); return; } if (_children.contains(oldChild)) { // �f�^�b�` ((AbstractFuzzyXMLNode) oldChild).setParentNode(null); ((AbstractFuzzyXMLNode) oldChild).setDocument(null); // ���X�g����폜 _children.remove(oldChild); // �C�x���g�̔��� fireModifyEvent("", oldChild.getOffset(), oldChild.getLength()); // �ʒu���̍X�V appendOffset(this, oldChild.getOffset(), oldChild.getLength() * -1); } } public void setAttribute(FuzzyXMLAttribute attr) { FuzzyXMLAttribute attrNode = getAttributeNode(attr.getNamespaceName()); if (attrNode == null) { if (_attributes.contains(attr)) { return; } if (getDocument() == null) { _attributes.add(attr); return; } FuzzyXMLAttributeImpl attrImpl = (FuzzyXMLAttributeImpl) attr; attrImpl.setDocument(getDocument()); attrImpl.setParentNode(this); // �lj�����A�g���r���[�g�̈ʒu������ FuzzyXMLAttribute[] attrs = getAttributes(); int offset = getOffset() + getName().length() + 1; for (int i = 0; i < attrs.length; i++) { offset = offset + attrs[i].toXMLString(new RenderContext(getDocument().isHTML())).length(); } // �X�V�C�x���g�𔭉� fireModifyEvent(attr.toXMLString(new RenderContext(getDocument().isHTML())), offset, 0); // �ʒu���̍X�V appendOffset(this, offset, attr.toXMLString(new RenderContext(getDocument().isHTML())).length()); // �Ō�ɒlj� attrImpl.setOffset(offset); attrImpl.setLength(attrImpl.toXMLString(new RenderContext(getDocument().isHTML())).length()); _attributes.add(attrImpl); } else { // ���̏ꍇ�̓A�g���r���[�g��setValue���\�b�h���ŃC�x���g���� FuzzyXMLAttributeImpl attrImpl = (FuzzyXMLAttributeImpl) attrNode; attrImpl.setValue(attr.getValue()); } } public FuzzyXMLAttribute getAttributeNode(String name) { String namespace; int colonIndex = name.indexOf(':'); if (colonIndex == -1) { namespace = null; } else { namespace = name.substring(0, colonIndex); name = name.substring(colonIndex + 1); } FuzzyXMLAttribute[] attrs = getAttributes(); for (int i = 0; i < attrs.length; i++) { if (ComparisonUtils.equals(namespace, attrs[i].getNamespace()) && attrs[i].getName().equalsIgnoreCase(name)) { return attrs[i]; } } return null; } public boolean hasAttribute(String name) { return getAttributeNode(name) != null; } public void removeAttributeNode(FuzzyXMLAttribute attr) { if (_attributes.contains(attr)) { // �f�^�b�` ((AbstractFuzzyXMLNode) attr).setParentNode(null); ((AbstractFuzzyXMLNode) attr).setDocument(null); // ���X�g����폜 _attributes.remove(attr); // �C�x���g�̔��� fireModifyEvent("", attr.getOffset(), attr.getLength()); // �ʒu���̍X�V appendOffset(this, attr.getOffset(), attr.getLength() * -1); } } public String getValue() { RenderContext rc = new RenderContext(false); rc.setIndent(0); rc.setIndentSize(2); rc.setSpaceInEmptyTags(true); return getValue(rc, new StringBuffer()); } public String getValue(RenderContext renderContext, StringBuffer xmlBuffer) { StringBuffer sb = new StringBuffer(xmlBuffer); int length = xmlBuffer.length(); FuzzyXMLNode[] children = getChildren(); RenderDelegate delegate = renderContext.getDelegate(); for (int i = 0; i < children.length; i++) { if (delegate == null || delegate.renderNode(children[i], renderContext, sb)) { children[i].toXMLString(renderContext, sb); } } sb.delete(0, length); return sb.toString(); } public String toDebugString() { StringBuffer sb = new StringBuffer(); toDebugString(sb, 0); return sb.toString(); } public void toDebugString(StringBuffer buffer, int indent) { for (int i = 0; i < indent; i ++) { buffer.append(" "); } String name = getName(); if (name != null && name.trim().length() > 0) { buffer.append(name); } else { buffer.append("[unknown: '" + name + "']"); } if (_attributes != null && _attributes.size() > 0) { buffer.append(", attributes={"); FuzzyXMLAttribute[] attributes = getAttributes(); for (int i = 0; i < attributes.length; i ++) { attributes[i].toDebugString(buffer, 0); if (i < attributes.length - 1) { buffer.append("; "); } } buffer.append("}"); } buffer.append("\n"); for (FuzzyXMLNode child : getChildren()) { child.toDebugString(buffer, indent + 1); } } public void toXMLString(RenderContext renderContext, StringBuffer xmlBuffer) { if (isSynthetic()) { return; } boolean isHTML = renderContext.isHtml(); boolean renderSurroundingTags = true; RenderDelegate delegate = renderContext.getDelegate(); if (delegate != null) { renderSurroundingTags = delegate.beforeOpenTag(this, renderContext, xmlBuffer); } try { String tagName = FuzzyXMLUtil.escape(getName(), isHTML); if (renderContext.isLowercaseTags() && FuzzyXMLUtil.isAllUppercase(tagName)) { tagName = tagName.toLowerCase(); } if (renderSurroundingTags) { xmlBuffer.append("<").append(tagName); FuzzyXMLAttribute[] attrs = getAttributes(); for (int i = 0; i < attrs.length; i++) { attrs[i].toXMLString(renderContext, xmlBuffer); } } if (isSelfClosing()) { if (renderSurroundingTags) { if (renderContext.isSpaceInEmptyTags()) { xmlBuffer.append(" "); } xmlBuffer.append("/>"); } xmlBuffer.append(getValue(renderContext, xmlBuffer)); } else { if (renderSurroundingTags) { xmlBuffer.append(">"); } if (delegate != null) { delegate.afterOpenTag(this, renderContext, xmlBuffer); } xmlBuffer.append(getValue(renderContext, xmlBuffer)); if (delegate != null) { delegate.beforeCloseTag(this, renderContext, xmlBuffer); } if (renderSurroundingTags) { xmlBuffer.append("</").append(tagName).append(">"); } } } catch (Exception e) { e.printStackTrace(); } finally { if (delegate != null) { delegate.afterCloseTag(this, renderContext, xmlBuffer); } } } @Override public boolean equals(Object obj) { if (obj instanceof FuzzyXMLElement) { FuzzyXMLElement element = (FuzzyXMLElement) obj; // �^�O�̖��O���������false if (!element.getName().equals(getName())) { return false; } // �e������Ƃ�null��������true FuzzyXMLNode parent = element.getParentNode(); if (parent == null) { if (getParentNode() == null) { return true; } return false; } // �J�n�I�t�Z�b�g��������������true if (element.getOffset() == getOffset()) { return true; } } return false; } public String getAttributeValue(String name) { FuzzyXMLAttribute attr = getAttributeNode(name); if (attr != null) { return attr.getValue(); } return null; } public void setAttribute(String namespace, String name, String value) { FuzzyXMLAttribute attr = new FuzzyXMLAttributeImpl(namespace, name, value, value); setAttribute(attr); } public void removeAttribute(String name) { FuzzyXMLAttribute attr = getAttributeNode(name); if (attr != null) { removeAttributeNode(attr); } } @Override public void setDocument(FuzzyXMLDocumentImpl doc) { super.setDocument(doc); FuzzyXMLNode[] nodes = getChildren(); for (int i = 0; i < nodes.length; i++) { ((AbstractFuzzyXMLNode) nodes[i]).setDocument(doc); } FuzzyXMLAttribute[] attrs = getAttributes(); for (int i = 0; i < attrs.length; i++) { ((AbstractFuzzyXMLNode) attrs[i]).setDocument(doc); } } @Override public String toString() { return "element: " + getName() + "; attributes = " + _attributes; } public void removeAllChildren() { FuzzyXMLNode[] children = getChildren(); for (int i = 0; i < children.length; i++) { removeChild(children[i]); } } public Region getRegionAtOffset(int offset, IDocument doc, boolean regionForInsert) throws BadLocationException { Region region; int openTagOffset = getOffset(); int openTagLength = getOpenTagLength() + 2; int openTagEndOffset = openTagOffset + openTagLength; if (hasCloseTag()) { int closeTagOffset = getCloseTagOffset(); int closeTagEndOffset = closeTagOffset + getCloseTagLength(); //if (modelOffset > openTagEndOffset && modelOffset < getCloseTagOffset()) { if (!regionForInsert) { region = new Region(openTagOffset, closeTagOffset - openTagOffset + getCloseTagLength() + 2); } else if ((offset >= openTagOffset && offset < openTagEndOffset) || (offset >= closeTagOffset && offset < closeTagEndOffset)) { if (doc != null) { IRegion lineRegion = doc.getLineInformationOfOffset(openTagEndOffset); int lineEndOffset = lineRegion.getOffset() + lineRegion.getLength(); if (openTagEndOffset == lineEndOffset) { openTagEndOffset++; openTagLength++; } } region = new Region(openTagOffset, openTagLength); } else { region = new Region(offset, 0); } } else { region = new Region(getOffset(), getLength()); } return region; } @Override public boolean isNonBreaking() { if (_isNonBreaking != null) { return _isNonBreaking; } FuzzyXMLNode children[] = getChildren(); boolean result = false; int textblocks = 0; int elementcount = 0; for (int i = 0; i < children.length; i++) { FuzzyXMLNode child = children[i]; if (child instanceof FuzzyXMLText) { FuzzyXMLText text = (FuzzyXMLText)child; if (!text.isHidden()) { //result = text.isNonBreaking(); textblocks++; if (text.hasLineBreaks()) textblocks++; } } else { if (child instanceof FuzzyXMLElement) { FuzzyXMLElement element = (FuzzyXMLElement)child; elementcount++; if (!isSelfClosing(element)) { //result &= element.isNonBreaking(); elementcount++; } } } } if (elementcount <= 1 && textblocks <= 1) result = true; else result = false; if (getParentNode() == null) result = false; // System.out.println("Elements " + elementcount + " Text blocks " + textblocks); // System.out.println(result?"Non breaking "+this:" Breaking " +this); _isNonBreaking = result; return _isNonBreaking; } public boolean isSelfClosing() { return isSelfClosing(this); } public boolean isForbiddenFromHavingChildren() { String tagName = getName().toLowerCase(); return FORBIDDEN_CLOSE_TAG.contains(tagName); } public static boolean isSelfClosing(FuzzyXMLElement node) { FuzzyXMLNode[] children = node.getChildren(); String tagName = node.getName().toLowerCase(); boolean forbiddenSelfClosing = FORBIDDEN_SELF_CLOSING.contains(tagName); boolean alwaysEmptyTag = FORBIDDEN_CLOSE_TAG.contains(tagName); return ((alwaysEmptyTag || children.length == 0) && !forbiddenSelfClosing); } @Override public boolean isHidden() { return getName() == null || getName().equals(""); } public void setSynthetic(boolean synthetic) { _synthetic = synthetic; } public boolean isSynthetic() { return _synthetic; } }