001    /*
002     * Copyright 2002 - 2007 JEuclid, http://jeuclid.sf.net
003     * 
004     * Licensed under the Apache License, Version 2.0 (the "License");
005     * you may not use this file except in compliance with the License.
006     * You may obtain a copy of the License at
007     *
008     *      http://www.apache.org/licenses/LICENSE-2.0
009     *
010     * Unless required by applicable law or agreed to in writing, software
011     * distributed under the License is distributed on an "AS IS" BASIS,
012     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013     * See the License for the specific language governing permissions and
014     * limitations under the License.
015     */
016    
017    /* $Id: AbstractChangeTrackingElement.java 310 2007-05-18 20:26:36Z maxberger $ */
018    
019    package net.sourceforge.jeuclid.dom;
020    
021    import java.util.HashSet;
022    import java.util.Set;
023    
024    import net.sourceforge.jeuclid.elements.support.ElementListSupport;
025    
026    import org.w3c.dom.Node;
027    
028    /**
029     * generic implementation of Element that tries to track if a change has
030     * happened.
031     * 
032     * @author Max Berger
033     * @version $Revision: 310 $
034     */
035    public abstract class AbstractChangeTrackingElement extends
036            AbstractPartialElementImpl implements ChangeTrackingInterface {
037    
038        private final Set<ChangeTrackingInterface> listeners = new HashSet<ChangeTrackingInterface>();
039    
040        /** {@inheritDoc} */
041        @Override
042        public final Node appendChild(final Node newChild) {
043            final Node retVal = super.appendChild(newChild);
044            this.fireChanged(true);
045            return retVal;
046        }
047    
048        /** {@inheritDoc} */
049        @Override
050        public final void setAttribute(final String name, final String value) {
051            super.setAttribute(name, value);
052            this.fireChanged(true);
053        }
054    
055        /** {@inheritDoc} */
056        @Override
057        public final void setTextContent(final String newTextContent) {
058            super.setTextContent(newTextContent);
059            this.fireChanged(true);
060        }
061    
062        /** {@inheritDoc} */
063        @Override
064        public final Node replaceChild(final Node newChild, final Node oldChild) {
065            final Node retVal = super.replaceChild(newChild, oldChild);
066            this.fireChanged(true);
067            return retVal;
068        }
069    
070        /** {@inheritDoc} */
071        public void fireChanged(final boolean propagate) {
072            this.changeHook();
073            if (propagate) {
074                final Node superNode = this.getParentNode();
075                if (superNode instanceof ChangeTrackingInterface) {
076                    ((ChangeTrackingInterface) superNode).fireChanged(propagate);
077                }
078                for (final ChangeTrackingInterface listener : this.listeners) {
079                    listener.fireChanged(false);
080                }
081            }
082        }
083    
084        /** {@inheritDoc} */
085        public void fireChangeForSubTree() {
086            this.fireChanged(false);
087            ElementListSupport.fireChangeForSubTree(ElementListSupport
088                    .createListOfChildren(this));
089        }
090    
091        /**
092         * Called on any change. Please override!
093         */
094        protected void changeHook() {
095            // Override me!
096        }
097    
098        /** {@inheritDoc} */
099        public void addListener(final ChangeTrackingInterface listener) {
100            this.listeners.add(listener);
101        }
102    
103    }