001 package railo.runtime.text.xml; 002 003 import java.util.ArrayList; 004 import java.util.Iterator; 005 006 import org.w3c.dom.Attr; 007 import org.w3c.dom.DOMException; 008 import org.w3c.dom.Document; 009 import org.w3c.dom.NamedNodeMap; 010 import org.w3c.dom.Node; 011 012 import railo.runtime.PageContext; 013 import railo.runtime.dump.DumpData; 014 import railo.runtime.dump.DumpProperties; 015 import railo.runtime.dump.DumpTable; 016 import railo.runtime.dump.DumpTablePro; 017 import railo.runtime.dump.DumpUtil; 018 import railo.runtime.dump.SimpleDumpData; 019 import railo.runtime.exp.ExpressionException; 020 import railo.runtime.exp.PageException; 021 import railo.runtime.exp.XMLException; 022 import railo.runtime.op.Caster; 023 import railo.runtime.op.Duplicator; 024 import railo.runtime.op.ThreadLocalDuplication; 025 import railo.runtime.type.Collection; 026 import railo.runtime.type.KeyImpl; 027 import railo.runtime.type.List; 028 import railo.runtime.type.Struct; 029 import railo.runtime.type.dt.DateTime; 030 import railo.runtime.type.util.StructSupport; 031 import railo.runtime.util.ArrayIterator; 032 033 /** 034 * represent a Struct and a NamedNodeMap 035 */ 036 public final class XMLAttributes extends StructSupport implements Struct,NamedNodeMap { 037 038 039 private NamedNodeMap nodeMap; 040 private Document owner; 041 private boolean caseSensitive; 042 043 /** 044 * constructor of the class 045 * @param owner 046 * @param nodeMap 047 */ 048 public XMLAttributes(Document owner,NamedNodeMap nodeMap,boolean caseSensitive) { 049 this.owner=owner; 050 this.nodeMap=nodeMap; 051 this.caseSensitive=caseSensitive; 052 } 053 /** 054 * constructor of the class (readonly) 055 * @param nodeMap 056 */ 057 public XMLAttributes(NamedNodeMap nodeMap,boolean caseSensitive) { 058 this.nodeMap=nodeMap; 059 try { 060 owner=this.nodeMap.item(0).getOwnerDocument(); 061 } 062 catch(Exception e) {} 063 this.caseSensitive=caseSensitive; 064 } 065 066 /** 067 * @see railo.runtime.dump.Dumpable#toDumpData(railo.runtime.PageContext, int) 068 */ 069 public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) { 070 String[] keys=keysAsString(); 071 maxlevel--; 072 DumpTable table = new DumpTablePro("xml","#999966","#cccc99","#000000"); 073 table.setTitle("Struct (XML Attributes)"); 074 075 int maxkeys=dp.getMaxKeys(); 076 int index=0; 077 for(int i=0;i<keys.length;i++) { 078 String key=keys[i]; 079 080 if(DumpUtil.keyValid(dp,maxlevel, key)){ 081 if(maxkeys<=index++)break; 082 table.appendRow(1,new SimpleDumpData(key),DumpUtil.toDumpData(get(key,null), pageContext,maxlevel,dp)); 083 } 084 } 085 return table; 086 } 087 088 089 /** 090 * @see railo.runtime.type.Collection#size() 091 */ 092 public int size() { 093 return nodeMap.getLength(); 094 } 095 096 /** 097 * 098 * @see railo.runtime.type.Collection#keysAsString() 099 */ 100 public String[] keysAsString() { 101 int len=nodeMap.getLength(); 102 ArrayList list = new ArrayList(); 103 for(int i=0;i<len;i++) { 104 Node item = nodeMap.item(i); 105 if(item instanceof Attr) 106 list.add(((Attr)item).getName()); 107 } 108 return (String[]) list.toArray(new String[list.size()]); 109 } 110 111 /** 112 * 113 * @see railo.runtime.type.Collection#keys() 114 */ 115 public Collection.Key[] keys() { 116 int len=nodeMap.getLength(); 117 ArrayList list = new ArrayList(); 118 for(int i=0;i<len;i++) { 119 Node item = nodeMap.item(i); 120 if(item instanceof Attr) 121 list.add(KeyImpl.init(((Attr)item).getName())); 122 } 123 return (Collection.Key[]) list.toArray(new Collection.Key[list.size()]); 124 } 125 126 /** 127 * @throws ExpressionException 128 * @see railo.runtime.type.Struct#remove(java.lang.String) 129 */ 130 public Object remove(String key) throws ExpressionException { 131 Node rtn=null; 132 if(!caseSensitive){ 133 int len = nodeMap.getLength(); 134 String nn; 135 for(int i=len-1;i>=0;i--) { 136 nn=nodeMap.item(i).getNodeName(); 137 if(key.equalsIgnoreCase(nn)) rtn=nodeMap.removeNamedItem(nn); 138 } 139 } 140 else rtn=nodeMap.removeNamedItem(toName(key)); 141 142 if(rtn!=null) return rtn.getNodeValue(); 143 throw new ExpressionException("can't remove element with name ["+key+"], element doesn't exist"); 144 } 145 146 /** 147 * 148 * @see railo.runtime.type.Collection#remove(railo.runtime.type.Collection.Key) 149 */ 150 public Object remove(Collection.Key key) throws PageException { 151 return remove(key.getString()); 152 } 153 154 /** 155 * @see railo.runtime.type.Struct#removeEL(java.lang.String) 156 */ 157 public Object removeEL(String key) { 158 Node rtn=null; 159 if(!caseSensitive){ 160 int len = nodeMap.getLength(); 161 String nn; 162 for(int i=len-1;i>=0;i--) { 163 nn=nodeMap.item(i).getNodeName(); 164 if(key.equalsIgnoreCase(nn)) rtn=nodeMap.removeNamedItem(nn); 165 } 166 } 167 else rtn=nodeMap.removeNamedItem(toName(key)); 168 169 if(rtn!=null) return rtn.getNodeValue(); 170 return null; 171 } 172 173 /** 174 * 175 * @see railo.runtime.type.Collection#removeEL(railo.runtime.type.Collection.Key) 176 */ 177 public Object removeEL(Collection.Key key) { 178 return removeEL(key.getString()); 179 } 180 181 /** 182 * @see railo.runtime.type.Collection#clear() 183 */ 184 public void clear() { 185 String[] keys=keysAsString(); 186 for(int i=0;i<keys.length;i++) { 187 nodeMap.removeNamedItem(keys[i]); 188 } 189 } 190 191 /** 192 * @see railo.runtime.type.Collection#get(java.lang.String) 193 */ 194 public Object get(Collection.Key key) throws ExpressionException { 195 Node rtn = nodeMap.getNamedItem(key.getString()); 196 if(rtn!=null) return rtn.getNodeValue(); 197 198 Collection.Key[] keys=keys(); 199 for(int i=0;i<keys.length;i++) { 200 if(key.equalsIgnoreCase(keys[i])) 201 return nodeMap.getNamedItem(keys[i].getString()).getNodeValue(); 202 } 203 throw new ExpressionException("No Attribute "+key.getString()+" defined for tag","attributes are ["+List.arrayToList(keys,", ")+"]"); 204 } 205 206 /** 207 * @see railo.runtime.type.Collection#get(railo.runtime.type.Collection.Key, java.lang.Object) 208 */ 209 public Object get(Collection.Key key, Object defaultValue) { 210 try { 211 return get(key); 212 } catch (PageException e) { 213 return defaultValue; 214 } 215 } 216 217 /** 218 * @see railo.runtime.type.Collection#set(java.lang.String, java.lang.Object) 219 */ 220 public Object set(Collection.Key key, Object value) throws PageException { 221 if(owner==null) return value; 222 223 try { 224 Attr attr=owner.createAttribute(toName(key.getString())); 225 attr.setValue(Caster.toString(value)); 226 nodeMap.setNamedItem(attr); 227 228 } 229 catch(DOMException de) { 230 throw new XMLException(de); 231 } 232 233 234 235 236 return value; 237 } 238 239 private String toName(String name) { 240 return toName(name,name); 241 } 242 private String toName(String name, String defaultValue) { 243 if(caseSensitive) return name; 244 245 Node n = nodeMap.getNamedItem(name); 246 if(n!=null) return n.getNodeName(); 247 248 int len = nodeMap.getLength(); 249 String nn; 250 for(int i=0;i<len;i++) { 251 nn=nodeMap.item(i).getNodeName(); 252 if(name.equalsIgnoreCase(nn)) return nn; 253 } 254 return defaultValue; 255 } 256 257 /** 258 * @see railo.runtime.type.Collection#setEL(java.lang.String, java.lang.Object) 259 */ 260 public Object setEL(Collection.Key key, Object value) { 261 if(owner==null) return value; 262 try { 263 Attr attr=owner.createAttribute(toName(key.getString())); 264 attr.setValue(Caster.toString(value)); 265 nodeMap.setNamedItem(attr); 266 } 267 catch(Exception e) { 268 return null; 269 } 270 return value; 271 } 272 273 274 /** 275 * @see railo.runtime.type.Collection#keyIterator() 276 */ 277 public Iterator keyIterator() { 278 return new ArrayIterator(keysAsString()); 279 } 280 281 /** 282 * @see org.w3c.dom.NamedNodeMap#getLength() 283 */ 284 public int getLength() { 285 return nodeMap.getLength(); 286 } 287 288 /** 289 * @see org.w3c.dom.NamedNodeMap#item(int) 290 */ 291 public Node item(int index) { 292 return nodeMap.item(index); 293 } 294 295 /** 296 * @see org.w3c.dom.NamedNodeMap#getNamedItem(java.lang.String) 297 */ 298 public Node getNamedItem(String name) { 299 return nodeMap.getNamedItem(name); 300 } 301 302 /** 303 * @see org.w3c.dom.NamedNodeMap#removeNamedItem(java.lang.String) 304 */ 305 public Node removeNamedItem(String name) throws DOMException { 306 return nodeMap.removeNamedItem(name); 307 } 308 309 /** 310 * @see org.w3c.dom.NamedNodeMap#setNamedItem(org.w3c.dom.Node) 311 */ 312 public Node setNamedItem(Node arg) throws DOMException { 313 return nodeMap.setNamedItem(arg); 314 } 315 316 /** 317 * @see org.w3c.dom.NamedNodeMap#setNamedItemNS(org.w3c.dom.Node) 318 */ 319 public Node setNamedItemNS(Node arg) throws DOMException { 320 return nodeMap.setNamedItemNS(arg); 321 } 322 323 /** 324 * @see org.w3c.dom.NamedNodeMap#getNamedItemNS(java.lang.String, java.lang.String) 325 */ 326 public Node getNamedItemNS(String namespaceURI, String localName) { 327 return nodeMap.getNamedItemNS(namespaceURI,localName); 328 } 329 330 /** 331 * @see org.w3c.dom.NamedNodeMap#removeNamedItemNS(java.lang.String, java.lang.String) 332 */ 333 public Node removeNamedItemNS(String namespaceURI, String localName) throws DOMException { 334 return nodeMap.removeNamedItemNS(namespaceURI, localName); 335 } 336 337 /** 338 * 339 * @see railo.runtime.type.Collection#duplicate(boolean) 340 */ 341 public Collection duplicate(boolean deepCopy) { 342 XMLAttributes sct=new XMLAttributes(owner,nodeMap,caseSensitive); 343 ThreadLocalDuplication.set(this, sct); 344 try{ 345 String[] keys=keysAsString(); 346 for(int i=0;i<keys.length;i++) { 347 String key=keys[i]; 348 sct.setEL(key,Duplicator.duplicate(get(key,null),deepCopy)); 349 } 350 } 351 finally { 352 ThreadLocalDuplication.remove(this); 353 } 354 return sct; 355 } 356 357 358 /** 359 * @return returns named Node map 360 */ 361 public NamedNodeMap toNamedNodeMap() { 362 return nodeMap; 363 } 364 365 /** 366 * 367 * @see railo.runtime.type.Collection#containsKey(railo.runtime.type.Collection.Key) 368 */ 369 public boolean containsKey(Collection.Key key) { 370 return get(key,null)!=null; 371 } 372 373 /** 374 * @see railo.runtime.op.Castable#castToString() 375 */ 376 public String castToString() throws ExpressionException { 377 throw new ExpressionException("Can't cast XML NamedNodeMap to String"); 378 } 379 380 /** 381 * @see railo.runtime.type.util.StructSupport#castToString(java.lang.String) 382 */ 383 public String castToString(String defaultValue) { 384 return defaultValue; 385 } 386 387 /** 388 * @see railo.runtime.op.Castable#castToBooleanValue() 389 */ 390 public boolean castToBooleanValue() throws ExpressionException { 391 throw new ExpressionException("Can't cast XML NamedNodeMap to a boolean value"); 392 } 393 394 /** 395 * @see railo.runtime.op.Castable#castToBoolean(java.lang.Boolean) 396 */ 397 public Boolean castToBoolean(Boolean defaultValue) { 398 return defaultValue; 399 } 400 401 402 /** 403 * @see railo.runtime.op.Castable#castToDoubleValue() 404 */ 405 public double castToDoubleValue() throws ExpressionException { 406 throw new ExpressionException("Can't cast XML NamedNodeMap to a number value"); 407 } 408 409 /** 410 * @see railo.runtime.op.Castable#castToDoubleValue(double) 411 */ 412 public double castToDoubleValue(double defaultValue) { 413 return defaultValue; 414 } 415 416 417 /** 418 * @see railo.runtime.op.Castable#castToDateTime() 419 */ 420 public DateTime castToDateTime() throws ExpressionException { 421 throw new ExpressionException("Can't cast XML NamedNodeMap to a date value"); 422 } 423 424 /** 425 * @see railo.runtime.op.Castable#castToDateTime(railo.runtime.type.dt.DateTime) 426 */ 427 public DateTime castToDateTime(DateTime defaultValue) { 428 return defaultValue; 429 } 430 431 /** 432 * @see railo.runtime.op.Castable#compare(boolean) 433 */ 434 public int compareTo(boolean b) throws ExpressionException { 435 throw new ExpressionException("can't compare XML NamedNodeMap with a boolean value"); 436 } 437 438 /** 439 * @see railo.runtime.op.Castable#compareTo(railo.runtime.type.dt.DateTime) 440 */ 441 public int compareTo(DateTime dt) throws PageException { 442 throw new ExpressionException("can't compare XML NamedNodeMap with a DateTime Object"); 443 } 444 445 /** 446 * @see railo.runtime.op.Castable#compareTo(double) 447 */ 448 public int compareTo(double d) throws PageException { 449 throw new ExpressionException("can't compare XML NamedNodeMap with a numeric value"); 450 } 451 452 /** 453 * @see railo.runtime.op.Castable#compareTo(java.lang.String) 454 */ 455 public int compareTo(String str) throws PageException { 456 throw new ExpressionException("can't compare XML NamedNodeMap with a String"); 457 } 458 }