1 38 package com.gargoylesoftware.htmlunit.javascript; 39 40 import java.util.ArrayList ; 41 import java.util.Iterator ; 42 import java.util.List ; 43 44 import org.apache.commons.collections.CollectionUtils; 45 import org.apache.commons.collections.Transformer; 46 import org.apache.commons.collections.functors.NOPTransformer; 47 import org.jaxen.JaxenException; 48 import org.jaxen.XPath; 49 import org.mozilla.javascript.Context; 50 import org.mozilla.javascript.Function; 51 import org.mozilla.javascript.JavaScriptException; 52 import org.mozilla.javascript.Scriptable; 53 import org.saxpath.SAXPathException; 54 55 import com.gargoylesoftware.htmlunit.WebWindow; 56 import com.gargoylesoftware.htmlunit.html.DomNode; 57 import com.gargoylesoftware.htmlunit.html.HtmlElement; 58 import com.gargoylesoftware.htmlunit.html.Util; 59 60 73 public class ElementArray extends SimpleScriptable implements Function { 74 77 public static final String JS_OBJECT_NAME = "ElementArray"; 78 private static final long serialVersionUID = 4049916048017011764L; 79 80 private XPath xpath_; 81 private DomNode node_; 82 86 private Transformer transformer_; 87 88 91 public ElementArray() { 92 93 } 94 95 101 public void init(final DomNode node, final XPath xpath) { 102 init(node, xpath, NOPTransformer.INSTANCE); 103 104 } 105 106 114 public void init(final DomNode node, final XPath xpath, final Transformer transformer) { 115 node_ = node; 116 xpath_ = xpath; 117 transformer_ = transformer; 118 } 119 120 124 public final void jsConstructor() { 125 } 127 128 131 public final Object call( 132 final Context context, final Scriptable scope, 133 final Scriptable thisObj, final Object [] args) 134 throws JavaScriptException { 135 if( args.length == 0 ) { 136 throw Context.reportRuntimeError( "Zero arguments; need an index or a key." ); 137 } 138 return get( args[0] ); 139 } 140 141 144 public final Scriptable construct( 145 final Context arg0, final Scriptable arg1, final Object [] arg2) 146 throws JavaScriptException { 147 return null; 148 } 149 150 156 private Object get( final Object o ) { 157 if( o instanceof Number ) { 158 final Number n = (Number ) o; 159 final int i = n.intValue(); 160 return get( i, this ); 161 } 162 else { 163 final String key = String.valueOf( o ); 164 return get( key, this ); 165 } 166 } 167 168 173 public final Object get( final int index, final Scriptable start ) { 174 final ElementArray array = (ElementArray) start; 175 final List elements = array.getElementsSorted(); 176 177 if( index >= 0 && index < elements.size()) { 178 return getScriptableFor(elements.get(index)); 179 } 180 else { 181 return NOT_FOUND; 182 } 183 } 184 185 191 private List getElementsSorted() { 192 final List nodes = getElements(); 193 final List sortedNodes; 194 if (nodes.size() > 1) { 195 sortedNodes = new ArrayList (); 196 for (final Iterator iter = Util.getFollowingAxisIterator(node_); iter.hasNext();) { 197 final Object node = iter.next(); 198 if (nodes.contains(node)) { 199 sortedNodes.add(node); 200 nodes.remove(node); 201 if (nodes.isEmpty()) { 202 break; } 204 } 205 } 206 } 207 else { 208 sortedNodes = nodes; } 210 211 CollectionUtils.transform(sortedNodes, transformer_); 212 return sortedNodes; 213 } 214 215 220 private List getElements() { 221 try { 222 final List list = xpath_.selectNodes(node_); 223 return list; 224 } 225 catch (final JaxenException e) { 226 throw Context.reportRuntimeError("Exeption getting elements: " + e.getMessage()); 227 } 228 } 229 230 238 public final Object get( final String name, final Scriptable start ) { 239 final Object result = super.get(name, start); 241 if( result != NOT_FOUND ) { 242 return result; 243 } 244 245 final ElementArray currentArray = ((ElementArray) start); 246 final List elements = currentArray.getElements(); 247 CollectionUtils.transform(elements, transformer_); 248 249 for (final Iterator iter = elements.iterator(); iter.hasNext();) { 251 final Object next = iter.next(); 252 if (next instanceof HtmlElement) { 253 final HtmlElement element = (HtmlElement) next; 254 final String id = element.getId(); 255 if( id != null && id.equals(name) ) { 256 getLog().debug("Property \"" + name + "\" evaluated (by id) to " + element); 257 return getScriptableFor( element ); 258 } 259 } 260 else if (next instanceof WebWindow) { 261 final WebWindow window = (WebWindow) next; 262 final String windowName = window.getName(); 263 if (windowName != null && windowName.equals(name)) { 264 getLog().debug("Property \"" + name + "\" evaluated (by name) to " + window); 265 return getScriptableFor( window ); 266 } 267 } 268 else { 269 getLog().debug("Unrecognized type in array: \"" + next.getClass().getName() + "\""); 270 } 271 } 272 273 final ElementArray array = (ElementArray) currentArray.makeJavaScriptObject(JS_OBJECT_NAME); 275 try { 276 final String newCondition = "@name = '" + name + "'"; 277 final String currentXPathExpr = currentArray.xpath_.toString(); 278 final String xpathExpr; 279 if (currentXPathExpr.endsWith("]")) { 280 xpathExpr = currentXPathExpr.substring(0, currentXPathExpr.length()-1) + " and " + newCondition + "]"; 281 } 282 else { 283 xpathExpr = currentXPathExpr + "[" + newCondition + "]"; 284 } 285 final XPath xpathName = currentArray.xpath_.getNavigator().parseXPath(xpathExpr); 286 array.init(currentArray.node_, xpathName); 287 } 288 catch (final SAXPathException e) { 289 throw Context.reportRuntimeError("Failed getting sub elements by name" + e.getMessage()); 290 } 291 292 if ("length".equals(name)) { 295 return NOT_FOUND; 296 } 297 298 final List subElements = array.getElements(); 299 if (subElements.size() > 1) { 300 getLog().debug("Property \"" + name + "\" evaluated (by name) to " + array + " with " 301 + subElements.size() + " elements"); 302 return array; 303 } 304 else if (subElements.size() == 1) { 305 final SimpleScriptable singleResult = getScriptableFor(subElements.get(0)); 306 getLog().debug("Property \"" + name + "\" evaluated (by name) to " + singleResult); 307 return singleResult; 308 } 309 310 return NOT_FOUND; 312 } 313 314 319 public final int jsGet_length() { 320 return getElements().size(); 321 } 322 323 329 public final Object jsFunction_item( final Object index ) { 330 return get( index ); 331 } 332 333 340 public final Object jsFunction_namedItem( final String name ) { 341 return get( name ); 342 } 343 344 352 public final Object jsFunction_tags( final String tagName ) { 353 final ElementArray array = (ElementArray) makeJavaScriptObject(JS_OBJECT_NAME); 354 try { 355 final String newXPathExpr = xpath_.toString() + "[name() = '" + tagName.toLowerCase() + "']"; 356 array.init(node_, xpath_.getNavigator().parseXPath(newXPathExpr)); 357 } 358 catch (final SAXPathException e) { 359 throw Context.reportRuntimeError("Failed call tags: " + e.getMessage()); 361 } 362 363 return array; 364 } 365 366 367 371 public String toString() { 372 if (xpath_ != null) { 373 return super.toString() + "<" + xpath_.toString() + ">"; 374 } 375 return super.toString(); 376 } 377 } 378 | Popular Tags |