/**
   * Returns a new {@code ITranslationRuleGen}.
   *
   * @param systemMappingsDoc
   * @param userRelationsDoc
   * @param builtInSchemasDoc
   * @param system
   * @return a new {@code ITranslationGen}
   * @throws XMLParseException
   * @throws ConfigurationException for an illegal combination of options
   */
  public static ITranslationRuleGen newInstance(
      Document systemMappingsDoc,
      Document userRelationsDoc,
      Document builtInSchemasDoc,
      OrchestraSystem system)
      throws XMLParseException {
    boolean outerUnion = Config.getOuterUnion();
    boolean outerJoin = Config.getOuterJoin();
    List<Mapping> systemMappings =
        deserializeVerboseMappings(systemMappingsDoc.getDocumentElement(), system);
    List<RelationContext> userRelations =
        deserializeRelationContexts(userRelationsDoc.getDocumentElement(), system);
    Map<String, Schema> builtInSchemas =
        OrchestraSystem.deserializeBuiltInFunctions(builtInSchemasDoc);
    boolean isBidirectional = system.isBidirectional();

    checkOptions(isBidirectional, outerUnion, outerJoin);
    if (outerUnion) {
      return new OuterUnionTranslationRuleGen(
          systemMappings, userRelations, builtInSchemas, isBidirectional);
    } else if (outerJoin) {
      return new OuterJoinTranslationRuleGen(
          systemMappings, userRelations, builtInSchemas, isBidirectional);
    } else {
      return new DefaultTranslationRuleGen(
          systemMappings, userRelations, builtInSchemas, isBidirectional);
    }
  }
  /**
   * Returns a new {@code ITranslationRuleGen}.
   *
   * @param systemMappings
   * @param userRelations
   * @param builtInSchemas
   * @param isBidirectional
   * @param bidirectional
   * @return a new {@code ITranslationGen}
   * @throws ConfigurationException for an illegal combination of options
   */
  public static ITranslationRuleGen newInstance(
      List<Mapping> systemMappings,
      List<RelationContext> userRelations,
      Map<String, Schema> builtInSchemas,
      boolean isBidirectional) {
    boolean outerUnion = Config.getOuterUnion();
    boolean outerJoin = Config.getOuterJoin();

    checkOptions(isBidirectional, outerUnion, outerJoin);
    if (outerUnion) {
      return new OuterUnionTranslationRuleGen(
          systemMappings, userRelations, builtInSchemas, isBidirectional);
    } else if (outerJoin) {
      return new OuterJoinTranslationRuleGen(
          systemMappings, userRelations, builtInSchemas, isBidirectional);
    } else {
      return new DefaultTranslationRuleGen(
          systemMappings, userRelations, builtInSchemas, isBidirectional);
    }
  }
  public List<Rule> computeTranslationRules() throws Exception {

    try {
      List<Mapping> mappings = OrchestraUtil.newArrayList(systemMappings);
      for (Mapping m : mappings) {
        m.renameExistentialVars();
      }
      List<RelationContext> relations = OrchestraUtil.newArrayList(userRelations);

      if (Config.getEdbbits()) MappingsTranslationMgt.addEdbBitsToMappings(mappings);

      state = new TranslationState(mappings, relations);

      Debug.println("Mappings: " + mappings.size());

      Calendar before = Calendar.getInstance();

      List<Rule> source2targetRules;
      List<Rule> local2peerRules = MappingsIOMgt.inOutTranslationL(builtInSchemas, relations);
      state.setLocal2PeerRules(local2peerRules);
      mappings.addAll(state.getLocal2PeerRules());

      // Make variables in each mapping different
      int i = 0;
      for (Mapping m : mappings) {
        m.renameVariables("_M" + i);
        i++;
      }
      state.setMappings(mappings);
      List<Mapping> skolMappings;
      List<RelationContext> allMappingRels;
      List<RelationContext> realMappingRels = new ArrayList<RelationContext>();

      skolMappings = MappingsInversionMgt.skolemizeMappings(mappings);
      MappingsCompositionMgt.composeMappings(skolMappings, builtInSchemas);

      allMappingRels = computeProvenanceRelations(skolMappings);

      // Need to change this - put it inside Provenance Relations
      // mappings = expandBidirectionalMappings(mappings);

      state.setMappingRels(allMappingRels);
      for (RelationContext relctx : allMappingRels) {
        ProvenanceRelation prvrel = (ProvenanceRelation) relctx.getRelation();

        if (!prvrel.getType().equals(ProvenanceRelation.ProvRelType.SINGLE)
            || !prvrel.getMappings().get(0).isFakeMapping()) {
          realMappingRels.add(relctx);
        }
      }
      state.setRealMappingRels(realMappingRels);

      List<Rule> source2provRules = new ArrayList<Rule>();
      List<Mapping> prov2targetMappings = new ArrayList<Mapping>();

      computeProvRules(source2provRules, prov2targetMappings);

      source2targetRules = MappingsInversionMgt.splitMappingsHeads(skolMappings, builtInSchemas);
      source2targetRules = MappingsIOMgt.inOutTranslationR(source2targetRules, true);

      state.setSource2TargetRules(source2targetRules);
      state.setSource2ProvRules(source2provRules);
      state.setProv2TargetMappings(prov2targetMappings);

      Calendar after = Calendar.getInstance();
      long time = after.getTimeInMillis() - before.getTimeInMillis();
      Debug.println("TOTAL RULE MANIPULATION TIME: " + time + "msec");
      return source2targetRules;
    } catch (Exception e) {
      e.printStackTrace();
      throw (e);
    }
  }