blob: 087c9b6e03bbb517258a68f677701334ad854510 [file] [log] [blame]
/*******************************************************************************
* 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;
/**
*
*/
public 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());
}
}
}
}
/**
* currently public but may be made default access protected in future.
*/
public CSSNodeImpl appendChild(CSSNodeImpl newChild) throws org.w3c.dom.DOMException {
return insertBefore(newChild, null);
}
/**
* currently public but may be made default access protected in future.
*/
protected 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());
}
/**
*
* currently public but may be made default access protected in future.
*
* @return java.lang.String
*/
protected String generateSource() {
CSSSourceGenerator formatter = CSSSourceFormatterFactory.getInstance().getSourceFormatter(this);
return formatter.format(this).toString();
}
/**
*
* currently public but may be made default access protected in future.
*
* @return java.lang.String
* @param name
* java.lang.String
*/
protected 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;
}
/**
* currently public but may be made default access protected in future.
*/
public 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);
}
/**
*
* currently public but may be made default access protected in future.
*
* @param name
* java.lang.String
* @param value
* java.lang.String
*/
public 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;
}
/**
* currently public but may be made default access protected in future.
*/
public 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;
}
}