001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.text.xml; 020 021import java.util.ArrayList; 022import java.util.Iterator; 023 024import lucee.runtime.PageContext; 025import lucee.runtime.dump.DumpData; 026import lucee.runtime.dump.DumpProperties; 027import lucee.runtime.dump.DumpTable; 028import lucee.runtime.dump.DumpUtil; 029import lucee.runtime.dump.SimpleDumpData; 030import lucee.runtime.exp.ExpressionException; 031import lucee.runtime.exp.PageException; 032import lucee.runtime.exp.XMLException; 033import lucee.runtime.op.Caster; 034import lucee.runtime.type.Collection; 035import lucee.runtime.type.KeyImpl; 036import lucee.runtime.type.Struct; 037import lucee.runtime.type.dt.DateTime; 038import lucee.runtime.type.it.EntryIterator; 039import lucee.runtime.type.it.KeyIterator; 040import lucee.runtime.type.it.StringIterator; 041import lucee.runtime.type.it.ValueIterator; 042import lucee.runtime.type.util.ListUtil; 043import lucee.runtime.type.util.StructSupport; 044 045import org.w3c.dom.Attr; 046import org.w3c.dom.DOMException; 047import org.w3c.dom.Document; 048import org.w3c.dom.NamedNodeMap; 049import org.w3c.dom.Node; 050 051/** 052 * represent a Struct and a NamedNodeMap 053 */ 054public final class XMLAttributes extends StructSupport implements Struct,NamedNodeMap { 055 056 057 private final NamedNodeMap nodeMap; 058 private final Document owner; 059 private final Node parent; 060 private final boolean caseSensitive; 061 062 /** 063 * constructor of the class (readonly) 064 * @param nodeMap 065 */ 066 public XMLAttributes(Node parent, boolean caseSensitive) { 067 this.owner=parent.getOwnerDocument(); 068 this.parent=parent; 069 this.nodeMap=parent.getAttributes(); 070 this.caseSensitive=caseSensitive; 071 } 072 073 @Override 074 public DumpData toDumpData(PageContext pageContext, int maxlevel, DumpProperties dp) { 075 Collection.Key[] keys=keys(); 076 maxlevel--; 077 DumpTable table = new DumpTable("xml","#999966","#cccc99","#000000"); 078 table.setTitle("Struct (XML Attributes)"); 079 080 int maxkeys=dp.getMaxKeys(); 081 int index=0; 082 Collection.Key k; 083 for(int i=0;i<keys.length;i++) { 084 k=keys[i]; 085 086 if(DumpUtil.keyValid(dp,maxlevel, k)){ 087 if(maxkeys<=index++)break; 088 table.appendRow(1,new SimpleDumpData(k.getString()),DumpUtil.toDumpData(get(k.getString(),null), pageContext,maxlevel,dp)); 089 } 090 } 091 return table; 092 } 093 094 095 @Override 096 public int size() { 097 return nodeMap.getLength(); 098 } 099 100 @Override 101 public Collection.Key[] keys() { 102 int len=nodeMap.getLength(); 103 ArrayList<Collection.Key> list =new ArrayList<Collection.Key>(); 104 for(int i=0;i<len;i++) { 105 Node item = nodeMap.item(i); 106 if(item instanceof Attr) 107 list.add(KeyImpl.init(((Attr)item).getName())); 108 } 109 return list.toArray(new Collection.Key[list.size()]); 110 } 111 112 @Override 113 public Object remove(Collection.Key k) throws PageException { 114 String key=k.getString(); 115 Node rtn=null; 116 if(!caseSensitive){ 117 int len = nodeMap.getLength(); 118 String nn; 119 for(int i=len-1;i>=0;i--) { 120 nn=nodeMap.item(i).getNodeName(); 121 if(key.equalsIgnoreCase(nn)) rtn=nodeMap.removeNamedItem(nn); 122 } 123 } 124 else rtn=nodeMap.removeNamedItem(toName(key)); 125 126 if(rtn!=null) return rtn.getNodeValue(); 127 throw new ExpressionException("can't remove element with name ["+key+"], element doesn't exist"); 128 } 129 130 @Override 131 public Object removeEL(Collection.Key k) { 132 String key=k.getString(); 133 Node rtn=null; 134 if(!caseSensitive){ 135 int len = nodeMap.getLength(); 136 String nn; 137 for(int i=len-1;i>=0;i--) { 138 nn=nodeMap.item(i).getNodeName(); 139 if(key.equalsIgnoreCase(nn)) rtn=nodeMap.removeNamedItem(nn); 140 } 141 } 142 else rtn=nodeMap.removeNamedItem(toName(key)); 143 144 if(rtn!=null) return rtn.getNodeValue(); 145 return null; 146 } 147 148 @Override 149 public void clear() { 150 Collection.Key[] keys=keys(); 151 for(int i=0;i<keys.length;i++) { 152 nodeMap.removeNamedItem(keys[i].getString()); 153 } 154 } 155 156 @Override 157 public Object get(Collection.Key key) throws ExpressionException { 158 Node rtn = nodeMap.getNamedItem(key.getString()); 159 if(rtn!=null) return rtn.getNodeValue(); 160 161 Collection.Key[] keys=keys(); 162 for(int i=0;i<keys.length;i++) { 163 if(key.equalsIgnoreCase(keys[i])) 164 return nodeMap.getNamedItem(keys[i].getString()).getNodeValue(); 165 } 166 throw new ExpressionException("No Attribute "+key.getString()+" defined for tag","attributes are ["+ListUtil.arrayToList(keys,", ")+"]"); 167 } 168 169 @Override 170 public Object get(Collection.Key key, Object defaultValue) { 171 Node rtn = nodeMap.getNamedItem(key.getString()); 172 if(rtn!=null) return rtn.getNodeValue(); 173 174 Collection.Key[] keys=keys(); 175 for(int i=0;i<keys.length;i++) { 176 if(key.equalsIgnoreCase(keys[i])) 177 return nodeMap.getNamedItem(keys[i].getString()).getNodeValue(); 178 } 179 return defaultValue; 180 } 181 182 @Override 183 public Object set(Collection.Key key, Object value) throws PageException { 184 if(owner==null) return value; 185 186 try { 187 Attr attr=owner.createAttribute(toName(key.getString())); 188 attr.setValue(Caster.toString(value)); 189 nodeMap.setNamedItem(attr); 190 191 } 192 catch(DOMException de) { 193 throw new XMLException(de); 194 } 195 196 197 198 199 return value; 200 } 201 202 private String toName(String name) { 203 return toName(name,name); 204 } 205 private String toName(String name, String defaultValue) { 206 if(caseSensitive) return name; 207 208 Node n = nodeMap.getNamedItem(name); 209 if(n!=null) return n.getNodeName(); 210 211 int len = nodeMap.getLength(); 212 String nn; 213 for(int i=0;i<len;i++) { 214 nn=nodeMap.item(i).getNodeName(); 215 if(name.equalsIgnoreCase(nn)) return nn; 216 } 217 return defaultValue; 218 } 219 220 @Override 221 public Object setEL(Collection.Key key, Object value) { 222 if(owner==null) return value; 223 try { 224 Attr attr=owner.createAttribute(toName(key.getString())); 225 attr.setValue(Caster.toString(value)); 226 nodeMap.setNamedItem(attr); 227 } 228 catch(Exception e) { 229 return null; 230 } 231 return value; 232 } 233 234 @Override 235 public Iterator<Collection.Key> keyIterator() { 236 return new KeyIterator(keys()); 237 } 238 239 @Override 240 public Iterator<String> keysAsStringIterator() { 241 return new StringIterator(keys()); 242 } 243 244 @Override 245 public Iterator<Entry<Key, Object>> entryIterator() { 246 return new EntryIterator(this,keys()); 247 } 248 249 @Override 250 public Iterator<Object> valueIterator() { 251 return new ValueIterator(this,keys()); 252 } 253 254 @Override 255 public int getLength() { 256 return nodeMap.getLength(); 257 } 258 259 @Override 260 public Node item(int index) { 261 return nodeMap.item(index); 262 } 263 264 @Override 265 public Node getNamedItem(String name) { 266 return nodeMap.getNamedItem(name); 267 } 268 269 @Override 270 public Node removeNamedItem(String name) throws DOMException { 271 return nodeMap.removeNamedItem(name); 272 } 273 274 @Override 275 public Node setNamedItem(Node arg) throws DOMException { 276 return nodeMap.setNamedItem(arg); 277 } 278 279 @Override 280 public Node setNamedItemNS(Node arg) throws DOMException { 281 return nodeMap.setNamedItemNS(arg); 282 } 283 284 @Override 285 public Node getNamedItemNS(String namespaceURI, String localName) { 286 return nodeMap.getNamedItemNS(namespaceURI,localName); 287 } 288 289 @Override 290 public Node removeNamedItemNS(String namespaceURI, String localName) throws DOMException { 291 return nodeMap.removeNamedItemNS(namespaceURI, localName); 292 } 293 294 @Override 295 public Collection duplicate(boolean deepCopy) { 296 return new XMLAttributes(parent.cloneNode(deepCopy),caseSensitive); 297 } 298 299 300 /** 301 * @return returns named Node map 302 */ 303 public NamedNodeMap toNamedNodeMap() { 304 return nodeMap; 305 } 306 307 @Override 308 public boolean containsKey(Collection.Key key) { 309 return get(key,null)!=null; 310 } 311 312 @Override 313 public String castToString() throws ExpressionException { 314 throw new ExpressionException("Can't cast XML NamedNodeMap to String"); 315 } 316 317 @Override 318 public String castToString(String defaultValue) { 319 return defaultValue; 320 } 321 322 @Override 323 public boolean castToBooleanValue() throws ExpressionException { 324 throw new ExpressionException("Can't cast XML NamedNodeMap to a boolean value"); 325 } 326 327 @Override 328 public Boolean castToBoolean(Boolean defaultValue) { 329 return defaultValue; 330 } 331 332 333 @Override 334 public double castToDoubleValue() throws ExpressionException { 335 throw new ExpressionException("Can't cast XML NamedNodeMap to a number value"); 336 } 337 338 @Override 339 public double castToDoubleValue(double defaultValue) { 340 return defaultValue; 341 } 342 343 344 @Override 345 public DateTime castToDateTime() throws ExpressionException { 346 throw new ExpressionException("Can't cast XML NamedNodeMap to a date value"); 347 } 348 349 @Override 350 public DateTime castToDateTime(DateTime defaultValue) { 351 return defaultValue; 352 } 353 354 @Override 355 public int compareTo(boolean b) throws ExpressionException { 356 throw new ExpressionException("can't compare XML NamedNodeMap with a boolean value"); 357 } 358 359 @Override 360 public int compareTo(DateTime dt) throws PageException { 361 throw new ExpressionException("can't compare XML NamedNodeMap with a DateTime Object"); 362 } 363 364 @Override 365 public int compareTo(double d) throws PageException { 366 throw new ExpressionException("can't compare XML NamedNodeMap with a numeric value"); 367 } 368 369 @Override 370 public int compareTo(String str) throws PageException { 371 throw new ExpressionException("can't compare XML NamedNodeMap with a String"); 372 } 373}