package org.intermine.webservice.server.output;

/*
 * Copyright (C) 2002-2011 FlyMine
 *
 * This code may be freely distributed and modified under the
 * terms of the GNU Lesser General Public Licence.  This should
 * be distributed with the code.  See the LICENSE file for more
 * information or http://www.gnu.org/copyleft/lesser.html.
 *
 */

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * @author Alexis Kalderimis
 *
 */
public abstract class JSONResultFormatter extends JSONFormatter
{
    /**
     * The key for the views
     */
    public static final String KEY_VIEWS = "views";
    /**
     * The key for the root class
     */
    public static final String KEY_ROOT_CLASS = "rootClass";
    /**
     * The key for the model name
     */
    public static final String KEY_MODEL_NAME = "modelName";
    /**
     * The key for the execution time
     */
    public static final String KEY_TIME = "executionTime";


    /**
     * Starts the result set object, and sets the attributes given into it
     * @see org.intermine.webservice.server.output.Formatter#formatHeader(java.util.Map)
     * @return the header
     * @param attributes the attributes passed in from the containing output
     */
    @Override
    public String formatHeader(Map<String, Object> attributes) {
        String superResults = super.formatHeader(attributes);
        StringBuilder sb = new StringBuilder(superResults);
        if (attributes != null) {
            for (String key : attributes.keySet()) {
                if (KEY_CALLBACK.equals(key)) { continue; }
                sb.append("\"" + key + "\":");
                // Format lists as arrays
                if (attributes.get(key) instanceof List) {
                    sb.append("[");
                    List<Object> attr = (List<Object>) attributes.get(key);
                    Iterator<Object> it = attr.iterator();
                    while (it.hasNext()) {
                        sb.append(quote(it.next()));
                        if (it.hasNext()) { sb.append(","); }
                    }
                    sb.append("]");
                } else {
                    // Format as attribute
                    sb.append(quote(attributes.get(key)));
                }
                sb.append(",");
            }
        }
        sb.append("\"results\":[");
        return sb.toString();
    }

    private String quote(Object o) {
        if (o == null) {
            return "null";
        }
        String sth = o.toString();
        if (attrNeedsQuotes(sth)) {
            return "\"" + sth + "\"";
        } else {
            return sth;
        }
    }

    private boolean attrNeedsQuotes(String attr) {
        if (attr == null) {
            return false;
        }
        return !attr.startsWith("{") // it is a javascript object
                && !attr.startsWith("[") // it is a javascript array
                && !attr.matches("[-+]?\\d+(\\.\\d+)?"); // it is numeric
    }

    /**
     * Closes the remaining open brackets (the root class array, and the
     * overall result set object), and adds an execution time property.
     * @see org.intermine.webservice.server.output.Formatter#formatFooter()
     * @return The formatted footer string.
     */
    @Override
    public String formatFooter(String errorMessage, int errorCode) {
        StringBuilder sb = new StringBuilder();
        sb.append("],");
        Date now = Calendar.getInstance().getTime();
        DateFormat dateFormatter = new SimpleDateFormat("yyyy.MM.dd HH:mm::ss");
        String executionTime = dateFormatter.format(now);
        sb.append("\"" + JSONResultFormatter.KEY_TIME + "\":\"" + executionTime + "\"");
        sb.append(super.formatFooter(errorMessage, errorCode));
        return sb.toString();
    }
}

@@ -525,16 +525,198 @@
 til.Map;
+%0Aimport org.json.JSONArray;%0Aimport org.json.JSONException;%0Aimport org.apache.commons.lang.StringEscapeUtils;%0Aimport org.intermine.webservice.server.exceptions.InternalErrorException;
 %0A%0A/**%0A *
@@ -2051,24 +2051,16 @@
     
- sb.append(%22%5B%22);
+// try %7B
 %0A   
@@ -2080,41 +2080,46 @@
     
-List%3CObject%3E attr = (List%3CObject%3E
+    JSONArray ja = new JSONArray((List
 ) at
@@ -2135,16 +2135,17 @@
 get(key)
+)
 ;%0A      
@@ -2162,86 +2162,84 @@
     
-Iterator%3CObject%3E it = attr.iterator();%0A                    while (it.hasNext()
+    sb.append(ja.toString());%0A                   // %7D catch (JSONException e
 ) %7B%0A
@@ -2261,103 +2261,92 @@
     
+//
      
-sb.append(quote(it.next()));%0A                        if (it.hasNext()) %7B sb.append(%22,%22); %7D
+throw new InternalErrorException(%22Error handling %22 + attributes.get(key), e);
 %0A   
@@ -2353,32 +2353,34 @@
                 
+//
  %7D%0A             
@@ -2386,44 +2386,8 @@
     
-    sb.append(%22%5D%22);%0A                
 %7D el
@@ -2433,16 +2433,121 @@
 ttribute
+%0A                    String attr = (attributes.get(key) == null) ? null : attributes.get(key).toString();
 %0A       
@@ -2575,34 +2575,55 @@
 d(quote(
-attributes.get(key
+StringEscapeUtils.escapeJavaScript(attr
 )));%0A   

Handle difficult characters better in JSON