001 
002 /*
003  *  JScripter Standard 1.0 - To Script In Java
004  *  Copyright (C) 2008-2011  J.J.Liu<jianjunliu@126.com> <http://www.jscripter.org>
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package jsx.http.rpc;
021 
022 import js.ArrayLike;
023 import js.Disposable;
024 import js.Id;
025 import js.Index;
026 import js.Initializer;
027 import js.Js;
028 import js.ObjectLike;
029 import jsx.Configurable;
030 import jsx.client.Global;
031 import jsx.core.ArrayLikes;
032 import jsx.core.ObjectLikes;
033 
034 /**
035  * <p>An abstract base class for HTTP remote objects.</p>
036  * 
037  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
038  */
039 public abstract class HttpRemote extends Disposable
040 {
041     private final ObjectLike ini;
042 
043     /**
044      * <p>The typical constructor which simply invokes the typical constructor of the 
045      * superclass passing the initializing object as argument.</p>
046      * @param o An object of {@link HttpRemote} or {@link ObjectLike}. 
047      * @since 1.0
048      */
049     protected HttpRemote(Object o) {
050         this.ini = o instanceof HttpRemote ? ((HttpRemote)o).ini : (ObjectLike)o;
051     }
052 
053     /**
054      * <p>Calls a remote method with the current instance.</p>
055      * <p>Subclasses should provide a concrete implementation.</p>
056      * @param url A HTTP resource locator of the remote method. 
057      * @param args An array of arguments for the invocation. 
058      * @return The value returned from the invocation. The value can only be a number, 
059      * string or {@link ObjectLike}. 
060      * @since 1.0
061      */
062     protected abstract Object call(String url, ArrayLike<?> args);
063 
064     /**
065      * <p>Gets the value of the number field of an HTTP remote object.</p>
066      * @param base A HTTP remote object to get the number field from. 
067      * @param idx The index specifying the number field. 
068      * @return The number value of the desired field. 
069      * @since 1.0
070      */
071     public static final Number number(HttpRemote base, Index<Number> idx) {
072         return base.ini.var(idx);
073     }
074 
075     /**
076      * <p>Sets value of the number field of an HTTP remote object.</p>
077      * @param base A HTTP remote object to set number field to. 
078      * @param idx The index specifying the number field. 
079      * @param val The new number value to set. 
080      * @return The new value of the number field. 
081      * @since 1.0
082      */
083     public static final Number number(HttpRemote base, Index<Number> idx, Number val) {
084         return base.ini.var(idx, val);
085     }
086 
087     /**
088      * <p>Gets the value of the string field of an HTTP remote object.</p>
089      * @param base A HTTP remote object to get the string field from. 
090      * @param idx The index specifying the string field. 
091      * @return The string value of the desired field. 
092      * @since 1.0
093      */
094     public static final String string(HttpRemote base, Index<String> idx) {
095         return base.ini.var(idx);
096     }
097 
098     /**
099      * <p>Sets value of the string field of an HTTP remote object.</p>
100      * @param base A HTTP remote object to set string field to. 
101      * @param idx The index specifying the string field. 
102      * @param val The new string value to set. 
103      * @return The new value of the string field. 
104      * @since 1.0
105      */
106     public static final String string(HttpRemote base, Index<String> idx, String val) {
107         return base.ini.var(idx, val);
108     }
109 
110     /**
111      * <p>Sets an object field of an HTTP remote object.</p>
112      * @param base A HTTP remote object to set field to. 
113      * @param idx The index specifying the object field. 
114      * @param o The new value to set. 
115      * @return The new value of the object field. 
116      * @since 1.0
117      */
118     public static final <T extends HttpRemote> T set(HttpRemote base, Index<T> idx, T o) {
119         return base.ini.var(idx, o);
120     }
121 
122     /**
123      * <p>Gets the value of the object field of the current HTTP remote object.</p>
124      * @param idx The index specifying the object field. 
125      * @return The value of the specified field. 
126      * @since 1.0
127      */
128     protected final Object get(Index<? extends HttpRemote> idx) {
129         return ini.var(new Id<Object>(idx));
130     }
131 
132     /**
133      * <p>An abstract base class for an HTTP serialization context.</p>
134      * 
135      * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.jscripter.org" target="_blank">http://www.jscripter.org</a>
136      */
137     public static abstract class Context extends Configurable
138     {
139         /**
140          * <p>Unwraps an object.</p>
141          * @param o An object of {@link HttpRemote} or {@link ObjectLike}. 
142          * @return An object of {@link ObjectLike}. 
143          * @since 1.0
144          */
145         protected static final ObjectLike unwrap(Object o) {
146             return o instanceof HttpRemote ? ((HttpRemote)o).ini : (ObjectLike)o;
147         }
148 
149         /**
150          * <p>The prefix for serialization IDs.</p>
151          * @since 1.0
152          */
153         protected final static String SID = "sid";
154         private int sids = 0;
155 
156         /**
157          * <p>The default constructor.</p>
158          * @since 1.0
159          */
160         protected Context() {
161             super(new Initializer().var());
162         }
163 
164         /**
165          * <p>Serializes the total number of the serialization IDs for the current context.</p>
166          * @return The total number of the serialization IDs for the current context. 
167          * @since 1.0
168          */
169         protected final String sids() {
170             return Js.toString(sids);
171         }
172 
173         /**
174          * <p>Serializes an object to the specified depth into the current context.</p>
175          * @param o A number, string, {@link ObjectLike} or {@link HttpRemote}. 
176          * @return A serialization of the specified object. 
177          * @since 1.0
178          */
179         protected abstract String serialize(Object o, int depth);
180 
181         /**
182          * <p>Serializes the current context.</p>
183          * @return A serialization of the current context. 
184          * @since 1.0
185          */
186         protected final String serialize() {
187             return serialize(ini(this), 2);
188         }
189 
190         /**
191          * <p>Creates a new serialization ID for the current context.</p>
192          * @return The newly created serialization ID. 
193          * @since 1.0
194          */
195         private final String sid() {
196             String sid = Js.add(SID, sids++);
197             if (Js.not(ini(this).var(sid))) {
198                 return sid;
199             }
200             return sid();
201         }
202 
203         /**
204          * <p>Determines the type of an object.</p>
205          * @param o A number, string or {@link ObjectLike}. 
206          * @return The type index of the object. 1 for a number, 2 for a string and 
207          * 0 for an object. 
208          * @since 1.0
209          */
210         protected static final int type(Object o) {
211             return o instanceof Number ? 1 : o instanceof String ? 2 : 0;
212         }
213 
214         /**
215          * <p>Gets the serialization ID of the specified object in the current context.</p>
216          * @param o An object. 
217          * @return The serialization ID of the specified object in the current context. 
218          * @since 1.0
219          */
220         protected final String sid(ObjectLike o) {
221             String sid = (String)o.var(SID);
222             if (Js.not(sid)) {
223                 sid = sid();
224                 o.var(SID, sid);
225             }
226             if (Js.not(ini(this).var(sid))) {
227                 ini(this).var(sid, o);
228                 ArrayLike<String> ids = Js.keys(o);
229                 for (int i = 0, len = ArrayLikes.length(ids); i < len; i++) {
230                     Object v = o.var(ids.get(i));
231                     if (Js.not(type(v))) {
232                         sid(unwrap(o));
233                     }
234                 }
235             }
236             return sid;
237         }
238 
239         /**
240          * <p>Expands the current context to have the specified total number of 
241          * serialization IDs.</p>
242          * @param ids The total number of serialization IDs. 
243          * @since 1.0
244          */
245         protected final void expand(String ids) {
246             int n = Global.parseInt(ids);
247             while (n > sids) {
248                 object(sid());
249             }
250         }
251 
252         /**
253          * <p>Releases the current context.</p>
254          * @since 1.0
255          */
256         protected final void release() {
257             ArrayLike<String> sids = Js.keys(ini(this));
258             for (int i = 0, len = ArrayLikes.length(sids); i < len; i++) {
259                 String sid = sids.get(i);
260                 ObjectLikes.delete((ObjectLike)ini(this).var(sid), SID);
261             }
262             this.sids = 0;
263         }
264 
265         /**
266          * <p>Gets an object with its serialization ID in the current context.</p>
267          * @param sid A serialization ID in the current context. 
268          * @return The object with the serialization ID in the current context. 
269          * @since 1.0
270          */
271         protected final ObjectLike object(String sid) {
272             ObjectLike o = (ObjectLike)ini(this).var(sid);
273             if (Js.not(o)) {
274                 o = new Initializer().var();
275                 o.var(SID, sid);
276                 ini(this).var(sid, o);
277             }
278             return o;
279         }
280     }
281 }