View Javadoc

1   /*
2    * Copyright 2002-2009 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package net.sf.json.groovy;
18  
19  import groovy.lang.Closure;
20  import groovy.lang.GString;
21  import groovy.lang.GroovyObjectSupport;
22  import groovy.lang.MissingMethodException;
23  
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Stack;
29  
30  import net.sf.json.JSON;
31  import net.sf.json.JSONArray;
32  import net.sf.json.JSONException;
33  import net.sf.json.JSONObject;
34  import net.sf.json.JSONSerializer;
35  
36  /**
37   * A Groovy builder for JSON values.
38   *
39   * <pre>
40  def books1 = builder.books {
41     book = [title: "The Definitive Guide to Grails", author: "Graeme Rocher"]
42     book = [title: "The Definitive Guide to Grails", author: "Graeme Rocher"]
43  }
44  
45  def books2 = builder.books {
46     book = new Book(title: "The Definitive Guide to Grails",
47                     author: "Graeme Rocher")
48     book = new Book(title: "The Definitive Guide to Grails",
49                     author: "Graeme Rocher")
50  }
51  
52  def books3 = builder.books {
53     book = {
54        title = "The Definitive Guide to Grails"
55        author= "Graeme Rocher"
56     }
57     book = {
58        title = "The Definitive Guide to Grails"
59        author= "Graeme Rocher"
60     }
61  }
62  
63  def books4 = builder.books {
64     book {
65        title = "The Definitive Guide to Grails"
66        author= "Graeme Rocher"
67     }
68     book {
69        title = "The Definitive Guide to Grails"
70        author= "Graeme Rocher"
71     }
72  }
73  
74  def books5 = builder.books {
75     2.times {
76        book = {
77           title = "The Definitive Guide to Grails"
78           author= "Graeme Rocher"
79        }
80     }
81  }
82  
83  def books6 = builder.books {
84     2.times {
85        book {
86           title = "The Definitive Guide to Grails"
87           author= "Graeme Rocher"
88        }
89     }
90  }
91  
92  
93   all 6 books variables output the same JSON
94  
95  {"books": {
96     "book": [{
97           "title": "The Definitive Guide to Grails",
98           "author": "Graeme Rocher"
99        },{
100          "title": "The Definitive Guide to Grails",
101          "author": "Graeme Rocher"
102       }]
103    }
104 }
105 </pre>
106  *
107  * @author Andres Almiray <aalmiray@users.sourceforge.net>
108  */
109 public class JsonGroovyBuilder extends GroovyObjectSupport {
110    private static final String JSON = "json";
111    private JSON current;
112    private Map properties;
113    private Stack stack;
114 
115    public JsonGroovyBuilder() {
116       stack = new Stack();
117       properties = new HashMap();
118    }
119 
120    public Object getProperty( String name ) {
121       if( !stack.isEmpty() ){
122          Object top = stack.peek();
123          if( top instanceof JSONObject ){
124             JSONObject json = (JSONObject) top;
125             if( json.containsKey( name ) ){
126                return json.get( name );
127             }else{
128                return _getProperty( name );
129             }
130          }else{
131             return _getProperty( name );
132          }
133       }else{
134          return _getProperty( name );
135       }
136    }
137 
138    public Object invokeMethod( String name, Object arg ) {
139       if( JSON.equals( name ) && stack.isEmpty() ){
140          return createObject( name, arg );
141       }
142 
143       Object[] args = (Object[]) arg;
144       if( args.length == 0 ){
145          throw new MissingMethodException( name, getClass(), args );
146       }
147 
148       Object value = null;
149       if( args.length > 1 ){
150          JSONArray array = new JSONArray();
151          stack.push( array );
152          for( int i = 0; i < args.length; i++ ){
153             if( args[i] instanceof Closure ){
154                append( name, createObject( (Closure) args[i] ) );
155             }else if( args[i] instanceof Map ){
156                append( name, createObject( (Map) args[i] ) );
157             }else if( args[i] instanceof List ){
158                append( name, createArray( (List) args[i] ) );
159             }else{
160                _append( name, args[i], (JSON) stack.peek() );
161             }
162          }
163          stack.pop();
164       }else{
165          if( args[0] instanceof Closure ){
166             value = createObject( (Closure) args[0] );
167          }else if( args[0] instanceof Map ){
168             value = createObject( (Map) args[0] );
169          }else if( args[0] instanceof List ){
170             value = createArray( (List) args[0] );
171          }
172       }
173 
174       if( stack.isEmpty() ){
175          JSONObject object = new JSONObject();
176          object.accumulate( name, current );
177          current = object;
178       }else{
179          JSON top = (JSON) stack.peek();
180          if( top instanceof JSONObject ){
181             append( name, current == null ? value : current );
182          }
183       }
184 
185       return current;
186    }
187 
188    public void setProperty( String name, Object value ) {
189       if( value instanceof GString ){
190          value = value.toString();
191          try{
192             value = JSONSerializer.toJSON( value );
193          }catch( JSONException jsone ){
194             // its a String literal
195          }
196       }else if( value instanceof Closure ){
197          value = createObject( (Closure) value );
198       }else if( value instanceof Map ){
199          value = createObject( (Map) value );
200       }else if( value instanceof List ){
201          value = createArray( (List) value );
202       }
203 
204       append( name, value );
205    }
206 
207    private Object _getProperty( String name ) {
208       if( properties.containsKey( name ) ){
209          return properties.get( name );
210       }else{
211          return super.getProperty( name );
212       }
213    }
214 
215    private void append( String key, Object value ) {
216       Object target = null;
217       if( !stack.isEmpty() ){
218          target = stack.peek();
219          current = (JSON) target;
220          _append( key, value, current );
221       }else{
222          properties.put( key, value );
223       }
224    }
225 
226    private void _append( String key, Object value, JSON target ) {
227       if( target instanceof JSONObject ){
228          ((JSONObject) target).accumulate( key, value );
229       }else if( target instanceof JSONArray ){
230          ((JSONArray) target).element( value );
231       }
232    }
233 
234    private JSON createArray( List list ) {
235       JSONArray array = new JSONArray();
236       stack.push( array );
237       for( Iterator elements = list.iterator(); elements.hasNext(); ){
238          Object element = elements.next();
239          if( element instanceof Closure ){
240             element = createObject( (Closure) element );
241          }else if( element instanceof Map ){
242             element = createObject( (Map) element );
243          }else if( element instanceof List ){
244             element = createArray( (List) element );
245          }
246          array.element( element );
247       }
248       stack.pop();
249       return array;
250    }
251 
252    private JSON createObject( Closure closure ) {
253       JSONObject object = new JSONObject();
254       stack.push( object );
255       closure.setDelegate( this );
256       closure.call();
257       stack.pop();
258       return object;
259    }
260 
261    private JSON createObject( Map map ) {
262       JSONObject object = new JSONObject();
263       stack.push( object );
264       for( Iterator properties = map.entrySet()
265             .iterator(); properties.hasNext(); ){
266          Map.Entry property = (Map.Entry) properties.next();
267          String key = String.valueOf( property.getKey() );
268          Object value = property.getValue();
269          if( value instanceof Closure ){
270             value = createObject( (Closure) value );
271          }else if( value instanceof Map ){
272             value = createObject( (Map) value );
273          }else if( value instanceof List ){
274             value = createArray( (List) value );
275          }
276          object.element( key, value );
277       }
278       stack.pop();
279       return object;
280    }
281 
282    private JSON createObject( String name, Object arg ) {
283       Object[] args = (Object[]) arg;
284       if( args.length == 0 ){
285          throw new MissingMethodException( name, getClass(), args );
286       }
287 
288       if( args.length == 1 ){
289          if( args[0] instanceof Closure ){
290             return createObject( (Closure) args[0] );
291          }else if( args[0] instanceof Map ){
292             return createObject( (Map) args[0] );
293          }else if( args[0] instanceof List ){
294             return createArray( (List) args[0] );
295          }else{
296             throw new JSONException( "Unsupported type" );
297          }
298       }else{
299          JSONArray array = new JSONArray();
300          stack.push( array );
301          for( int i = 0; i < args.length; i++ ){
302             if( args[i] instanceof Closure ){
303                append( name, createObject( (Closure) args[i] ) );
304             }else if( args[i] instanceof Map ){
305                append( name, createObject( (Map) args[i] ) );
306             }else if( args[i] instanceof List ){
307                append( name, createArray( (List) args[i] ) );
308             }else{
309                _append( name, args[i], (JSON) stack.peek() );
310             }
311          }
312          stack.pop();
313          return array;
314       }
315    }
316 }