@Override
  public void start(final Input input, final IGGeneratorCallback callback)
      throws InterruptedException {
    // find processor mappings for all folders
    final Map<FolderType, Map<String, Processor<Element>>> registeredProcessors =
        getRegisteredProcessors();

    final List<Element> documentRules = new ArrayList<Element>();
    final List<Element> schemaQueryRules = new ArrayList<Element>();

    // run processors on input, gather IG rules
    documentRules.addAll(
        getRulesFromInput(input.getDocuments(), registeredProcessors.get(FolderType.DOCUMENT)));
    verifySimpleGrammar(documentRules);
    schemaQueryRules.addAll(
        getRulesFromInput(input.getSchemas(), registeredProcessors.get(FolderType.SCHEMA)));
    schemaQueryRules.addAll(
        getRulesFromInput(input.getQueries(), registeredProcessors.get(FolderType.QUERY)));

    // if there are no schema/query rules, or the next module can handle simple
    // grammar, just output all of it without expansion
    if (BaseUtils.isEmpty(schemaQueryRules)
        || RunningProject.getNextModuleCaps()
            .getCapabilities()
            .contains("can.handle.complex.regexps")) {
      documentRules.addAll(schemaQueryRules);

      // show the rules
      RuleDisplayerHelper.showRulesAsync("IG", new CloneHelper().cloneGrammar(documentRules), true);

      callback.finished(documentRules);
      return;
    }

    // otherwise, we have to expand
    // show the rules before expansion
    final List<Element> before = new ArrayList<Element>();
    before.addAll(documentRules);
    before.addAll(schemaQueryRules);
    RuleDisplayerHelper.showRulesAsync("Raw", new CloneHelper().cloneGrammar(before), true);

    // lookup expander
    final Expander expander = Lookup.getDefault().lookup(Expander.class);
    final List<Element> expanded = expander.expand(schemaQueryRules);

    final List<Element> ret = new ArrayList<Element>();
    ret.addAll(documentRules);
    ret.addAll(expanded);

    // show the rules after expansion
    RuleDisplayerHelper.showRulesAsync("Expanded", new CloneHelper().cloneGrammar(ret), true);

    // return expanded
    callback.finished(ret);
  }
  @Override
  public void start(
      final Input input,
      final List<Element> grammar,
      final NonGrammaticalInputProcessorCallback callback)
      throws InterruptedException {
    List<ModuleNode> xquerySyntaxTrees =
        (processXQueries(input.getQueries(), getXQueryProcessor()));
    LOG.info("Input XQuery files parsed into " + xquerySyntaxTrees.size() + " syntax trees");

    final List<InferredTypeStatement> inferredTypes = new ArrayList<InferredTypeStatement>();
    final KeysInferrer keysInferrer = new KeysInferrer();

    for (final ModuleNode root : xquerySyntaxTrees) {
      final SyntaxTreeProcessor syntaxTreeProcessor = new SyntaxTreeProcessor(root);
      syntaxTreeProcessor.process();
      inferredTypes.addAll(syntaxTreeProcessor.getInferredTypes());
      keysInferrer.addJoinPatterns(syntaxTreeProcessor.getJoinPatterns());
      keysInferrer.addNegativeUniquenessStatements(
          syntaxTreeProcessor.getNegativeUniquenessStatements());

      for (final NegativeUniquenessStatement negativeUniquenessStatement :
          syntaxTreeProcessor.getNegativeUniquenessStatements()) {
        LOG.info(
            "Inferred negative uniqueness statement: "
                + negativeUniquenessStatement.getTargetPath()
                + ", weight: "
                + negativeUniquenessStatement.getWeight());
      }
    }

    for (final InferredTypeStatement inferredTypeStatement : inferredTypes) {
      final NormalizedPathType path = new NormalizedPathType(inferredTypeStatement.getPathType());
      final XSDBuiltinAtomicType inferredType =
          ((XSDType) inferredTypeStatement.getType()).getAtomicType();
      if (path.getInitialStep() != InitialStep.ROOT) {
        LOG.error("Inferred type statement should not contain a relative path. Probably a bug.");
      }
      LOG.info("Inferred type statement: " + path.toString() + " -> " + inferredType);
    }
    LOG.info("Total number of inferred type statements: " + inferredTypes.size());

    keysInferrer.summarize();
    final Collection<SummarizedKey> keys = keysInferrer.getKeys();
    final Map<Key, Set<ForeignKey>> foreignKeys = keysInferrer.getForeignKeys();
    for (final SummarizedKey summarizedKey : keys) {
      final Key key = summarizedKey.getKey();
      final NormalizedPathType contextPath = key.getContextPath();
      if (contextPath != null) {
        LOG.info(
            "Inferred key: ("
                + contextPath
                + ", "
                + key.getTargetPath()
                + ", {"
                + key.getKeyPath()
                + "}), normalized weight: "
                + summarizedKey.getNormalizedWeight());
      } else {
        LOG.info(
            "Inferred key: ("
                + key.getTargetPath()
                + ", {"
                + key.getKeyPath()
                + "}), normalized weight: "
                + summarizedKey.getNormalizedWeight());
      }
    }
    LOG.info("Total number of inferred key statements: " + keys.size());

    final Merger merger = new Merger(grammar);
    merger.mergeInferredTypes(inferredTypes);
    merger.mergeInferredKeys(keys, foreignKeys);

    callback.finished(grammar);
  }