docs: @SuppressWarnings("unchecked")
    public static void main(String[] args) {
  
      // System.out.print("GrammaticalRelations under DEPENDENT:");
      // System.out.println(DEPENDENT.toPrettyString());
  
      MemoryTreebank tb = new MemoryTreebank(new TreeNormalizer());
      Collection<GrammaticalStructure> gsBank = null;
      Properties props = StringUtils.argsToProperties(args);
  
      String encoding = props.getProperty("encoding", "utf-8");
      try {
        System.setOut(new PrintStream(System.out, true, encoding));
      } catch (IOException e) {
        throw new RuntimeException(e);
      }
  
      String treeFileName = props.getProperty("treeFile");
      String sentFileName = props.getProperty("sentFile");
      String conllXFileName = props.getProperty("conllxFile");
      String altDepPrinterName = props.getProperty("altprinter");
      String altDepReaderName = props.getProperty("altreader");
      String altDepReaderFilename = props.getProperty("altreaderfile");
  
      String filter = props.getProperty("filter");
  
      boolean makeCopulaHead = props.getProperty("makeCopulaHead") != null;
  
      // TODO: if a parser is specified, load this from the parser
      // instead of ever loading it from this way
      String tLPP = props.getProperty("tLPP", "edu.stanford.nlp.parser.lexparser.EnglishTreebankParserParams");
      TreebankLangParserParams params = ReflectionLoading.loadByReflection(tLPP);
      if (makeCopulaHead) {
        // TODO: generalize and allow for more options
        String[] options = { "-makeCopulaHead" };
        params.setOptionFlag(options, 0);
      }
  
      if (sentFileName == null && (altDepReaderName == null || altDepReaderFilename == null) && treeFileName == null && conllXFileName == null && filter == null) {
        try {
          System.err.println("Usage: java GrammaticalStructure [options]* [-sentFile|-treeFile|-conllxFile file] [-testGraph]");
          System.err.println("  options: -basic, -collapsed, -CCprocessed [the default], -collapsedTree, -parseTree, -test, -parserFile file, -conllx, -keepPunct, -altprinter -altreader -altreaderfile");
          TreeReader tr = new PennTreeReader(new StringReader("((S (NP (NNP Sam)) (VP (VBD died) (NP-TMP (NN today)))))"));
          tb.add(tr.readTree());
        } catch (Exception e) {
          System.err.println("Horrible error: " + e);
          e.printStackTrace();
        }
      } else if (altDepReaderName != null && altDepReaderFilename != null) {
        DependencyReader altDepReader = loadAlternateDependencyReader(altDepReaderName);
        try {
          gsBank = altDepReader.readDependencies(altDepReaderFilename);
        } catch (IOException e) {
          System.err.println("Error reading " + altDepReaderFilename);
          return;
        }
      } else if (treeFileName != null) {
        tb.loadPath(treeFileName);
      } else if (filter != null) {
        tb.load(new BufferedReader(new InputStreamReader(System.in)));
      } else if (conllXFileName != null) {
        try {
          gsBank = params.readGrammaticalStructureFromFile(conllXFileName);
        } catch (RuntimeIOException e) {
          System.err.println("Error reading " + conllXFileName);
          return;
        }
      } else {
        String parserFile = props.getProperty("parserFile");
        String parserOpts = props.getProperty("parserOpts");
        boolean tokenized = props.getProperty("tokenized") != null;
        Function<Object, Tree> lp = loadParser(parserFile, parserOpts, makeCopulaHead);
        if (!loadTreebankByParsing(tb, sentFileName, encoding, tokenized, lp))
          return;
  
        // Instead of getting this directly from the LP, use reflection
        // so that a package which uses GrammaticalStructure doesn't
        // necessarily have to use LexicalizedParser
        try {
          Method method = lp.getClass().getMethod("getTLPParams");
          params = (TreebankLangParserParams) method.invoke(lp);
        } catch (Exception cnfe) {
          throw new RuntimeException(cnfe);
        }
      }
  
      // treats the output according to the options passed
      boolean basic = props.getProperty("basic") != null;
      boolean collapsed = props.getProperty("collapsed") != null;
      boolean CCprocessed = props.getProperty("CCprocessed") != null;
      boolean tree = props.getProperty("collapsedTree") != null;
      boolean nonCollapsed = props.getProperty("nonCollapsed") != null;
      boolean extraSep = props.getProperty("extraSep") != null;
      boolean parseTree = props.getProperty("parseTree") != null;
      boolean test = props.getProperty("test") != null;
      boolean keepPunct = props.getProperty("keepPunct") != null;
      boolean conllx = props.getProperty("conllx") != null;
      boolean checkConnected = props.getProperty("checkConnected") != null;
      boolean portray = props.getProperty("portray") != null;
  
      // make keepPunct default if conllx is turned on
      if(conllx) {
        keepPunct = true;
      }
  
      // If requested load alternative printer
      DependencyPrinter altDepPrinter = null;
      if (altDepPrinterName != null) {
        altDepPrinter = loadAlternateDependencyPrinter(altDepPrinterName);
      }
  
      // System.err.println("First tree in tb is");
      // System.err.println(((MemoryTreebank) tb).get(0));
  
      Method m = null;
      if (test) {
        // see if we can use SemanticGraph(Factory) to check for being a DAG
        // Do this by reflection to avoid this becoming a dependency when we distribute the parser
        try {
          Class sgf = Class.forName("edu.stanford.nlp.trees.semgraph.SemanticGraphFactory");
          m = sgf.getDeclaredMethod("makeFromTree", GrammaticalStructure.class, boolean.class, boolean.class, boolean.class, boolean.class, boolean.class, boolean.class, Filter.class, String.class, int.class);
        } catch (Exception e) {
          System.err.println("Test cannot check for cycles in tree format (classes not available)");
        }
      }
  
      if (gsBank == null) {
        gsBank = new TreeBankGrammaticalStructureWrapper(tb, keepPunct, params);
      }
  
      for (GrammaticalStructure gs : gsBank) {
  
        Tree t;
        if (gsBank instanceof TreeBankGrammaticalStructureWrapper) {
          // System.err.println("Using TreeBankGrammaticalStructureWrapper branch");
          t = ((TreeBankGrammaticalStructureWrapper) gsBank).getOriginalTree(gs);
          // System.err.println("Tree is: ");
          // System.err.println(t);
        } else {
          // System.err.println("Using gs.root() branch");
          t = gs.root(); // recover tree
        }
  
        if (test) {// print the grammatical structure, the basic, collapsed and
          // CCprocessed
  
          System.out.println("============= parse tree =======================");
          t.pennPrint();
          System.out.println();
  
          System.out.println("------------- GrammaticalStructure -------------");
          System.out.println(gs);
  
          System.out.println("------------- basic dependencies ---------------");
          System.out.println(StringUtils.join(gs.typedDependencies(false), "\n"));
  
          System.out.println("------------- non-collapsed dependencies (basic + extra) ---------------");
          System.out.println(StringUtils.join(gs.typedDependencies(true), "\n"));
  
          System.out.println("------------- collapsed dependencies -----------");
          System.out.println(StringUtils.join(gs.typedDependenciesCollapsed(true), "\n"));
  
          System.out.println("------------- collapsed dependencies tree -----------");
          System.out.println(StringUtils.join(gs.typedDependenciesCollapsedTree(), "\n"));
  
          System.out.println("------------- CCprocessed dependencies --------");
          System.out.println(StringUtils.join(gs.typedDependenciesCCprocessed(true), "\n"));
  
          System.out.println("-----------------------------------------------");
          // connectivity test
          boolean connected = GrammaticalStructure.isConnected(gs.typedDependenciesCollapsed(true));
          System.out.println("collapsed dependencies form a connected graph: " + connected);
          if (!connected) {
            System.out.println("possible offending nodes: " + GrammaticalStructure.getRoots(gs.typedDependenciesCollapsed(true)));
          }
  
          // test for collapsed dependencies being a tree:
          // make sure at least it doesn't contain cycles (i.e., is a DAG)
          // Do this by reflection so parser doesn't need SemanticGraph and its
          // libraries
          if (m != null) {
            try {
              // the first arg is null because it's a static method....
              Object semGraph = m.invoke(null, gs, false, true, false, false, false, false, null, null, 0);
              Class sg = Class.forName("edu.stanford.nlp.trees.semgraph.SemanticGraph");
              Method mDag = sg.getDeclaredMethod("isDag");
              boolean isDag = (Boolean) mDag.invoke(semGraph);
  
              System.out.println("tree dependencies form a DAG: " + isDag);
            } catch (Exception e) {
              e.printStackTrace();
            }
          }
        }// end of "test" output
  
        else {
          if (parseTree) {
            System.out.println("============= parse tree =======================");
            t.pennPrint();
            System.out.println();
          }
  
          if (basic) {
            if (collapsed || CCprocessed || tree || nonCollapsed) {
              System.out.println("------------- basic dependencies ---------------");
            }
            if (altDepPrinter == null) {
              printDependencies(gs, gs.typedDependencies(false), t, conllx, false);
            } else {
              System.out.println(altDepPrinter.dependenciesToString(gs, gs.typedDependencies(false), t));
            }
          }
  
          if (nonCollapsed) {
            if (basic || CCprocessed || collapsed || tree) {
              System.out.println("----------- non-collapsed dependencies (basic + extra) -----------");
            }
            printDependencies(gs, gs.allTypedDependencies(), t, conllx, extraSep);
          }
  
          if (collapsed) {
            if (basic || CCprocessed || tree || nonCollapsed) {
              System.out.println("----------- collapsed dependencies -----------");
            }
            printDependencies(gs, gs.typedDependenciesCollapsed(true), t, conllx, false);
          }
  
          if (CCprocessed) {
            if (basic || collapsed || tree || nonCollapsed) {
              System.out.println("---------- CCprocessed dependencies ----------");
            }
            List<TypedDependency> deps = gs.typedDependenciesCCprocessed(true);
            if (checkConnected) {
              if (!GrammaticalStructure.isConnected(deps)) {
                System.err.println("Graph is not connected for:");
                System.err.println(t);
                System.err.println("possible offending nodes: " + GrammaticalStructure.getRoots(deps));
              }
            }
            printDependencies(gs, deps, t, conllx, false);
          }
  
          if (tree) {
            if (basic || CCprocessed || collapsed || nonCollapsed) {
              System.out.println("----------- collapsed dependencies tree -----------");
            }
            printDependencies(gs, gs.typedDependenciesCollapsedTree(), t, conllx, false);
          }
  
          // default use: CCprocessed (to parallel what happens within the parser)
          if (!basic && !collapsed && !CCprocessed && !tree && !nonCollapsed) {
            // System.out.println("----------- CCprocessed dependencies -----------");
            printDependencies(gs, gs.typedDependenciesCCprocessed(true), t, conllx, false);
          }
        }
  
        if (portray) {
          try {
            // put up a window showing it
            Class sgu = Class.forName("edu.stanford.nlp.trees.semgraph.SemanticGraphUtils");
            Method mRender = sgu.getDeclaredMethod("render", GrammaticalStructure.class, String.class);
            // the first arg is null because it's a static method....
            mRender.invoke(null, gs, "Collapsed, CC processed deps");
          } catch (Exception e) {
            throw new RuntimeException("Couldn't use swing to portray semantic graph", e);
          }
        }
  
      } // end for
    }