/**
  * Creates {@link RulesModuleBindingContext} and populates it with types from dependent modules.
  *
  * @param moduleNode just for processing error
  * @param bindingContext
  * @param moduleDependencies
  * @param moduleOpenClass
  * @return {@link RulesModuleBindingContext} created with bindingContext and moduleOpenClass.
  */
 private RulesModuleBindingContext populateBindingContextWithDependencies(
     XlsModuleSyntaxNode moduleNode,
     IBindingContext bindingContext,
     Set<CompiledDependency> moduleDependencies,
     XlsModuleOpenClass moduleOpenClass) {
   RulesModuleBindingContext moduleContext =
       createRulesBindingContext(bindingContext, moduleOpenClass);
   try {
     moduleContext.addTypes(
         filterDependencyTypes(moduleOpenClass.getTypes(), moduleContext.getInternalTypes()));
   } catch (Exception ex) {
     SyntaxNodeException error =
         SyntaxNodeExceptionUtils.createError(
             "Can`t add datatype from dependency", ex, moduleNode);
     BindHelper.processError(error);
   }
   return moduleContext;
 }
  /**
   * Common binding cycle.
   *
   * @param moduleNode
   * @param openl
   * @param moduleContext
   * @param moduleOpenClass
   * @param bindingContext
   * @return
   */
  private IBoundNode processBinding(
      XlsModuleSyntaxNode moduleNode,
      OpenL openl,
      RulesModuleBindingContext moduleContext,
      XlsModuleOpenClass moduleOpenClass,
      IBindingContext bindingContext) {

    IVocabulary vocabulary = makeVocabulary(moduleNode);

    if (vocabulary != null) {
      processVocabulary(vocabulary, moduleContext);
    }

    //
    // Selectors
    //
    ASelector<ISyntaxNode> propertiesSelector = getSelector(XlsNodeTypes.XLS_PROPERTIES);
    ASelector<ISyntaxNode> dataTypeSelector = getSelector(XlsNodeTypes.XLS_DATATYPE);

    ISelector<ISyntaxNode> notPropertiesAndNotDatatypeSelector =
        propertiesSelector.not().and(dataTypeSelector.not());

    ISelector<ISyntaxNode> spreadsheetSelector = getSelector(XlsNodeTypes.XLS_SPREADSHEET);
    ISelector<ISyntaxNode> testMethodSelector = getSelector(XlsNodeTypes.XLS_TEST_METHOD);
    ISelector<ISyntaxNode> runMethodSelector = getSelector(XlsNodeTypes.XLS_RUN_METHOD);

    ISelector<ISyntaxNode> commonTablesSelector =
        notPropertiesAndNotDatatypeSelector.and(
            spreadsheetSelector.not().and(testMethodSelector.not().and(runMethodSelector.not())));

    // Bind property node at first.
    //
    TableSyntaxNode[] propertiesNodes = selectNodes(moduleNode, propertiesSelector);
    bindInternal(moduleNode, moduleOpenClass, propertiesNodes, openl, moduleContext);

    bindPropertiesForAllTables(moduleNode, moduleOpenClass, openl, moduleContext);

    IBoundNode topNode = null;

    // Bind datatype nodes.
    TableSyntaxNode[] datatypeNodes = selectNodes(moduleNode, dataTypeSelector);

    /*
     * Processes datatype table nodes before the bind operation. Checks type
     * declarations and finds invalid using of inheritance feature at this
     * step.
     */
    TableSyntaxNode[] processedDatatypeNodes =
        new DatatypesSorter()
            .sort(
                datatypeNodes,
                moduleContext); // Rewrite this sorter with TableSyntaxNodeRelationsUtils

    bindInternal(moduleNode, moduleOpenClass, processedDatatypeNodes, openl, moduleContext);

    // Select nodes excluding Properties, Datatype, Spreadsheet, Test,
    // RunMethod tables
    TableSyntaxNode[] commonTables = selectNodes(moduleNode, commonTablesSelector);

    // Select and sort Spreadsheet tables
    TableSyntaxNode[] spreadsheets = selectSpreadsheetNodes(moduleNode, spreadsheetSelector);
    if (OpenLSystemProperties.isCustomSpreadsheetType(bindingContext.getExternalParams())) {
      try {
        spreadsheets =
            TableSyntaxNodeRelationsUtils.sort(
                spreadsheets, new SpreadsheetTableSyntaxNodeRelationsDeterminer());
      } catch (TableSyntaxNodeCircularDependencyException e) {
        for (TableSyntaxNode tsn : e.getTableSyntaxNodes()) {
          SyntaxNodeException error = SyntaxNodeExceptionUtils.createError(e, tsn);
          processError(error, tsn, moduleContext);
        }
      }
    }

    TableSyntaxNode[] commonAndSpreadsheetTables = ArrayUtils.addAll(commonTables, spreadsheets);
    bindInternal(moduleNode, moduleOpenClass, commonAndSpreadsheetTables, openl, moduleContext);

    // Select Test and RunMethod tables
    TableSyntaxNode[] runTables = selectNodes(moduleNode, runMethodSelector);
    bindInternal(moduleNode, moduleOpenClass, runTables, openl, moduleContext);

    TableSyntaxNode[] testTables = selectNodes(moduleNode, testMethodSelector);
    topNode = bindInternal(moduleNode, moduleOpenClass, testTables, openl, moduleContext);

    if (moduleOpenClass.isUseDescisionTableDispatcher()) {
      DispatcherTablesBuilder dispTableBuilder =
          new DispatcherTablesBuilder((XlsModuleOpenClass) topNode.getType(), moduleContext);
      dispTableBuilder.build();
    }

    ((XlsModuleOpenClass) topNode.getType()).setRulesModuleBindingContext(moduleContext);
    ((XlsModuleOpenClass) topNode.getType()).completeOpenClassBuilding();

    processErrors(moduleOpenClass.getErrors(), bindingContext);

    return topNode;
  }
  private void addDataTables(CompiledOpenClass dependency) {
    IOpenClass openClass = dependency.getOpenClassWithErrors();

    Map<String, IOpenField> fieldsMap = openClass.getFields();

    Set<String> tableUrls = new HashSet<String>();
    Map<String, IOpenField> fields = getFields();
    for (IOpenField openField : fields.values()) {
      if (openField instanceof DataOpenField) {
        tableUrls.add(((DataOpenField) openField).getTableUri());
      }
    }
    for (String key : fieldsMap.keySet()) {
      IOpenField field = fieldsMap.get(key);
      if (field instanceof DataOpenField) {
        DataOpenField dataOpenField = (DataOpenField) field;
        try {
          String tableUrl = dataOpenField.getTableUri();
          // Test tables are added both as methods and variables.
          if (!tableUrls.contains(tableUrl) && !duplicatedMethodUrls.contains(tableUrl)) {
            boolean containsInDependency = false;
            if (VirtualSourceCodeModule.SOURCE_URI.equals(metaInfo.getSourceUrl())) {
              for (CompiledDependency d : getDependencies()) {
                IOpenClass dependentModuleClass = d.getCompiledOpenClass().getOpenClassWithErrors();
                if (dependentModuleClass instanceof XlsModuleOpenClass) {
                  if (((XlsModuleOpenClass) dependentModuleClass)
                      .duplicatedMethodUrls.contains(tableUrl)) {
                    containsInDependency = true;
                    break;
                  }
                }
              }
            }
            if (!containsInDependency) {
              addField(field);
              tableUrls.add(tableUrl);
            }
          }
        } catch (OpenlNotCheckedException e) {
          SyntaxNodeException error =
              SyntaxNodeExceptionUtils.createError(
                  e.getMessage(), e, dataOpenField.getTable().getTableSyntaxNode());
          addError(error);
        }
      }
    }

    if (openClass instanceof XlsModuleOpenClass) {
      XlsModuleOpenClass xlsModuleOpenClass = (XlsModuleOpenClass) openClass;
      if (xlsModuleOpenClass.getDataBase() != null) {
        for (ITable table : xlsModuleOpenClass.getDataBase().getTables()) {
          if (XlsNodeTypes.XLS_DATA.toString().equals(table.getTableSyntaxNode().getType())) {
            try {
              getDataBase().registerTable(table);
            } catch (DuplicatedTableException e) {
              addError(e);
            } catch (OpenlNotCheckedException e) {
              addError(e);
            }
          }
        }
      }
    }
  }