| /******************************************************************************* |
| * Copyright (c) 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wst.css.core.internal.document; |
| |
| |
| |
| import java.util.Iterator; |
| |
| import org.eclipse.wst.css.core.internal.formatter.CSSSourceFormatterFactory; |
| import org.eclipse.wst.css.core.internal.formatter.CSSSourceGenerator; |
| import org.eclipse.wst.css.core.internal.provisional.document.ICSSDocument; |
| import org.eclipse.wst.css.core.internal.provisional.document.ICSSModel; |
| import org.eclipse.wst.css.core.internal.provisional.document.ICSSNamedNodeMap; |
| import org.eclipse.wst.css.core.internal.provisional.document.ICSSNode; |
| import org.eclipse.wst.css.core.internal.provisional.document.ICSSNodeList; |
| import org.eclipse.wst.css.core.internal.util.ImportRuleCollector; |
| import org.eclipse.wst.sse.core.internal.model.FactoryRegistry; |
| import org.eclipse.wst.sse.core.internal.provisional.AbstractNotifier; |
| import org.eclipse.wst.sse.core.internal.provisional.IndexedRegion; |
| import org.w3c.dom.DOMException; |
| |
| |
| /** |
| * |
| */ |
| abstract class CSSNodeImpl extends AbstractNotifier implements ICSSNode, IndexedRegion { |
| |
| private CSSDocumentImpl fOwnerDocument = null; |
| private CSSNodeImpl fParentNode = null; |
| private CSSNodeImpl fNextSibling = null; |
| private CSSNodeImpl fPreviousSibling = null; |
| private CSSNodeImpl fFirstChild = null; |
| private CSSNodeImpl fLastChild = null; |
| protected CSSNamedNodeMapImpl fAttrs = null; |
| |
| /** |
| * CSSNodeImpl constructor comment. |
| */ |
| CSSNodeImpl() { |
| super(); |
| } |
| |
| CSSNodeImpl(CSSNodeImpl that) { |
| if (that != null) { |
| this.fOwnerDocument = that.fOwnerDocument; |
| if (that.fAttrs != null) { |
| int nAttrs = that.fAttrs.getLength(); |
| for (int i = 0; i < nAttrs; i++) { |
| CSSAttrImpl attr = (CSSAttrImpl) that.fAttrs.item(i); |
| setAttribute(attr.getName(), attr.getValue()); |
| } |
| } |
| } |
| } |
| |
| protected CSSNodeImpl appendChild(CSSNodeImpl newChild) throws org.w3c.dom.DOMException { |
| return insertBefore(newChild, null); |
| } |
| |
| void cloneChildNodes(ICSSNode newParent, boolean deep) { |
| if (newParent == null || newParent == this) |
| return; |
| |
| CSSNodeImpl container = (CSSNodeImpl) newParent; |
| container.removeChildNodes(); |
| |
| for (ICSSNode child = getFirstChild(); child != null; child = child.getNextSibling()) { |
| CSSNodeImpl cloned = (CSSNodeImpl) child.cloneNode(deep); |
| if (cloned != null) |
| container.appendChild(cloned); |
| } |
| } |
| |
| /** |
| * @return boolean |
| * @param offset |
| * int |
| */ |
| public boolean contains(int offset) { |
| return (getStartOffset() <= offset && offset < getEndOffset()); |
| } |
| |
| /** |
| * @return java.lang.String |
| */ |
| String generateSource() { |
| CSSSourceGenerator formatter = CSSSourceFormatterFactory.getInstance().getSourceFormatter(this); |
| return formatter.format(this).toString(); |
| } |
| |
| /** |
| * @return java.lang.String |
| * @param name |
| * java.lang.String |
| */ |
| String getAttribute(String name) { |
| CSSAttrImpl attr = getAttributeNode(name); |
| if (attr != null) |
| return attr.getValue(); |
| return null; |
| } |
| |
| protected CSSAttrImpl getAttributeNode(String name) { |
| if (fAttrs == null) |
| return null; |
| |
| int nAttrs = fAttrs.getLength(); |
| for (int i = 0; i < nAttrs; i++) { |
| CSSAttrImpl attr = (CSSAttrImpl) fAttrs.item(i); |
| if (attr.matchName(name)) |
| return attr; |
| } |
| return null; |
| } |
| |
| /** |
| * @return org.eclipse.wst.css.core.model.interfaces.ICSSNamedNodeMap |
| */ |
| public ICSSNamedNodeMap getAttributes() { |
| if (fAttrs == null) |
| fAttrs = new CSSNamedNodeMapImpl(); |
| return fAttrs; |
| } |
| |
| public ICSSNodeList getChildNodes() { |
| CSSNodeListImpl list = new CSSNodeListImpl(); |
| for (ICSSNode node = getFirstChild(); node != null; node = node.getNextSibling()) { |
| list.appendNode(node); |
| } |
| return list; |
| } |
| |
| ICSSNode getCommonAncestor(ICSSNode node) { |
| if (node == null) |
| return null; |
| |
| for (ICSSNode na = node; na != null; na = na.getParentNode()) { |
| for (ICSSNode ta = this; ta != null; ta = ta.getParentNode()) { |
| if (ta == na) |
| return ta; |
| } |
| } |
| |
| return null; // not found |
| } |
| |
| CSSDocumentImpl getContainerDocument() { |
| for (ICSSNode node = this; node != null; node = node.getParentNode()) { |
| if (node instanceof CSSDocumentImpl) { |
| CSSDocumentImpl doc = (CSSDocumentImpl) node; |
| if (doc.isDocument()) |
| return doc; |
| } |
| } |
| return null; |
| } |
| |
| CSSNodeImpl getContainerNode(int offset) { |
| if (!contains(offset)) |
| return null; |
| |
| for (ICSSNode child = getFirstChild(); child != null; child = child.getNextSibling()) { |
| ICSSNode found = ((CSSNodeImpl) child).getContainerNode(offset); |
| if (found != null) |
| return (CSSNodeImpl) found; |
| } |
| |
| return this; |
| } |
| |
| /** |
| */ |
| public FactoryRegistry getFactoryRegistry() { |
| ICSSModel model = getOwnerDocument().getModel(); |
| if (model != null) { |
| FactoryRegistry reg = model.getFactoryRegistry(); |
| if (reg != null) |
| return reg; |
| } |
| return null; |
| } |
| |
| public ICSSNode getFirstChild() { |
| return this.fFirstChild; |
| } |
| |
| public ICSSNode getLastChild() { |
| return this.fLastChild; |
| } |
| |
| public ICSSNode getNextSibling() { |
| return this.fNextSibling; |
| } |
| |
| ICSSNode getNodeAt(int offset) { |
| // the same as getContainerNode() |
| return getContainerNode(offset); |
| } |
| |
| public ICSSDocument getOwnerDocument() { |
| return this.fOwnerDocument; |
| } |
| |
| public ICSSNode getParentNode() { |
| return this.fParentNode; |
| } |
| |
| public ICSSNode getPreviousSibling() { |
| return this.fPreviousSibling; |
| } |
| |
| ICSSNode getRootNode() { |
| CSSNodeImpl parent = (CSSNodeImpl) getParentNode(); |
| if (parent == null) |
| return this; |
| return parent.getRootNode(); |
| } |
| |
| /** |
| * @return boolean |
| */ |
| public boolean hasChildNodes() { |
| return (this.fFirstChild != null); |
| } |
| |
| /** |
| * @return boolean |
| */ |
| public boolean hasProperties() { |
| return false; |
| } |
| |
| protected CSSNodeImpl insertBefore(CSSNodeImpl newChild, CSSNodeImpl refChild) throws org.w3c.dom.DOMException { |
| if (newChild == null) |
| return null; |
| |
| CSSNodeImpl child = newChild; |
| CSSNodeImpl next = refChild; |
| CSSNodeImpl prev = null; |
| if (next == null) { |
| prev = this.fLastChild; |
| this.fLastChild = child; |
| } |
| else { |
| prev = (CSSNodeImpl) next.getPreviousSibling(); |
| next.setPreviousSibling(child); |
| } |
| |
| if (prev == null) |
| this.fFirstChild = child; |
| else |
| prev.setNextSibling(child); |
| child.setPreviousSibling(prev); |
| child.setNextSibling(next); |
| child.setParentNode(this); |
| |
| notifyChildReplaced(child, null); |
| |
| return newChild; |
| } |
| |
| protected void notifyAttrReplaced(CSSNodeImpl newAttr, CSSNodeImpl oldAttr) { |
| // for model |
| ICSSDocument doc = getContainerDocument(); |
| if (doc == null) |
| return; |
| CSSModelImpl model = (CSSModelImpl) doc.getModel(); |
| if (model == null) |
| return; |
| model.attrReplaced(this, newAttr, oldAttr); |
| |
| // for adapters |
| int type = CHANGE; |
| if (newAttr == null) |
| type = REMOVE; |
| else if (oldAttr == null) |
| type = ADD; |
| notify(type, oldAttr, oldAttr, newAttr, getStartOffset()); |
| } |
| |
| protected void notifyChildReplaced(CSSNodeImpl newChild, CSSNodeImpl oldChild) { |
| // for model |
| ICSSDocument doc = getContainerDocument(); |
| if (doc == null) |
| return; |
| CSSModelImpl model = (CSSModelImpl) doc.getModel(); |
| if (model == null) |
| return; |
| model.childReplaced(this, newChild, oldChild); |
| |
| // for adapters |
| int type = CHANGE; |
| if (newChild == null) |
| type = REMOVE; |
| else if (oldChild == null) |
| type = ADD; |
| notify(type, oldChild, oldChild, newChild, getStartOffset()); |
| } |
| |
| void removeAttributeNode(CSSNodeImpl attr) { |
| // find |
| int nAttrs = fAttrs.getLength(); |
| for (int i = 0; i < nAttrs; i++) { |
| if (fAttrs.item(i) == attr) { |
| fAttrs.removeNode(i); |
| notifyAttrReplaced(null, attr); |
| return; |
| } |
| } |
| } |
| |
| protected CSSNodeImpl removeChild(CSSNodeImpl oldChild) throws org.w3c.dom.DOMException { |
| if (oldChild == null) |
| return null; |
| if (oldChild.getParentNode() != this) |
| return null; |
| |
| // close import rules |
| ImportRuleCollector trav = new ImportRuleCollector(); |
| trav.apply(oldChild); |
| Iterator it = trav.getRules().iterator(); |
| while (it.hasNext()) { |
| ((CSSImportRuleImpl) it.next()).closeStyleSheet(); |
| } |
| |
| CSSNodeImpl child = oldChild; |
| CSSNodeImpl prev = (CSSNodeImpl) child.getPreviousSibling(); |
| CSSNodeImpl next = (CSSNodeImpl) child.getNextSibling(); |
| |
| if (prev == null) |
| this.fFirstChild = next; |
| else |
| prev.setNextSibling(next); |
| |
| if (next == null) |
| this.fLastChild = prev; |
| else |
| next.setPreviousSibling(prev); |
| |
| child.setPreviousSibling(null); |
| child.setNextSibling(null); |
| child.setParentNode(null); |
| |
| notifyChildReplaced(null, child); |
| |
| return child; |
| } |
| |
| /** |
| * |
| */ |
| void removeChildNodes() { |
| ICSSNode nextChild = null; |
| for (ICSSNode child = getFirstChild(); child != null; child = nextChild) { |
| nextChild = child.getNextSibling(); |
| removeChild((CSSNodeImpl) child); |
| } |
| } |
| |
| protected CSSNodeImpl replaceChild(CSSNodeImpl newChild, CSSNodeImpl oldChild) throws org.w3c.dom.DOMException { |
| if (oldChild == null) |
| return newChild; |
| if (newChild != null) |
| insertBefore(newChild, oldChild); |
| return removeChild(oldChild); |
| } |
| |
| /** |
| * @param name |
| * java.lang.String |
| * @param value |
| * java.lang.String |
| */ |
| void setAttribute(String name, String value) { |
| if (name == null) |
| return; |
| |
| CSSAttrImpl attr = getAttributeNode(name); |
| if (attr != null) { |
| String oldValue = attr.getValue(); |
| if (value != null && value.equals(oldValue)) |
| return; |
| if (value == null) { |
| if (oldValue != null) { |
| removeAttributeNode(attr); |
| } |
| return; |
| } |
| } |
| else { |
| if (value == null) |
| return; |
| if (fAttrs == null) |
| fAttrs = new CSSNamedNodeMapImpl(); |
| CSSDocumentImpl doc = (CSSDocumentImpl) getOwnerDocument(); |
| if (doc == null) |
| return; |
| attr = (CSSAttrImpl) doc.createCSSAttr(name); |
| attr.setOwnerCSSNode(this); |
| fAttrs.appendNode(attr); |
| notifyAttrReplaced(attr, null); |
| } |
| attr.setValue(value); |
| } |
| |
| /** |
| * @param cssText |
| * java.lang.String |
| */ |
| public void setCssText(String cssText) { |
| // TODO : call flat model parser and replace myself with new three!! |
| throw new DOMException(DOMException.INVALID_MODIFICATION_ERR, "");//$NON-NLS-1$ |
| } |
| |
| private void setNextSibling(ICSSNode nextSibling) { |
| this.fNextSibling = (CSSNodeImpl) nextSibling; |
| } |
| |
| void setOwnerDocument(ICSSDocument ownerDocument) { |
| this.fOwnerDocument = (CSSDocumentImpl) ownerDocument; |
| } |
| |
| private void setParentNode(ICSSNode parentNode) { |
| this.fParentNode = (CSSNodeImpl) parentNode; |
| } |
| |
| private void setPreviousSibling(ICSSNode previousSibling) { |
| this.fPreviousSibling = (CSSNodeImpl) previousSibling; |
| } |
| |
| public int getLength() { |
| int result = -1; |
| int start = getStartOffset(); |
| if (start >= 0) { |
| int end = getEndOffset(); |
| if (end >= 0) { |
| result = end - start; |
| if (result < -1) { |
| result = -1; |
| } |
| } |
| } |
| return result; |
| } |
| } |