import java.io.*;
import java.util.*;
import org.apache.commons.io.IOUtils;
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.node.*;
import com.dnanexus.*;

public class DXTrimReads {
    public static void main(String[] args) throws Exception {
        System.out.println("This is the DNAnexus Java Read Trimmer Example App");
        ObjectMapper mapper = new ObjectMapper();

        String JobInput = IOUtils.toString(new FileInputStream("job_input.json"));
        JsonNode JobInputJson = (JsonNode)(new MappingJsonFactory().createJsonParser(JobInput).readValueAsTree());

        // TODO: check for presence of params instead of NullPointerException
        String gtableId = JobInputJson.get("reads").get("$dnanexus_link").textValue();
        int trimLength = JobInputJson.get("trimLength").intValue();

        System.out.println("Trimming reads in "+gtableId);
        
        JsonNode tableDesc = DXAPI.gtableDescribe(gtableId, mapper.readTree("{}"));

        ObjectNode gtableNewInput = mapper.createObjectNode();
        gtableNewInput.put("initializeFrom", mapper.createObjectNode());
        // ((ObjectNode)(gtableNewInput.get("initializeFrom"))).put("project", tableDesc.get("project"));
        ((ObjectNode)(gtableNewInput.get("initializeFrom"))).put("project", System.getenv("DX_WORKSPACE_ID"));
        ((ObjectNode)(gtableNewInput.get("initializeFrom"))).put("id", tableDesc.get("id"));
        String outputGTableId = DXAPI.gtableNew(gtableNewInput).get("id").textValue();

        int sequenceColumnIndex = -1, qualColumnIndex = -1;
        for (int i=0; i<tableDesc.get("columns").size(); i++) {
            if (tableDesc.get("columns").get(i).get("name").asText().equals("sequence")) {
                sequenceColumnIndex = i+1; // element 0 is the row index
            } else if (tableDesc.get("columns").get(i).get("name").asText().equals("quality")) {
                qualColumnIndex = i+1;
            }
        }
        
        int step = 10000;
        for (int i=0; i<tableDesc.get("length").intValue(); i += step) {
            ObjectNode gtableGetInput = mapper.createObjectNode();
            gtableGetInput.put("starting", i);
            gtableGetInput.put("limit", step);
            ArrayNode outputRows = mapper.createArrayNode();
            for (JsonNode row : DXAPI.gtableGet(gtableId, gtableGetInput).get("data")) {
                ArrayNode arrayRow = (ArrayNode)row;
                arrayRow.remove(0); // First row is the index - don't send it back
                String sequence = arrayRow.get(sequenceColumnIndex).textValue();
                String quality = arrayRow.get(qualColumnIndex).textValue();
                String sequenceSubstr = sequence.substring(0, Math.max(sequence.length()-trimLength, 0));
                String qualitySubstr = quality.substring(0, Math.max(quality.length()-trimLength, 0));
                arrayRow.set(sequenceColumnIndex, TextNode.valueOf(sequenceSubstr));
                arrayRow.set(qualColumnIndex, TextNode.valueOf(qualitySubstr));
                outputRows.add(row);
            }
            ObjectNode gtableAddRowsInput = mapper.createObjectNode();
            gtableAddRowsInput.put("part", i+1);
            gtableAddRowsInput.put("data", outputRows);
            DXAPI.gtableAddRows(outputGTableId, gtableAddRowsInput);
        }
        DXAPI.gtableClose(outputGTableId, mapper.readTree("{}"));

        ObjectNode JobOutput = mapper.createObjectNode();
        JobOutput.put("trimmedReads", mapper.createObjectNode());
        ((ObjectNode)(JobOutput.get("trimmedReads"))).put("$dnanexus_link", outputGTableId);
        mapper.writeValue(new File("job_output.json"), JobOutput);
        System.out.println("Trimming complete!");
    }
}

@@ -931,24 +931,16 @@
 bleId);%0A
-        
 %0A       
@@ -1809,41 +1809,9 @@
  = i
-+1; // element 0 is the row index
+;
 %0A   
@@ -1939,18 +1939,16 @@
 ndex = i
-+1
 ;%0A      
@@ -1965,24 +1965,16 @@
       %7D%0A
-        
 %0A       
@@ -2448,28 +2448,8 @@
     
- arrayRow.remove(0);
  // 
@@ -2491,16 +2491,172 @@
  it back
+. After removing%0A                // it, the remaining columns should correspond to the GTable%0A                // schema.%0A                arrayRow.remove(0);
 %0A       

Fix DXTrimReads.