public function generate(array $possiblyLinkableDatasetNames = NULL) {
        $adjustedPossiblyLinkableDatasetNames = NULL;
        if (isset($possiblyLinkableDatasetNames)) {
            foreach ($possiblyLinkableDatasetNames as $possiblyLinkableDatasetName) {
                $adjustedPossiblyLinkableDatasetName = $this->adjustDatasetName($possiblyLinkableDatasetName);
                // we should not try to link with itself
                if ($adjustedPossiblyLinkableDatasetName == $this->primaryDatasetName) {
                    continue;
                }

                ArrayHelper::addUniqueValue($adjustedPossiblyLinkableDatasetNames, $adjustedPossiblyLinkableDatasetName);
            }
        }

        // preparing reference paths for all datasets which we need to test for possible linkage
        $referencePaths = NULL;
        if (isset($adjustedPossiblyLinkableDatasetNames)) {
            foreach ($adjustedPossiblyLinkableDatasetNames as $adjustedPossiblyLinkableDatasetName) {
                // column name should not be provided because we just try to link with the dataset
                $referencePath = ReferencePathHelper::assembleReference($adjustedPossiblyLinkableDatasetName, NULL);

                $referencePaths[$referencePath] = FALSE; // FALSE - if we cannot find link it is ok. Not everything could be connected
            }
        }

        $linkBuilder = new ReferenceLinkBuilder();
        $link = $linkBuilder->prepareReferenceBranches($this->primaryDatasetName, $referencePaths);

        return $link;
    }
    protected static function prepareReferencedElementName($referencePath, $datasetName, $elementName) {
        $referencedElementName = $elementName;
        if (isset($referencePath)) {
            $nestedReference = ReferencePathHelper::assembleReference($datasetName, $elementName);
            $referencedElementName = ReferencePathHelper::assembleReferencePath(array($nestedReference, $referencePath));
        }

        return $referencedElementName;
    }
    protected function prepareColumnUIMetaData(array $datasetStack, $referencePath, DatasetMetaData $dataset, $columnName) {
        $column = $dataset->getColumn($columnName);

        $columnUIMetaData = new AttributeUIMetaData();
        $columnUIMetaData->name = self::prepareColumnUIMetaDataName($referencePath, $dataset->name, $column->name);
        $columnUIMetaData->publicName = $column->publicName;
        $columnUIMetaData->description = $column->description;
        $columnUIMetaData->columnIndex = $column->columnIndex;
        $columnUIMetaData->type = clone $column->type;

        list($referencedDatasetName) = ReferencePathHelper::splitReference($column->type->getLogicalApplicationType());
        if (isset($referencedDatasetName)) {
            $metamodel = data_controller_get_metamodel();
            $referencedDataset = $metamodel->getDataset($referencedDatasetName);

            if (!isset($datasetStack[$referencedDataset->name])) {
                $datasetStack[$referencedDataset->name] = TRUE;

                $branchReferencePath = isset($referencePath)
                    ? self::prepareReferencedElementName($referencePath, $dataset->name, $column->name)
                    : ReferencePathHelper::assembleReference($dataset->name, $column->name);

                foreach ($referencedDataset->getColumns() as $referencedColumn) {
                    if (!$referencedColumn->isVisible()) {
                        continue;
                    }

                    $referencedColumnMetaData = $this->prepareColumnUIMetaData($datasetStack, $branchReferencePath, $referencedDataset, $referencedColumn->name);
                    if ($referencedColumn->isKey()) {
                        if (count($referencedColumnMetaData->elements) == 0) {
                            continue;
                        }

                        $referencedColumnMetaData->publicName = $referencedDataset->publicName;
                        $referencedColumnMetaData->description = $referencedDataset->description;
                        $referencedColumnMetaData->isSelectable = FALSE;
                    }

                    $columnUIMetaData->registerElement($referencedColumnMetaData);
                }
            }
        }

        return $columnUIMetaData;
    }
    public static function getExportColumnName ( $uiMetaDataName, MetaModel $metamodel ) {

        if ( trim($uiMetaDataName) == '' ) {
            $message = t('Empty columnName discovered');
            drupal_set_message($message, 'warning');
            LogHelper::log_warn($message);

            return $uiMetaDataName;
        }


        list($elementNameSpace, $name) = AbstractDatasetUIMetaDataGenerator::splitElementUIMetaDataName($uiMetaDataName);
        switch ( $elementNameSpace ) {

            case AbstractAttributeUIMetaData::NAME_SPACE:
                list($referencedDimensionName, $dimensionColumnName) = ParameterNameHelper::split($name);
                list($datasetName, $dimensionName) = ReferencePathHelper::splitReference($referencedDimensionName);
                if (isset($datasetName)) {
                    $adjustedReferencedDimensionName = ReferencePathHelper::assembleReference(self::getExportDatasetName($datasetName,$metamodel), $dimensionName);
                    $name = ParameterNameHelper::assemble($adjustedReferencedDimensionName, $dimensionColumnName);
                }
                break;

            case AbstractMeasureUIMetaData::NAME_SPACE:
                list($datasetName, $measureName) = ReferencePathHelper::splitReference($name);
                if (isset($datasetName)) {
                    $name = ReferencePathHelper::assembleReference(self::getExportDatasetName($datasetName,$metamodel), $measureName);
                }
                break;

            case FormulaUIMetaData::NAME_SPACE:
                list($datasetName, $formulaName) = ReferencePathHelper::splitReference($name);
                if (isset($datasetName)) {
                    $name = ReferencePathHelper::assembleReference(self::getExportDatasetName($datasetName,$metamodel), $formulaName);
                }
                break;

            default:
                $message = t('Unsupported UI Meta Data name space: @uiMetaDataName', array('@uiMetaDataName' => $uiMetaDataName));
                LogHelper::log_error($message);
                throw new UnsupportedOperationException($message);
        }

        return AbstractDatasetUIMetaDataGenerator::prepareElementUIMetaDataName($elementNameSpace, $name);
    }
    protected function generateLogicalDatasets(SystemTableMetaModelLoaderCallContext $callcontext, AbstractMetaModel $metamodel) {
        $datasetCount = 0;

        foreach ($callcontext->datasets as $tableAccessKey => $dataset) {
            $logicalDataset = new DatasetMetaData();
            $logicalDataset->name = $this->generateLogicalDatasetName($dataset->name);
            $logicalDataset->publicName = $dataset->publicName;
            $logicalDataset->description = $dataset->description;
            $logicalDataset->datasourceName = $dataset->datasourceName;
            $logicalDataset->sourceType = StarSchemaDatasetSourceTypeHandler::SOURCE_TYPE;

            $callcontext->logicalDatasetNameMappings[$logicalDataset->name] = $tableAccessKey;

            foreach ($dataset->columns as $column) {
                $logicalColumn = $logicalDataset->initiateColumn();
                $logicalColumn->name = $column->name;
                $logicalColumn->publicName = $column->publicName;
                $logicalColumn->description = $column->description;
                $logicalColumn->persistence = $column->persistence;
                $logicalColumn->columnIndex = $column->columnIndex;
                $logicalColumn->key = $column->key;
                $logicalColumn->visible = $column->visible;
                $logicalColumn->used = $column->used;

                if (isset($column->type->logicalApplicationType)) {
                    list($refDatasetName, $refColumnName) = ReferencePathHelper::splitReference($column->type->logicalApplicationType);

                    $refDataset = $metamodel->getDataset($refDatasetName);
                    $refColumn = $refDataset->getColumn($refColumnName);

                    $logicalColumn->type->databaseType = $refColumn->type->databaseType;
                    $logicalColumn->type->length = $refColumn->type->length;
                    $logicalColumn->type->precision = $refColumn->type->precision;
                    $logicalColumn->type->scale = $refColumn->type->scale;

                    $logicalColumn->type->applicationType = ReferencePathHelper::assembleReference(
                        $this->generateLogicalDatasetName($refDataset->name), $refColumnName);
                }
                else {
                    $logicalColumn->initializeTypeFrom($column->type);
                }

                $logicalDataset->registerColumnInstance($logicalColumn);
            }

            $callcontext->logicalDatasets[$tableAccessKey] = $logicalDataset;
            $datasetCount++;
        }

        LogHelper::log_info(t('Generated @datasetCount logical datasets', array('@datasetCount' => $datasetCount)));
    }
    protected function prepareAttributeColumnUIMetaData(array $datasetStack, $referencePath, CubeMetaData $cube, $dimensionName, $dimensionReferencePath, DatasetMetaData $dataset = NULL, ColumnMetaData $column) {
        $dimensionColumnName = isset($dimensionReferencePath)
            ? self::prepareReferencedElementName($dimensionReferencePath, $dataset->name, $column->name)
            : $column->name;

        $attributeUIMetaData = new AttributeColumnUIMetaData();
        $attributeUIMetaData->name = self::prepareAttributeUIMetaDataName($referencePath, $cube->factsDatasetName, $dimensionName, $dimensionColumnName);
        $attributeUIMetaData->publicName = $column->publicName;
        $attributeUIMetaData->description = $column->description;
        $attributeUIMetaData->columnIndex = $column->columnIndex;
        $attributeUIMetaData->type = clone $column->type;
        $attributeUIMetaData->datasetName = $cube->factsDatasetName;

        if ($column->isKey()) {
            $attributeUIMetaData->isSelectable = FALSE;
        }

        if (!$column->isUsed()) {
            $attributeUIMetaData->isSelectable = FALSE;
        }

        if (!$column->isKey()) {
            $this->prepareAttributeColumnUIMetaData4ColumnBranch($datasetStack, $attributeUIMetaData, $referencePath, $cube, $dimensionName, $dimensionReferencePath, $dataset, $column);
        }

        list($referencedDatasetName) = ReferencePathHelper::splitReference($column->type->getLogicalApplicationType());
        if (isset($referencedDatasetName)) {
            $branchReferencePath = NULL;
            if (isset($dataset)) {
                $branchReferencePath = isset($dimensionReferencePath)
                    ? self::prepareReferencedElementName($dimensionReferencePath, $dataset->name, $column->name)
                    : ReferencePathHelper::assembleReference($dataset->name, $column->name);
            }
            $this->generateColumnUIMetaData4DimensionDataset(
                $datasetStack, $attributeUIMetaData, $referencePath, $cube, $dimensionName, $branchReferencePath, $referencedDatasetName);
        }

        return $attributeUIMetaData;
    }
 protected function assembleConnectedDatasetSourceStatement(DataControllerCallContext $callcontext, ReferenceLink $link, array $columnNames, array $linkExecutionStack = NULL)
 {
     $TABLE_ALIAS__LINK = 'l';
     $nestedLinkExecutionStack = $linkExecutionStack;
     $nestedLinkExecutionStack[] = $link;
     $selectedColumnNames = ReferenceLinkBuilder::selectReferencedColumnNames4ReferenceLink($nestedLinkExecutionStack, $columnNames);
     $linkTableAliasPrefix = $TABLE_ALIAS__LINK . $link->linkId;
     $statement = $this->assembleDatasetSourceStatement($callcontext, $link->dataset, $selectedColumnNames);
     $statement->addTableAliasPrefix($linkTableAliasPrefix);
     // adding columns which we use to join with parent dataset
     if (!$link->isRoot()) {
         foreach ($link->columnNames as $columnName) {
             $joinColumnAlias = ReferencePathHelper::assembleDatabaseColumnName($this->getMaximumEntityNameLength(), ReferencePathHelper::assembleReference($link->dataset->name, $columnName));
             $joinTable = $statement->findColumnTable($columnName);
             if (!isset($joinTable)) {
                 $joinTable = $statement->tables[0];
             }
             $joinColumn = $joinTable->findColumnByAlias($joinColumnAlias);
             if (!isset($joinColumn)) {
                 $joinTable->columns[] = new ColumnSection($columnName, $joinColumnAlias);
             }
         }
     }
     // adding columns which we use to join with nested datasets
     if (isset($link->nestedLinks)) {
         foreach ($link->nestedLinks as $nestedLink) {
             foreach ($nestedLink->parentColumnNames as $parentColumnName) {
                 $parentColumnAlias = ReferencePathHelper::assembleDatabaseColumnName($this->getMaximumEntityNameLength(), ReferencePathHelper::assembleReference($link->dataset->name, $parentColumnName));
                 $joinTable = $statement->getColumnTable($parentColumnName);
                 $joinColumn = $joinTable->findColumnByAlias($parentColumnAlias);
                 if (!isset($joinColumn)) {
                     $joinTable->columns[] = new ColumnSection($parentColumnName, $parentColumnAlias);
                 }
             }
         }
     }
     // collecting columns which we need to mark as invisible
     $shouldBeInvisibleColumns = NULL;
     foreach ($statement->tables as $table) {
         if (isset($table->columns)) {
             foreach ($table->columns as $column) {
                 $shouldBeInvisibleColumns[$table->alias][$column->alias] = $column;
             }
         } else {
             $table->columns = array();
             // We do not need any columns
         }
     }
     // adding or making as visible columns which we need to return
     if (isset($selectedColumnNames)) {
         foreach ($selectedColumnNames as $originalColumnName => $selectedColumnName) {
             // we need to show only those columns which are requested.
             // All intermediate columns (which are used to link with nested datasets) will not be shown
             if (array_search($originalColumnName, $columnNames) !== FALSE) {
                 $databaseColumnName = ReferencePathHelper::assembleDatabaseColumnName($this->getMaximumEntityNameLength(), $originalColumnName);
                 $table = $statement->getColumnTable($selectedColumnName, TRUE);
                 $column = $table->findColumnByAlias($databaseColumnName);
                 if (isset($column)) {
                     $column->visible = TRUE;
                     unset($shouldBeInvisibleColumns[$table->alias][$column->alias]);
                 } else {
                     $column = $table->findColumnByAlias($selectedColumnName);
                     if (isset($column)) {
                         // adding clone of the same column with another alias
                         $column = clone $column;
                         $column->visible = TRUE;
                         $column->alias = $databaseColumnName;
                     } else {
                         $column = new ColumnSection($selectedColumnName, $databaseColumnName);
                     }
                     $table->columns[] = $column;
                 }
                 $callcontext->columnMapping[$databaseColumnName] = $originalColumnName;
             }
         }
     }
     if (isset($shouldBeInvisibleColumns)) {
         foreach ($shouldBeInvisibleColumns as $tableColumns) {
             foreach ($tableColumns as $column) {
                 $column->visible = FALSE;
             }
         }
     }
     // supporting nested links
     if (isset($link->nestedLinks)) {
         foreach ($link->nestedLinks as $nestedLink) {
             $nestedStatement = $this->assembleConnectedDatasetSourceStatement($callcontext, $nestedLink, $columnNames, $nestedLinkExecutionStack);
             foreach ($nestedLink->parentColumnNames as $referencePointColumnIndex => $parentColumnName) {
                 // preparing parent table alias
                 $parentColumnAlias = ReferencePathHelper::assembleDatabaseColumnName($this->getMaximumEntityNameLength(), ReferencePathHelper::assembleReference($link->dataset->name, $parentColumnName));
                 $parentTableAlias = $statement->getColumnTable($parentColumnAlias)->alias;
                 // linking with parent
                 $nestedColumnName = $nestedLink->columnNames[$referencePointColumnIndex];
                 $nestedColumnAlias = ReferencePathHelper::assembleDatabaseColumnName($this->getMaximumEntityNameLength(), ReferencePathHelper::assembleReference($nestedLink->dataset->name, $nestedColumnName));
                 $nestedStatement->getColumnTable($nestedColumnAlias)->conditions[] = new JoinConditionSection($nestedColumnName, new TableColumnConditionSectionValue($parentTableAlias, $parentColumnName));
             }
             $statement->merge($nestedStatement);
         }
     }
     return $statement;
 }
    protected static function prepareListColumnMappings(array $cubes = NULL, array $parsedUIMetaDataNames) {
        $columnMappings = NULL;

        foreach ($parsedUIMetaDataNames as $columnName => $parsedUIMetaDataName) {
            $datasetColumnName = NULL;
            if ($parsedUIMetaDataName instanceof AttributeParsedUIMetaDataName) {
                $cube = isset($cubes[$parsedUIMetaDataName->datasetName]) ? $cubes[$parsedUIMetaDataName->datasetName] : NULL;
                if (isset($cube)) {
                    $dimension = $cube->getDimension($parsedUIMetaDataName->name);

                    $references = NULL;
                    if (isset($parsedUIMetaDataName->columnName)) {
                        $column = $cube->factsDataset->getColumn($dimension->attributeColumnName);
                        $branch = $column->findBranch($parsedUIMetaDataName->columnName);
                        // the dimension column can be a branch, not a dimension dataset column
                        if (!isset($branch) && isset($dimension->datasetName)) {
                            list($adjustedDimensionDatasetName) = gd_data_controller_metamodel_adjust_dataset_name($dimension->datasetName);
                            $references[] = ReferencePathHelper::assembleReference($adjustedDimensionDatasetName, $parsedUIMetaDataName->columnName);
                        }
                    }

                    if (isset($references)) {
                        $references[] = ReferencePathHelper::assembleReference($cube->factsDatasetName, $dimension->attributeColumnName);
                        $datasetColumnName = ReferencePathHelper::assembleReferencePath($references);
                    }
                    else {
                        $datasetColumnName = isset($parsedUIMetaDataName->columnName)
                            ? $parsedUIMetaDataName->columnName
                            : $dimension->attributeColumnName;
                    }
                }
                else {
                    $datasetColumnName = $parsedUIMetaDataName->name;
                }
            }
            elseif ($parsedUIMetaDataName instanceof FormulaAttributeParsedUIMetaDataName) {
                $datasetColumnName = $parsedUIMetaDataName->name;
            }
            elseif ($parsedUIMetaDataName instanceof FormulaMeasureParsedUIMetaDataName) {
                $datasetColumnName = $parsedUIMetaDataName->name;
            }

            if (isset($datasetColumnName)) {
                $columnMappings[$columnName] = $datasetColumnName;
            }
        }

        return $columnMappings;
    }
    public function generateStatement(AbstractSQLDataSourceQueryHandler $datasourceHandler, DataControllerCallContext $callcontext, AbstractCubeQueryRequest $request) {
        $statement = $this->prepareSelectedCubeQueryStatement($datasourceHandler, $callcontext, $request);
        if (!isset($request->referencedRequests)) {
            return $statement;
        }

        $combinedStatement = new Statement();

        $metamodel = data_controller_get_metamodel();

        $cubeName = $request->getCubeName();
        $cube = $metamodel->getCube($cubeName);

        $datasetMappedCubeRequests = array($cube->factsDatasetName => $request);

        // preparing list of reference paths
        $referencePaths = NULL;
        foreach ($request->referencedRequests as $referencedRequest) {
            $referencedCubeName = $referencedRequest->getCubeName();
            $referencedCube = $metamodel->getCube($referencedCubeName);
            $referencedDatasetName = $referencedCube->factsDatasetName;

            $referencePath = ReferencePathHelper::assembleReference($referencedDatasetName, NULL);
            $referencePaths[$referencePath] = TRUE; // TRUE - required reference

            $datasetMappedCubeRequests[$referencedDatasetName] = $referencedRequest;
        }

        // finding ways to link the referenced cubes
        $linkBuilder = new ReferenceLinkBuilder();
        $link = $linkBuilder->prepareReferenceBranches($cube->factsDatasetName, $referencePaths);

        // preparing primary cube aggregation statement
        list($isSubqueryRequired, $assembledPrimaryCubeAggregationSections) = $statement->prepareSections(NULL);
        $primaryCubeAggregationTableSection = new SubquerySection(
            Statement::assemble($isSubqueryRequired, NULL, $assembledPrimaryCubeAggregationSections, SelectStatementPrint::INDENT__SUBQUERY, FALSE),
            self::$TABLE_ALIAS__REFERENCED . $link->linkId);

        // adding columns which are returned by primary aggregation
        foreach ($statement->tables as $table) {
            if (!isset($table->columns)) {
                continue;
            }

            foreach ($table->columns as $column) {
                if (!$column->visible) {
                    continue;
                }

                $primaryCubeAggregationTableSection->columns[] = new ColumnSection($column->alias);
            }
        }

        // registering primary cube statement in resulting statement
        $combinedStatement->tables[] = $primaryCubeAggregationTableSection;

        $this->prepareReferencedCubeQueryStatement($datasourceHandler, $callcontext, $combinedStatement, $datasetMappedCubeRequests, $link);

        return $combinedStatement;
    }
    protected function prepareColumns(&$datasets) {
        // prepare dataset columns for import
        $datasetCount = count($datasets);
        for ( $i = 0; $i < $datasetCount; $i++ ) {
            foreach ( $datasets[$i]->columns as &$column ) {
                // unset private properties. TODO export only public properties
                unset($column->type->referencedApplicationType);
                unset($column->type->referencedDatasetName);
                unset($column->type->referencedColumnName);

                // reset storage flags
                if ($column->persistence == ColumnMetaData::PERSISTENCE__STORAGE_CREATED) {
                    $column->persistence = ColumnMetaData::PERSISTENCE__NO_STORAGE;
                }

                // re-link column references
                list($datasetName, $columnName) = ReferencePathHelper::splitReference($column->type->applicationType);
                if ( !empty($datasetName) ) {
                    $reference_datatype = null;
                    for ( $j = 0; $j < $datasetCount; $j++ ) {
                        if ( empty($reference_datatype) && $datasetName == $datasets[$j]->originalName ) {
                            $reference_datatype = ReferencePathHelper::assembleReference($datasets[$j]->name,$columnName);
                        }
                    }
                    if ( empty($reference_datatype) ) {
                        throw new Exception('Dataset column reference lookup failed. Dataset: '.$datasets[$j]->name.' Column: '.$column->name.' Type: '.$column->type->applicationType);
                    } else {
                        $column->type->applicationType = $reference_datatype;
                    }
                }
            }
        }
    }
    protected function processLookupDataTypes(DataTypeUIMetaData $rootDataType, array $lookupDataTypes) {
        $metamodel = data_controller_get_metamodel();

        $datatypeMappings = &drupal_static(__CLASS__ . '::datatypeMappings');

        foreach ($lookupDataTypes as $datasetName) {
            $dataset = $metamodel->getDataset($datasetName);

            $datasetElement = new DataTypeUIMetaData();
            $datasetElement->name = $dataset->name;
            $datasetElement->publicName = $dataset->publicName;
            $datasetElement->description = $dataset->description;

            // processing primary key and columns which contain unique values
            foreach ($dataset->getColumns() as $column) {
                if (!$column->isVisible()) {
                    continue;
                }

                if (!isset($column->type->applicationType)) {
                    continue;
                }

                if (!$column->isKey()) {
                    if (!isset($datatypeMappings[$column->type->applicationType])) {
                        continue;
                    }

                    $datatypeMapping = $datatypeMappings[$column->type->applicationType];
                    if (!$datatypeMapping->isVisible) {
                        continue;
                    }
                    if (!$datatypeMapping->isKeyCompatible) {
                        continue;
                    }
                }

                $columnElement = new DataTypeUIMetaData();
                $columnElement->name = ReferencePathHelper::assembleReference($dataset->name, $column->name);
                $columnElement->publicName = $column->publicName;
                $columnElement->description = $column->description;
                $columnElement->isSelectable = TRUE;
                $columnElement->parentName = $datasetElement->name;
                $columnElement->isParentShownOnSelect = TRUE;
                $columnElement->isKeyCompatible = TRUE;
                $datasetElement->registerElement($columnElement);
            }

            $rootDataType->registerElement($datasetElement);
        }
    }