/*
* 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;
}
}
}