package org.intermine.webservice.server.output;

import java.util.HashMap;
import java.util.Map;

import org.intermine.api.InterMineAPI;
import org.intermine.api.LinkRedirectManager;
import org.intermine.api.results.ResultCell;
import org.intermine.model.InterMineObject;
import org.intermine.objectstore.query.ClobAccess;
import org.intermine.web.logic.PortalHelper;
import org.json.JSONObject;

public class TableCellFormatter
{

    private static final String CELL_KEY_URL = "url";
    private static final String CELL_KEY_VALUE = "value";
    private static final String CELL_KEY_CLASS = "class";
    private static final String CELL_KEY_COLUMN = "column";
    private static final String CELL_KEY_ID = "id";

    private final LinkRedirectManager redirector;
    private final InterMineAPI im;
    
    public TableCellFormatter(InterMineAPI im) {
        this.im = im;
        this.redirector = im.getLinkRedirector();
    }
    
    public Map<String, Object> toMap(ResultCell cell) {
        Map<String, Object> mapping = new HashMap<String, Object>();
        if (cell == null) {
            mapping.put(CELL_KEY_URL, null);
            mapping.put(CELL_KEY_VALUE, null);
            mapping.put(CELL_KEY_COLUMN, null);
        } else {
            String link = null;
            // Try to generate a link using the redirector.
            if (redirector != null && cell.getObject() instanceof InterMineObject) {
                link = redirector.generateLink(im, (InterMineObject) cell.getObject());
            }
            if (link == null) {
                link = PortalHelper.generateReportPath(cell);
            }
            mapping.put(CELL_KEY_URL, link);
            mapping.put(CELL_KEY_CLASS, cell.getType());
            mapping.put(CELL_KEY_ID, cell.getId());
            mapping.put(CELL_KEY_COLUMN, cell.getPath().toStringNoConstraints());
            Object raw = cell.getField();
            
            // Important that CLOBs go in as strings, to prevent infinite recursions
            // by moronic JSON libraries... - place other edge cases here.
            Object cooked = (raw != null && raw instanceof ClobAccess) ? raw.toString() : raw;
            mapping.put(CELL_KEY_VALUE, cooked);
        }
        return mapping;
    }

    /**
     * Get the JSONObject that represents each cell in the results row
     * @param cell The result element with the data
     * @return A JSONObject
     */
    public JSONObject toJSON(ResultCell cell) {
        Map<String, Object> mapping = toMap(cell);
        JSONObject ret = new JSONObject(mapping);
        return ret;
    }
}

@@ -316,16 +316,67 @@
 Access;%0A
+import org.intermine.web.context.InterMineContext;%0A
 import o
@@ -408,24 +408,24 @@
 rtalHelper;%0A
-
 import org.j
@@ -840,24 +840,59 @@
 MineAPI im;%0A
+    private Integer maxCellLength;%0A
     %0A    pub
@@ -951,24 +951,208 @@
 is.im = im;%0A
+        this.maxCellLength = Integer.valueOf(String.valueOf(InterMineContext.getWebProperties().getProperty(%0A                %22webservice.tablecellformatter.cell.length.max%22, %22200%22)));%0A
         this
@@ -1256,32 +1256,38 @@
 cell) %7B%0A        
+final 
 Map%3CString, Obje
@@ -2147,24 +2147,30 @@
             
+final 
 Object raw =
@@ -2195,24 +2195,90 @@
             
+final Object cooked; // After formatting and any transformations.%0A
 %0A           
@@ -2442,90 +2442,443 @@
     
-Object cooked = (raw != null && raw instanceof ClobAccess) ? raw.toString() : raw;
+// Also, don't return too much data...%0A            if (raw != null && raw instanceof CharSequence) %7B%0A                final CharSequence cs = (CharSequence) raw;%0A                if (cs.length() %3C= maxCellLength) %7B%0A                    cooked = cs.toString();%0A                %7D else %7B%0A                    cooked = cs.subSequence(0, maxCellLength) + %22...%22;%0A                %7D%0A            %7D else %7B%0A                cooked = raw;%0A            %7D
 %0A   

Set a max length on any character sequence in a table cell.