2b5066cd5893d4e971fafb0d8338eba897a161c9
kent
  Mon Apr 1 22:05:31 2013 -0700
Standalone meta hoister seems to work.
diff --git src/meta/metaHoist/metaHoist.c src/meta/metaHoist/metaHoist.c
new file mode 100644
index 0000000..aa5ec02
--- /dev/null
+++ src/meta/metaHoist/metaHoist.c
@@ -0,0 +1,170 @@
+/* metaHoist - Move tags that are shared by all siblings up a level.. */
+#include "common.h"
+#include "linefile.h"
+#include "hash.h"
+#include "options.h"
+#include "meta.h"
+
+boolean trimEmpty = FALSE;
+boolean withParent = FALSE;
+int indent = 3;
+char *keyTag="meta";
+char *heavy = NULL;
+char *parentTag = "parent";
+
+void usage()
+/* Explain usage and exit. */
+{
+errAbort(
+  "metaHoist - Move tags that are shared by all siblings up a level.\n"
+  "usage:\n"
+  "   metaHoist inFile outFile\n"
+  "options:\n"
+  "   -trimEmpty - If set then trim stanzas that are empty after lifting.\n"
+  "   -withParent - If set include parent in output.\n"
+  "   -heavy=tag - Define a tag not hoisted even if all siblings agree\n"
+  "   -keyTag=name - Defines key tag that starts stanzas\n"
+  "   -parentTag=name - Defines tag used as parent\n"
+  "   -indent=N - Define how much to indent each level, default %d\n"
+  , indent
+  );
+}
+
+/* Command line validation table. */
+static struct optionSpec options[] = {
+   {"trimEmpty", OPTION_BOOLEAN},
+   {"withParent", OPTION_BOOLEAN},
+   {"heavy", OPTION_STRING},
+   {"keyTag", OPTION_STRING},
+   {"parentTag", OPTION_STRING},
+   {"indent", OPTION_INT},
+   {NULL, 0},
+};
+
+
+boolean metaLocalTagRemove(struct meta *meta, char *tag)
+/* Find given variable in list and remove it. Returns TRUE if it
+ * actually removed it,  FALSE if it never found it. */
+{
+struct metaTagVal **ln = &meta->tagList;
+struct metaTagVal *v;
+for (v = *ln; v != NULL; v = v->next)
+    {
+    if (sameString(v->tag, tag))
+        {
+	*ln = v->next;
+	return TRUE;
+	}
+    ln = &v->next;
+    }
+return FALSE;
+}
+
+void hoistOne(struct meta *meta, char *tag, char *val)
+/* We've already determined that tag exists and has same value in all children.
+ * What we do here is add it to ourselves and remove it from children. */
+{
+metaAddTag(meta, tag, val);
+struct meta *child;
+for (child = meta->children; child != NULL; child = child->next)
+    metaLocalTagRemove(child, tag);
+metaSortTags(meta);
+}
+
+struct slName *tagsInAny(struct meta *metaList)
+/* Return list of variables that are used in any node in list. */
+{
+struct hash *tagHash = hashNew(6);
+struct slName *tag, *tagList = NULL;
+struct meta *meta;
+for (meta = metaList; meta != NULL; meta = meta->next)
+    {
+    struct metaTagVal *v;
+    for (v = meta->tagList; v != NULL; v = v->next)
+        {
+	if (!hashLookup(tagHash, v->tag))
+	    {
+	    tag = slNameAddHead(&tagList, v->tag);
+	    hashAdd(tagHash, tag->name, tag);
+	    }
+	}
+    }
+hashFree(&tagHash);
+return tagList;
+}
+
+char *allSameVal(char *tag, struct meta *metaList)
+/* Return value of tag if it exists and is the same in each meta on list */
+{
+char *val = NULL;
+struct meta *meta;
+for (meta = metaList; meta != NULL; meta = meta->next)
+    {
+    char *oneVal = metaLocalTagVal(meta, tag);
+    if (oneVal == NULL)
+        return NULL;
+    if (val == NULL)
+        val = oneVal;
+    else
+        {
+	if (!sameString(oneVal, val))
+	    return NULL;
+	}
+    }
+return val;
+}
+
+void metaTreeHoist(struct meta *meta)
+/* Move tags that are the same in all children up to parent. */
+{
+/* Do depth first recursion, but get early return if we're a leaf. */
+struct meta *child;
+if (meta->children == NULL)
+    return;
+for (child = meta->children; child != NULL; child = child->next)
+    metaTreeHoist(child);
+
+/* Build up list of tags used in any child. */
+struct slName *tag, *tagList = tagsInAny(meta->children);
+
+/* Go through list and figure out ones that are same in all children. */
+for (tag = tagList; tag != NULL; tag = tag->next)
+    {
+    if (!sameString(tag->name, keyTag))
+	{
+	char *val;
+	val = allSameVal(tag->name, meta->children);
+	if (val != NULL)
+	    hoistOne(meta, tag->name, val);
+	}
+    }
+slFreeList(&tagList);
+}
+
+
+
+void metaHoist(char *inFile, char *outFile)
+/* metaHoist - Move tags that are shared by all siblings up a level.. */
+{
+struct meta *metaList = metaLoadAll(inFile, keyTag, parentTag, FALSE, FALSE);
+struct meta *meta;
+for (meta = metaList; meta != NULL; meta = meta->next)
+    metaTreeHoist(meta);
+metaWriteAll(metaList, outFile, indent, withParent);
+}
+
+int main(int argc, char *argv[])
+/* Process command line. */
+{
+optionInit(&argc, argv, options);
+trimEmpty = optionExists("trimEmpty");
+withParent = optionExists("withParent");
+indent = optionInt("indent", indent);
+keyTag = optionVal("keyTag", keyTag);
+heavy = optionVal("heavy", heavy);
+parentTag = optionVal("parentTag", parentTag);
+if (argc != 3)
+    usage();
+metaHoist(argv[1], argv[2]);
+return 0;
+}