/* ****************************************************************************** * Copyright (c) 2006-2012 XMind Ltd. and others. * * This file is a part of XMind 3. XMind releases 3 and * above are dual-licensed under the Eclipse Public License (EPL), * which is available at http://www.eclipse.org/legal/epl-v10.html * and the GNU Lesser General Public License (LGPL), * which is available at http://www.gnu.org/licenses/lgpl.html * See http://www.xmind.net/license.html for details. * * Contributors: * XMind Ltd. - initial API and implementation *******************************************************************************/ package org.xmind.core.internal.dom; import static org.xmind.core.internal.dom.DOMConstants.ATTR_NAME; import static org.xmind.core.internal.dom.DOMConstants.ATTR_STYLE_FAMILY; import static org.xmind.core.internal.dom.DOMConstants.ATTR_STYLE_ID; import static org.xmind.core.internal.dom.DOMConstants.ATTR_TYPE; import static org.xmind.core.internal.dom.DOMConstants.TAG_DEFAULT_STYLE; import static org.xmind.core.internal.dom.DOMConstants.TAG_PROPERTIES; import java.util.Iterator; import java.util.Properties; import org.w3c.dom.Attr; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xmind.core.Core; import org.xmind.core.event.ICoreEventListener; import org.xmind.core.event.ICoreEventRegistration; import org.xmind.core.event.ICoreEventSource; import org.xmind.core.event.ICoreEventSupport; import org.xmind.core.internal.Style; import org.xmind.core.style.IStyleSheet; import org.xmind.core.util.DOMUtils; import org.xmind.core.util.Property; public class StyleImpl extends Style implements ICoreEventSource { private final class PropertyIter implements Iterator<Property> { Iterator<Element> it = propertiesElementIter(); Element propEle = it.next(); NamedNodeMap map = propEle == null ? null : propEle.getAttributes(); int index = 0; Property next = findNextProperty(); private Property findNextProperty() { if (map != null) { if (index < map.getLength()) { Attr attr = (Attr) map.item(index); index++; return new Property(attr.getName(), attr.getValue()); } if (it.hasNext()) { propEle = it.next(); map = propEle == null ? null : propEle.getAttributes(); index = 0; if (map != null) { return findNextProperty(); } } } return null; } public void remove() { } public Property next() { if (next == null) return next; Property result = next; next = findNextProperty(); return result; } public boolean hasNext() { return next != null; } } private final class DefaultStyleIter implements Iterator<Property> { Iterator<Element> propEleIt = propertiesElementIter(); Element propEle = null; Iterator<Element> defaultStyleEleIt = null; Property next = findNextDefaultStyle(); private Property findNextDefaultStyle() { if (defaultStyleEleIt != null) { while (defaultStyleEleIt.hasNext()) { Element defaultStyleEle = defaultStyleEleIt.next(); String family = DOMUtils.getAttribute(defaultStyleEle, ATTR_STYLE_FAMILY); if (family != null) { String styleId = DOMUtils.getAttribute(defaultStyleEle, ATTR_STYLE_ID); return new Property(family, styleId); } } } if (propEleIt.hasNext()) { propEle = propEleIt.next(); defaultStyleEleIt = DOMUtils.childElementIterByTag(propEle, TAG_DEFAULT_STYLE); return findNextDefaultStyle(); } return null; } public boolean hasNext() { return next != null; } public Property next() { if (next == null) return next; Property result = next; next = findNextDefaultStyle(); return result; } public void remove() { } } private Element implementation; private StyleSheetImpl ownedSheet; public StyleImpl(Element implementation, StyleSheetImpl ownedSheet) { this.implementation = DOMUtils.addIdAttribute(implementation); this.ownedSheet = ownedSheet; } public String getId() { return implementation.getAttribute(DOMConstants.ATTR_ID); } public String getType() { return implementation.getAttribute(ATTR_TYPE); } public Element getImplementation() { return implementation; } public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || !(obj instanceof StyleImpl)) return false; StyleImpl that = (StyleImpl) obj; return this.implementation == that.implementation; } public int hashCode() { return implementation.hashCode(); } public String toString() { return "STY#" + getId() + "(" + getName() + ")"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ } public <T> T getAdapter(Class<T> adapter) { if (ICoreEventSource.class.equals(adapter)) return adapter.cast(this); if (adapter.isAssignableFrom(Element.class)) return adapter.cast(implementation); return super.getAdapter(adapter); } public IStyleSheet getOwnedStyleSheet() { return ownedSheet; } public String getName() { String name = implementation.getAttribute(ATTR_NAME); if (name.startsWith("%")) { //$NON-NLS-1$ Properties properties = ownedSheet.getProperties(); if (properties != null) { String key = name.substring(1); name = properties.getProperty(key, name); } } return name; } // public Properties getProperties() { // Properties prop = new Properties(); // Iterator<Element> it = propertiesElementsIter(); // while (it.hasNext()) { // Element propEle = it.next(); // NamedNodeMap attrs = propEle.getAttributes(); // for (int i = 0; i < attrs.getLength(); i++) { // Attr attr = (Attr) attrs.item(i); // prop.setProperty(attr.getName(), attr.getValue()); // } // } // return prop; // } public String getProperty(String key) { Iterator<Element> it = propertiesElementIter(); while (it.hasNext()) { String value = DOMUtils.getAttribute(it.next(), key); if (value != null) return value; } return null; } private Iterator<Element> propertiesElementIter() { return DOMUtils.childElementIterByTag(implementation, getPropertiesElementName()); } public void setProperty(String key, String value) { String oldValue = getProperty(key); String propEleName = getPropertiesElementName(); Element p = DOMUtils.ensureChildElement(implementation, propEleName); DOMUtils.setAttribute(p, key, value); String newValue = getProperty(key); firePropertyChange(key, oldValue, newValue); } private String getPropertiesElementName() { return getType().toLowerCase() + "-" + TAG_PROPERTIES; //$NON-NLS-1$ } public int size() { Iterator<Element> it = propertiesElementIter(); int size = 0; while (it.hasNext()) { size += it.next().getAttributes().getLength(); } return size; } public boolean isEmpty() { Iterator<Element> it = propertiesElementIter(); while (it.hasNext()) { Element propEle = it.next(); if (propEle.hasAttributes() || propEle.hasChildNodes()) return false; } return true; } public Iterator<Property> properties() { return new PropertyIter(); } public void setName(String name) { String oldValue = implementation.hasAttribute(ATTR_NAME) ? getName() : null; DOMUtils.setAttribute(implementation, ATTR_NAME, name); String newValue = implementation.hasAttribute(ATTR_NAME) ? getName() : null; fireValueChange(Core.Name, oldValue, newValue); } public Iterator<Property> defaultStyles() { return new DefaultStyleIter(); } public String getDefaultStyleId(String styleFamily) { if (styleFamily == null || "".equals(styleFamily)) //$NON-NLS-1$ return null; Iterator<Element> it = propertiesElementIter(); while (it.hasNext()) { Iterator<Element> it2 = DOMUtils.childElementIterByTag(it.next(), TAG_DEFAULT_STYLE); while (it2.hasNext()) { Element ds = it2.next(); if (styleFamily.equals(ds.getAttribute(ATTR_STYLE_FAMILY))) { return DOMUtils.getAttribute(ds, ATTR_STYLE_ID); } } } return null; } public void setDefaultStyleId(String styleFamily, String styleId) { if (styleFamily == null || "".equals(styleFamily)) //$NON-NLS-1$ return; String propEleName = getPropertiesElementName(); if (styleId != null) { Element p = DOMUtils.ensureChildElement(implementation, propEleName); Element ds = findDefaultStyleElement(p, styleFamily); if (ds == null) { ds = DOMUtils.createElement(p, TAG_DEFAULT_STYLE); DOMUtils.setAttribute(ds, ATTR_STYLE_FAMILY, styleFamily); } DOMUtils.setAttribute(ds, ATTR_STYLE_ID, styleId); } else { Element p = DOMUtils.getFirstChildElementByTag(implementation, propEleName); if (p != null) { Element ds = findDefaultStyleElement(p, styleFamily); if (ds != null) { Node n = p.removeChild(ds); if (n != null) { if (!p.hasChildNodes()) { implementation.removeChild(p); } } } } } } private Element findDefaultStyleElement(Element propEle, String styleFamily) { Iterator<Element> it = DOMUtils.childElementIterByTag(propEle, TAG_DEFAULT_STYLE); while (it.hasNext()) { Element ds = it.next(); if (styleFamily.equals(ds.getAttribute(ATTR_STYLE_FAMILY))) { return ds; } } return null; } // public boolean contentEquals(IStyle style) { // StyleImpl s = (StyleImpl) style; // if (!getType().equals(s.getType())) // return false; // // int propSize = 0; // int dsSize = 0; // // Iterator<Element> it = propertiesElementIter(); // while (it.hasNext()) { // Element propEle = it.next(); // NamedNodeMap attrs = propEle.getAttributes(); // for (int i = 0; i < attrs.getLength(); i++) { // Node attr = attrs.item(i); // String key = attr.getNodeName(); // String value = attr.getNodeValue(); // if (!value.equals(s.getProperty(key))) // return false; // } // Iterator<Element> dsIt = DOMUtils.childElementIterByTag(propEle, // TAG_DEFAULT_STYLE); // while (dsIt.hasNext()) { // // } // } // // return true; // } // // public int getParentGroupId() { // Node p = implementation.getParentNode(); // if (p != null && p instanceof Element) { // Element g = (Element) p; // String name = g.getTagName(); // if (DOMConstants.TAG_STYLES.equals(name)) // return IStyleSheet.GROUP_NORMAL; // if (DOMConstants.TAG_MASTER_STYLES.equals(name)) // return IStyleSheet.GROUP_MASTER; // if (DOMConstants.TAG_AUTOMATIC_STYLES.equals(name)) // return IStyleSheet.GROUP_AUTOMATIC; // } // return IStyleSheet.GROUP_NONE; // } public ICoreEventRegistration registerCoreEventListener(String type, ICoreEventListener listener) { return getCoreEventSupport().registerCoreEventListener(this, type, listener); } private void fireValueChange(String eventType, Object oldValue, Object newValue) { getCoreEventSupport().dispatchValueChange(this, eventType, oldValue, newValue); } private void firePropertyChange(String key, String oldValue, String newValue) { getCoreEventSupport().dispatchTargetValueChange(this, Core.Property, key, oldValue, newValue); } public ICoreEventSupport getCoreEventSupport() { return ownedSheet.getCoreEventSupport(); } }