/* * Copyright 2002-2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.sf.json.groovy; import groovy.lang.Closure; import groovy.lang.GString; import groovy.lang.GroovyObjectSupport; import groovy.lang.MissingMethodException; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Stack; import net.sf.json.JSON; import net.sf.json.JSONArray; import net.sf.json.JSONException; import net.sf.json.JSONObject; import net.sf.json.JSONSerializer; import net.sf.json.JsonConfig; /** * A Groovy builder for JSON values. * * <pre> def books1 = builder.books { book = [title: "The Definitive Guide to Grails", author: "Graeme Rocher"] book = [title: "The Definitive Guide to Grails", author: "Graeme Rocher"] } def books2 = builder.books { book = new Book(title: "The Definitive Guide to Grails", author: "Graeme Rocher") book = new Book(title: "The Definitive Guide to Grails", author: "Graeme Rocher") } def books3 = builder.books { book = { title = "The Definitive Guide to Grails" author= "Graeme Rocher" } book = { title = "The Definitive Guide to Grails" author= "Graeme Rocher" } } def books4 = builder.books { book { title = "The Definitive Guide to Grails" author= "Graeme Rocher" } book { title = "The Definitive Guide to Grails" author= "Graeme Rocher" } } def books5 = builder.books { 2.times { book = { title = "The Definitive Guide to Grails" author= "Graeme Rocher" } } } def books6 = builder.books { 2.times { book { title = "The Definitive Guide to Grails" author= "Graeme Rocher" } } } all 6 books variables output the same JSON {"books": { "book": [{ "title": "The Definitive Guide to Grails", "author": "Graeme Rocher" },{ "title": "The Definitive Guide to Grails", "author": "Graeme Rocher" }] } } </pre> * * @author Andres Almiray <aalmiray@users.sourceforge.net> */ public class JsonGroovyBuilder extends GroovyObjectSupport { private static final String JSON = "json"; private JSON current; private Map properties; private Stack stack; private JsonConfig jsonConfig; public JsonGroovyBuilder() { stack = new Stack(); properties = new HashMap(); jsonConfig = new JsonConfig(); } public JsonConfig getJsonConfig() { return jsonConfig; } public void setJsonConfig(JsonConfig jsonConfig) { this.jsonConfig = jsonConfig; } public Object getProperty( String name ) { if( !stack.isEmpty() ){ Object top = stack.peek(); if( top instanceof JSONObject ){ JSONObject json = (JSONObject) top; if( json.containsKey( name ) ){ return json.get( name ); }else{ return _getProperty( name ); } }else{ return _getProperty( name ); } }else{ return _getProperty( name ); } } public Object invokeMethod( String name, Object arg ) { if( JSON.equals( name ) && stack.isEmpty() ){ return createObject( name, arg ); } Object[] args = (Object[]) arg; if( args.length == 0 ){ throw new MissingMethodException( name, getClass(), args ); } Object value = null; if( args.length > 1 ){ JSONArray array = new JSONArray(); stack.push( array ); for( int i = 0; i < args.length; i++ ){ if( args[i] instanceof Closure ){ append( name, createObject( (Closure) args[i] ) ); }else if( args[i] instanceof Map ){ append( name, createObject( (Map) args[i] ) ); }else if( args[i] instanceof List ){ append( name, createArray( (List) args[i] ) ); }else{ _append( name, args[i], (JSON) stack.peek() ); } } stack.pop(); }else{ if( args[0] instanceof Closure ){ value = createObject( (Closure) args[0] ); }else if( args[0] instanceof Map ){ value = createObject( (Map) args[0] ); }else if( args[0] instanceof List ){ value = createArray( (List) args[0] ); } } if( stack.isEmpty() ){ JSONObject object = new JSONObject(); object.accumulate( name, current ); current = object; }else{ JSON top = (JSON) stack.peek(); if( top instanceof JSONObject ){ append( name, current == null ? value : current ); } } return current; } public void setProperty( String name, Object value ) { if( value instanceof GString ){ value = value.toString(); try{ value = JSONSerializer.toJSON( value ); }catch( JSONException jsone ){ // its a String literal } }else if( value instanceof Closure ){ value = createObject( (Closure) value ); }else if( value instanceof Map ){ value = createObject( (Map) value ); }else if( value instanceof List ){ value = createArray( (List) value ); } append( name, value ); } private Object _getProperty( String name ) { if( properties.containsKey( name ) ){ return properties.get( name ); }else{ return super.getProperty( name ); } } private void append( String key, Object value ) { Object target = null; if( !stack.isEmpty() ){ target = stack.peek(); current = (JSON) target; _append( key, value, current ); }else{ properties.put( key, value ); } } private void _append( String key, Object value, JSON target ) { if( target instanceof JSONObject ){ ((JSONObject) target).accumulate( key, value, jsonConfig ); }else if( target instanceof JSONArray ){ ((JSONArray) target).element( value, jsonConfig ); } } private JSON createArray( List list ) { JSONArray array = new JSONArray(); stack.push( array ); for( Iterator elements = list.iterator(); elements.hasNext(); ){ Object element = elements.next(); if( element instanceof Closure ){ element = createObject( (Closure) element ); }else if( element instanceof Map ){ element = createObject( (Map) element ); }else if( element instanceof List ){ element = createArray( (List) element ); } array.element( element ); } stack.pop(); return array; } private JSON createObject( Closure closure ) { JSONObject object = new JSONObject(); stack.push( object ); closure.setDelegate( this ); closure.setResolveStrategy( Closure.DELEGATE_FIRST ); closure.call(); stack.pop(); return object; } private JSON createObject( Map map ) { JSONObject object = new JSONObject(); stack.push( object ); for( Iterator properties = map.entrySet() .iterator(); properties.hasNext(); ){ Map.Entry property = (Map.Entry) properties.next(); String key = String.valueOf( property.getKey() ); Object value = property.getValue(); if( value instanceof Closure ){ value = createObject( (Closure) value ); }else if( value instanceof Map ){ value = createObject( (Map) value ); }else if( value instanceof List ){ value = createArray( (List) value ); } object.element( key, value ); } stack.pop(); return object; } private JSON createObject( String name, Object arg ) { Object[] args = (Object[]) arg; if( args.length == 0 ){ throw new MissingMethodException( name, getClass(), args ); } if( args.length == 1 ){ if( args[0] instanceof Closure ){ return createObject( (Closure) args[0] ); }else if( args[0] instanceof Map ){ return createObject( (Map) args[0] ); }else if( args[0] instanceof List ){ return createArray( (List) args[0] ); }else{ throw new JSONException( "Unsupported type" ); } }else{ JSONArray array = new JSONArray(); stack.push( array ); for( int i = 0; i < args.length; i++ ){ if( args[i] instanceof Closure ){ append( name, createObject( (Closure) args[i] ) ); }else if( args[i] instanceof Map ){ append( name, createObject( (Map) args[i] ) ); }else if( args[i] instanceof List ){ append( name, createArray( (List) args[i] ) ); }else{ _append( name, args[i], (JSON) stack.peek() ); } } stack.pop(); return array; } } }