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 */