/* ****************************************************************************** * 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_BRANCH; import static org.xmind.core.internal.dom.DOMConstants.ATTR_HREF; import static org.xmind.core.internal.dom.DOMConstants.ATTR_ID; import static org.xmind.core.internal.dom.DOMConstants.ATTR_MARKER_ID; import static org.xmind.core.internal.dom.DOMConstants.ATTR_PROVIDER; import static org.xmind.core.internal.dom.DOMConstants.ATTR_STRUCTURE_CLASS; 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.ATTR_WIDTH; import static org.xmind.core.internal.dom.DOMConstants.ATTR_X; import static org.xmind.core.internal.dom.DOMConstants.ATTR_Y; import static org.xmind.core.internal.dom.DOMConstants.TAG_BOUNDARIES; import static org.xmind.core.internal.dom.DOMConstants.TAG_BOUNDARY; import static org.xmind.core.internal.dom.DOMConstants.TAG_CHILDREN; import static org.xmind.core.internal.dom.DOMConstants.TAG_EXTENSION; import static org.xmind.core.internal.dom.DOMConstants.TAG_EXTENSIONS; import static org.xmind.core.internal.dom.DOMConstants.TAG_LABEL; import static org.xmind.core.internal.dom.DOMConstants.TAG_LABELS; import static org.xmind.core.internal.dom.DOMConstants.TAG_MARKER_REF; import static org.xmind.core.internal.dom.DOMConstants.TAG_MARKER_REFS; import static org.xmind.core.internal.dom.DOMConstants.TAG_POSITION; import static org.xmind.core.internal.dom.DOMConstants.TAG_SHEET; import static org.xmind.core.internal.dom.DOMConstants.TAG_SUMMARIES; import static org.xmind.core.internal.dom.DOMConstants.TAG_SUMMARY; import static org.xmind.core.internal.dom.DOMConstants.TAG_TITLE; import static org.xmind.core.internal.dom.DOMConstants.TAG_TOPIC; import static org.xmind.core.internal.dom.DOMConstants.TAG_TOPICS; import static org.xmind.core.internal.dom.DOMConstants.VAL_FOLDED; import static org.xmind.core.internal.dom.NumberUtils.safeParseInt; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xmind.core.Core; import org.xmind.core.IBoundary; import org.xmind.core.IImage; import org.xmind.core.INotes; import org.xmind.core.INumbering; import org.xmind.core.ISheet; import org.xmind.core.ISummary; import org.xmind.core.ITopic; import org.xmind.core.ITopicExtension; import org.xmind.core.ITopicPath; import org.xmind.core.IWorkbook; import org.xmind.core.event.CoreEvent; 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.Topic; import org.xmind.core.marker.IMarkerRef; import org.xmind.core.util.DOMUtils; import org.xmind.core.util.ILabelRefCounter; import org.xmind.core.util.IMarkerRefCounter; import org.xmind.core.util.Point; /** * @author briansun */ public class TopicImpl extends Topic implements ICoreEventSource { private Element implementation; private WorkbookImpl ownedWorkbook; private NotesImpl notes; private ImageImpl image; private NumberingImpl numbering; private Map<String, TopicExtensionImpl> extensions = new HashMap<String, TopicExtensionImpl>(); // private boolean updatingTimestamp = false; /** * @param implementation */ public TopicImpl(Element implementation, WorkbookImpl ownedWorkbook) { super(); this.implementation = DOMUtils.addIdAttribute(implementation); this.ownedWorkbook = ownedWorkbook; // installTimestampUpdater(implementation); } /** * @return the implementation */ public Element getImplementation() { return implementation; } /** * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(Object obj) { if (obj == this) return true; if (obj == null || !(obj instanceof TopicImpl)) return false; TopicImpl that = (TopicImpl) obj; return this.implementation == that.implementation; } @Override public int hashCode() { return implementation.hashCode(); } public String toString() { return "TPC#" + getId() + "(" + getTitleText() + ")"; //$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); } /** * @see org.xmind.core.ITopic#getId() */ public String getId() { return implementation.getAttribute(ATTR_ID); } /** * @see org.xmind.core.internal.Topic#getLocalTitleText() */ @Override protected String getLocalTitleText() { return DOMUtils.getTextContentByTag(implementation, TAG_TITLE); } /** * @see org.xmind.core.ITitled#setTitleText(java.lang.String) */ public void setTitleText(String titleText) { String oldValue = getLocalTitleText(); DOMUtils.setText(implementation, TAG_TITLE, titleText); String newValue = getLocalTitleText(); fireValueChange(Core.TitleText, oldValue, newValue); updateModificationInfo(); } /** * @see org.xmind.core.ITopic#isFolded() */ public boolean isFolded() { String value = DOMUtils.getAttribute(implementation, ATTR_BRANCH); return value != null && value.contains(VAL_FOLDED); } /** * @see org.xmind.core.ITopic#setFolded(boolean) */ public void setFolded(boolean folded) { Boolean oldValue = isFolded(); String value = folded ? VAL_FOLDED : null; DOMUtils.setAttribute(implementation, ATTR_BRANCH, value); Boolean newValue = isFolded(); fireValueChange(Core.TopicFolded, oldValue, newValue); updateModificationInfo(); } /** * @see org.xmind.core.style.IStyled#getStyleId() */ public String getStyleId() { return DOMUtils.getAttribute(implementation, ATTR_STYLE_ID); } /** * @see org.xmind.core.style.IStyled#setStyleId(java.lang.String) */ public void setStyleId(String styleId) { String oldValue = getStyleId(); WorkbookImpl workbook = getRealizedWorkbook(); decreaseStyleRef(workbook); DOMUtils.setAttribute(implementation, ATTR_STYLE_ID, styleId); increaseStyleRef(workbook); String newValue = getStyleId(); fireValueChange(Core.Style, oldValue, newValue); updateModificationInfo(); } public boolean hasPosition() { Element e = DOMUtils.getFirstChildElementByTag(implementation, TAG_POSITION); if (e == null) return false; return e.hasAttribute(ATTR_X) && e.hasAttribute(ATTR_Y); } /** * @see org.xmind.core.ITopic#getPosition() */ public Point getPosition() { Element e = DOMUtils.getFirstChildElementByTag(implementation, TAG_POSITION); if (e == null) return null; String x = DOMUtils.getAttribute(e, ATTR_X); String y = DOMUtils.getAttribute(e, ATTR_Y); if (x == null && y == null) return null; return new Point(safeParseInt(x, 0), safeParseInt(y, 0)); } /** * @see org.xmind.core.ITopic#setPosition(int, int) */ public void setPosition(int x, int y) { Point oldValue = getPosition(); Element e = DOMUtils.ensureChildElement(implementation, TAG_POSITION); DOMUtils.setAttribute(e, ATTR_X, Integer.toString(x)); DOMUtils.setAttribute(e, ATTR_Y, Integer.toString(y)); Point newValue = getPosition(); fireValueChange(Core.Position, oldValue, newValue); updateModificationInfo(); } /** * @see org.xmind.core.internal.Topic#removePosition() */ @Override protected void removePosition() { Point oldValue = getPosition(); Element e = DOMUtils.getFirstChildElementByTag(implementation, TAG_POSITION); if (e != null) implementation.removeChild(e); Point newValue = getPosition(); fireValueChange(Core.Position, oldValue, newValue); updateModificationInfo(); } public String getType() { if (isRoot()) return ITopic.ROOT; Node p = implementation.getParentNode(); if (DOMUtils.isElementByTag(p, TAG_TOPICS)) { return getType((Element) p); } return null; } public List<ITopic> getChildren(String type) { if (type == null) return NO_CHILDREN; Element ts = findSubtopicsElement(implementation, type); if (ts != null) return getChildren(ts); return NO_CHILDREN; } public boolean hasChildren(String type) { if (type == null) return false; Element ts = findSubtopicsElement(implementation, type); if (ts != null) return DOMUtils.hasChildElementByTag(ts, TAG_TOPIC); return false; } public Iterator<ITopic> getChildrenIterator(String type) { if (type == null) return NO_CHILDREN.iterator(); Element ts = findSubtopicsElement(implementation, type); if (ts != null) { return DOMUtils.getChildIterator(ts, TAG_TOPIC, ownedWorkbook.getAdaptableRegistry(), ITopic.class); } return NO_CHILDREN.iterator(); } public Iterator<ITopic> getAllChildrenIterator() { Element c = DOMUtils.getFirstChildElementByTag(implementation, TAG_CHILDREN); if (c == null) return NO_CHILDREN.iterator(); final Iterator<Element> tsIter = DOMUtils.childElementIterByTag(c, TAG_TOPICS); return new Iterator<ITopic>() { Iterator<ITopic> childTopicIter = null; ITopic next = findNextChildTopic(); private ITopic findNextChildTopic() { if (childTopicIter == null || !childTopicIter.hasNext()) { childTopicIter = nextChildTopicIter(); } if (childTopicIter == null) return null; return childTopicIter.next(); } private Iterator<ITopic> nextChildTopicIter() { while (tsIter.hasNext()) { Element ts = tsIter.next(); if (ts != null) { return DOMUtils.getChildIterator(ts, TAG_TOPIC, ownedWorkbook.getAdaptableRegistry(), ITopic.class); } } return null; } public void remove() { throw new UnsupportedOperationException(); } public ITopic next() { ITopic n = next; next = findNextChildTopic(); return n; } public boolean hasNext() { return next != null; } }; } protected static Element findSubtopicsElement(Element topicElement, String type) { Element c = DOMUtils.getFirstChildElementByTag(topicElement, TAG_CHILDREN); if (c != null) { return findSubtopicsElementFromChildren(c, type); } return null; } private static Element findSubtopicsElementFromChildren(Element c, String type) { Iterator<Element> it = DOMUtils.childElementIterByTag(c, TAG_TOPICS); while (it.hasNext()) { Element ts = it.next(); if (type.equals(getType(ts))) return ts; } return null; } protected static String getType(Element topicsImpl) { return DOMUtils.getAttribute(topicsImpl, ATTR_TYPE); } private List<ITopic> getChildren(Element ts) { return DOMUtils.getChildList(ts, TAG_TOPIC, ownedWorkbook.getAdaptableRegistry()); } public Set<String> getChildrenTypes() { Element c = DOMUtils.getFirstChildElementByTag(implementation, TAG_CHILDREN); if (c == null) return NO_TYPES; List<String> list = new ArrayList<String>( c.getChildNodes().getLength()); Iterator<Element> it = DOMUtils.childElementIterByTag(c, TAG_TOPICS); while (it.hasNext()) { Element ts = it.next(); String type = getType(ts); if (type != null && !list.contains(type)) { list.add(type); } } return DOMUtils.unmodifiableSet(list); } public void add(ITopic child, int index, String type) { Element t = ((TopicImpl) child).getImplementation(); Element c = DOMUtils.ensureChildElement(implementation, TAG_CHILDREN); Element ts = findSubtopicsElementFromChildren(c, type); if (ts == null) { ts = DOMUtils.createElement(c, TAG_TOPICS); DOMUtils.setAttribute(ts, ATTR_TYPE, type); } Node n = null; Element[] es = DOMUtils.getChildElementsByTag(ts, TAG_TOPIC); if (index >= 0 && index < es.length) { n = ts.insertBefore(t, es[index]); } else { n = ts.appendChild(t); } if (n != null) { if (!isOrphan()) { ((TopicImpl) child).addNotify(getRealizedWorkbook(), getRealizedSheet(), this); } fireIndexedTargetChange(Core.TopicAdd, child, child.getIndex(), type); updateModificationInfo(); } } public void add(ITopic child, String type) { add(child, -1, type); } public void add(ITopic child) { add(child, -1, ITopic.ATTACHED); } public void remove(ITopic child) { Element t = ((TopicImpl) child).getImplementation(); Node p = t.getParentNode(); if (DOMUtils.isElementByTag(p, TAG_TOPICS)) { Element ts = (Element) p; p = p.getParentNode(); if (DOMUtils.isElementByTag(p, TAG_CHILDREN)) { Element c = (Element) p; p = p.getParentNode(); if (p == implementation) { int index = child.getIndex(); String type = DOMUtils.getAttribute(ts, ATTR_TYPE); if (!isOrphan()) { ((TopicImpl) child).removeNotify(getRealizedWorkbook(), getRealizedSheet(), null); } Node n = ts.removeChild(t); if (!ts.hasChildNodes()) { c.removeChild(ts); if (!c.hasChildNodes()) { implementation.removeChild(c); } } if (n != null) { fireIndexedTargetChange(Core.TopicRemove, child, index, type); updateModificationInfo(); } } } } } /** * @see org.xmind.core.ITopic#getParent() */ public ITopic getParent() { Node p = implementation.getParentNode(); if (DOMUtils.isElementByTag(p, TAG_TOPICS)) { p = p.getParentNode(); if (DOMUtils.isElementByTag(p, TAG_CHILDREN)) { p = p.getParentNode(); if (DOMUtils.isElementByTag(p, TAG_TOPIC)) { return (ITopic) ownedWorkbook.getAdaptableRegistry() .getAdaptable(p); } } } return null; } /** * @see org.xmind.core.ITopic#isRoot() */ public boolean isRoot() { Node p = implementation.getParentNode(); return DOMUtils.isElementByTag(p, TAG_SHEET); } /** * @see org.xmind.core.internal.Topic#getOwnedSheet() */ @Override public ISheet getOwnedSheet() { Node s = implementation.getParentNode(); if (DOMUtils.isElementByTag(s, TAG_SHEET)) { return (ISheet) ownedWorkbook.getAdaptableRegistry() .getAdaptable(s); } return super.getOwnedSheet(); } /** * @see org.xmind.core.internal.Topic#getOwnedWorkbook() */ @Override public IWorkbook getOwnedWorkbook() { return ownedWorkbook; } public boolean isOrphan() { return DOMUtils.isOrphanNode(implementation); } /** * @see org.xmind.core.ITopic#getPath() */ public ITopicPath getPath() { return new TopicPathImpl(this); } /** * @see org.xmind.core.ITopic#getIndex() */ public int getIndex() { Node p = implementation.getParentNode(); if (DOMUtils.isElementByTag(p, TAG_TOPICS)) { return DOMUtils.getElementIndex(p, TAG_TOPIC, implementation); } return -1; } /** * @see org.xmind.core.ITopic#getHyperlink() */ public String getHyperlink() { return DOMUtils.getAttribute(implementation, ATTR_HREF); } /** * @see org.xmind.core.ITopic#setHyperlink(java.lang.String) */ public void setHyperlink(String hyperlink) { String oldValue = getHyperlink(); WorkbookImpl workbook = getRealizedWorkbook(); InternalHyperlinkUtils.deactivateHyperlink(workbook, oldValue, this); DOMUtils.setAttribute(implementation, ATTR_HREF, hyperlink); InternalHyperlinkUtils.activateHyperlink(workbook, getHyperlink(), this); String newValue = getHyperlink(); fireValueChange(Core.TopicHyperlink, oldValue, newValue); updateModificationInfo(); } /** * @see org.xmind.core.ITopic#getNotes() */ public INotes getNotes() { if (notes == null) { notes = new NotesImpl(implementation, this); } return notes; } public INumbering getNumbering() { if (numbering == null) { numbering = new NumberingImpl(implementation, this); } return numbering; } public void addMarker(String markerId) { if (markerId == null) return; Element ms = DOMUtils.ensureChildElement(implementation, TAG_MARKER_REFS); Element m = getMarkerRefElement(markerId, ms); if (m != null) return; m = DOMUtils.createElement(ms, TAG_MARKER_REF); m.setAttribute(ATTR_MARKER_ID, markerId); WorkbookImpl workbook = getRealizedWorkbook(); if (workbook != null) { workbook.getMarkerRefCounter().increaseRef(markerId); } fireTargetChange(Core.MarkerRefAdd, markerId); SheetImpl sheet = getRealizedSheet(); if (sheet != null) { sheet.getMarkerRefCounter().increaseRef(markerId); } updateModificationInfo(); } public void removeMarker(String markerId) { if (markerId == null) return; Element ms = getMarkerRefsElement(); if (ms == null) return; Element m = getMarkerRefElement(markerId, ms); if (m == null) return; Node n = ms.removeChild(m); if (!ms.hasChildNodes()) { implementation.removeChild(ms); } if (n != null) { WorkbookImpl workbook = getRealizedWorkbook(); if (workbook != null) { workbook.getMarkerRefCounter().decreaseRef(markerId); } fireTargetChange(Core.MarkerRefRemove, markerId); SheetImpl sheet = getRealizedSheet(); if (sheet != null) { sheet.getMarkerRefCounter().decreaseRef(markerId); } updateModificationInfo(); } } public boolean hasMarker(String markerId) { if (markerId == null) return false; return getMarkerRefElement(markerId) != null; } public IMarkerRef getMarkerRef(String markerId) { if (markerId == null) return null; Element m = getMarkerRefElement(markerId); if (m != null) return (IMarkerRef) ownedWorkbook.getAdaptableRegistry() .getAdaptable(m); return null; } public Set<IMarkerRef> getMarkerRefs() { Element ms = getMarkerRefsElement(); if (ms == null) return NO_MARKER_REFS; return DOMUtils.getChildSet(ms, TAG_MARKER_REF, ownedWorkbook.getAdaptableRegistry()); } private Element getMarkerRefsElement() { return DOMUtils.getFirstChildElementByTag(implementation, TAG_MARKER_REFS); } private Element getMarkerRefElement(String markerId) { Element ms = getMarkerRefsElement(); if (ms == null) return null; return getMarkerRefElement(markerId, ms); } private Element getMarkerRefElement(String markerId, Element ms) { Iterator<Element> it = DOMUtils.childElementIterByTag(ms, TAG_MARKER_REF); while (it.hasNext()) { Element m = it.next(); if (markerId.equals(m.getAttribute(ATTR_MARKER_ID))) return m; } return null; } public void addBoundary(IBoundary boundary) { Element b = ((BoundaryImpl) boundary).getImplementation(); Element bs = DOMUtils.ensureChildElement(implementation, TAG_BOUNDARIES); Node n = bs.appendChild(b); if (n != null) { if (!isOrphan()) { ((BoundaryImpl) boundary).addNotify(getRealizedWorkbook(), this); } fireTargetChange(Core.BoundaryAdd, boundary); updateModificationInfo(); } } public void removeBoundary(IBoundary boundary) { Element bs = DOMUtils.getFirstChildElementByTag(implementation, TAG_BOUNDARIES); if (bs == null) return; Element b = ((BoundaryImpl) boundary).getImplementation(); if (b.getParentNode() == bs) { ((BoundaryImpl) boundary).removeNotify(getRealizedWorkbook(), this); Node n = bs.removeChild(b); if (!bs.hasChildNodes()) implementation.removeChild(bs); if (n != null) { fireTargetChange(Core.BoundaryRemove, boundary); updateModificationInfo(); } } } public Set<IBoundary> getBoundaries() { Element bs = DOMUtils.getFirstChildElementByTag(implementation, TAG_BOUNDARIES); if (bs == null) return NO_BOUNDARIES; return DOMUtils.getChildSet(bs, TAG_BOUNDARY, ownedWorkbook.getAdaptableRegistry()); } public void addSummary(ISummary summary) { Element s = ((SummaryImpl) summary).getImplementation(); Element ss = DOMUtils.ensureChildElement(implementation, TAG_SUMMARIES); Node n = ss.appendChild(s); if (n != null) { if (!isOrphan()) { ((SummaryImpl) summary).addNotify(getRealizedWorkbook(), this); } fireTargetChange(Core.SummaryAdd, summary); updateModificationInfo(); } } public void removeSummary(ISummary summary) { Element ss = DOMUtils.getFirstChildElementByTag(implementation, TAG_SUMMARIES); if (ss == null) return; Element s = ((SummaryImpl) summary).getImplementation(); if (s.getParentNode() == ss) { ((SummaryImpl) summary).removeNotify(getRealizedWorkbook(), this); Node n = ss.removeChild(s); if (!ss.hasChildNodes()) implementation.removeChild(ss); if (n != null) { fireTargetChange(Core.SummaryRemove, summary); updateModificationInfo(); } } } public Set<ISummary> getSummaries() { Element ss = DOMUtils.getFirstChildElementByTag(implementation, TAG_SUMMARIES); if (ss == null) return NO_SUMMARIES; return DOMUtils.getChildSet(ss, TAG_SUMMARY, ownedWorkbook.getAdaptableRegistry()); } public String getStructureClass() { return DOMUtils.getAttribute(implementation, ATTR_STRUCTURE_CLASS); } public void setStructureClass(String structureClass) { String oldValue = getStructureClass(); DOMUtils.setAttribute(implementation, ATTR_STRUCTURE_CLASS, structureClass); String newValue = getStructureClass(); fireValueChange(Core.StructureClass, oldValue, newValue); updateModificationInfo(); } public Set<String> getLabels() { Element ls = DOMUtils.getFirstChildElementByTag(implementation, TAG_LABELS); if (ls != null) { NodeList c = ls.getChildNodes(); int num = c.getLength(); if (num > 0) { List<String> set = new ArrayList<String>(num); for (int i = 0; i < num; i++) { Node n = c.item(i); if (DOMUtils.isElementByTag(n, TAG_LABEL)) { set.add(n.getTextContent()); } } return DOMUtils.unmodifiableSet(set); } } return NO_LABELS; } private Element findLabelElement(String label, Element ls) { NodeList c = ls.getChildNodes(); for (int i = 0; i < c.getLength(); i++) { Node n = c.item(i); if (DOMUtils.isElementByTag(n, TAG_LABEL)) { String text = n.getTextContent(); if (label.equals(text)) return (Element) n; } } return null; } public void addLabel(String label) { if (label == null) return; Element ls = DOMUtils.getFirstChildElementByTag(implementation, TAG_LABELS); if (ls != null) { Element existing = findLabelElement(label, ls); if (existing != null) return; } Set<String> oldValue = getLabels(); addLabelElement(ls, label); Set<String> newValue = getLabels(); fireValueChange(Core.Labels, oldValue, newValue); SheetImpl sheet = getRealizedSheet(); if (sheet != null) { sheet.getLabelRefCounter().increaseRef(label); } updateModificationInfo(); } public void removeLabel(String label) { if (label == null) return; Element ls = DOMUtils.getFirstChildElementByTag(implementation, TAG_LABELS); if (ls == null) return; Element l = findLabelElement(label, ls); if (l == null) return; Set<String> oldValue = getLabels(); Node n = ls.removeChild(l); if (n != null) { if (!ls.hasChildNodes()) { implementation.removeChild(ls); } Set<String> newValue = getLabels(); fireValueChange(Core.Labels, oldValue, newValue); SheetImpl sheet = getRealizedSheet(); if (sheet != null) { sheet.getLabelRefCounter().decreaseRef(label); } updateModificationInfo(); } } public void setLabels(Collection<String> labels) { Set<String> oldValue = getLabels(); Element ls = DOMUtils.getFirstChildElementByTag(implementation, TAG_LABELS); if (ls != null) { implementation.removeChild(ls); ls = null; } if (labels != null && !labels.isEmpty()) { for (String label : labels) { if (label != null) { ls = addLabelElement(ls, label); } } } Set<String> newValue = getLabels(); fireValueChange(Core.Labels, oldValue, newValue); SheetImpl sheet = getRealizedSheet(); if (sheet != null) { ILabelRefCounter counter = sheet.getLabelRefCounter(); List<String> added = new ArrayList<String>(newValue); added.removeAll(oldValue); List<String> removed = new ArrayList<String>(oldValue); removed.removeAll(newValue); for (String increased : added) { counter.increaseRef(increased); } for (String decreased : removed) { counter.decreaseRef(decreased); } } updateModificationInfo(); } public void removeAllLabels() { setLabels(NO_LABELS); } private Element addLabelElement(Element ls, String label) { if (ls == null) ls = DOMUtils.createElement(implementation, TAG_LABELS); Element l = DOMUtils.createElement(ls, TAG_LABEL); l.setTextContent(label); return ls; } public IImage getImage() { if (image == null) { image = new ImageImpl(implementation, this); } return image; } public int getTitleWidth() { Element t = DOMUtils.getFirstChildElementByTag(implementation, TAG_TITLE); return t == null ? UNSPECIFIED : NumberUtils.safeParseInt(DOMUtils.getAttribute(t, ATTR_WIDTH), UNSPECIFIED); } public void setTitleWidth(int width) { Integer oldValue = getTitleWidthValue(); if (width == UNSPECIFIED) { Element t = DOMUtils.getFirstChildElementByTag(implementation, TAG_TITLE); if (t != null) { t.removeAttribute(ATTR_WIDTH); if (!t.hasChildNodes() && !t.hasAttributes()) { implementation.removeChild(t); } } } else { Element t = DOMUtils.ensureChildElement(implementation, TAG_TITLE); t.setAttribute(ATTR_WIDTH, String.valueOf(width)); } Integer newValue = getTitleWidthValue(); fireValueChange(Core.TitleWidth, oldValue, newValue); updateModificationInfo(); } private Integer getTitleWidthValue() { int width = getTitleWidth(); if (width != UNSPECIFIED) return Integer.valueOf(width); return null; } private Iterator<TopicExtensionImpl> iterExtensions() { return iterExtensions(!isOrphan()); } private Iterator<TopicExtensionImpl> iterExtensions( final boolean realized) { Element es = DOMUtils.getFirstChildElementByTag(implementation, TAG_EXTENSIONS); final Iterator<Element> it = es == null ? null : DOMUtils.childElementIterByTag(es, TAG_EXTENSION); return new Iterator<TopicExtensionImpl>() { TopicExtensionImpl next = findNext(); public void remove() { } private TopicExtensionImpl findNext() { if (it == null) return null; while (it.hasNext()) { Element ele = it.next(); String providerName = ele.getAttribute(ATTR_PROVIDER); if (providerName != null && !"".equals(providerName)) { //$NON-NLS-1$ TopicExtensionImpl ext = extensions.get(providerName); if (ext == null) { ext = new TopicExtensionImpl(ele, TopicImpl.this); extensions.put(providerName, ext); if (realized) { ext.addNotify(ownedWorkbook); } } return ext; } } return null; } public TopicExtensionImpl next() { TopicExtensionImpl n = next; next = findNext(); return n; } public boolean hasNext() { return next != null; } }; } // private TopicExtensionImpl getExtension(String providerName, Element extImpl) { // TopicExtensionImpl ext = extensions.get(providerName); // if (ext == null) { // ext = new TopicExtensionImpl(extImpl, this); // extensions.put(providerName, ext); // ext.addNotify(getRealizedWorkbook()); // } // return ext; // } public List<ITopicExtension> getExtensions() { List<ITopicExtension> extensions = new ArrayList<ITopicExtension>(); Iterator<TopicExtensionImpl> it = iterExtensions(); while (it.hasNext()) { extensions.add(it.next()); } return extensions; } public ITopicExtension getExtension(String providerName) { Iterator<TopicExtensionImpl> it = iterExtensions(); while (it.hasNext()) { TopicExtensionImpl ext = it.next(); if (providerName.equals(ext.getProviderName())) { return ext; } } return null; } public ITopicExtension createExtension(String providerName) { ITopicExtension ext = getExtension(providerName); if (ext == null) { Element es = DOMUtils.ensureChildElement(implementation, TAG_EXTENSIONS); Element e = DOMUtils.createElement(es, TAG_EXTENSION); e.setAttribute(ATTR_PROVIDER, providerName); ext = new TopicExtensionImpl(e, this); extensions.put(providerName, (TopicExtensionImpl) ext); if (!isOrphan()) { ((TopicExtensionImpl) ext).addNotify(ownedWorkbook); } updateModificationInfo(); } return ext; // Element e = findExtensionElement(es, providerName); // if (e == null) { // } // return getExtension(providerName, e); } private Element findExtensionElement(Element es, String providerName) { Iterator<Element> it = DOMUtils.childElementIterByTag(es, TAG_EXTENSION); while (it.hasNext()) { Element e = it.next(); if (providerName.equals(e.getAttribute(ATTR_PROVIDER))) { return e; } } return null; } public void deleteExtension(String providerName) { Element es = DOMUtils.getFirstChildElementByTag(implementation, TAG_EXTENSIONS); if (es != null) { Element e = findExtensionElement(es, providerName); if (e != null) { es.removeChild(e); if (!es.hasChildNodes()) implementation.removeChild(es); updateModificationInfo(); } } } public ICoreEventRegistration registerCoreEventListener(String type, ICoreEventListener listener) { return getCoreEventSupport().registerCoreEventListener(this, type, listener); } protected void fireValueChange(String type, Object oldValue, Object newValue) { ICoreEventSupport coreEventSupport = getCoreEventSupport(); if (coreEventSupport != null) { coreEventSupport.dispatchValueChange(this, type, oldValue, newValue); } } protected void fireIndexedTargetChange(String type, Object target, int index, Object data) { ICoreEventSupport coreEventSupport = getCoreEventSupport(); if (coreEventSupport != null) { CoreEvent event = new CoreEvent(this, type, target, index); event.setData(data); coreEventSupport.dispatch(this, event); } } protected void fireTargetChange(String type, Object target) { ICoreEventSupport coreEventSupport = getCoreEventSupport(); if (coreEventSupport != null) { coreEventSupport.dispatchTargetChange(this, type, target); } } public ICoreEventSupport getCoreEventSupport() { // Use workbook's core event support directly, so that // orphan components can have events broadcasted, which // will enable transient actions (such as dragging topics // or adjusting relationship control points, etc.) to // perform correctly. return ownedWorkbook.getCoreEventSupport(); } protected WorkbookImpl getRealizedWorkbook() { if (getPath().getWorkbook() == ownedWorkbook) return ownedWorkbook; return null; } protected SheetImpl getRealizedSheet() { ISheet sheet = getOwnedSheet(); if (sheet != null && sheet instanceof SheetImpl) return (SheetImpl) sheet; return null; } protected void addNotify(WorkbookImpl workbook, SheetImpl sheet, TopicImpl parent) { getImplementation().setIdAttribute(DOMConstants.ATTR_ID, true); workbook.getAdaptableRegistry().registerById(this, getId(), getImplementation().getOwnerDocument()); increaseLabelRefs(sheet); increaseMarkerRefs(workbook, sheet); increaseStyleRef(workbook); activateHyperlinks(workbook); ((NotesImpl) getNotes()).addNotify(workbook); for (ITopic t : getAllChildren()) { ((TopicImpl) t).addNotify(workbook, sheet, this); } for (IBoundary b : getBoundaries()) { ((BoundaryImpl) b).addNotify(workbook, this); } for (ISummary s : getSummaries()) { ((SummaryImpl) s).addNotify(workbook, this); } extensionsAddNotify(workbook); boolean isRevising = (workbook.getAdaptableRegistry() .getAdaptableByNode(sheet.getImplementation()) != sheet); if (!isRevising) { ((CommentManagerImpl) workbook.getCommentManager()) .objectAddNotify(getId(), this); } } protected void removeNotify(WorkbookImpl workbook, SheetImpl sheet, TopicImpl parent) { boolean isRevising = (workbook.getAdaptableRegistry() .getAdaptableByNode(sheet.getImplementation()) != sheet); if (!isRevising) { ((CommentManagerImpl) workbook.getCommentManager()) .objectRemoveNotify(getId(), this); } extensionsRemoveNotify(workbook); for (ISummary s : getSummaries()) { ((SummaryImpl) s).removeNotify(workbook, this); } for (IBoundary b : getBoundaries()) { ((BoundaryImpl) b).removeNotify(workbook, this); } for (ITopic t : getAllChildren()) { ((TopicImpl) t).removeNotify(workbook, sheet, this); } ((NotesImpl) getNotes()).removeNotify(workbook); deactivateHyperlinks(workbook); decreaseStyleRef(workbook); decreaseMarkerRefs(workbook, sheet); decreaseLabelRefs(sheet); workbook.getAdaptableRegistry().unregisterById(this, getId(), getImplementation().getOwnerDocument()); getImplementation().setIdAttribute(DOMConstants.ATTR_ID, false); } private void extensionsAddNotify(WorkbookImpl workbook) { Iterator<TopicExtensionImpl> it = iterExtensions(false); while (it.hasNext()) { it.next().addNotify(workbook); } // Element es = getFirstChildElementByTag(implementation, TAG_EXTENSIONS); // if (es != null) { // Iterator<Element> it = childElementIterByTag(es, TAG_EXTENSION); // while (it.hasNext()) { // Element e = it.next(); // String providerName = getAttribute(e, ATTR_PROVIDER); // if (providerName != null) { // TopicExtensionImpl ext = extensions.get(providerName); // if (ext == null) { // ext = new TopicExtensionImpl(e, this); // extensions.put(providerName, ext); // } // ext.addNotify(workbook); // } // } // } } private void extensionsRemoveNotify(WorkbookImpl workbook) { Iterator<TopicExtensionImpl> it = iterExtensions(true); while (it.hasNext()) { it.next().removeNotify(workbook); } // Element es = DOMUtils.getFirstChildElementByTag(implementation, // TAG_EXTENSIONS); // if (es != null) { // Iterator<Element> it = DOMUtils.childElementIterByTag(es, // TAG_EXTENSION); // while (it.hasNext()) { // Element e = it.next(); // String providerName = DOMUtils.getAttribute(e, ATTR_PROVIDER); // if (providerName != null) { // TopicExtensionImpl ext = getExtension(providerName, e); // ext.removeNotify(workbook); // } // } // } } protected void increaseStyleRef(WorkbookImpl workbook) { if (workbook == null) return; String styleId = getStyleId(); if (styleId != null) { workbook.getStyleRefCounter().increaseRef(styleId); } } protected void decreaseStyleRef(WorkbookImpl workbook) { if (workbook == null) return; String styleId = getStyleId(); if (styleId != null) { workbook.getStyleRefCounter().decreaseRef(styleId); } } protected void increaseMarkerRefs(WorkbookImpl workbook, SheetImpl sheet) { IMarkerRefCounter counter = sheet == null ? null : sheet.getMarkerRefCounter(); if (workbook == null && counter == null) return; Element mrs = getMarkerRefsElement(); if (mrs == null) return; Iterator<Element> it = DOMUtils.childElementIterByTag(mrs, TAG_MARKER_REF); while (it.hasNext()) { String markerId = DOMUtils.getAttribute(it.next(), ATTR_MARKER_ID); if (markerId != null) { if (workbook != null) workbook.getMarkerRefCounter().increaseRef(markerId); if (counter != null) counter.increaseRef(markerId); } } } protected void decreaseMarkerRefs(WorkbookImpl workbook, SheetImpl sheet) { IMarkerRefCounter counter = sheet == null ? null : sheet.getMarkerRefCounter(); if (workbook == null && counter == null) return; Element mrs = getMarkerRefsElement(); if (mrs == null) return; Iterator<Element> it = DOMUtils.childElementIterByTag(mrs, TAG_MARKER_REF); while (it.hasNext()) { String markerId = DOMUtils.getAttribute(it.next(), ATTR_MARKER_ID); if (markerId != null) { if (workbook != null) workbook.getMarkerRefCounter().decreaseRef(markerId); if (counter != null) { counter.decreaseRef(markerId); } } } } private void increaseLabelRefs(SheetImpl sheet) { if (sheet == null) return; Element ls = DOMUtils.getFirstChildElementByTag(implementation, TAG_LABELS); if (ls == null) return; ILabelRefCounter counter = sheet.getLabelRefCounter(); Iterator<Element> it = DOMUtils.childElementIterByTag(ls, TAG_LABEL); while (it.hasNext()) { String label = it.next().getTextContent(); counter.increaseRef(label); } } private void decreaseLabelRefs(SheetImpl sheet) { if (sheet == null) return; Element ls = DOMUtils.getFirstChildElementByTag(implementation, TAG_LABELS); if (ls == null) return; ILabelRefCounter counter = sheet.getLabelRefCounter(); Iterator<Element> it = DOMUtils.childElementIterByTag(ls, TAG_LABEL); while (it.hasNext()) { String label = it.next().getTextContent(); counter.decreaseRef(label); } } protected void activateHyperlinks(WorkbookImpl workbook) { InternalHyperlinkUtils.activateHyperlink(workbook, getHyperlink(), this); ((ImageImpl) getImage()).activateHyperlink(workbook); } protected void deactivateHyperlinks(WorkbookImpl workbook) { ((ImageImpl) getImage()).deactivateHyperlink(workbook); InternalHyperlinkUtils.deactivateHyperlink(workbook, getHyperlink(), this); } public long getModifiedTime() { return InternalDOMUtils.getModifiedTime(this, implementation); } public String getModifiedBy() { return InternalDOMUtils.getModifiedBy(this, implementation); } protected void updateModificationInfo() { InternalDOMUtils.updateModificationInfo(this); ISheet sheet = getOwnedSheet(); if (sheet != null) { ((SheetImpl) sheet).updateModificationInfo(); } } }