/* ****************************************************************************** * 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_TYPE; import static org.xmind.core.internal.dom.DOMConstants.TAG_AUTOMATIC_STYLES; import static org.xmind.core.internal.dom.DOMConstants.TAG_MASTER_STYLES; import static org.xmind.core.internal.dom.DOMConstants.TAG_STYLE; import static org.xmind.core.internal.dom.DOMConstants.TAG_STYLES; import static org.xmind.core.internal.dom.DOMConstants.TAG_STYLE_SHEET; import java.io.IOException; import java.io.OutputStream; import java.util.Properties; import java.util.Set; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.xmind.core.Core; import org.xmind.core.CoreException; import org.xmind.core.IAdaptable; import org.xmind.core.IManifest; 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.ElementRegistry; import org.xmind.core.internal.StyleSheet; import org.xmind.core.internal.event.CoreEventSupport; import org.xmind.core.style.IStyle; import org.xmind.core.util.CloneHandler; import org.xmind.core.util.DOMUtils; import org.xmind.core.util.IPropertiesProvider; public class StyleSheetImpl extends StyleSheet implements INodeAdaptableFactory, ICoreEventSource, IPropertiesProvider { private Document implementation; private ElementRegistry elementRegistry = null; private NodeAdaptableProvider nodeAdaptableProvider = null; private CoreEventSupport coreEventSupport = null; private Properties properties = null; private IManifest manifest = null; public StyleSheetImpl(Document implementation) { this.implementation = implementation; init(); } private void init() { Element s = DOMUtils.ensureChildElement(implementation, TAG_STYLE_SHEET); NS.setNS(NS.Style, s, NS.SVG, NS.Fo); InternalDOMUtils.addVersion(implementation); } public Document getImplementation() { return implementation; } public <T> T getAdapter(Class<T> adapter) { if (IManifest.class.equals(adapter)) return adapter.cast(manifest); if (ICoreEventSource.class.equals(adapter)) return adapter.cast(this); if (ElementRegistry.class.equals(adapter)) return adapter.cast(getElementRegistry()); if (ICoreEventSupport.class.equals(adapter)) return adapter.cast(getCoreEventSupport()); if (IPropertiesProvider.class.equals(adapter)) return adapter.cast(this); if (Properties.class.equals(adapter)) return adapter.cast(getProperties()); if (INodeAdaptableFactory.class.equals(adapter)) return adapter.cast(this); if (INodeAdaptableProvider.class.equals(adapter)) return adapter.cast(getNodeAdaptableProvider()); if (adapter.isAssignableFrom(Document.class)) return adapter.cast(implementation); return super.getAdapter(adapter); } protected Element getSheetElement() { return implementation.getDocumentElement(); } public boolean isEmpty() { return !getSheetElement().hasChildNodes(); } @Override protected IStyle getLocalStyle(String styleId) { Object element = getElementById(styleId); return element instanceof IStyle ? (IStyle) element : null; } public IStyle createStyle(String type) { Element s = implementation.createElement(TAG_STYLE); s.setAttribute(ATTR_TYPE, type); StyleImpl style = new StyleImpl(s, this); getElementRegistry().register(style); return style; } public Set<IStyle> getStyles(String groupName) { String groupTag = getGroupTag(groupName); if (groupTag != null) { Element ss = DOMUtils.getFirstChildElementByTag(getSheetElement(), groupTag); if (ss != null) { return DOMUtils.getChildSet(ss, TAG_STYLE, getNodeAdaptableProvider()); } } return NO_STYLES; } public void addStyle(IStyle style, String groupName) { String groupTag = getGroupTag(groupName); if (groupTag == null) return; Element s = ((StyleImpl) style).getImplementation(); Element as = DOMUtils.ensureChildElement(getSheetElement(), groupTag); Node n = as.appendChild(s); if (n != null) { fireTargetChange(Core.StyleAdd, style); } } public String findOwnedGroup(IStyle style) { StyleImpl s = (StyleImpl) style; Node p = s.getImplementation().getParentNode(); if (p instanceof Element) { String groupTag = ((Element) p).getTagName(); return getGroupName(groupTag); } return null; } private String getGroupTag(String groupName) { if (NORMAL_STYLES.equals(groupName)) return TAG_STYLES; if (MASTER_STYLES.equals(groupName)) return TAG_MASTER_STYLES; if (AUTOMATIC_STYLES.equals(groupName)) return TAG_AUTOMATIC_STYLES; return null; } private String getGroupName(String groupTag) { if (TAG_STYLES.equals(groupTag)) return NORMAL_STYLES; if (TAG_MASTER_STYLES.equals(groupTag)) return MASTER_STYLES; if (TAG_AUTOMATIC_STYLES.equals(groupTag)) return AUTOMATIC_STYLES; return null; } public void removeStyle(IStyle style) { Element s = ((StyleImpl) style).getImplementation(); Node p = s.getParentNode(); if (p instanceof Element) { Element ss = (Element) p; Element sheet = getSheetElement(); if (ss.getParentNode() == sheet) { Node n = ss.removeChild(s); if (n != null) { if (!ss.hasChildNodes()) { sheet.removeChild(ss); } fireTargetChange(Core.StyleRemove, style); } } } } protected Object getElementById(String id) { Object element = getElementRegistry().getElement(id); if (element == null) { Element domElement = implementation.getElementById(id); if (domElement != null) { element = getNodeAdaptable(domElement); } } return element; } public ElementRegistry getElementRegistry() { if (elementRegistry == null) elementRegistry = new ElementRegistry(); return elementRegistry; } protected NodeAdaptableProvider getNodeAdaptableProvider() { if (nodeAdaptableProvider == null) nodeAdaptableProvider = new NodeAdaptableProvider( getElementRegistry(), this, implementation); return nodeAdaptableProvider; } protected IAdaptable getNodeAdaptable(Node node) { return getNodeAdaptableProvider().getAdaptable(node); } public IAdaptable createAdaptable(Node node) { if (node instanceof Element) { Element e = (Element) node; String tagName = e.getTagName(); if (TAG_STYLE.equals(tagName)) { return new StyleImpl(e, this); } } return null; } public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || !(obj instanceof StyleSheetImpl)) return false; StyleSheetImpl that = (StyleSheetImpl) obj; return this.implementation == that.implementation; } public int hashCode() { return implementation.hashCode(); } public String toString() { return DOMUtils.toString(implementation); } public Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } public IManifest getManifest() { return manifest; } public void setManifest(IManifest manifest) { this.manifest = manifest; } public void save(OutputStream out) throws IOException, CoreException { DOMUtils.save(implementation, out, false); } public IStyle importStyle(IStyle style) { if (style == null) return null; try { return (IStyle) new CloneHandler() .withStyleSheets(style.getOwnedStyleSheet(), this) .cloneObject(style); } catch (IOException e) { Core.getLogger().log(e); } return null; } public ICoreEventRegistration registerCoreEventListener(String type, ICoreEventListener listener) { return getCoreEventSupport().registerCoreEventListener(this, type, listener); } public CoreEventSupport getCoreEventSupport() { if (coreEventSupport != null) return coreEventSupport; coreEventSupport = new CoreEventSupport(); return coreEventSupport; } private void fireTargetChange(String type, Object target) { getCoreEventSupport().dispatchTargetChange(this, type, target); } }