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&lt;T&gt; 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}