001/*
002 * Copyright 2001 (C) MetaStuff, Ltd. All Rights Reserved.
003 * 
004 * This software is open source. 
005 * See the bottom of this file for the licence.
006 * 
007 * $Id: IndexedElement.java,v 1.3 2001/06/29 12:33:02 jstrachan Exp $
008 */
009
010package org.dom4j.util;
011
012import java.io.IOException;
013import java.io.StringWriter;
014import java.io.PrintWriter;
015import java.util.ArrayList;
016import java.util.Collections;
017import java.util.HashMap;
018import java.util.Iterator;
019import java.util.List;
020import java.util.Map;
021import java.util.StringTokenizer;
022
023import org.dom4j.Attribute;
024import org.dom4j.CDATA;
025import org.dom4j.CharacterData;
026import org.dom4j.Comment;
027import org.dom4j.Document;
028import org.dom4j.Element;
029import org.dom4j.Entity;
030import org.dom4j.IllegalAddException;
031import org.dom4j.Node;
032import org.dom4j.Namespace;
033import org.dom4j.QName;
034import org.dom4j.ProcessingInstruction;
035import org.dom4j.Text;
036import org.dom4j.tree.BackedList;
037import org.dom4j.tree.DefaultElement;
038
039/** <p><code>IndexedElement</code> is an implementation of {@link Element}
040  * which maintains an index of the attributes and elements it contains to
041  * optimise lookups via name.</p>
042  *
043  * @author <a href="mailto:james.strachan@metastuff.com">James Strachan</a>
044  * @version $Revision: 1.3 $
045  */
046public class IndexedElement extends DefaultElement {
047
048    /** Lazily constructed index for elements */
049    private Map elementIndex;
050
051    /** Lazily constructed index for attributes */
052    private Map attributeIndex;
053
054    
055    public IndexedElement(String name) { 
056        super(name);
057    }
058
059    public IndexedElement(QName qname) { 
060        super(qname);
061    }
062
063    public IndexedElement(QName qname, int attributeCount) { 
064        super(qname, attributeCount);
065    }
066    
067    public Attribute attribute(String name) {
068        return (Attribute) attributeIndex().get(name);
069    }
070
071    public Attribute attribute(QName qName) {
072        return (Attribute) attributeIndex().get(qName);
073    }
074
075    public Element element(String name) {
076        return asElement( elementIndex().get(name) );
077    }
078    
079    public Element element(QName qName) {
080        return asElement( elementIndex().get(qName) );
081    }
082        
083    public List elements(String name) {
084        return asElementList( elementIndex().get(name) );
085    }
086    
087    public List elements(QName qName) {
088        return asElementList( elementIndex().get(qName) );
089    }
090        
091    public Iterator elementIterator(String name) {
092        return asElementIterator( elementIndex().get(name) );
093    }
094    
095    public Iterator elementIterator(QName qName) {
096        return asElementIterator( elementIndex().get(qName) );
097    }
098    
099    
100    // Implementation methods
101    //-------------------------------------------------------------------------    
102    
103    protected Element asElement(Object object) {
104        if ( object instanceof Element ) {
105            return (Element) object;
106        }
107        else if ( object != null ) {
108            List list = (List) object;
109            if ( list.size() >= 1 ) {
110                return (Element) list.get(0);
111            }
112        }
113        return null;
114    }
115    
116    protected List asElementList(Object object) {
117        if ( object instanceof Element ) {
118            return createSingleResultList( object );
119        }
120        else if ( object != null ) {
121            List list = (List) object;
122            BackedList answer = createResultList();
123            for ( int i = 0, size = list.size(); i < size; i++ ) {
124                answer.addLocal( list.get(i) );
125            }
126            return answer;
127        }
128        return createEmptyList();
129    }
130    
131    protected Iterator asElementIterator(Object object) {
132        if ( object instanceof Element ) {
133            return createSingleIterator( object );
134        }
135        else if ( object != null ) {
136            List list = (List) object;
137            return list.iterator();
138        }
139        return EMPTY_ITERATOR;
140    }
141    
142    
143    // #### could we override the add(Element) remove(Element methods?
144    
145    protected void addNode(Node node) {
146        super.addNode(node);
147        if ( elementIndex != null && node instanceof Element ) {
148            addToElementIndex( (Element) node );
149        }
150        else if ( attributeIndex != null && node instanceof Attribute ) {
151            addToAttributeIndex( (Attribute) node );
152        }
153    }
154    
155    protected boolean removeNode(Node node) {
156        if ( super.removeNode(node) ) {
157            if ( elementIndex != null && node instanceof Element ) {
158                removeFromElementIndex( (Element) node );
159            }
160            else if ( attributeIndex != null && node instanceof Attribute ) {
161                removeFromAttributeIndex( (Attribute) node );
162            }
163            return true;
164        }
165        return false;
166    }
167
168    protected Map attributeIndex() {
169        if ( attributeIndex == null ) {
170            attributeIndex = createAttributeIndex();
171            for (Iterator iter = attributeIterator(); iter.hasNext(); ) {
172                addToAttributeIndex( (Attribute) iter.next() );
173            }
174        }
175        return attributeIndex;
176    }
177    
178    protected Map elementIndex() {
179        if ( elementIndex == null ) {
180            elementIndex = createElementIndex();
181            for (Iterator iter = elementIterator(); iter.hasNext(); ) {
182                addToElementIndex( (Element) iter.next() );
183            }
184        }
185        return elementIndex;
186    }
187    
188    /** A Factory Method to create the index for attributes
189      */
190    protected Map createAttributeIndex() {
191        Map answer = createIndex();
192        return answer;
193    }
194    
195    /** A Factory Method to create the index for elements
196      */
197    protected Map createElementIndex() {
198        Map answer = createIndex();
199        return answer;
200    }
201
202    protected void addToElementIndex(Element element) {
203        QName qName = element.getQName();
204        String name = qName.getName();
205        addToElementIndex(qName, element);
206        addToElementIndex(name, element);
207    }
208    
209    protected void addToElementIndex(Object key, Element value) {
210        Object oldValue = elementIndex.get(key);
211        if (oldValue == null) {
212            elementIndex.put(key, value);
213        }
214        else {
215            if ( oldValue instanceof List ) {
216                List list = (List) oldValue;
217                list.add(value);
218            }
219            else {
220                List list = createList();
221                list.add(oldValue);
222                list.add(value);
223                elementIndex.put(key, list);
224            }
225        }
226    }
227    
228    protected void removeFromElementIndex(Element element) {
229        QName qName = element.getQName();
230        String name = qName.getName();
231        removeFromElementIndex(qName, element);
232        removeFromElementIndex(name, element);
233    }
234    
235    protected void removeFromElementIndex(Object key, Element value) {
236        Object oldValue = elementIndex.get(key);
237        if ( oldValue instanceof List ) {
238            List list = (List) oldValue;
239            list.remove(value);
240        }
241        else {
242            elementIndex.remove(key);
243        }
244    }
245
246    
247    protected void addToAttributeIndex(Attribute attribute) {
248        QName qName = attribute.getQName();
249        String name = qName.getName();
250        addToAttributeIndex(qName, attribute);
251        addToAttributeIndex(name, attribute);
252    }
253    
254    protected void addToAttributeIndex(Object key, Attribute value) {
255        Object oldValue = attributeIndex.get(key);
256        if (oldValue != null) {
257            attributeIndex.put(key, value);
258        }
259    }
260    
261    protected void removeFromAttributeIndex(Attribute attribute) {
262        QName qName = attribute.getQName();
263        String name = qName.getName();
264        removeFromAttributeIndex(qName, attribute);
265        removeFromAttributeIndex(name, attribute);
266    }
267    
268    protected void removeFromAttributeIndex(Object key, Attribute value) {
269        Object oldValue = attributeIndex.get(key);
270        if ( oldValue != null && oldValue.equals( value ) ) {
271            attributeIndex.remove(key);
272        }
273    }
274
275    /** Factory method to return a new map implementation for indices
276      */
277    protected Map createIndex() {
278        return new HashMap();
279    }
280    
281    /** Factory method to return a list implementation for indices
282      */
283    protected List createList() {
284        return new ArrayList();
285    }
286}
287
288
289
290
291/*
292 * Redistribution and use of this software and associated documentation
293 * ("Software"), with or without modification, are permitted provided
294 * that the following conditions are met:
295 *
296 * 1. Redistributions of source code must retain copyright
297 *    statements and notices.  Redistributions must also contain a
298 *    copy of this document.
299 *
300 * 2. Redistributions in binary form must reproduce the
301 *    above copyright notice, this list of conditions and the
302 *    following disclaimer in the documentation and/or other
303 *    materials provided with the distribution.
304 *
305 * 3. The name "DOM4J" must not be used to endorse or promote
306 *    products derived from this Software without prior written
307 *    permission of MetaStuff, Ltd.  For written permission,
308 *    please contact dom4j-info@metastuff.com.
309 *
310 * 4. Products derived from this Software may not be called "DOM4J"
311 *    nor may "DOM4J" appear in their names without prior written
312 *    permission of MetaStuff, Ltd. DOM4J is a registered
313 *    trademark of MetaStuff, Ltd.
314 *
315 * 5. Due credit should be given to the DOM4J Project
316 *    (http://dom4j.org/).
317 *
318 * THIS SOFTWARE IS PROVIDED BY METASTUFF, LTD. AND CONTRIBUTORS
319 * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
320 * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
321 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
322 * METASTUFF, LTD. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
323 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
324 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
325 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
326 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
327 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
328 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
329 * OF THE POSSIBILITY OF SUCH DAMAGE.
330 *
331 * Copyright 2001 (C) MetaStuff, Ltd. All Rights Reserved.
332 *
333 * $Id: IndexedElement.java,v 1.3 2001/06/29 12:33:02 jstrachan Exp $
334 */