private E extractLastRedundant() { E maxElement = null; int maxLocation = -1; for (E element : redundantElements) { // NOTE(user): there is a possible bug here, which will be fixed by // not individualising the element events in the ElementListener // interface. int location = getDocument().getLocation(element); if (location > maxLocation) { maxLocation = location; maxElement = element; } } redundantElements.remove(maxElement); return maxElement; }
@Override public void onElementRemoved(E oldElement) { if (!tag.equals(getDocument().getTagName(oldElement))) { return; } redundantElements.remove(oldElement); if (oldElement == valueElement) { // Reference value is removed. Find new best value from redundant collection. changeValue(extractLastRedundant()); } }
/** * Deletes redundant elements. */ private void cleanup() { Boolean beforeValue = get(); // Delete all elements identified as redundant. We don't delete _every_ // element in the document in order that this type can be embedded // collaboratively in the same document as other types. Collection<E> toDelete = new ArrayList<E>(); toDelete.addAll(redundantElements); ObservableMutableDocument<? super E, E, ?> doc = getDocument(); for (E e : toDelete) { doc.deleteNode(e); } // Callbacks should have emptied the redundant collection. assert redundantElements.isEmpty(); // Check that cleanup did not change the interpretation. assert equals(beforeValue, get()); }
/** * Loads state from the substrate document. */ private void load() { ObservableMutableDocument<? super E, E, ?> document = getDocument(); E child = DocHelper.getFirstChildElement(document, container); while (child != null) { onElementAdded(child); child = DocHelper.getNextSiblingElement(document, child); } }
private void changeValue(E newElement) { Boolean oldValue = get(); if (newElement != null) { valueElement = newElement; // It's a positive element if it has the right tagname and does not have // value = false. Note, absence of attributes is interpreted as a positive // element. value = !FALSE.equals(getDocument().getAttribute(valueElement, valueAttr)); } else { valueElement = null; value = null; } Boolean newValue = get(); maybeTriggerOnValueChanged(oldValue, newValue); }
@Override public void onElementAdded(E newElement) { ObservableMutableDocument<? super E, E, ?> document = getDocument(); assert container.equals(document.getParentElement(newElement)); if (!tag.equals(document.getTagName(newElement))) { return; } // Possibly changing an existing value? if (valueElement != null) { if (document.getLocation(newElement) < document.getLocation(valueElement)) { // New element loses. redundantElements.add(newElement); } else { // New element wins. redundantElements.add(valueElement); changeValue(newElement); } } else { // New element is the new value. changeValue(newElement); } }
@Override public void set(Boolean newValue) { Boolean oldValue = get(); if (equals(oldValue, newValue)) { return; } if (newValue != null) { // Add an element to reflect new value. getDocument().createChildElement(container, tag, new AttributesImpl(valueAttr, Serializer.BOOLEAN.toString(newValue))); cleanup(); } else { // Erase all elements. Cleanup first, so that removal of real element // does not promote a redundant element temporarily. cleanup(); getDocument().deleteNode(valueElement); } assert equals(newValue, get()); }