001package votorola.g.web.gwt; // Copyright 2011-2012, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE. 002 003import com.google.gwt.core.client.*; 004import java.util.*; 005 006 007/** An extended handle to a native JavaScript object. 008 */ 009public class JavaScriptObjectX extends JavaScriptObject 010{ 011 012 protected JavaScriptObjectX() {} // "precisely one constructor... protected, empty, and no-argument" 013 014 015 016 // /** Returns an empty array that is intended to be immutable. Do not modify it. 017 // */ 018 // public static <T extends JavaScriptObject> JsArray<T> _emptyArray() 019 // { 020 // assert EMPTY_ARRAY.length() == 0; 021 // return EMPTY_ARRAY.cast(); 022 // } 023 // 024 // 025 // private static final JsArray<? extends JavaScriptObject> EMPTY_ARRAY = createArray().cast(); 026 027 028 029 /** Returns the names of all enumerable properties of this object, including any 030 * inherited ones. 031 * 032 * @see #_in() 033 * @see #_propertyIsEnumerable(String) 034 */ 035 public final native JsArrayString _enumerablePropertyNames() 036 /*-{ 037 var array = []; 038 for( var name in this ) array.push( name ); 039 return array; 040 }-*/; 041 042 043 044 /** Answers whether the specified object looks like an instance of a particular 045 * JavaScriptObject subclass defined by the names of its non-inherited, {@linkplain 046 * #_hasOwnProperty(String) own properties}. Returns true if it might be an 047 * instance, false if it definitely is not. This <em>duck typing</em> is required 048 * for instances of JavaScriptObject because they are typeless at runtime, having 049 * only compile-time "overlay typing". 050 * 051 * @see <a href='http://code.google.com/p/google-web-toolkit/wiki/OverlayTypes#Solution' target='_top' 052 * >Overlay typing and instanceof operator</a> 053 */ 054 public static boolean _isDuckType( final JavaScriptObject js, final String... ownPropertyNames ) 055 { 056 assert ownPropertyNames.length > 0; 057 for( final String name: ownPropertyNames ) if( !js._hasOwnProperty( name )) return false; 058 059 return true; 060 } 061 062 063 064 /** Answers whether the specified object looks like an instance of a particular 065 * JavaScriptObject subclass defined by the names of its non-inherited, {@linkplain 066 * #_hasOwnProperty(String) own properties}. Returns true if it might be an 067 * instance, false if it definitely is not. This <em>duck typing</em> is required 068 * for instances of JavaScriptObject because they are typeless at runtime, having 069 * only compile-time "overlay typing". 070 * 071 * @see <a href='http://code.google.com/p/google-web-toolkit/wiki/OverlayTypes#Solution' target='_top' 072 * >Overlay typing and instanceof operator</a> 073 */ 074 public static boolean _isDuckType( final Object o, final String... ownPropertyNames ) 075 { 076 return o instanceof JavaScriptObject? _isDuckType( (JavaScriptObject)o, ownPropertyNames ): 077 false; 078 } 079 080 081 082 // - I t e r a b l e ------------------------------------------------------------------ 083 084 085 /** Constructs an iterable over the enumerable property names of this object. This 086 * allows for iteration over the property names of the underlying JavaScript object 087 * (o), using something like JavaScript's <code>for/in</code> syntax:<pre> 088 * 089 * for( var name in o ) window.alert( name ); // JavaScript</pre> 090 * 091 * <p>The ideal equivalent in Java would probably be a <code>foreach</code> loop over 092 * an iterable JavaScriptObject (jso), similar to this:</p><pre> 093 * 094 * for( String name: jso ) Window.alert( name ); // not possible</pre> 095 * 096 * <p>But that is not possible because a GWT bug prevents JavaScriptObject from 097 * implementing Iterable. Hence this method, which allows for iteration over a 098 * JavaScriptObjectX (jsox) like this:</p><pre> 099 * 100 * for( String name: jsox._in() ) Window.alert( name );</pre> 101 * 102 * @see #_enumerablePropertyNames() 103 * @see <a href='http://code.google.com/p/google-web-toolkit/issues/detail?id=4864' 104 * target='_top'>JavaScriptObject with Iterable<T> breaks Development Mode</a> 105 */ 106 // public final Iterator<String> iterator() 107 // { 108 // return new IteratorA<String>() 109 public final Iterable<String> _in() 110 { 111 return new Iterable<String>() // OPT could cache this as property, but seems heavy handed 112 { 113 public Iterator<String> iterator() 114 { 115 return new votorola.g.util.IteratorA<String>() 116 { 117 private int n; 118 119 private final JsArrayString names = _enumerablePropertyNames(); 120 121 public boolean hasNext() { return n < names.length(); } 122 123 public String next() 124 { 125 if( n >= names.length() ) throw new NoSuchElementException(); 126 127 return names.get( n++ ); 128 } 129 }; 130 } 131 }; 132 } 133 134 135}