cc3c8382f4e4564b4f6381acf87c7c7528d31aa0
angie
  Wed Jan 7 12:10:56 2015 -0800
Added annoFormatTabSetColumnVis so that columns can be selectively included in output.
diff --git src/lib/annoFormatTab.c src/lib/annoFormatTab.c
index a8f1d45..e7dbf7a 100644
--- src/lib/annoFormatTab.c
+++ src/lib/annoFormatTab.c
@@ -1,71 +1,127 @@
 /* annoFormatTab -- collect fields from all inputs and print them out, tab-separated. */
 
 /* Copyright (C) 2013 The Regents of the University of California 
  * See README in this or parent directory for licensing information. */
 
 #include "annoFormatTab.h"
 #include "annoGratorQuery.h"
 #include "dystring.h"
 
 struct annoFormatTab
     {
-    struct annoFormatter formatter;
-    char *fileName;
-    FILE *f;
+    struct annoFormatter formatter;     // External interface
+    char *fileName;                     // Output file name, can be "stdout"
+    FILE *f;                            // Output file handle
+    struct hash *columnVis;             // Hash of columns that have been explicitly selected
+                                        // or deselected by user.
     boolean needHeader;			// TRUE if we should print out the header
     };
 
-static void printHeaderColumns(FILE *f, struct annoStreamer *source, boolean isFirst)
+static void makeFullColumnName(char *fullName, size_t size, char *sourceName, char *colName)
+/* If sourceName is non-full, make fullName sourceName.colName, otherwise just colName. */
+{
+if (sourceName)
+    safef(fullName, size, "%s.%s", sourceName, colName);
+else
+    safecpy(fullName, size, colName);
+}
+
+void annoFormatTabSetColumnVis(struct annoFormatter *vSelf, char *sourceName, char *colName,
+                               boolean enabled)
+/* Explicitly include or exclude column in output.  sourceName must be the same
+ * as the corresponding annoStreamer source's name. */
+{
+struct annoFormatTab *self = (struct annoFormatTab *)vSelf;
+if (! self->columnVis)
+    self->columnVis = hashNew(0);
+char fullName[PATH_LEN];
+makeFullColumnName(fullName, sizeof(fullName), sourceName, colName);
+hashAddInt(self->columnVis, fullName, enabled);
+}
+
+static boolean columnIsIncluded(struct annoFormatTab *self, char *sourceName, char *colName)
+// Return TRUE if column has not been explicitly deselected.
+{
+if (self->columnVis)
+    {
+    char fullName[PATH_LEN];
+    makeFullColumnName(fullName, sizeof(fullName), sourceName, colName);
+    int vis = hashIntValDefault(self->columnVis, fullName, 1);
+    if (vis == 0)
+        return FALSE;
+    }
+return TRUE;
+}
+
+static void printHeaderColumns(struct annoFormatTab *self, struct annoStreamer *source,
+                               boolean isFirst)
 /* Print names of included columns from this source. */
 {
+FILE *f = self->f;
+char *sourceName = source->name;
 if (source->rowType == arWig)
     {
     // Fudge in the row's chrom, start, end as output columns even though they're not in autoSql
     if (isFirst)
 	{
+        if (sourceName)
+            fprintf(f, "#%s.chrom", sourceName);
+        else
             fputs("#chrom", f);
 	isFirst = FALSE;
 	}
+    if (sourceName)
+        fprintf(f, "\t%s.start\t%s.end", sourceName, sourceName);
+    else
         fputs("\tstart\tend", f);
     }
 struct asColumn *col;
 int i;
 for (col = source->asObj->columnList, i = 0;  col != NULL;  col = col->next, i++)
     {
-    if (isFirst && i == 0)
+    if (columnIsIncluded(self, sourceName, col->name))
+        {
+        if (isFirst)
+            {
             fputc('#', f);
+            isFirst = FALSE;
+            }
         else
             fputc('\t', f);
+        if (sourceName)
+            fprintf(f, "%s.%s", sourceName, col->name);
+        else
             fputs(col->name, f);
         }
     }
+}
 
 static void aftInitialize(struct annoFormatter *vSelf, struct annoStreamer *primary,
 			  struct annoStreamer *integrators)
 /* Print header, regardless of whether we get any data after this. */
 {
 struct annoFormatTab *self = (struct annoFormatTab *)vSelf;
 if (self->needHeader)
     {
     char *primaryHeader = primary->getHeader(primary);
     if (isNotEmpty(primaryHeader))
 	fprintf(self->f, "# Header from primary input:\n%s", primaryHeader);
-    printHeaderColumns(self->f, primary, TRUE);
+    printHeaderColumns(self, primary, TRUE);
     struct annoStreamer *grator;
     for (grator = integrators;  grator != NULL;  grator = grator->next)
-	printHeaderColumns(self->f, grator, FALSE);
+	printHeaderColumns(self, grator, FALSE);
     fputc('\n', self->f);
     self->needHeader = FALSE;
     }
 }
 
 static double wigRowAvg(struct annoRow *row)
 /* Return the average value of floats in row->data. */
 {
 float *vector = row->data;
 int len = row->end - row->start;
 double sum = 0.0;
 int i;
 for (i = 0;  i < len;  i++)
     sum += vector[i];
 return sum / (double)len;
@@ -114,59 +170,66 @@
     //#*** config options: avg? more stats? list of values?
     boolean doAvg = FALSE;
     if (doAvg)
 	words = wordsFromWigRowAvg(row);
     else
 	words = wordsFromWigRowVals(row);
     }
 else
     errAbort("annoFormatTab: unrecognized row type %d from source %s",
 	     source->rowType, source->name);
 if (retFreeWhenDone != NULL)
     *retFreeWhenDone = freeWhenDone;
 return words;
 }
 
-static void printColumns(FILE *f, struct annoStreamer *streamer, struct annoRow *row,
-			 boolean isFirst)
+static void printColumns(struct annoFormatTab *self, struct annoStreamer *streamer,
+			 struct annoRow *row, boolean isFirst)
 /* Print columns in streamer's row (if NULL, print the right number of empty fields). */
 {
+FILE *f = self->f;
+char *sourceName = streamer->name;
 boolean freeWhenDone = FALSE;
 char **words = wordsFromRow(row, streamer, &freeWhenDone);
 if (streamer->rowType == arWig)
     {
     // Fudge in the row's chrom, start, end as output columns even though they're not in autoSql
     if (isFirst)
 	{
 	if (row != NULL)
 	    fputs(row->chrom, f);
 	isFirst = FALSE;
 	}
     if (row != NULL)
 	fprintf(f, "\t%u\t%u", row->start, row->end);
     else
 	fputs("\t\t", f);
     }
-int colCount = slCount(streamer->asObj->columnList);
+struct asColumn *col;
 int i;
-for (i = 0;  i < colCount;  i++)
+for (col = streamer->asObj->columnList, i = 0;  col != NULL;  col = col->next, i++)
     {
-    if (!isFirst || i > 0)
+    if (columnIsIncluded(self, sourceName, col->name))
+        {
+        if (isFirst)
+            isFirst = FALSE;
+        else
             fputc('\t', f);
         if (words != NULL)
             fputs((words[i] ? words[i] : ""), f);
         }
+    }
 if (freeWhenDone)
     {
     freeMem(words[0]);
     freeMem(words);
     }
 }
 
 static void aftComment(struct annoFormatter *fSelf, char *content)
 /* Print out a comment line. */
 {
 if (strchr(content, '\n'))
     errAbort("aftComment: no multi-line input");
 struct annoFormatTab *self = (struct annoFormatTab *)fSelf;
 fprintf(self->f, "# %s\n", content);
 }
@@ -178,35 +241,35 @@
 {
 struct annoFormatTab *self = (struct annoFormatTab *)vSelf;
 // Got one row from primary; what's the largest # of rows from any grator?
 int maxRows = 1;
 int iG;
 for (iG = 0;  iG < gratorCount;  iG++)
     {
     int gratorRowCount = slCount(gratorData[iG].rowList);
     if (gratorRowCount > maxRows)
 	maxRows = gratorRowCount;
     }
 // Print out enough rows to make sure that all grator rows are included.
 int iR;
 for (iR = 0;  iR < maxRows;  iR++)
     {
-    printColumns(self->f, primaryData->streamer, primaryData->rowList, TRUE);
+    printColumns(self, primaryData->streamer, primaryData->rowList, TRUE);
     for (iG = 0;  iG < gratorCount;  iG++)
 	{
 	struct annoRow *gratorRow = slElementFromIx(gratorData[iG].rowList, iR);
-	printColumns(self->f, gratorData[iG].streamer, gratorRow, FALSE);
+	printColumns(self, gratorData[iG].streamer, gratorRow, FALSE);
 	}
     fputc('\n', self->f);
     }
 }
 
 static void aftClose(struct annoFormatter **pVSelf)
 /* Close file handle, free self. */
 {
 if (pVSelf == NULL)
     return;
 struct annoFormatTab *self = *(struct annoFormatTab **)pVSelf;
 freeMem(self->fileName);
 carefulClose(&(self->f));
 annoFormatterFree(pVSelf);
 }