*/
  public void integrateImportedGrammars(Grammar rootGrammar) {
    List<Grammar> imports = rootGrammar.getAllImportedGrammars();
    if ( imports==null ) return;
    GrammarAST root = rootGrammar.ast;
    GrammarAST id = (GrammarAST) root.getChild(0);
    GrammarASTAdaptor adaptor = new GrammarASTAdaptor(id.token.getInputStream());
     GrammarAST tokensRoot = (GrammarAST)root.getFirstChildWithType(ANTLRParser.TOKENS_SPEC);
    List<GrammarAST> actionRoots = root.getNodesWithType(ANTLRParser.AT);
    // Compute list of rules in root grammar and ensure we have a RULES node
    GrammarAST RULES = (GrammarAST)root.getFirstChildWithType(ANTLRParser.RULES);
    Set<String> rootRuleNames = new HashSet<String>();
    if ( RULES==null ) { // no rules in root, make RULES node, hook in
      RULES = (GrammarAST)adaptor.create(ANTLRParser.RULES, "RULES");
      RULES.g = rootGrammar;
      root.addChild(RULES);
    }
    else {
      // make list of rules we have in root grammar
      List<GrammarAST> rootRules = RULES.getNodesWithType(ANTLRParser.RULE);
      for (GrammarAST r : rootRules) rootRuleNames.add(r.getChild(0).getText());
    }
    for (Grammar imp : imports) {
      // COPY TOKENS
      GrammarAST imp_tokensRoot = (GrammarAST)imp.ast.getFirstChildWithType(ANTLRParser.TOKENS_SPEC);
      if ( imp_tokensRoot!=null ) {
        rootGrammar.tool.log("grammar", "imported tokens: "+imp_tokensRoot.getChildren());
        if ( tokensRoot==null ) {
          tokensRoot = (GrammarAST)adaptor.create(ANTLRParser.TOKENS_SPEC, "TOKENS");
          tokensRoot.g = rootGrammar;
          root.insertChild(1, tokensRoot); // ^(GRAMMAR ID TOKENS...)
        }
        tokensRoot.addChildren(Arrays.asList(imp_tokensRoot.getChildren().toArray(new Tree[0])));
      }
      List<GrammarAST> all_actionRoots = new ArrayList<GrammarAST>();
      List<GrammarAST> imp_actionRoots = imp.ast.getAllChildrenWithType(ANTLRParser.AT);
      if ( actionRoots!=null ) all_actionRoots.addAll(actionRoots);
      all_actionRoots.addAll(imp_actionRoots);
      // COPY ACTIONS
      if ( imp_actionRoots!=null ) {
        DoubleKeyMap<String, String, GrammarAST> namedActions =
          new DoubleKeyMap<String, String, GrammarAST>();
        rootGrammar.tool.log("grammar", "imported actions: "+imp_actionRoots);
        for (GrammarAST at : all_actionRoots) {
          String scopeName = rootGrammar.getDefaultActionScope();
          GrammarAST scope, name, action;
          if ( at.getChildCount()>2 ) { // must have a scope
            scope = (GrammarAST)at.getChild(0);
            scopeName = scope.getText();
            name = (GrammarAST)at.getChild(1);
            action = (GrammarAST)at.getChild(2);
          }
          else {
            name = (GrammarAST)at.getChild(0);
            action = (GrammarAST)at.getChild(1);
          }
          GrammarAST prevAction = namedActions.get(scopeName, name.getText());
          if ( prevAction==null ) {
            namedActions.put(scopeName, name.getText(), action);
          }
          else {
            if ( prevAction.g == at.g ) {
              rootGrammar.tool.errMgr.grammarError(ErrorType.ACTION_REDEFINITION,
                        at.g.fileName, name.token, name.getText());
            }
            else {
              String s1 = prevAction.getText();
              s1 = s1.substring(1, s1.length()-1);
              String s2 = action.getText();
              s2 = s2.substring(1, s2.length()-1);
              String combinedAction = "{"+s1 + '\n'+ s2+"}";
              prevAction.token.setText(combinedAction);
            }
          }
        }
        // at this point, we have complete list of combined actions,
        // some of which are already living in root grammar.
        // Merge in any actions not in root grammar into root's tree.
        for (String scopeName : namedActions.keySet()) {
          for (String name : namedActions.keySet(scopeName)) {
            GrammarAST action = namedActions.get(scopeName, name);
            rootGrammar.tool.log("grammar", action.g.name+" "+scopeName+":"+name+"="+action.getText());
            if ( action.g != rootGrammar ) {
              root.insertChild(1, action.getParent());
            }
          }
        }
      }
      // COPY RULES
      List<GrammarAST> rules = imp.ast.getNodesWithType(ANTLRParser.RULE);
      if ( rules!=null ) {
        for (GrammarAST r : rules) {
          rootGrammar.tool.log("grammar", "imported rule: "+r.toStringTree());
          String name = r.getChild(0).getText();
          boolean rootAlreadyHasRule = rootRuleNames.contains(name);
          if ( !rootAlreadyHasRule ) {
            RULES.addChild(r); // merge in if not overridden
            rootRuleNames.add(name);
          }
        }
      }
      GrammarAST optionsRoot = (GrammarAST)imp.ast.getFirstChildWithType(ANTLRParser.OPTIONS);
      if ( optionsRoot!=null ) {
        // suppress the warning if the options match the options specified
        // in the root grammar
        // https://github.com/antlr/antlr4/issues/707