1 38 package com.gargoylesoftware.htmlunit.javascript; 39 40 import java.lang.reflect.InvocationTargetException ; 41 import java.lang.reflect.Method ; 42 import java.util.Map ; 43 44 import org.apache.commons.collections.Transformer; 45 import org.apache.commons.logging.Log; 46 import org.apache.commons.logging.LogFactory; 47 import org.mozilla.javascript.Context; 48 import org.mozilla.javascript.FunctionObject; 49 import org.mozilla.javascript.Scriptable; 50 import org.mozilla.javascript.ScriptableObject; 51 52 import com.gargoylesoftware.htmlunit.Assert; 53 import com.gargoylesoftware.htmlunit.BrowserVersion; 54 import com.gargoylesoftware.htmlunit.ScriptException; 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.javascript.configuration.JavaScriptConfiguration; 59 import com.gargoylesoftware.htmlunit.javascript.host.Window; 60 61 70 public class SimpleScriptable extends ScriptableObject { 71 private static final long serialVersionUID = 3120000176890886780L; 72 73 private JavaScriptEngine.PageInfo pageInfo_; 74 private DomNode domNode_; 75 76 77 80 public SimpleScriptable() { 81 } 82 83 84 90 public static synchronized Map getHtmlJavaScriptMapping() { 91 return JavaScriptConfiguration.getHtmlJavaScriptMapping(); 92 } 93 94 95 96 private JavaScriptConfiguration getJavaScriptConfiguration() { 97 final BrowserVersion browserVersion 98 = getDomNodeOrDie().getPage().getWebClient().getBrowserVersion(); 99 return JavaScriptConfiguration.getInstance(browserVersion); 100 } 101 102 103 107 public String getClassName() { 108 final String javaClassName = getClass().getName(); 109 final int index = javaClassName.lastIndexOf("."); 110 if( index == -1 ) { 111 throw new IllegalStateException ("No dot in classname: "+javaClassName); 112 } 113 114 return javaClassName.substring(index+1); 115 } 116 117 118 122 public final void setPageInfo( final JavaScriptEngine.PageInfo pageInfo ) { 123 Assert.notNull("pageInfo", pageInfo); 124 pageInfo_ = pageInfo; 125 } 126 127 132 public SimpleScriptable makeJavaScriptObject( final String className ) { 133 final JavaScriptEngine.PageInfo pageInfo = getPageInfo(); 134 135 final SimpleScriptable newObject; 136 try { 137 newObject = (SimpleScriptable) Context.getCurrentContext().newObject( 138 pageInfo.getScope(), className); 139 initJavaScriptObject( newObject ); 140 return newObject; 141 } 142 catch( final Exception e ) { 143 throw new ScriptException(e); 144 } 145 } 146 147 148 152 protected void initJavaScriptObject( final SimpleScriptable newObject ) { 153 final JavaScriptEngine.PageInfo pageInfo = getPageInfo(); 154 155 newObject.setPageInfo( pageInfo ); 156 } 157 158 159 165 public final DomNode getDomNodeOrDie() throws IllegalStateException { 166 if( domNode_ == null ) { 167 final String clazz = getClass().getName(); 168 throw new IllegalStateException ("DomNode has not been set for this SimpleScriptable: " + clazz); 169 } 170 else { 171 return domNode_; 172 } 173 } 174 175 176 182 public final HtmlElement getHtmlElementOrDie() throws IllegalStateException { 183 return (HtmlElement) getDomNodeOrDie(); 184 } 185 186 187 192 public final DomNode getDomNodeOrNull() { 193 return domNode_; 194 } 195 196 197 202 public final HtmlElement getHtmlElementOrNull() { 203 return (HtmlElement) getDomNodeOrNull(); 204 } 205 206 207 211 public void setDomNode( final DomNode domNode ) { 212 setDomNode( domNode, true ); 213 } 214 215 220 protected void setDomNode( final DomNode domNode, final boolean assignScriptObject ) { 221 Assert.notNull("domNode", domNode); 222 domNode_ = domNode; 223 if (assignScriptObject) { 224 domNode_.setScriptObject(this); 225 } 226 } 227 228 232 public void setHtmlElement( final HtmlElement htmlElement ) { 233 setDomNode(htmlElement); 234 } 235 236 237 244 public Object get( final String name, final Scriptable start ) { 245 if( domNode_ == null ) { 249 final Object result = super.get(name, start); 250 if (result == NOT_FOUND) { 252 getLog().debug("Property \"" + name + "\" of " + start + " not defined as pure js property"); 253 } 254 return result; 255 } 256 257 final JavaScriptConfiguration configuration = getJavaScriptConfiguration(); 258 final Class clazz = getClass(); 259 final Object result; 260 261 final Method propertyMethod = configuration.getPropertyReadMethod(clazz, name); 262 final Method functionMethod = configuration.getFunctionMethod(clazz, name); 263 if (propertyMethod != null && functionMethod != null) { 264 throw new IllegalStateException ("Name is both a property and a function: name=[" 265 + name + "] class=[" + clazz.getName() + "]"); 266 } 267 if (propertyMethod == null) { 268 if (functionMethod == null) { 269 result = super.get(name, start); 270 } 271 else { 272 result = new FunctionObject(name, functionMethod, this); 273 } 274 } 275 else { 276 try { 277 result = propertyMethod.invoke(this, new Object [0]); 278 } 279 catch (final Exception e) { 280 throw new ScriptException(e); 281 } 282 } 283 284 if (result == NOT_FOUND) { 286 getLog().debug("Property \"" + name + "\" of " + start + " not defined as fixed property"); 287 } 288 289 return result; 290 } 291 292 293 299 public void put( final String name, final Scriptable start, Object newValue ) { 300 final SimpleScriptable simpleScriptable = (SimpleScriptable) start; 304 if (simpleScriptable.domNode_ == null ) { 305 super.put(name, start, newValue); 306 return; 307 } 308 309 final JavaScriptConfiguration configuration = simpleScriptable.getJavaScriptConfiguration(); 310 final Method setterMethod = configuration.getPropertyWriteMethod(getClass(), name); 311 312 if (setterMethod == null) { 313 if (configuration.propertyExists(getClass(), name)) { 314 throw Context.reportRuntimeError("Property \"" + name + "\" is not writable for " + start + ". " 315 + "Cant set it to: " + newValue); 316 } 317 else { 318 getLog().debug("No configured setter \"" + name + "\" found for " 319 + start + ". Setting it as pure javascript property."); 320 321 super.put(name, start, newValue); 322 } 323 } 324 else { 325 final Class parameterClass = setterMethod.getParameterTypes()[0]; 326 if( parameterClass == String .class) { 327 newValue = Context.toString(newValue); 328 } 329 else if (Integer.TYPE.equals(parameterClass)) { 330 newValue = new Integer ((new Double (Context.toNumber(newValue))).intValue()); 331 } 332 else if (Boolean.TYPE.equals(parameterClass)) { 333 newValue = Boolean.valueOf(Context.toBoolean(newValue)); 334 } 335 try { 336 setterMethod.invoke( 337 simpleScriptable.findMatchingScriptable(start, setterMethod), 338 new Object []{ newValue } ); 339 } 340 catch( final InvocationTargetException e ) { 341 throw new ScriptException(e.getTargetException()); 342 } 343 catch( final Exception e ) { 344 throw new ScriptException(e); 345 } 346 347 } 348 } 349 350 351 359 private Scriptable findMatchingScriptable( final Scriptable start, final Method method ) { 360 final Class declaringClass = method.getDeclaringClass(); 361 Scriptable scriptable = start; 362 while( declaringClass.isInstance(start) == false ) { 363 scriptable = scriptable.getPrototype(); 364 if( scriptable == null ) { 365 throw new IllegalStateException ("Couldn't find a matching scriptable"); 366 } 367 } 368 369 return scriptable; 370 } 371 372 373 377 protected final Log getLog() { 378 return LogFactory.getLog(getClass()); 379 } 380 381 382 386 protected JavaScriptEngine.PageInfo getPageInfo() { 387 if( pageInfo_ == null ) { 388 throw new IllegalStateException ("pageInfo_ has not been initialized!"); 389 } 390 return pageInfo_; 391 } 392 393 394 402 protected SimpleScriptable getScriptableFor(final Object object) { 403 if (object instanceof WebWindow) { 404 return (SimpleScriptable) ((WebWindow) object).getScriptObject(); 405 } 406 407 final DomNode domNode = (DomNode) object; 408 409 final Object scriptObject = domNode.getScriptObject(); 410 if( scriptObject != null ) { 411 return (SimpleScriptable)scriptObject; 412 } 413 else { 414 return makeScriptableFor(domNode); 415 } 416 } 417 418 423 public SimpleScriptable makeScriptableFor(final DomNode domNode) { 424 425 final String javaScriptClassName = (String )getHtmlJavaScriptMapping().get(domNode.getClass()); 426 final SimpleScriptable scriptable; 427 if (javaScriptClassName == null) { 428 scriptable = makeJavaScriptObject("HTMLElement"); 430 getLog().debug("No javascript class found for element <"+domNode.getNodeName()+">. Using HTMLElement"); 431 } 432 else { 433 scriptable = makeJavaScriptObject(javaScriptClassName); 434 } 435 scriptable.setDomNode(domNode); 436 scriptable.setParentScope((Scriptable) domNode.getPage().getEnclosingWindow().getScriptObject()); 438 return scriptable; 439 } 440 441 442 446 protected Transformer getTransformerScriptableFor() { 447 return new Transformer() { 448 public Object transform(final Object obj) { 449 return getScriptableFor(obj); 450 } 451 }; 452 } 453 454 463 public static Object getObjectArg( final int index, final Object [] args, final Object defaultValue ) { 464 if( index >= args.length ) { 465 return defaultValue; 466 } 467 else { 468 return args[index]; 469 } 470 } 471 472 473 482 public static String getStringArg( final int index, final Object [] args, final String defaultValue ) { 483 return Context.toString(getObjectArg(index, args, defaultValue)); 484 } 485 486 487 496 public static boolean getBooleanArg( final int index, final Object [] args, final boolean defaultValue ) { 497 final Boolean defaultBoolean = Boolean.valueOf(defaultValue); 498 499 return Context.toBoolean(getObjectArg(index, args, defaultBoolean)); 500 } 501 502 503 512 public static int getIntArg( final int index, final Object [] args, final int defaultValue ) { 513 return (int) Context.toNumber(getObjectArg(index, args, new Integer (defaultValue))); 514 } 515 516 517 524 public Object getDefaultValue( final Class hint ) { 525 return toString(); 526 } 527 528 533 protected Window getWindow() throws RuntimeException { 534 Scriptable current = this; 535 while (current != null) { 536 if (current instanceof Window) { 537 return (Window) current; 538 } 539 current = current.getParentScope(); 540 } 541 throw new RuntimeException ("Unable to find associated window to " + this); 542 } 543 } 544 545 | Popular Tags |