001package com.thetransactioncompany.jsonrpc2;
002
003
004import java.util.List;
005import java.util.Map;
006
007import net.minidev.json.JSONObject;
008
009
010/** 
011 * Represents a JSON-RPC 2.0 notification. 
012 *
013 * <p>Notifications provide a mean for calling a remote procedure without 
014 * generating a response. Note that notifications are inherently unreliable 
015 * as no confirmation is sent back to the caller.
016 *
017 * <p>Notifications have the same JSON structure as requests, except that they
018 * lack an identifier:
019 * <ul>
020 *     <li>{@code method} The name of the remote method to call.
021 *     <li>{@code params} The required method parameters (if any), which can 
022 *         be packed into a JSON array or object.
023 *     <li>{@code jsonrpc} A string indicating the JSON-RPC protocol version 
024 *         set to "2.0".
025 * </ul>
026 *
027 * <p>Here is a sample JSON-RPC 2.0 notification string:
028 *
029 * <pre>
030 * {  
031 *    "method"  : "progressNotify",
032 *    "params"  : ["75%"],
033 *    "jsonrpc" : "2.0"
034 * }
035 * </pre>
036 *
037 * <p>This class provides two methods to obtain a request object:
038 * <ul>
039 *     <li>Pass a JSON-RPC 2.0 notification string to the static 
040 *         {@link #parse} method, or 
041 *     <li>Invoke one of the constructors with the appropriate arguments.
042 * </ul>
043 *
044 * <p>Example 1: Parsing a notification string:
045 *
046 * <pre>
047 * String jsonString = "{\"method\":\"progressNotify\",\"params\":[\"75%\"],\"jsonrpc\":\"2.0\"}";
048 * 
049 * JSONRPC2Notification notification = null;
050 * 
051 * try {
052 *         notification = JSONRPC2Notification.parse(jsonString);
053 *
054 * } catch (JSONRPC2ParseException e) {
055 *         // handle exception
056 * }
057 * </pre>
058 *
059 * <p>Example 2: Recreating the above request:
060 * 
061 * <pre>
062 * String method = "progressNotify";
063 * List&lt;Object&gt; params = new Vector&lt;Object&gt;();
064 * params.add("75%");
065 *
066 * JSONRPC2Notification notification = new JSONRPC2Notification(method, params);
067 *
068 * System.out.println(notification);
069 * </pre>
070 *
071 * <p id="map">The mapping between JSON and Java entities (as defined by the 
072 * underlying JSON Smart library): 
073 *
074 * <pre>
075 *     true|false  <--->  java.lang.Boolean
076 *     number      <--->  java.lang.Number
077 *     string      <--->  java.lang.String
078 *     array       <--->  java.util.List
079 *     object      <--->  java.util.Map
080 *     null        <--->  null
081 * </pre>
082 *
083 * <p>The JSON-RPC 2.0 specification and user group forum can be found 
084 * <a href="http://groups.google.com/group/json-rpc">here</a>.
085 * 
086 * @author Vladimir Dzhuvinov
087 */
088public class JSONRPC2Notification extends JSONRPC2Message {
089
090
091        /** 
092         * The requested method name. 
093         */
094        private String method;
095
096        
097        /**
098         * The positional parameters, {@code null} if none.
099         */
100        private List<Object> positionalParams;
101
102
103        /**
104         * The named parameters, {@code null} if none.
105         */
106        private Map<String,Object> namedParams;
107        
108        
109        /** 
110         * Parses a JSON-RPC 2.0 notification string. This method is 
111         * thread-safe.
112         *
113         * @param jsonString The JSON-RPC 2.0 notification string, UTF-8 
114         *                   encoded. Must not be {@code null}.
115         *
116         * @return The corresponding JSON-RPC 2.0 notification object.
117         *
118         * @throws JSONRPC2ParseException With detailed message if parsing 
119         *                                failed.
120         */
121        public static JSONRPC2Notification parse(final String jsonString)
122                throws JSONRPC2ParseException {
123        
124                return parse(jsonString, false, false, false);
125        }
126        
127        
128        /** 
129         * Parses a JSON-RPC 2.0 notification string. This method is 
130         * thread-safe.
131         *
132         * @param jsonString    The JSON-RPC 2.0 notification string, UTF-8 
133         *                      encoded. Must not be {@code null}.
134         * @param preserveOrder {@code true} to preserve the order of JSON 
135         *                      object members in parameters.
136         *
137         * @return The corresponding JSON-RPC 2.0 notification object.
138         *
139         * @throws JSONRPC2ParseException With detailed message if parsing 
140         *                                failed.
141         */
142        public static JSONRPC2Notification parse(final String jsonString, 
143                                                 final boolean preserveOrder)
144                throws JSONRPC2ParseException {
145                
146                return parse(jsonString, preserveOrder, false, false);
147        }
148        
149        
150        /** 
151         * Parses a JSON-RPC 2.0 notification string. This method is 
152         * thread-safe.
153         *
154         * @param jsonString    The JSON-RPC 2.0 notification string, UTF-8 
155         *                      encoded. Must not be {@code null}.
156         * @param preserveOrder {@code true} to preserve the order of JSON 
157         *                      object members in parameters.
158         * @param ignoreVersion {@code true} to skip a check of the 
159         *                      {@code "jsonrpc":"2.0"} version attribute in the 
160         *                      JSON-RPC 2.0 message.
161         *
162         * @return The corresponding JSON-RPC 2.0 notification object.
163         *
164         * @throws JSONRPC2ParseException With detailed message if parsing 
165         *                                failed.
166         */
167        public static JSONRPC2Notification parse(final String jsonString, 
168                                                 final boolean preserveOrder, 
169                                                 final boolean ignoreVersion)
170                throws JSONRPC2ParseException {
171                
172                return parse(jsonString, preserveOrder, ignoreVersion, false);
173        }
174        
175        
176        /** 
177         * Parses a JSON-RPC 2.0 notification string. This method is 
178         * thread-safe.
179         *
180         * @param jsonString            The JSON-RPC 2.0 notification string, 
181         *                              UTF-8 encoded. Must not be {@code null}.
182         * @param preserveOrder         {@code true} to preserve the order of
183         *                              JSON object members in parameters.
184         * @param ignoreVersion         {@code true} to skip a check of the 
185         *                              {@code "jsonrpc":"2.0"} version 
186         *                              attribute in the JSON-RPC 2.0 message.
187         * @param parseNonStdAttributes {@code true} to parse non-standard
188         *                              attributes found in the JSON-RPC 2.0 
189         *                              message.
190         *
191         * @return The corresponding JSON-RPC 2.0 notification object.
192         *
193         * @throws JSONRPC2ParseException With detailed message if parsing 
194         *                                failed.
195         */
196        public static JSONRPC2Notification parse(final String jsonString, 
197                                                 final boolean preserveOrder, 
198                                                 final boolean ignoreVersion, 
199                                                 final boolean parseNonStdAttributes)
200                throws JSONRPC2ParseException {
201                
202                JSONRPC2Parser parser = new JSONRPC2Parser(preserveOrder, ignoreVersion, parseNonStdAttributes);
203                
204                return parser.parseJSONRPC2Notification(jsonString);
205        }
206        
207        
208        /** 
209         * Constructs a new JSON-RPC 2.0 notification with no parameters.
210         *
211         * @param method The name of the requested method. Must not be 
212         *               {@code null}.
213         */
214        public JSONRPC2Notification(final String method) {
215                
216                setMethod(method);
217                setParams(null);
218        }
219
220
221        /** 
222         * Constructs a new JSON-RPC 2.0 notification with positional (JSON 
223         * array) parameters.
224         *
225         * @param method           The name of the requested method. Must not 
226         *                         be {@code null}.
227         * @param positionalParams The positional (JSON array) parameters, 
228         *                         {@code null} if none.
229         */
230        public JSONRPC2Notification(final String method, 
231                                    final List<Object> positionalParams) {
232                
233                setMethod(method);
234                setPositionalParams(positionalParams);
235        }
236                
237        
238        /** 
239         * Constructs a new JSON-RPC 2.0 notification with named (JSON object)
240         * parameters.
241         *
242         * @param method      The name of the requested method.
243         * @param namedParams The named (JSON object) parameters, {@code null} 
244         *                    if none.
245         */
246        public JSONRPC2Notification(final String method, 
247                                    final Map <String,Object> namedParams) {
248                
249                setMethod(method);
250                setNamedParams(namedParams);
251        }
252        
253        
254        /** 
255         * Gets the name of the requested method.
256         *
257         * @return The method name.
258         */
259        public String getMethod() {
260                
261                return method;
262        }
263        
264        
265        /**
266         * Sets the name of the requested method.
267         *
268         * @param method The method name. Must not be {@code null}.
269         */
270        public void setMethod(final String method) {
271                
272                // The method name is mandatory
273                if (method == null)
274                        throw new IllegalArgumentException("The method name must not be null");
275
276                this.method = method;
277        }
278        
279        
280        /** 
281         * Gets the parameters type ({@link JSONRPC2ParamsType#ARRAY positional}, 
282         * {@link JSONRPC2ParamsType#OBJECT named} or 
283         * {@link JSONRPC2ParamsType#NO_PARAMS none}).
284         *
285         * @return The parameters type.
286         */
287        public JSONRPC2ParamsType getParamsType() {
288        
289                if (positionalParams == null && namedParams == null)
290                        return JSONRPC2ParamsType.NO_PARAMS;
291
292                if (positionalParams != null)
293                        return JSONRPC2ParamsType.ARRAY;
294
295                if (namedParams != null)
296                        return JSONRPC2ParamsType.OBJECT;
297
298                else
299                        return JSONRPC2ParamsType.NO_PARAMS;
300        }
301        
302        /** 
303         * Gets the notification parameters.
304         *
305         * <p>This method was deprecated in version 1.30. Use
306         * {@link #getPositionalParams} or {@link #getNamedParams} instead.
307         *
308         * @return The parameters as {@code List&lt;Object&gt;} for positional
309         *         (JSON array), {@code Map&lt;String,Object&gt;} for named
310         *         (JSON object), or {@code null} if none.
311         */
312        @Deprecated
313        public Object getParams() {
314                
315                switch (getParamsType()) {
316
317                        case ARRAY:
318                                return positionalParams;
319
320                        case OBJECT:
321                                return namedParams;
322
323                        default:
324                                return null;
325                }
326        }
327
328
329        /**
330         * Gets the positional (JSON array) parameters.
331         *
332         * @since 1.30
333         *
334         * @return The positional (JSON array) parameters, {@code null} if none
335         *         or named.
336         */
337        public List<Object> getPositionalParams() {
338
339                return positionalParams;
340        }
341
342
343        /**
344         * Gets the named parameters.
345         *
346         * @since 1.30
347         *
348         * @return The named (JSON object) parameters, {@code null} if none or 
349         *         positional.
350         */
351        public Map<String,Object> getNamedParams() {
352
353                return namedParams;
354        }
355        
356        
357        /**
358         * Sets the notification parameters.
359         *
360         * <p>This method was deprecated in version 1.30. Use
361         * {@link #setPositionalParams} or {@link #setNamedParams} instead.
362         *
363         * @param params The parameters. For positional (JSON array) pass a 
364         *               {@code List&lt;Object&gt;}. For named (JSON object)
365         *               pass a {@code Map&lt;String,Object&gt;}. If there are
366         *               no parameters pass {@code null}.
367         */
368        @Deprecated
369        @SuppressWarnings("unchecked")
370        public void setParams(final Object params) {
371        
372                if (params == null) {
373                        positionalParams = null;
374                        namedParams = null;
375                } else if (params instanceof List) {
376                        positionalParams = (List<Object>) params;
377                } else if (params instanceof Map) {
378                        namedParams = (Map<String, Object>) params;
379                } else {
380                        throw new IllegalArgumentException("The notification parameters must be of type List, Map or null");
381                }
382        }
383
384
385        /**
386         * Sets the positional (JSON array) request parameters.
387         *
388         * @since 1.30
389         *
390         * @param positionalParams The positional (JSON array) request 
391         *                         parameters, {@code null} if none.
392         */
393        public void setPositionalParams(final List<Object> positionalParams) {
394
395                if (positionalParams == null)
396                        return;
397
398                this.positionalParams = positionalParams;
399        }
400
401
402        /**
403         * Sets the named (JSON object) request parameters.
404         *
405         * @since 1.30
406         *
407         * @param namedParams The named (JSON object) request parameters,
408         *                    {@code null} if none.
409         */
410        public void setNamedParams(final Map<String,Object> namedParams) {
411
412                if (namedParams == null)
413                        return;
414
415                this.namedParams = namedParams;
416        }
417        
418        
419        @Override
420        public JSONObject toJSONObject() {
421        
422                JSONObject notf = new JSONObject();
423                
424                notf.put("method", method);
425                
426                // The params can be omitted if none
427                switch (getParamsType()) {
428
429                        case ARRAY:
430                                notf.put("params", positionalParams);
431                                break;
432
433                        case OBJECT:
434                                notf.put("params", namedParams);
435                                break;
436                }
437                
438                notf.put("jsonrpc", "2.0");
439                
440                
441                Map <String,Object> nonStdAttributes = getNonStdAttributes();
442                
443                if (nonStdAttributes != null) {
444                
445                        for (final Map.Entry<String,Object> attr: nonStdAttributes.entrySet())
446                                notf.put(attr.getKey(), attr.getValue());
447                }
448                
449                return notf;
450        }
451}