56c5a21bd036712b38873f830b46b33351fbb5e4
giardine
  Fri Nov 5 13:21:35 2010 -0700
Added error checking for pgSnp and bedDetail custom track loaders, issue #1595
diff --git src/hg/lib/bedDetail.c src/hg/lib/bedDetail.c
index 48a32a4..23459e1 100644
--- src/hg/lib/bedDetail.c
+++ src/hg/lib/bedDetail.c
@@ -298,15 +298,137 @@
     int sizeOne;
     sqlSignedDynamicArray(row[10], &ret->blockSizes, &sizeOne);
     assert(sizeOne == ret->blockCount);
     }
 if (size > 13 && row[11] != NULL)
     {
     int sizeOne;
     sqlSignedDynamicArray(row[11], &ret->chromStarts, &sizeOne);
     assert(sizeOne == ret->blockCount);
     }
 /* these 2 should not be null, but always last 2 */
 ret->id = cloneString(row[size-2]);
 ret->description = cloneString(row[size-1]);
 return ret;
 }
+
+struct bedDetail *bedDetailLineFileLoad (char **row, int size, struct lineFile *lf)
+/* load from linefile line, with error checking */
+{
+struct bedDetail *item;
+int count; /* block count */
+int wordCount = size - 2; /* bed part of row */
+AllocVar(item);
+item->chrom = cloneString(row[0]);
+item->chromStart = lineFileNeedNum(lf, row, 1);
+item->chromEnd = lineFileNeedNum(lf, row, 2);
+if (item->chromEnd < 1)
+    lineFileAbort(lf, "chromEnd less than 1 (%d)", item->chromEnd);
+if (item->chromEnd < item->chromStart)
+    lineFileAbort(lf, "chromStart after chromEnd (%d > %d)",
+        item->chromStart, item->chromEnd);
+if (wordCount > 3)
+     item->name = cloneString(row[3]);
+if (wordCount > 4)
+     item->score = lineFileNeedNum(lf, row, 4);
+if (wordCount > 5)
+     {
+     strncpy(item->strand, row[5], sizeof(item->strand));
+     if (item->strand[0] != '+' && item->strand[0] != '-' && item->strand[0] != '.')
+          lineFileAbort(lf, "Expecting + or - in strand");
+     }
+if (wordCount > 6)
+     item->thickStart = lineFileNeedNum(lf, row, 6);
+else
+     item->thickStart = item->chromStart;
+if (wordCount > 7)
+     {
+     item->thickEnd = lineFileNeedNum(lf, row, 7);
+     if (item->thickEnd < item->thickStart)
+         lineFileAbort(lf, "thickStart after thickEnd");
+     if ((item->thickStart != 0) &&
+         ((item->thickStart < item->chromStart) ||
+          (item->thickStart > item->chromEnd)))
+         lineFileAbort(lf,
+             "thickStart out of range (chromStart to chromEnd, or 0 if no CDS)");
+     if ((item->thickEnd != 0) &&
+         ((item->thickEnd < item->chromStart) ||
+          (item->thickEnd > item->chromEnd)))
+         lineFileAbort(lf,
+             "thickEnd out of range for %s:%d-%d, thick:%d-%d (chromStart to chromEnd, or 0 if no CDS)",
+                       item->name, item->chromStart, item->chromEnd,
+                       item->thickStart, item->thickEnd);
+     }
+else
+     item->thickEnd = item->chromEnd;
+if (wordCount > 8)
+    {
+    char *comma;
+    /*  Allow comma separated list of rgb values here   */
+    comma = strchr(row[8], ',');
+    if (comma)
+        {
+        int rgb = bedParseRgb(row[8]);
+        if (rgb < 0)
+            lineFileAbort(lf,
+                "Expecting 3 comma separated numbers for r,g,b bed item color.");
+        else
+            item->reserved = rgb;
+        }
+    else
+        item->reserved = lineFileNeedNum(lf, row, 8);
+    }
+if (wordCount > 9)
+    item->blockCount = lineFileNeedNum(lf, row, 9);
+if (wordCount > 10)
+    {
+    sqlSignedDynamicArray(row[10], &item->blockSizes, &count);
+    if (count != item->blockCount)
+        lineFileAbort(lf,  "expecting %d elements in array", item->blockCount);
+    }
+if (wordCount > 11)
+    {
+    int i;
+    int lastEnd, lastStart;
+    sqlSignedDynamicArray(row[11], &item->chromStarts, &count);
+    if (count != item->blockCount)
+        lineFileAbort(lf, "expecting %d elements in array", item->blockCount);
+    // tell the user if they appear to be using absolute starts rather than
+    // relative... easy to forget!  Also check block order, coord ranges...
+    lastStart = -1;
+    lastEnd = 0;
+    for (i=0;  i < item->blockCount;  i++)
+        {
+        if (item->chromStarts[i]+item->chromStart >= item->chromEnd)
+            {
+            if (item->chromStarts[i] >= item->chromStart)
+                lineFileAbort(lf,
+                    "BED chromStarts offsets must be relative to chromStart, "
+                    "not absolute.  Try subtracting chromStart from each offset "
+                    "in chromStarts.");
+            else
+                lineFileAbort(lf,
+                    "BED chromStarts[i]+chromStart must be less than chromEnd.");
+            }
+        lastStart = item->chromStarts[i];
+        lastEnd = item->chromStart + item->chromStarts[i] + item->blockSizes[i];
+        }
+    if (item->chromStarts[0] != 0)
+        lineFileAbort(lf,
+            "BED blocks must span chromStart to chromEnd.  "
+            "BED chromStarts[0] must be 0 (==%d) so that (chromStart + "
+            "chromStarts[0]) equals chromStart.", item->chromStarts[0]);
+    i = item->blockCount-1;
+    if ((item->chromStart + item->chromStarts[i] + item->blockSizes[i]) !=
+        item->chromEnd)
+        {
+        lineFileAbort(lf,
+            "BED blocks must span chromStart to chromEnd.  (chromStart + "
+            "chromStarts[last] + blockSizes[last]) must equal chromEnd.");
+        }
+    }
+    /* these 2 should not be null (maybe empty string), but always last 2 */
+    item->id = cloneString(row[size-2]);
+    item->description = cloneString(row[size-1]);
+return item;
+}
+