All Downloads are FREE. Search and download functionalities are using the official Maven repository.

core.pure.graphFetch.graphExtension.pure Maven / Gradle / Ivy

There is a newer version: 4.57.1
Show newest version
// Copyright 2020 Goldman Sachs
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import meta::pure::graphFetch::*;
import meta::pure::graphFetch::routing::*;
import meta::pure::lineage::scanProperties::*;
import meta::pure::lineage::scanProperties::propertyTree::*;
import meta::pure::mapping::*;
import meta::external::store::model::*;
import meta::pure::mapping::modelToModel::contract::*;
import meta::pure::mapping::modelToModel::inMemory::*;
import meta::pure::metamodel::constraint::*;
import meta::pure::metamodel::serialization::grammar::*;
import meta::pure::milestoning::*;
import meta::pure::extension::*;


Class meta::pure::graphFetch::ExtendedRootGraphFetchTree extends RootGraphFetchTree
{
   requiredQualifiedProperties: QualifiedProperty[*];
   constraintsExclusions: String[*];
}

Class meta::pure::graphFetch::ExtendedPropertyGraphFetchTree extends PropertyGraphFetchTree
{
   requiredQualifiedProperties: QualifiedProperty[*];
   constraintsExclusions: String[*];
}

Class {doc.doc = 'Nodes that were added by the system to meet e.g. constraints requirements'} meta::pure::graphFetch::SystemGeneratedPropertyGraphFetchTree extends PropertyGraphFetchTree
{
}

function meta::pure::graphFetch::switchToMilestoneProperties(tree:GraphFetchTree[1], parameters:ValueSpecification[*]): GraphFetchTree[1]
{
   if($tree->instanceOf(PropertyGraphFetchTree) && $tree->cast(@PropertyGraphFetchTree).property->hasGeneratedMilestoningPropertyStereotype() && !$tree->cast(@PropertyGraphFetchTree).property->instanceOf(QualifiedProperty),
      {|
         let pgft = $tree->cast(@PropertyGraphFetchTree);
         ^$pgft(
            property   = $pgft.property->getMilestonedGeneratedQualifiedPropertiesForEdgePointProperty()->filter(x|!$x->isNoArgMilestonedGeneratedQualifiedProperty())->at(0),
            parameters = $parameters,
            subTrees   = $tree.subTrees->map(gft| $gft->switchToMilestoneProperties($parameters))
         );
      },
      | ^$tree(subTrees = $tree.subTrees->map(gft| $gft->switchToMilestoneProperties($parameters)))
   );
}



function meta::pure::graphFetch::addSubTrees(onto:GraphFetchTree[1], subTrees:PropertyGraphFetchTree[*]): GraphFetchTree[1]
{
   $subTrees->fold({st, node|$node->addSubTree($st)}, $onto);
}

function <> meta::pure::graphFetch::addSubTree(onto:GraphFetchTree[1], subTree:PropertyGraphFetchTree[1]): GraphFetchTree[1]
{
   let existingSubTrees = $onto.subTrees->filter(st|$st->cast(@PropertyGraphFetchTree).property == $subTree.property && $st->cast(@PropertyGraphFetchTree).subType == $subTree.subType);
   assert($existingSubTrees->size() < 2, 'Too many existing');
   $existingSubTrees->match([
      stree0: PropertyGraphFetchTree[0] | ^$onto(subTrees=$onto.subTrees->concatenate($subTree)),
      stree : PropertyGraphFetchTree[1] | ^$onto(subTrees=$onto.subTrees->cast(@PropertyGraphFetchTree)->filter(st|!$st->forSameProperty($stree))->concatenate($stree->addSubTrees($subTree.subTrees->cast(@PropertyGraphFetchTree))))
   ]);
}

function <> meta::pure::graphFetch::forSameProperty(t1:PropertyGraphFetchTree[1], t2:PropertyGraphFetchTree[1]): Boolean[1]
{
   $t1.property == $t2.property && $t1.parameters == $t2.parameters && $t1.alias == $t2.alias && $t1.subType == $t2.subType;
}

function meta::pure::graphFetch::addSubTypeTrees(onto:GraphFetchTree[1], subTypeTrees:SubTypeGraphFetchTree[*]): GraphFetchTree[1]
{
   $subTypeTrees->fold({st, node|$node->addSubTypeTree($st)}, $onto);
}

function <> meta::pure::graphFetch::addSubTypeTree(onto:GraphFetchTree[1], subTree:SubTypeGraphFetchTree[1]): GraphFetchTree[1]
{
   let existingSubTrees = $onto.subTypeTrees->filter(st| $st->forSameSubTypeClass($subTree));
   assert($existingSubTrees->size() < 2, 'Too many existing');

   let treeWithSubTypeTrees= $existingSubTrees->match([
      stree0: SubTypeGraphFetchTree[0] | ^$onto(subTypeTrees = $onto.subTypeTrees->concatenate($subTree)),
      stree : SubTypeGraphFetchTree[1] | ^$onto(subTrees = $onto.subTypeTrees->cast(@SubTypeGraphFetchTree)->filter(st|!$st->forSameSubTypeClass($stree))
                                                              ->concatenate($stree->addSubTypeTrees($subTree.subTypeTrees->cast(@SubTypeGraphFetchTree))))        // recursively add subtype trees if already existing subtype tree found

   ]);
   let treeWithBasePropertiesCopiedToSubTypeTrees = $treeWithSubTypeTrees->meta::pure::graphFetch::copyPropertiesFromBaseTreeToSubTypeTrees();
}

function <> meta::pure::graphFetch::copyPropertiesFromBaseTreeToSubTypeTrees(subTypeTrees:SubTypeGraphFetchTree[*], baseTree:GraphFetchTree[1]): SubTypeGraphFetchTree[*]
{
   $subTypeTrees->map(st|$st->addSubTrees($baseTree.subTrees->cast(@PropertyGraphFetchTree)))->cast(@SubTypeGraphFetchTree);
}

function <> meta::pure::graphFetch::copyPropertiesFromBaseTreeToSubTypeTrees(baseTree:GraphFetchTree[1]): GraphFetchTree[1]
{
  ^$baseTree(subTypeTrees = meta::pure::graphFetch::copyPropertiesFromBaseTreeToSubTypeTrees($baseTree.subTypeTrees, $baseTree));
}

function <> meta::pure::graphFetch::forSameSubTypeClass(t1:SubTypeGraphFetchTree[1], t2:SubTypeGraphFetchTree[1]): Boolean[1]
{
   $t1.subTypeClass == $t2.subTypeClass;
}

function meta::pure::graphFetch::allClasses(root:GraphFetchTree[1]): Class[*]
{
   $root->extractClasses()->removeDuplicates();
}

function <> meta::pure::graphFetch::extractClasses(tree:GraphFetchTree[1]): Class[*]
{
   let class = $tree->match([
      r: RootGraphFetchTree[1] | $r.class,
      a: Any[1]                     | []->cast(@Class)
   ]);

   let propertyClasses = $tree.subTrees->cast(@PropertyGraphFetchTree)
      ->map(pgft| $pgft.property.genericType.rawType)
      ->filter(t | $t->instanceOf(Class))
      ->cast(@Class);

   $class->concatenate($propertyClasses)->concatenate($tree.subTrees->map(st| $st->extractClasses()));
}

function meta::pure::graphFetch::printTree(root:GraphFetchTree[1]): Any[*]
{
   $root->treeToString()->println();
}

function meta::pure::graphFetch::treeToString(root:GraphFetchTree[1]): String[1]
{
   $root->nodeToString('');
}

function meta::pure::graphFetch::treeToString(root:GraphFetchTree[1], debug:DebugContext[1]): String[1]
{
   $root->nodeToString(if($debug.debug,|$debug.space, |''));
}

function <> meta::pure::graphFetch::nodeToString(node:GraphFetchTree[1], indent: String[1]): String[1]
{
   let indentSep = '  ';

   let requiredQP = $node->match([
      r: ExtendedRootGraphFetchTree[1] | $r.requiredQualifiedProperties,
      p: ExtendedPropertyGraphFetchTree[1]  | $p.requiredQualifiedProperties,
      r: RoutedRootGraphFetchTree[1]   | $r.requiredQualifiedProperties,
      p: RoutedPropertyGraphFetchTree[1]    | $p.requiredQualifiedProperties,
      a: GraphFetchTree[1]                  | []
   ]);

   let requiredQPText = if($requiredQP->isEmpty(), |'', |' [requires: '+$requiredQP.name->joinStrings(',')+ ']');

   let constraintsExclusions = $node->match([
      r: ExtendedRootGraphFetchTree[1] | $r.constraintsExclusions,
      p: ExtendedPropertyGraphFetchTree[1]  | $p.constraintsExclusions,
      r: RoutedRootGraphFetchTree[1]   | $r.constraintsExclusions,
      p: RoutedPropertyGraphFetchTree[1]    | $p.constraintsExclusions,
      a: GraphFetchTree[1]                  | []
   ]);

   let constraintsExclusionsText = if($constraintsExclusions->isEmpty(), |'', |' [exclude constraints: '+ $constraintsExclusions->joinStrings(',')+ ']');

   let nodeText = $node->match([
      rr: RoutedRootGraphFetchTree[1] | $indent + $rr.class.name->toOne() + $rr.sets.id->map(id|$id->toString())->joinStrings('[',',',']'),
      rp: RoutedPropertyGraphFetchTree[1]  | $indent + $rp.property.name->toOne()+if($rp.property->instanceOf(QualifiedProperty), |'('+$rp.parameters->evaluateAndDeactivate()->map(p|$p->printValueSpecification(''))->joinStrings(', ')+')', |'') + $rp.sets.id->map(id|$id->toString())->joinStrings(' sets: [',',',']') + $rp.propertyMapping->map(pm|$pm.targetSetImplementationId)->joinStrings(' targetSets: [',',',']'),
      r: RootGraphFetchTree[1]        | $indent + $r.class.name->toOne(),
      p: PropertyGraphFetchTree[1]         | $indent + $p.property.name->toOne()+if($p.property->instanceOf(QualifiedProperty), |'('+$p.parameters->evaluateAndDeactivate()->map(p|$p->printValueSpecification(''))->joinStrings(', ')+')', |'')+if($p.subType->isNotEmpty(),| '->subType(' + $p.subType->toOne().name->toOne() + ')', | '')
   ]) +  $requiredQPText + $constraintsExclusionsText;

  $nodeText +
    if($node.subTypeTrees->isEmpty() && $node.subTrees->isEmpty(),
      | '',
      | '\n' + $indent + '(' +
           if($node.subTrees->isEmpty(),       |'', | '\n' + $node.subTrees->map(st|$st->nodeToString($indent+$indentSep))->joinStrings( '\n')) +
           if($node.subTypeTrees->isEmpty(),   |'', | '\n'+ $node.subTypeTrees->map(rst|$rst->subTypeNodeToString($indent+$indentSep))->joinStrings('\n')) +
        '\n'+  $indent + ')'
    );
}

function <> meta::pure::graphFetch::subTypeNodeToString(stgft:SubTypeGraphFetchTree[1], indent: String[1]): String[1]
{
  let indentSep = '  ';

  $indent + '->SubType(' + $stgft.subTypeClass->toOne().name->toOne() + ')'
          + if($stgft.subTrees->isEmpty(),
               |'',
               |'\n' + $indent + '(' + $stgft.subTrees->map(st|$st->nodeToString( $indent + $indentSep))->joinStrings('\n', '\n', '\n') + $indent + ')';
            );
}

function meta::pure::graphFetch::getNodesOrdered(tree: GraphFetchTree[1]):GraphFetchTree[*]
{
   $tree->match([
      cls : ClusteredGraphFetchTree[1] | $cls.tree->getNodesOrdered(),
      g   : GraphFetchTree[1]          | $tree->concatenate($tree.subTrees->map(x | $x->getNodesOrdered()))
   ])
}

function meta::pure::graphFetch::getPathsOrdered(tree:GraphFetchTree[1]):String[*]
{
   $tree->match([
      cls : ClusteredGraphFetchTree[1] | $cls.tree->getPathsOrdered(),
      {g : GraphFetchTree[1]|
         let name = $g->nodePathName();
         $name->concatenate($tree.subTrees->map(x | $x->getPathsOrdered())->map(subPath| $name+'.'+$subPath));
      }
   ])
}

function meta::pure::graphFetch::getPathTo(tree:GraphFetchTree[1], node:GraphFetchTree[1]):String[0..1]
{
   if($tree == $node,
      | [],
      | $tree->match([
           cls : ClusteredGraphFetchTree[1] | $cls.tree->getPathTo($node),
           g   : GraphFetchTree[1]          | $g.subTrees->map(x | getPathTo($g->nodePathName(), $x, $node));
        ])->first()
   );
}

function <> meta::pure::graphFetch::getPathTo(path:String[1], tree:GraphFetchTree[1], node:GraphFetchTree[1]):String[0..1]
{
   if($tree == $node,
      | $path,
      | $tree->match([
           cls : ClusteredGraphFetchTree[1] | getPathTo($path, $cls.tree, $node),
           g   : GraphFetchTree[1]          | $g.subTrees->map(x | getPathTo($path+'.'+$g->nodePathName(), $x, $node));
        ])->first()
   );
}

function meta::pure::graphFetch::nodePathName(tree:GraphFetchTree[1]): String[1]
{
   $tree->match([
      r:RootGraphFetchTree[1] | 'root',
      p:PropertyGraphFetchTree[1]  | if($p.alias->isEmpty(), |$p.property.name->toOne() + if($p.property->instanceOf(QualifiedProperty), | $p.parameters->evaluateAndDeactivate()->map(x | $x->meta::pure::router::printer::asString())->joinStrings('(', ', ', ')'), | ''), |$p.alias)->toOne() +
                                     if($p->instanceOf(RoutedPropertyGraphFetchTree) && $p->cast(@RoutedPropertyGraphFetchTree).sets->isNotEmpty(), | $p->cast(@RoutedPropertyGraphFetchTree).sets->map(s | $s.id)->joinStrings('@', '+', ''), | '')
    ]);
}

function meta::pure::graphFetch::typeFromGraphFetchTree(g: GraphFetchTree[1]):Type[1]
{
   $g->match([
      r : RootGraphFetchTree[1] | $r.class,
      p : PropertyGraphFetchTree[1]  | if($p.subType->isNotEmpty(), | $p.subType->toOne(), | $p.property->functionReturnType().rawType->toOne())
   ])
}




function <> meta::pure::graphFetch::isUnitFunctionReturnType(tree:PropertyGraphFetchTree[1]): Boolean[1]
{
   $tree.property->functionReturnType().rawType->toOne()->instanceOf(Unit)
}

function meta::pure::graphFetch::sortTree(tree:GraphFetchTree[1]): GraphFetchTree[1]
{
   let sortedPropertyTrees = $tree.subTrees->cast(@PropertyGraphFetchTree)
                                ->sortBy(pgft| $pgft->getSubTreeName())
                                ->map(pgft| $pgft->sortTree());

   let sortedSubTypeTrees = $tree.subTypeTrees
                               ->sortBy(stgft| $stgft->getSubTreeName())
                               ->map(stgft| $stgft->sortTree());

   ^$tree
   (
      subTrees = $sortedPropertyTrees,
      subTypeTrees = $sortedSubTypeTrees->cast(@SubTypeGraphFetchTree)
   );
}

function meta::pure::graphFetch::getSubTreeName(tree:GraphFetchTree[1]): String[1]
{
  $tree->match([
    pgft:PropertyGraphFetchTree[1]|
        let subTypeName = if($pgft.subType->isEmpty(),|'',|$pgft.subType->toOne().name->toOne());
        $pgft.property.name->toOne() + $subTypeName;,
    stgft:SubTypeGraphFetchTree[1] |
         $stgft.subTypeClass->toOne().name->toOne();
   ]);
}

function meta::pure::graphFetch::pathsForConstraintFunctions(class:Class[1], functions:FunctionDefinition[*]): List[*]
{
   let generalizations = $class->getAllTypeGeneralisations();
   let functionsPaths  = $functions->map(c| $c.expressionSequence->at(0)->evaluateAndDeactivate()->scanProperties());
   $functionsPaths.result->filter(path| $path.values->isNotEmpty() && $path.values->at(0).class->in($generalizations));
}

function meta::pure::graphFetch::ensureConstraintsRequirements(treeRoot:RootGraphFetchTree[1]): RootGraphFetchTree[1]
{
   let constraintsForClass = {c:Class[1] |
      let allConstraints = $c->getAllTypeGeneralisations()->filter(x| $x->instanceOf(ElementWithConstraints))->cast(@ElementWithConstraints).constraints;
      $allConstraints.functionDefinition->concatenate($allConstraints.messageFunction);
   };
   ensureFunctionRequirements($treeRoot, $treeRoot.class, $constraintsForClass, [], true)->cast(@RootGraphFetchTree);
}

function meta::pure::graphFetch::ensureFunctionRequirementsFromLambda(node:GraphFetchTree[1], class:Class[1], f:FunctionDefinition[1], processed:Class[*], ensureConstraintsForSubTrees:Boolean[1]): GraphFetchTree[1]
{
   let paths  =  meta::pure::graphFetch::pathsForConstraintFunctions($class, $f);
   let qualifiedPropertyPaths    = $paths->filter(path| $path.values->exists(x| $x.property->instanceOf(QualifiedProperty)));
   let inlinedPropertyTree       = $paths->buildPropertyTree()->inlineQualifiedPropertyNodes();
   let inlinedGraphTree          = $inlinedPropertyTree->propertyTreeToGraphFetchTree($class);
   let inlinedPropertyGraphTrees = $inlinedGraphTree.subTrees->cast(@PropertyGraphFetchTree);
   let withFoundProperties       = $node->addSubTrees($inlinedPropertyGraphTrees);
   let updatedForClass           = $qualifiedPropertyPaths->fold({path, gt| $gt->recordQualifiedProperties($path)}, $withFoundProperties);

   if($ensureConstraintsForSubTrees,
      {|
         let updatedProcessed          = $processed->add($class);
         let newSubTrees = $updatedForClass.subTrees->map({st|
            let returns = if($st->cast(@PropertyGraphFetchTree).subType->isEmpty(),
                            | $st->cast(@PropertyGraphFetchTree).property->functionReturnType().rawType->toOne(),
                            | $st->cast(@PropertyGraphFetchTree).subType->toOne()
                            );
            if($returns->instanceOf(Class) && !$updatedProcessed->contains($returns),
               | $st->ensureFunctionRequirementsFromLambda($returns->cast(@Class), $f, $updatedProcessed, $ensureConstraintsForSubTrees),
               | $st
            );
         });
         ^$updatedForClass(subTrees=$newSubTrees);
      },
      | $updatedForClass
   );

}


function meta::pure::graphFetch::ensureFunctionRequirements(node:GraphFetchTree[1], class:Class[1], f:Function<{Class[1]->FunctionDefinition[*]}>[1], processed:Class[*], ensureConstraintsForSubTrees:Boolean[1]): GraphFetchTree[1]
{
   let constraintResult          = pathsForConstraintFunctions($class, $f->eval($class));
   let qualifiedPropertyPaths    = $constraintResult->filter(path| $path.values->exists(x| $x.property->instanceOf(QualifiedProperty)));
   let inlinedPropertyTree       = $constraintResult->buildPropertyTree()->inlineQualifiedPropertyNodes();
   let inlinedGraphTree          = $inlinedPropertyTree->propertyTreeToGraphFetchTree($class);
   let inlinedPropertyGraphTrees = $inlinedGraphTree.subTrees->cast(@PropertyGraphFetchTree);
   let withFoundProperties       = $node->addSubTrees($inlinedPropertyGraphTrees);
   let updatedForClass           = $qualifiedPropertyPaths->fold({path, gt| $gt->recordQualifiedProperties($path)}, $withFoundProperties);
   let updatedProcessed          = $processed->add($class);

   if($ensureConstraintsForSubTrees,
      {|
         let newSubTrees = $updatedForClass.subTrees->map({st|
            let returns = if($st->cast(@PropertyGraphFetchTree).subType->isEmpty(),
                            | $st->cast(@PropertyGraphFetchTree).property->functionReturnType().rawType->toOne(),
                            | $st->cast(@PropertyGraphFetchTree).subType->toOne()
                            );
            if($returns->instanceOf(Class) && !$updatedProcessed->contains($returns),
               | $st->ensureFunctionRequirements($returns->cast(@Class), $f, $updatedProcessed, $ensureConstraintsForSubTrees),
               | $st
            );
         });
         ^$updatedForClass(subTrees=$newSubTrees);
      },
      | $updatedForClass
   );
}

function meta::pure::graphFetch::canEvaluateForTree(pTree:GraphFetchTree[1], gfTree:GraphFetchTree[1]): Boolean[1]
{
   $pTree->match([
     r:RootGraphFetchTree[1] | if(!$gfTree->instanceOf(RootGraphFetchTree),
                                      | false,
                                      | let subTreesIndexedByProperty = $gfTree.subTrees->groupBy(st | $st->cast(@PropertyGraphFetchTree).property);

                                        ($gfTree->cast(@RootGraphFetchTree).class == $r.class || $gfTree->cast(@RootGraphFetchTree).class->_subTypeOf($r.class))
                                        && $r.subTrees->forAll(st | let reqSubTrees = $subTreesIndexedByProperty->get($st->cast(@PropertyGraphFetchTree).property).values;
                                                                    $reqSubTrees->isNotEmpty() && $reqSubTrees->exists(reqSubTree | $st->canEvaluateForTree($reqSubTree));););,
     p:PropertyGraphFetchTree[1]  | if(!$gfTree->instanceOf(PropertyGraphFetchTree),
                                      | false,
                                      | let subTreesIndexedByProperty = $gfTree.subTrees->groupBy(st | $st->cast(@PropertyGraphFetchTree).property);
                                        $gfTree->cast(@PropertyGraphFetchTree).property == $p.property
                                        && $p.subTrees->forAll(st | let reqSubTrees = $subTreesIndexedByProperty->get($st->cast(@PropertyGraphFetchTree).property).values;
                                                                    $reqSubTrees->isNotEmpty() && $reqSubTrees->exists(reqSubTree | $st->canEvaluateForTree($reqSubTree));););
   ])
}

function meta::pure::graphFetch::calculateSourceTree(tree:RootGraphFetchTree[1], mapping: Mapping[1], extensions:meta::pure::extension::Extension[*]): RootGraphFetchTree[1]
{
   let replaced = $tree->replaceQualifiedPropertiesWithRequiredProperties();

   let rootSetImplementation = $tree->match([r:RoutedRootGraphFetchTree[1]|$r.sets->toOne(),
                                             r:RootGraphFetchTree[1]|$mapping->rootClassMappingByClass($replaced.class)]);

   assert($rootSetImplementation->isNotEmpty(), |'Mapping ' + $mapping->elementToPath() + ' does not map class from tree: ' + $replaced.class->elementToPath());

   $rootSetImplementation->toOne()->match($extensions.graphExtension_calculateSourceTree->map(ext | $ext->eval($replaced))->concatenate([
      {pisi: PureInstanceSetImplementation[1]  |
         assert($pisi.srcClass->isNotEmpty() && $pisi.srcClass->toOne()->instanceOf(Class), 'Pure mapping does not have a class as ~src for root of tree');
         let srcClass     = $pisi.srcClass->toOne()->cast(@Class);
         let root         = ^RootGraphFetchTree(class=$srcClass);
         let withChildren = $root->enrichSourceTreeNode($pisi, $replaced, $extensions)->mergeSubTrees()->sortTree()->cast(@RootGraphFetchTree);
      }
   ])->toOneMany());
}


function meta::pure::graphFetch::mergeSubTrees(tree:GraphFetchTree[1]): GraphFetchTree[1]
{
   ^$tree(subTrees = $tree.subTrees->cast(@PropertyGraphFetchTree)->mergeSubTreesRecur());
}

function meta::pure::graphFetch::mergeSubTreesRecur(subTrees:PropertyGraphFetchTree[*]): PropertyGraphFetchTree[*]
{
   let subTreesGroupedByProperty = $subTrees->groupBy(c|$c->cast(@PropertyGraphFetchTree).property);
   let updatedSubTrees = $subTreesGroupedByProperty->keyValues()->map(p|mergePropertiesFromBaseSubTreeToSubTypedSubTree($p.second.values->cast(@PropertyGraphFetchTree)));
}

function <> meta::pure::graphFetch::mergePropertiesFromBaseSubTreeToSubTypedSubTree(subTrees:PropertyGraphFetchTree[*]): PropertyGraphFetchTree[*]
{
   let subTreeWithBaseClass = $subTrees->filter(st|$st->cast(@PropertyGraphFetchTree).subType->isEmpty())->cast(@PropertyGraphFetchTree);
   let subTreesWithSubTypes = $subTrees->removeAll($subTreeWithBaseClass);
   let updatedSubTreeWithBaseClass = $subTreeWithBaseClass->first()->map(st| ^$st(subTrees = $subTreeWithBaseClass->first().subTrees->concatenate($subTreeWithBaseClass->tail().subTrees)->cast(@PropertyGraphFetchTree)->mergeSubTreesRecur()));
   $updatedSubTreeWithBaseClass->concatenate($subTreesWithSubTypes->map(st|^$st(subTrees = $st.subTrees->concatenate($subTreeWithBaseClass->cast(@PropertyGraphFetchTree).subTrees)->cast(@PropertyGraphFetchTree)->mergeSubTreesRecur())));
}

function <> meta::pure::graphFetch::enrichSourceTreeNodeForProperty(srcNode:GraphFetchTree[1], setImplementation: PureInstanceSetImplementation[1], tgtPgft:PropertyGraphFetchTree[1], extensions:meta::pure::extension::Extension[*], visitedFunctions:Map[1]): Pair>[1]
{
   let owner = $srcNode->match([
      r:RootGraphFetchTree[1] | $r.class,
      p:PropertyGraphFetchTree[1]  | if ($p.subType->isNotEmpty(), |$p.subType->toOne(), | $p.property->functionReturnType().rawType->cast(@Class)->toOne())
   ]);

   let isPropertyTemporalMilestoned = $tgtPgft.property->hasGeneratedMilestoningPropertyStereotype();

   let requiredProperty = if($isPropertyTemporalMilestoned, |$setImplementation.class->propertyByName($tgtPgft.property->edgePointPropertyName()->toOne()), |$tgtPgft.property);
   let propertyMappings = $setImplementation.propertyMappings->filter(pm|$pm.property == $requiredProperty)->cast(@PurePropertyMapping);

   if($propertyMappings->isNotEmpty() || ($requiredProperty->isNotEmpty() && ($requiredProperty->toOne()->isPropertyAutoMapped($setImplementation) || $setImplementation->isNoMappingDefaultToEmpty($requiredProperty->toOne()) || $setImplementation->isPartOfMerge())),
      {|
         let returnType   = $tgtPgft.property->functionReturnType().rawType->toOne();

         let childSetImpls = $returnType->match([
            {c:Class[1]|
               let childSIs = $tgtPgft->match([
                  r:RoutedPropertyGraphFetchTree[1] | if($r.sets->isNotEmpty(), |$r.sets, |$setImplementation.parent->rootClassMappingByClass($c)),
                  r:PropertyGraphFetchTree[1]       | $setImplementation.parent->rootClassMappingByClass($c)
               ]);

               let childSIsResolved = $childSIs->map(childSI|
                  $childSI->match([
                     {o:OperationSetImplementation[1] |
                        assert($o.parameters.setImplementation->forAll(s|$s->instanceOf(PureInstanceSetImplementation)), |'Unsupported types: ' + $o.parameters.setImplementation->filter(i|!$i->instanceOf(PureInstanceSetImplementation))->type()->elementToPath());
                        $o->meta::pure::router::clustering::resolveInstanceSetImplementations();
                     },
                     {e:EmbeddedSetImplementation[1] |
                        [];
                     },
                     {i:InstanceSetImplementation[1] |
                        assert($i->instanceOf(PureInstanceSetImplementation), 'Unsupported type: ' + $childSI->type()->elementToPath());
                        $i;
                     }
                  ])->cast(@PureInstanceSetImplementation)
               );

               let allowComplexPropertyMappingWithoutChildSI = $requiredProperty->toOne()->isPropertyAutoMapped($setImplementation);
               assert($propertyMappings->isEmpty() || $childSIsResolved->isNotEmpty() || $allowComplexPropertyMappingWithoutChildSI , |'Mapping ' + $setImplementation.parent->elementToPath() + ' does not map class from tree: ' + $c->elementToPath());
               $childSIsResolved;
            },
            {a:Any[1]| []}
         ]);

         let propertyPaths = $propertyMappings->match([
            {none:PurePropertyMapping[0] |
               if($requiredProperty->toOne()->isPropertyAutoMapped($setImplementation),
                  {|
                     let path = ^PropertyPathNode(
                        class    = $setImplementation.srcClass->toOne()->cast(@Class),
                        property = $setImplementation->noMappingPassThruSourceProperty($requiredProperty->toOne())->toOne()
                     );
                  ^ScanPropertiesState(current=list($path), result=list($path), visitedFunctions = ^Map());
                  },
                  {|
                    assert($setImplementation->isNoMappingDefaultToEmpty($requiredProperty->toOne()) || $setImplementation->isPartOfMerge(), |'No mapping found for property \'' + $tgtPgft.property.name->toOne() + '\'');
                    [];
                  }
               );
            },
            {pms:PurePropertyMapping[*] |
              let result = $pms->fold({pm, prevRes | let result = $pm.transform.expressionSequence->at(0)->evaluateAndDeactivate()->scanProperties(noDebug(), ^ScanConfig(scanClasses=true, explodeMilestonedProperties=false), $prevRes->last()->getVisitedFunctions());
                                                     $prevRes->concatenate($result);
                                      },
                                      [^ScanPropertiesState(current = emptyPath(), result = [], visitedFunctions = $visitedFunctions)]
                                    );

              $result->slice(1, $result->size());
            }
         ]);

         let inlinedPropertyTree = $propertyPaths.result->buildPropertyTree()->inlineQualifiedPropertyNodes();

         // Property tree root node may not start at owner (e.g. when the source of a mapping is wrapped in a new class and passed to a property mapping).
         // Here we try and find the part of the tree that starts at the owner - in usual case we will return the same tree but now also handle case mentioned above. 
         // Due to subTypes we could end up returning multiple trees hence we check if there is only one and start there, otherwise continue with old behaviour. 
         let treeStartingAtOwner = findSubTreeWithOwner($inlinedPropertyTree, $owner);

         let inlinedGraphTree = if($treeStartingAtOwner->size() == 1, 
                                    | $treeStartingAtOwner->toOne()->propertyTreeToGraphFetchTree($owner)->removeDummyProperties(),
                                    | $inlinedPropertyTree->propertyTreeToGraphFetchTree($owner)->removeDummyProperties()
                                );

         // copy common properties from base type tree to subtype trees at property level
         // TODO - remove when propertySubTypes are embedded in propertyTrees
         let setClasses = $childSetImpls->map(s | $s.srcClass);
         let inlinedPropertyGraphTrees = $inlinedGraphTree.subTrees->cast(@PropertyGraphFetchTree);

         let withSubTypes        = $inlinedPropertyGraphTrees->concatenate($inlinedPropertyGraphTrees->map(pgft | let appendAtPath = $propertyPaths.current->filter(path| $path.values->size() > 1)->cast(@List);
                                                                                                            if($appendAtPath->isEmpty(),
                                                                                                               | $setClasses->map(sc | if($sc->getAllTypeGeneralisationsExcluded()->contains($pgft.property.genericType.rawType->toOne()), | ^$pgft(subType=$sc->cast(@Class)), | [])),
                                                                                                               | let val = list($appendAtPath->map(x|$x.values)->cast(@PropertyPathNode)->tail());
                                                                                                                 $pgft->getSubTreeNodesAtPath($val, $setClasses->cast(@Class), $extensions);
                                                                                                            );
                                                                                                     ));
         let srcNodeProperties   = $withSubTypes->filter(p | $owner->getAllTypeGeneralisations()->contains($p.property->meta::pure::functions::meta::ownerClass()));
         let withFoundProperties = $srcNode->addSubTrees($srcNodeProperties);


         // copy common properties from base type tree to subtype trees at root level
         let withRootSubTypes= if($srcNode->instanceOf(RootGraphFetchTree), //TODO : will be handled properly when property subtype gfts are moved within property access gfts
                                   | let sourceNodeWithSubTypes = $srcNode->addSubTypeTrees($inlinedGraphTree.subTypeTrees);
                                     ^$withFoundProperties(subTypeTrees=$sourceNodeWithSubTypes.subTypeTrees);, // copy properties except current one
                                   | $withFoundProperties
                                  );

         let withNextSetImpl     = $childSetImpls->fold({setImpl, tree |
                                                        let appendAtPaths = $propertyPaths.current->removeDuplicates();
                                                        if($appendAtPaths->isEmpty(),
                                                            | $tree->enrichSourceTreeNode($setImpl, $tgtPgft, $extensions),
                                                            | $appendAtPaths->fold({path,tree |
                                                                      if($path.values->isEmpty(),
                                                                        | $tree->enrichSourceTreeNode($setImpl, $tgtPgft, $extensions),
                                                                        | $tree->enrichSourceTreeNodeAtPath($path, $setImpl, $tgtPgft, $extensions))
                                                              }, $tree)
                                                        );
                                                        },
                                                        $withRootSubTypes
                                                      );

         let withPassThruSubTrees = if($childSetImpls->isEmpty() && $returnType->instanceOf(Class) && $requiredProperty->toOne()->isPropertyAutoMapped($setImplementation),
                                      |let appendAtPaths = $propertyPaths.current->filter(path| $path.values->isNotEmpty());
                                       if($appendAtPaths->isEmpty(),
                                          | $withNextSetImpl->addPassThroughSubTrees($tgtPgft, $extensions),
                                          | $appendAtPaths->fold({path,tree | $tree->addPassThroughSubTreesAtPath($path, $tgtPgft, $extensions)}, $withNextSetImpl)
                                       );,
                                      |$withNextSetImpl);


         let withQPsRecorded     = $propertyPaths.result->filter(path| $path.values->exists(x| $x.property->instanceOf(QualifiedProperty)))
                                                        ->fold({path, gt| $gt->recordQualifiedProperties($path)}, $withPassThruSubTrees);


         let lastVisitedFunctions = $propertyPaths->last()->getVisitedFunctions();
         if($isPropertyTemporalMilestoned,
            | pair(^$withQPsRecorded(subTrees=$withQPsRecorded.subTrees->map(st| $st->switchToMilestoneProperties($tgtPgft.parameters->evaluateAndDeactivate()))), $lastVisitedFunctions),
            | pair($withQPsRecorded, $lastVisitedFunctions)
         );
      },
      | pair($srcNode, ^Map())
   );
}

function <> meta::pure::graphFetch::getSubTreeNodesAtPath(srcNode:PropertyGraphFetchTree[1], path:List[1], setClasses: Class[*], extensions:meta::pure::extension::Extension[*]): PropertyGraphFetchTree[*]
{
   let head = $path.values->at(0);
   let tail = $path.values->tail();

   let requiredSubTree = $srcNode.subTrees->cast(@PropertyGraphFetchTree)->filter(st|$st.property == $head.property);
   if($tail->isEmpty(),
      | let updatedSubTrees = $requiredSubTree->map(rst | $setClasses->map(sc | if($sc->getAllTypeGeneralisationsExcluded()->contains($rst.property.genericType.rawType->toOne()), | ^$rst(subType=$sc->cast(@Class)), | [])));
        $updatedSubTrees->map(ust | ^$srcNode(subTrees=$srcNode.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $head.property, |$ust, |$st))));,
      | let updatedSubTrees = $requiredSubTree->map(rst | $rst->getSubTreeNodesAtPath(list($tail), $setClasses, $extensions));
        $updatedSubTrees->map(ust | ^$srcNode(subTrees=$srcNode.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $head.property, |$ust, |$st))));
   );
}

function <> meta::pure::graphFetch::enrichSourceTreeNodeForExpression(srcNode:GraphFetchTree[1], exprThisClass:Class[1], expression:ValueSpecification[1]): GraphFetchTree[1]
{
   let propertyPath = $expression->evaluateAndDeactivate()->scanProperties(^List(), [], [], noDebug());
   $srcNode->enrichSourceTreeNodeForPropertyPath($exprThisClass, $propertyPath);
}

function <> meta::pure::graphFetch::enrichSourceTreeNodeForPropertyPath(srcNode:GraphFetchTree[1], baseClass:Class[1], propertyPath:ScanPropertiesState[0..1]): GraphFetchTree[1]
{
   let inlinedPropertyTree        = $propertyPath.result->buildPropertyTree()->inlineQualifiedPropertyNodes();
   let inlinedPropertyGraphTrees  = $inlinedPropertyTree->propertyTreeToGraphFetchTree($baseClass).subTrees->cast(@PropertyGraphFetchTree);
   let srcNodeProperties          = $inlinedPropertyGraphTrees->filter(p | $baseClass->getAllTypeGeneralisations()->contains($p.property->meta::pure::functions::meta::ownerClass()));
   let withFoundProperties        = $srcNode->addSubTrees($srcNodeProperties);
   let withQPsRecorded            = $propertyPath.result->filter(path| $path.values->exists(x| $x.property->instanceOf(QualifiedProperty)))
                                                 ->fold({path, gt| $gt->recordQualifiedProperties($path)}, $withFoundProperties);
}

function <> meta::pure::graphFetch::enrichSourceTreeNode(srcNode:GraphFetchTree[1], setImplementation: PureInstanceSetImplementation[1], tgtNode:GraphFetchTree[1], extensions:meta::pure::extension::Extension[*]): GraphFetchTree[1]
{
   let setContainsExplodeProperty = $setImplementation.propertyMappings->filter(pm|if($pm->instanceOf(PurePropertyMapping),| $pm->cast(@PurePropertyMapping).explodeProperty->isTrue(), |false))->isNotEmpty();
   let subTreeProperties          = $tgtNode.subTrees->cast(@PropertyGraphFetchTree).property.name;
   let requiredTgtSubTrees        = $tgtNode.subTrees->cast(@PropertyGraphFetchTree)->concatenate(if($setContainsExplodeProperty,
                                                                                                     |$setImplementation.propertyMappings->filter(pm | $pm->instanceOf(PurePropertyMapping)).property->map(p | if(!$p.name->in($subTreeProperties),
                                                                                                                                                                                                                  |^PropertyGraphFetchTree(property = $p),
                                                                                                                                                                                                                  |[])),
                                                                                                     |[]));
   let subTrees = $requiredTgtSubTrees->fold({st,n | $n.first->enrichSourceTreeNodeForProperty($setImplementation, $st, $extensions, $n.second)}, 
                                              pair($srcNode, ^Map())
                                            ).first;

   let srcClass = $setImplementation.srcClass->toOne()->cast(@Class);
   let srcNodeOwner = $srcNode->typeFromGraphFetchTree();

   if($setImplementation.filter->isNotEmpty() && $srcClass == $srcNodeOwner,
      | $subTrees->enrichSourceTreeNodeForExpression($srcClass, $setImplementation.filter.expressionSequence->at(0)),
      | $subTrees
   );
}

function <> meta::pure::graphFetch::enrichSourceTreeNodeAtPath(srcNode:GraphFetchTree[1], path:List[1], setImplementation: PureInstanceSetImplementation[1], tgtPgft:PropertyGraphFetchTree[1], extensions:meta::pure::extension::Extension[*]): GraphFetchTree[1]
{
  // If path is empty then we are enriching the source node rather than the subTrees
   if($path.values->isEmpty(),
      | let enrichedSourceNode = enrichSourceTreeNode($srcNode, $setImplementation, $tgtPgft, $extensions);
        ^$enrichedSourceNode(subTrees=$enrichedSourceNode.subTrees->concatenate($srcNode.subTrees)->removeDuplicates());,
      | let head = $path.values->at(0);
        let tail = $path.values->tail();

        if($tail->isEmpty(),
            | ^$srcNode(subTrees=$srcNode.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $head.property, |$st->enrichSourceTreeNode($setImplementation, $tgtPgft, $extensions), |$st))),
            | ^$srcNode(subTrees=$srcNode.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $head.property, |$st->enrichSourceTreeNodeAtPath(list($tail), $setImplementation, $tgtPgft, $extensions), |$st)))
        );
   );
}

function <> meta::pure::graphFetch::addPassThroughSubTrees(srcNode:GraphFetchTree[1], tgtNode:GraphFetchTree[1], extensions:meta::pure::extension::Extension[*]): GraphFetchTree[1]
{
   ^$srcNode(subTrees += $tgtNode.subTrees);
}

function <> meta::pure::graphFetch::addPassThroughSubTreesAtPath(srcNode:GraphFetchTree[1], path:List[1], tgtPgft:PropertyGraphFetchTree[1], extensions:meta::pure::extension::Extension[*]): GraphFetchTree[1]
{
   let head = $path.values->at(0);
   let tail = $path.values->tail();

   if($tail->isEmpty(),
      | ^$srcNode(subTrees=$srcNode.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $head.property, |$st->addPassThroughSubTrees($tgtPgft, $extensions), |$st))),
      | ^$srcNode(subTrees=$srcNode.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $head.property, |$st->addPassThroughSubTreesAtPath(list($tail), $tgtPgft, $extensions), |$st)))
   );
}

function meta::pure::graphFetch::findSubTreeWithOwner(pTree:PropertyPathTree[1], ownerClass:Class[1]) : PropertyPathTree[*]
{
  $pTree.value->match([
    node:PropertyPathNode[1] | if($ownerClass == $node.class || $node.class->isStrictSubType($ownerClass),
                                  | $pTree,
                                  | $pTree.children->map(ch | findSubTreeWithOwner($ch, $ownerClass))
                                ),
    clz:Class[1] | if($clz == $ownerClass || $ownerClass->isStrictSubType($clz),
                          | $pTree,
                          | $pTree.children->map(ch | findSubTreeWithOwner($ch, $ownerClass))
                        ),
    any:Any[1] | $pTree.children->map(ch | findSubTreeWithOwner($ch, $ownerClass))
  ]);
}

function meta::pure::graphFetch::propertyTreeToGraphFetchTree(pTree:PropertyPathTree[1], ownerClass:Class[1]): RootGraphFetchTree[1]
{
   let root = ^RootGraphFetchTree(class = $ownerClass);
   $root->addPropertyGraphFetchTrees($pTree, $ownerClass)->copyPropertiesFromBaseTreeToSubTypeTrees()->cast(@RootGraphFetchTree);
}

function meta::pure::graphFetch::propertyGraphFetchTreeToRootGraphFetchTree(pTree:PropertyGraphFetchTree[1]): RootGraphFetchTree[1]
{
   let ownerClass = if($pTree.subType->isNotEmpty(), | $pTree.subType, | $pTree.property->functionReturnType().rawType)->toOne();
   assert($ownerClass->instanceOf(Class), | 'Primitive property tree cannot be converted to RootGraphFetchTree');
   ^RootGraphFetchTree(class = $ownerClass->cast(@Class), subTrees = $pTree.subTrees);
}

function <> meta::pure::graphFetch::addPropertyGraphFetchTrees(onto:GraphFetchTree[1], pTree:PropertyPathTree[1], ownerClass:Class[1]): GraphFetchTree[1]
{
   $pTree.value->match([
      node: PropertyPathNode[1] | if($ownerClass == $node.class  || $node.class->isStrictSubType($ownerClass),
                                     | $onto->addPropertyGraphFetchTrees($pTree, $ownerClass, $node),
                                     | $onto
                                  ),
      clz: Class[1]        |  if($clz->isStrictSubType($ownerClass),
                                      |
                                        let pTreesInBaseType=$pTree.children->map(c|$c.value->match([
                                                   node: PropertyPathNode[1] | if($ownerClass->allProperties()->contains($node.property), |$c, |[]),
                                                   a:Any[1] | []
                                        ]));
                                        let processedBaseTree= $pTreesInBaseType->fold({child, gft|$gft->addPropertyGraphFetchTrees($child, $ownerClass)}, $onto);

                                        // add subtype trees also
                                        let rootSubTypeTree= ^SubTypeGraphFetchTree(subTypeClass=$clz);
                                        let rootSubTypeTreeWithProperties= $pTree.children->fold({child, gft|
                                                                                                        $gft->addPropertyGraphFetchTrees($child, $clz)->cast(@SubTypeGraphFetchTree)
                                                                                                }, $rootSubTypeTree);

                                        let withSubTypeTree = $processedBaseTree->addSubTypeTrees($rootSubTypeTreeWithProperties);,

                                      | if($clz == $ownerClass ||  $ownerClass->isStrictSubType($clz),                               // get properties from superclass also ,
                                           | $pTree.children->fold({child, gft|$gft->addPropertyGraphFetchTrees($child, $clz) }, $onto),
                                           | $onto // dont process further
                                        )
                                    );,
      any: Any[1]               | $pTree.children->fold({child, gft| $gft->addPropertyGraphFetchTrees($child, $ownerClass)}, $onto)
   ]);
}

function meta::pure::functions::meta::isStrictSubType(subType:Type[1], superType:Type[1]):Boolean[1]
{
    if($subType == Nil,
       |true,
       |$subType->getAllTypeGeneralisations()->map(st|$st->elementToPath())->contains($superType->elementToPath())   && $subType->elementToPath()!=$superType->elementToPath()   // object comparison might not work due to lazy evalution of some duplicate objects
    );
}

function <> meta::pure::graphFetch::addPropertyGraphFetchTrees(onto:GraphFetchTree[1], pTree:PropertyPathTree[1], ownerClass:Class[1], node: PropertyPathNode[1]): GraphFetchTree[1]
{
   let pgft = ^SystemGeneratedPropertyGraphFetchTree(property = $node.property,
                                                     parameters = if($node.property->hasGeneratedMilestoningPropertyStereotype(),|$node.parameters,|[])
                                                   );

   if($pTree.children->size() == 0,
      | $onto->addSubTree($pgft),
      {|
         let rtnClass        = $node.property.genericType.rawType->cast(@Class)->toOne();
         let childrenByClass = $pTree.children->groupBy(c|$c.value->cast(@PropertyPathNode).class);
         $childrenByClass->keyValues()->fold(
            {kv, gft|
               let childrenOwner = $kv.first;
               let children      = $kv.second.values;

               if($childrenOwner == $rtnClass || $childrenOwner->_subTypeOf($rtnClass) || $rtnClass->_subTypeOf($childrenOwner),
                  {|
                     let withSubtype  = if($childrenOwner == $rtnClass || $rtnClass->_subTypeOf($childrenOwner),
                                           | $pgft,
                                           | ^$pgft(subType = $childrenOwner)
                                        );
                     let withChildren = $children->fold({child, gft2| $gft2->addPropertyGraphFetchTrees($child, $childrenOwner)->cast(@PropertyGraphFetchTree)}, $withSubtype->cast(@PropertyGraphFetchTree));
                     $gft->addSubTree($withChildren);
                  },
                  | $gft
               );
            },
            $onto
         );
      }
   );
}

function <> meta::pure::graphFetch::replaceQualifiedPropertiesWithRequiredProperties(pgft:PropertyGraphFetchTree[1], class:Class[1]):GraphFetchTree[*]
{
   let copy = ^$pgft(subTrees=[]);

   if($pgft.property->hasGeneratedMilestoningPropertyStereotype(),
   |
      let rtns = $pgft.property->functionReturnType().rawType->toOne();
      let subTrees = $pgft.subTrees->cast(@PropertyGraphFetchTree)->map(st| $st->replaceQualifiedPropertiesWithRequiredProperties($rtns->cast(@Class)))->cast(@PropertyGraphFetchTree);
      $copy->addSubTrees($subTrees);,
   |
      $pgft.property->match([
         {p:Property[1]|
            let rtns = $p->functionReturnType().rawType->toOne();
            let subTrees = if($rtns->instanceOf(Class),
                              |$pgft.subTrees->cast(@PropertyGraphFetchTree)->map(st| $st->replaceQualifiedPropertiesWithRequiredProperties($rtns->cast(@Class))),
                              |[]
                           )->cast(@PropertyGraphFetchTree);
            $copy->addSubTrees($subTrees);
         },
         {qp:QualifiedProperty[1]|
            let propertyPaths       = $qp.expressionSequence->evaluateAndDeactivate()->map(e| $e->scanProperties());
            let inlinedPropertyTree = $propertyPaths.result->buildPropertyTree()->inlineQualifiedPropertyNodes();
            $inlinedPropertyTree->propertyTreeToGraphFetchTree($class).subTrees;
         }
      ]);
   );
}

function <> meta::pure::graphFetch::replaceQualifiedPropertiesWithRequiredProperties(rgft:RootGraphFetchTree[1]):RootGraphFetchTree[1]
{
   let copy = ^$rgft(subTrees=[]);
   let subTrees = $rgft.subTrees->cast(@PropertyGraphFetchTree)->map(st| $st->replaceQualifiedPropertiesWithRequiredProperties($rgft.class))->cast(@PropertyGraphFetchTree);
   $copy->addSubTrees($subTrees)->cast(@RootGraphFetchTree);
}

function <> meta::pure::graphFetch::recordQualifiedProperties(tree:GraphFetchTree[1], path:List[1]): GraphFetchTree[1]
{
   if($path.values->isEmpty(),
      | $tree,
      {|
         let head         = $path.values->at(0);
         let tail         = if($head.property->instanceOf(QualifiedProperty),
                               | list($head.nestedQualifierReturn.values->tail()->concatenate($path.values->tail())),
                               | list($path.values->tail())
                            );
         let nextProperty = if($head.property->instanceOf(QualifiedProperty),
                               | $head.nestedQualifierReturn.values->first().property,
                               | $head.property
                            );
         let withQp       = if($head.property->instanceOf(QualifiedProperty),
                               {|
                                  let qp = $head.property->cast(@QualifiedProperty);
                                  $tree->match([
                                     ergft: ExtendedRootGraphFetchTree[1] | ^$ergft(requiredQualifiedProperties=$ergft.requiredQualifiedProperties->concatenate($qp)->removeDuplicates()),
                                     epgft: ExtendedPropertyGraphFetchTree[1]  | ^$epgft(requiredQualifiedProperties=$epgft.requiredQualifiedProperties->concatenate($qp)->removeDuplicates()),
                                     rgft : RootGraphFetchTree[1]         | ^ExtendedRootGraphFetchTree(requiredQualifiedProperties=$qp, class=$rgft.class, subTrees=$rgft.subTrees),
                                     pgft : PropertyGraphFetchTree[1]          | ^ExtendedPropertyGraphFetchTree(requiredQualifiedProperties=$qp, property=$pgft.property, subTrees=$pgft.subTrees)
                                  ]);
                               },
                               | $tree;
                            );

         ^$withQp(subTrees=$tree.subTrees->cast(@PropertyGraphFetchTree)->map(st|if($st.property == $nextProperty, |$st->recordQualifiedProperties($tail), |$st)));
      }
   );
}

function <> meta::pure::graphFetch::removeDummyProperties(tree:GraphFetchTree[1]): GraphFetchTree[0..1]
{
   $tree->match([
    pgft:PropertyGraphFetchTree[1] |
         if($pgft.property == getDummyProperty(),
            | [],
            | $pgft
          );,
    t:GraphFetchTree[1] |  ^$t(subTrees= $t.subTrees->map(st|$st->removeDummyProperties()),
                               subTypeTrees = $t.subTypeTrees->map(st|$st->removeDummyProperties())->cast(@SubTypeGraphFetchTree)
                               );
   ])
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy