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

core.pure.lineage.scanProperties.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::milestoning::*;
import meta::pure::metamodel::serialization::grammar::*;
import meta::pure::lineage::scanExecutes::*;
import meta::pure::mapping::*;
import meta::pure::lineage::scanProperties::propertyTree::*;
import meta::pure::metamodel::path::*;
import meta::pure::lineage::scanProperties::*;
import meta::pure::graphFetch::*;
import meta::pure::router::metamodel::*;


Class meta::pure::lineage::scanProperties::PropertyPathNode
{
   <> class : Class[1];
   <> property : AbstractProperty[1];
   nestedQualifier : List[*];
   parameters      : ValueSpecification[*];
   nestedQualifierReturn : List[0..1];
}

Class meta::pure::lineage::scanProperties::Res
{
   current : List[1];
   result : List[*];
}

Class meta::pure::lineage::scanProperties::ScanPropertiesState
{
   current : List[1];
   result : List[*];
   visitedFunctions:Map[1];
}

Class meta::pure::lineage::scanProperties::DummyPropertyPathNode extends PropertyPathNode
{
  <> id : Integer[1];
}

Class meta::pure::lineage::scanProperties::propertyTree::PropertyPathTree
{
   display : String[1];
   value : Any[1];
   children : PropertyPathTree[*];
   qualifierSubTree : PropertyPathTree[0..1];
}

Class meta::pure::lineage::scanProperties::ScanConfig
{
  scanClasses:Boolean[1];
  explodeMilestonedProperties:Boolean[1];
}

function  meta::pure::lineage::scanProperties::defaultScanConfig(): ScanConfig[1]
{
  ^ScanConfig(scanClasses=false, explodeMilestonedProperties= true);
}

// embedd dummyProp for every classOnlyAccess as a hack not to alter current structure of propertyPathTree
Class meta::pure::lineage::scanProperties::DummyClass
{
  dummyProp : String[1];
}

function  meta::pure::lineage::scanProperties::getDummyProperty():AbstractProperty[1]
{
  meta::pure::lineage::scanProperties::DummyClass->propertyByName('dummyProp')->toOne()
}

function <> meta::pure::lineage::scanProperties::dummyNodeForClass(c:Class[1]):PropertyPathNode[1]
{
    ^PropertyPathNode( class = $c,  property= getDummyProperty());
}

function <> meta::pure::lineage::scanProperties::findAndAddChildrenAtTheEnd(tree: PropertyPathTree[1], path: PropertyPathNode[*], toAdd: PropertyPathTree[*]):PropertyPathTree[1]
{
   if($path->isEmpty(),
      | ^$tree(children += $toAdd),
      | let first = $path->at(0);
        let correctTree = $tree.children->filter(x | $x.value->instanceOf(PropertyPathNode) && ($x.value->cast(@PropertyPathNode).property == $first.property))->toOne();
        let otherTrees = $tree.children->remove($correctTree);
        ^$tree(
           children = $otherTrees->concatenate($correctTree->findAndAddChildrenAtTheEnd($path->tail(), $toAdd))
        );
   )
}

function <> meta::pure::lineage::scanProperties::test::dummy():Any[*]
{
   'To ensure integration build does not fail on meta::pure::lineage::scanProperties::test package'
}

function meta::pure::lineage::scanProperties::getVisitedFunctions(maybeRes:ScanPropertiesState[0..1]):Map[1]
{
   $maybeRes->match([
      r0:ScanPropertiesState[0] | ^Map(),
      r1:ScanPropertiesState[1] | $r1.visitedFunctions
   ]);
}

function meta::pure::lineage::scanProperties::makeScanPropertiesState(current:List[1], result:List[*], visitedFunctions:Map[1]):ScanPropertiesState[1]
{
  ^ScanPropertiesState( current= $current,
                        result = $result->removeDuplicates(),
                        visitedFunctions = $visitedFunctions
                      );
}

function <> {doc.doc = 'Use scanProperties_ValueSpecification_1__List_MANY_ instead'}
meta::pure::lineage::scanProperties::scanProperties(vs:ValueSpecification[1], list:List[1], processed:Function[*], vars:Map>[0..1]):Res[0..1]
{
  let res = scanProperties($vs, $list, $processed, $vars, defaultScanConfig(), noDebug())->orElseEmptyPath();
  ^Res(current = $res.current, result = $res.result);
} 

function <> {doc.doc = 'Use scanProperties_ValueSpecification_1__List_MANY_ instead'}
meta::pure::lineage::scanProperties::scanProperties(vs:ValueSpecification[1], list:List[1], processed:Function[*], vars:Map>[0..1], config:ScanConfig[1]):ScanPropertiesState[0..1]
{
   scanProperties($vs, $list, $processed, $vars, $config, noDebug());
}

function <> {doc.doc = 'Use scanProperties_ValueSpecification_1__DebugContext_1__List_MANY_ instead'}
meta::pure::lineage::scanProperties::scanProperties(vs:ValueSpecification[1], list:List[1], processed:Function[*], vars:Map>[0..1] , debug: meta::pure::tools::DebugContext[1]):ScanPropertiesState[0..1]
{
  scanProperties($vs, $list, $processed, $vars, defaultScanConfig(), $debug);
} 

function <> {doc.doc = 'Use scanProperties_ValueSpecification_1__DebugContext_1__List_MANY_ instead'}
meta::pure::lineage::scanProperties::scanProperties(vs:ValueSpecification[1], list:List[1], processed:Function[*], vars:Map>[0..1], config:ScanConfig[1], debug: meta::pure::tools::DebugContext[1]):ScanPropertiesState[0..1]
{
   assert($list.values->isEmpty(),                               |'Do not supply base path.  Use scanProperties_ValueSpecification_1__List_MANY_ instead');
   assert($processed->isEmpty(),                                 |'Do not supply processed.  Use scanProperties_ValueSpecification_1__List_MANY_ instead');
   assert($vars->isEmpty() || $vars->toOne()->keys()->isEmpty(), |'Do not supply vars.  Use scanProperties_ValueSpecification_1__List_MANY_ instead');

   $vs->scanProperties($debug, $config);
}


function meta::pure::lineage::scanProperties::scanProperties(vs:ValueSpecification[1]):ScanPropertiesState[0..1]
{
   scanProperties($vs, noDebug(), defaultScanConfig());
}

function meta::pure::lineage::scanProperties::scanProperties(vs:ValueSpecification[1],config:ScanConfig[1]):ScanPropertiesState[0..1]
{
   scanProperties($vs, noDebug(), $config);
}

function meta::pure::lineage::scanProperties::scanProperties(vs:ValueSpecification[1], debug:DebugContext[1]):ScanPropertiesState[0..1]
{ 
   scanProperties($vs, $debug, defaultScanConfig());
}

function meta::pure::lineage::scanProperties::scanProperties(vs:ValueSpecification[1], debug:DebugContext[1], config:ScanConfig[1]):ScanPropertiesState[0..1]
{
   print(if($debug.debug,|$debug.space+'>>> ScanProperties in : ' +$vs->printValueSpecification('') + ' \n',|''));
   internal_scanProperties($vs, [], noVars(), $config, ^Map(), ^$debug(space=$debug.space+'    '));
}

function meta::pure::lineage::scanProperties::scanProperties(vs:ValueSpecification[1], debug:DebugContext[1], config:ScanConfig[1], visitedFunctions:Map[1]):ScanPropertiesState[0..1]
{ 
   internal_scanProperties($vs, [], noVars(), $config, $visitedFunctions, ^$debug(space=$debug.space+'    '));
}

function <> meta::pure::lineage::scanProperties::internal_scanProperties(vs:ValueSpecification[1], processed:Function[*], vars:Map>[1], config:ScanConfig[1], visitedFunctions:Map[1], debug: meta::pure::tools::DebugContext[1]):ScanPropertiesState[0..1]
{
  
  print(if($debug.debug,|$vars->keyValues()->map(kv | '{' + $kv.first + ' : ' + $kv.second->printPropertyNodeLists('') + '}')->makeString($debug.space + 'Context : ', ', ', '\n'),|''));

   let res = $vs->match([
               fe:FunctionExpression[1] | print(if($debug.debug,|$debug.space+'Processing Function Expression\n',|''));
                                          let params = $fe.parametersValues->evaluateAndDeactivate();
                                          $fe.func->match([
                                                            p:Property[1]|print(if($debug.debug,|$debug.space+'  property:'+$p.name->toOne()+'\n', |''));
                                                                                     let leftSide = $params->at(0)->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'  '));
                                                                                     if($params->at(0)->extractType()->instanceOf(Class), 
                                                                                        {|
                                                                                           let srcClass = $params->at(0)->extractType()->cast(@Class);
                                                                                           let nlist = $p->processProperty($srcClass, $leftSide.current->orElseEmptyPath(), true);
                                                                                           makeScanPropertiesState(
                                                                                              $nlist->at(0),
                                                                                              $nlist->concatenate($leftSide.result->filter(x | $x.values->isNotEmpty())),
                                                                                              $leftSide->getVisitedFunctions()
                                                                                           );
                                                                                        },
                                                                                        | $leftSide
                                                                                     );,
                                                            q:QualifiedProperty[1]|print(if($debug.debug,|$debug.space+'  qualifier:'+$q.name->toOne()+'\n', |''));
                                                                                        let srcClass = $params->at(0)->extractType()->cast(@Class);
                                                                                        let leftSide = $params->at(0)->map(v|$v->internal_scanProperties($processed->concatenate($q), $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    ')));
                                                                                        if(!$processed->contains($q),
                                                                                           {|
                                                                                              let nlist = $q->processQualifier($srcClass, $params->tail(), $leftSide.current->orElseEmptyPath(), $processed, $vars, true, $config, $leftSide->getVisitedFunctions(), $debug);
                                                                                              makeScanPropertiesState($nlist, $nlist, $leftSide->getVisitedFunctions());
                                                                                           },
                                                                                           | makeScanPropertiesState(emptyPath(), [], $leftSide->getVisitedFunctions())
                                                                                        );,
                                                            z:Any[1]|print(if($debug.debug,|$debug.space+'  function: \''+$fe.func.name->toOne()+'\'\n',|''));
                                                                         if($fe.func.name->in(['eval_Function_1__T_n__V_m_']),
                                                                            {|
                                                                               print(if($debug.debug,|$debug.space+'  - param 1\n',|''));
                                                                               let collectionPath = $params->at(1)->internal_scanProperties($processed, $vars, $config, $visitedFunctions,^$debug(space=$debug.space+'    '))->orElseEmptyPath();
                                                                              
                                                                               $params->at(0)->findFunction()->processFunction($collectionPath, $vars, $processed, $config, $collectionPath.visitedFunctions, ^$debug(space=$debug.space+'    '));
                                                                            },
                                                                            |
                                                                         if($fe.func.name == 'sort_T_m__Function_$0_1$__T_m_',
                                                                            {|
                                                                               let collectionPath = $params->at(0)->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    '))->orElseEmptyPath();
                                                                               let func           = $params->at(1)->findFunction();
                                                                               let funcResult     = $func->processFunction([$collectionPath, $collectionPath], $vars, $processed, $config, $collectionPath.visitedFunctions, ^$debug(space=$debug.space+'    '));
                                                                               ^$funcResult(current=$collectionPath.current, visitedFunctions = $funcResult.visitedFunctions);
                                                                            },
                                                                            |
                                                                         if($fe.func.name == 'sort_T_m__Function_$0_1$__Function_$0_1$__T_m_',
                                                                            {|
                                                                               let collectionPath = $params->at(0)->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    '))->orElseEmptyPath();
                                                                               let keyFunc        = $params->at(1)->findFunction();
                                                                               let comparator     = $params->at(2)->findFunction();
                                                                               let keyFuncResult  = $keyFunc->processFunction($collectionPath, $vars, $processed, $config,  $collectionPath.visitedFunctions, ^$debug(space=$debug.space+'    '));
                                                                               let compResult     = $comparator->processFunction([$keyFuncResult, $keyFuncResult], $vars, $processed, $config, $keyFuncResult.visitedFunctions, ^$debug(space=$debug.space+'    '));
                                                                               makeScanPropertiesState(
                                                                                  $collectionPath.current,
                                                                                  $compResult.result->concatenate($keyFuncResult.result),
                                                                                  $compResult.visitedFunctions
                                                                               );
                                                                            },
                                                                            |
                                                                         if($fe.func.name == 'removeDuplicates_T_MANY__Function_$0_1$__Function_$0_1$__T_MANY_',
                                                                            {|
                                                                               let collectionPath = $params->at(0)->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    '))->orElseEmptyPath();
                                                                               let keyFunc        = $params->at(1)->findFunction();
                                                                               let predicate      = $params->at(2)->findFunction();
                                                                               let keyFuncResult  = $keyFunc->processFunction($collectionPath, $vars, $processed, $config,  $collectionPath.visitedFunctions, ^$debug(space=$debug.space+'    '));
                                                                               let predResult     = $predicate->processFunction($keyFuncResult, $vars, $processed, $config, $keyFuncResult.visitedFunctions,^$debug(space=$debug.space+'    '));
                                                                               makeScanPropertiesState(
                                                                                  $collectionPath.current,
                                                                                  $predResult.result->concatenate($keyFuncResult.result),
                                                                                  $predResult.visitedFunctions
                                                                               );
                                                                            },
                                                                            |
                                                                         if($fe.func.name == 'letFunction_String_1__T_m__T_m_',
                                                                            {|
                                                                               print(if($debug.debug,|$debug.space+'  - param 1\n',|''));
                                                                               $params->at(1)->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    '));
                                                                            },
                                                                            |
                                                                         if($fe.func.name == 'if_Boolean_1__Function_1__Function_1__T_m_',
                                                                            {|
                                                                               print(if($debug.debug,|$debug.space+'  - param 0\n',|''));
                                                                               let testPath = $params->at(0)->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    '))->orElseEmptyPath();
                                                                               print(if($debug.debug,|$debug.space+'  - param 1\n',|''));
                                                                               let thenPath = $params->at(1)->findFunction()->processFunction([], $vars, $processed,  $config, $testPath.visitedFunctions, ^$debug(space=$debug.space+'    '));
                                                                               print(if($debug.debug,|$debug.space+'  - param 2\n',|''));
                                                                               let elsePath = $params->at(2)->findFunction()->processFunction([], $vars, $processed,  $config, $thenPath.visitedFunctions, ^$debug(space=$debug.space+'    '));
                                                                               makeScanPropertiesState(
                                                                                  $thenPath.current->concatenate($elsePath.current)->removeEmptyPaths()->first()->orElseEmptyPath(),  // TODO handle when both paths are relevant 
                                                                                  $testPath.result->concatenate($thenPath.result)->concatenate($elsePath.result),
                                                                                  $elsePath.visitedFunctions
                                                                               );
                                                                            },
                                                                            |
                                                                         if($fe.func.name == 'match_Any_MANY__Function_$1_MANY$__T_m_',
                                                                            {|
                                                                               print(if($debug.debug,|$debug.space+'  - param 0\n',|''));

                                                                                print(if($debug.debug,|$debug.space+' match function expression \n',|''));
                                                                                          
                                                                                print(if(!$debug.debug,|'', | ^$debug(space=$debug.space+'    ').space+'processing match input expression \n'));   
                                                                                let inputPath   = $params->at(0)->internal_scanProperties($processed, $vars, $config,  $visitedFunctions, ^$debug(space=$debug.space+'    '+'    '))->orElseEmptyPath();
                                                                                      
                                                                                let addMatchConditionClasses = $config.scanClasses;

                                                                                print(if(!$debug.debug,|'', |^$debug(space=$debug.space+'    ').space+'processing match conditions: \n'));  
                                                                                let matchLambdas= $params->at(1)->findFunctions();
                                                                                let funcResults = $matchLambdas->fold({f, r| 
                                                                                                                            let res = $f->processFunction($inputPath, $vars, $processed, $config, $r.visitedFunctions, ^$debug(space=$debug.space+'    '+'    '), $addMatchConditionClasses);
                                                                                                                            makeScanPropertiesState($r.current->concatenate($res.current)->removeEmptyPaths()->first()->orElseEmptyPath(),
                                                                                                                                                    $r.result->concatenate($res.result),
                                                                                                                                                    $res.visitedFunctions
                                                                                                                                                   );
                                                                                                                      },
                                                                                                                      makeScanPropertiesState(emptyPath(), [], $inputPath.visitedFunctions)
                                                                                                                    );
                                                                                $funcResults;
                                                                            },
                                                                            |
                                                                         if($fe.func.name->in(['assert_Boolean_1__Function_1__Boolean_1_',
                                                                                               'exists_T_MANY__Function_1__Boolean_1_', 
                                                                                               'forAll_T_MANY__Function_1__Boolean_1_']),
                                                                            {|
                                                                               // Unary function, result is primitive
                                                                               print(if($debug.debug,|$debug.space+'  - param 0\n',|''));
                                                                               let collectionPath = $params->at(0)->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    '))->orElseEmptyPath();
                                                                               let funcResult     = $params->at(1)->findFunction()->processFunction($collectionPath, $vars, $processed,  $config, $collectionPath.visitedFunctions, ^$debug(space=$debug.space+'    '));
                                                                               ^$funcResult(current=emptyPath());
                                                                            },
                                                                            |
                                                                         if($fe.func.name->in(['fold_T_MANY__Function_1__V_m__V_m_', 
                                                                                               'map_T_MANY__Function_1__V_MANY_',
                                                                                               'map_T_m__Function_1__V_m_',
                                                                                               'map_T_$0_1$__Function_1__V_$0_1$_']),
                                                                            {|
                                                                               // Unary function, return is that function's return
                                                                               print(if($debug.debug,|$debug.space+'  - param 0\n',|''));
                                                                               let collectionPath = $params->at(0)->internal_scanProperties($processed, $vars, $config,  $visitedFunctions, ^$debug(space=$debug.space+'    '))->orElseEmptyPath();
                                                                               $params->at(1)->findFunction()->processFunction($collectionPath, $vars, $processed, $config, $collectionPath.visitedFunctions, ^$debug(space=$debug.space+'    '));
                                                                            },
                                                                            |
                                                                         if($fe.func.name->in(['filter_T_MANY__Function_1__T_MANY_',
                                                                                               'removeDuplicates_T_MANY__Function_1__T_MANY_',
                                                                                               'removeDuplicatesBy_T_MANY__Function_1__T_MANY_',
                                                                                               'sortBy_T_m__Function_$0_1$__T_m_']),
                                                                            {|
                                                                               // Unary function, return is same as collection in
                                                                               let collectionPath = $params->at(0)->internal_scanProperties($processed, $vars, $config,  $visitedFunctions, ^$debug(space=$debug.space+'    '))->orElseEmptyPath();
                                                                               let func           = $params->at(1)->findFunction();
                                                                               let funcResult     = $func->processFunction($collectionPath, $vars, $processed, $config, $collectionPath.visitedFunctions, ^$debug(space=$debug.space+'    '));
                                                                               ^$funcResult(current=$collectionPath.current);
                                                                            },
                                                                            |
                                                                         if($fe.func.name->in(['at_T_MANY__Integer_1__T_1_',
                                                                                               'cast_Any_m__T_1__T_m_',
                                                                                               'distinct_T_MANY__T_MANY_',
                                                                                               'drop_T_MANY__Integer_1__T_MANY_', 
                                                                                               'first_T_MANY__T_$0_1$_', 
                                                                                               'init_T_MANY__T_MANY_', 
                                                                                               'last_T_MANY__T_$0_1$_', 
                                                                                               'limit_T_MANY__Integer_1__T_MANY_', 
                                                                                               'removeDuplicates_T_MANY__T_MANY_', 
                                                                                               'reverse_T_m__T_m_', 
                                                                                               'slice_T_MANY__Integer_1__Integer_1__T_MANY_', 
                                                                                               'subType_Any_m__T_1__T_m_',
                                                                                               'whenSubType_Any_1__T_1__T_$0_1$_',
                                                                                               'whenSubType_Any_$0_1$__T_1__T_$0_1$_',
                                                                                               'whenSubType_Any_MANY__T_1__T_MANY_',
                                                                                               'sort_T_m__T_m_', 
                                                                                               'tail_T_MANY__T_MANY_', 
                                                                                               'take_T_MANY__Integer_1__T_MANY_',
                                                                                               'toOne_T_MANY__T_1_',
                                                                                               'toOneMany_T_MANY__T_$1_MANY$_']),
                                                                            {|
                                                                               // No function, return is same as collection in
                                                                               print(if($debug.debug,|$debug.space+'  - param 0\n',|''));
                                                                               $params->at(0)->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    '))->orElseEmptyPath();
                                                                            },
                                                                            |
                                                                         if($fe.func.name->in(['add_T_MANY__T_1__T_$1_MANY$_', 
                                                                                               'concatenate_T_MANY__T_MANY__T_MANY_', 
                                                                                               'union_T_MANY__T_MANY__T_MANY_']),
                                                                            {|
                                                                               // Combine 2 collections
                                                                               print(if($debug.debug,|$debug.space+'  - param 0\n',|''));
                                                                               let col1 = $params->at(0)->internal_scanProperties($processed, $vars, $config,  $visitedFunctions, ^$debug(space=$debug.space+'    '))->orElseEmptyPath();
                                                                               print(if($debug.debug,|$debug.space+'  - param 1\n',|''));
                                                                               let col2 = $params->at(1)->internal_scanProperties($processed, $vars, $config,  $col1.visitedFunctions, ^$debug(space=$debug.space+'    '))->orElseEmptyPath();
                                                                               makeScanPropertiesState(
                                                                                  $col1.current->concatenate($col2.current)->removeEmptyPaths()->first()->orElseEmptyPath(),  // TODO handle when both paths are relevant 
                                                                                  $col1.result->concatenate($col2.result),
                                                                                  $col2.visitedFunctions
                                                                               );
                                                                            },
                                                                            {|
                                                                               print(if($debug.debug,|$debug.space+'processing params \n',|''));
                                                                            

                                                                              let pp = $params->map(v|$v->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    '))->orElseEmptyPath());
                                                                               
                                                                               print(if($debug.debug,|$debug.space+'checking if func should be processed further \n',|''));
                                                                               let func = $fe.func->filter(f| $f->shouldBeProcessed($processed))->first();
                                                                               $func->processFunction($pp, $vars, $processed,  $config, $visitedFunctions, ^$debug(space=$debug.space+'    '));
                                                                            }
                                                                         ))))))))))));
                                                          ]);,
               i:InstanceValue[1] | print(if($debug.debug,|$debug.space+'Processing Instance\n',|''));
                                    let instanceRes = $i.values->evaluateAndDeactivate()->map( v |
                                       $v->match([
                                          a: ValueSpecification[1]                                             | $a->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    '));,
                                          k: KeyExpression[1]                                                  | $k.expression->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    '));,
                                          a: meta::pure::functions::collection::AggregateValue[1] | makeScanPropertiesState(emptyPath(), $a.mapFn.expressionSequence->map(e | $e->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    '))).result, $visitedFunctions),
                                          a: meta::pure::tds::BasicColumnSpecification[1]                 | makeScanPropertiesState(emptyPath(), $a.func->cast(@FunctionDefinition).expressionSequence->map(e | $e->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    '))).result,$visitedFunctions),
                                          l: LambdaFunction[1]                                            | $l.expressionSequence->map(e | $e->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    ')));,
                                          l: ConcreteFunctionDefinition[1]                                | $l.expressionSequence->map(e | $e->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    ')));,
                                          a: String[1]                                                         | makeScanPropertiesState(emptyPath(), [], $visitedFunctions),
                                          p: Path[1]                                                | makeScanPropertiesState(emptyPath(), $p->scanPath(emptyPath(), $processed, $vars, $config, $visitedFunctions, $debug), $visitedFunctions),
                                          r: RootGraphFetchTree[1]                                        | makeScanPropertiesState(emptyPath(), $r->scanGraphFetchTree($r->typeFromGraphFetchTree()->cast(@Class), emptyPath(), $processed, $vars, $config, $visitedFunctions, $debug), $visitedFunctions),
                                          c: Class[1]                                                      | if($config.scanClasses->toOne(),
                                                                                                                    |   print(if($debug.debug,|$debug.space+'  Class : '+ $c.name->toOne()+ '\n',|''));
                                                                                                                        let v=  list(meta::pure::lineage::scanProperties::dummyNodeForClass($c));
                                                                                                                        makeScanPropertiesState($v, $v, $visitedFunctions);    ,                    
                                                                                                                  | []
                                                                                                                  );,
                                          a: Any[1]                                                            | print(if($debug.debug,|$debug.space+'  Unknown\n',|''));[];
                                    ]));
                                    makeScanPropertiesState(
                                       $instanceRes.current->removeEmptyPaths()->first()->orElseEmptyPath(), 
                                       $instanceRes.result,
                                       $visitedFunctions
                                    );,
               ve:VariableExpression[1] | print(if($debug.debug,|$debug.space+'Processing Variable '+$ve.name+'\n',|''));
                                          let v = $vars->get($ve.name)->orElseEmptyPath();
                                          makeScanPropertiesState($v, $v, $visitedFunctions);,
               ervs:ExtendedRoutedValueSpecification[1] | print(if($debug.debug,|$debug.space+'Bypassing ExtendedRoutedValueSpecification\n',|''));
                                                          $ervs.value->internal_scanProperties($processed, $vars, $config,$visitedFunctions, ^$debug(space=$debug.space+'    '));,
               frvs:FunctionRoutedValueSpecification[1] | print(if($debug.debug,|$debug.space+'Bypassing FunctionRoutedValueSpecification\n',|''));
                                                          $frvs.value->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    '));
             ]
          )->removeEmptyResults();

   print(if($debug.debug,|$debug.space+'Current:'+$res.current->printPropertyNodeLists($debug.space+'          ')+'\n',|''));
   print(if($debug.debug,|$debug.space+'Results:'+$res.result->printPropertyNodeLists($debug.space+'          ')+'\n',|''));
   $res;
}

function <> meta::pure::lineage::scanProperties::isPackageOk(f:Function[1]):Boolean[1]
{
  !$f->isWithinPackage(meta) || $f->meta::alloy::isMetaAlloyTestDependency() || $f->isWithinPackage(meta::pure::lineage::scanProperties::test);
}

function <> meta::pure::lineage::scanProperties::shouldBeProcessed(f:Function[1], processed:Function[*]):Boolean[1]
{
   isPackageOk($f) && !$processed->contains($f);
}

function <> meta::pure::lineage::scanProperties::extractType(vs:ValueSpecification[1]):Type[1]
{
   $vs->match([
      ervs :ExtendedRoutedValueSpecification[1] | $ervs.value->extractType(),
      frvs :FunctionRoutedValueSpecification[1] | $frvs.value->extractType(),
      other:ValueSpecification[1]               | $other.genericType.rawType->toOne()
   ]);
}

function <> meta::pure::lineage::scanProperties::processFunction(fn:Function[0..1], paramPaths:ScanPropertiesState[*], vars:Map>[1], processed:Function[*], config:ScanConfig[1], visitedFunctions:Map[1], debug:DebugContext[1]):ScanPropertiesState[1]
{
   processFunction($fn, $paramPaths, $vars, $processed, $config, $visitedFunctions, $debug,  false);
} 

function <> meta::pure::lineage::scanProperties::processLambdaFunction(fn:Function[1], paramPaths:ScanPropertiesState[*], vars:Map>[1], processed:Function[*], config:ScanConfig[1],visitedFunctions:Map[1], debug:DebugContext[1] , addFunctionParamClasses : Boolean[1]):ScanPropertiesState[1]
{
  let fromParams = makeScanPropertiesState(emptyPath(), $paramPaths.result, $visitedFunctions);

  let funcParamNodes=   if($addFunctionParamClasses,
    |  let funcParamClasses = $fn->functionType().parameters.genericType.rawType ->cast(@Class);
        list($funcParamClasses -> map(c|$c->dummyNodeForClass()));,
    | [] 
    );

  let newVars     = $fn->functionType().parameters.name->zip($paramPaths.current)->newMap();
  let allVars     = $vars->putAll($newVars);
  let expressions = $fn->match([l:FunctionDefinition[1]| $l.expressionSequence, n:NativeFunction[1]| []]);

  let body = $expressions->processExpressionSequence($allVars, $processed->concatenate($fn), $config, $visitedFunctions, $debug);
  ^$body(result=$fromParams.result->concatenate($body.result)->concatenate($funcParamNodes));

}

function <> meta::pure::lineage::scanProperties::processFunction(fn:Function[0..1], paramPaths:ScanPropertiesState[*], vars:Map>[1], processed:Function[*], config:ScanConfig[1], visitedFunctions:Map[1], debug:DebugContext[1] , addFunctionParamClasses : Boolean[1]):ScanPropertiesState[1]
{
  print(if($debug.debug,|$debug.space+'  - func '+ if($fn->isEmpty(), |'not found or excuded', |if($fn.name->isEmpty(), |$fn->toOne()->type().name->toOne(), |$fn.name->toOne()))+'\n',|''));
  
  let fromParams = makeScanPropertiesState(emptyPath(), $paramPaths.result, $visitedFunctions);
      $fn->match([
          {f0:Function[0] | $fromParams},
          {f:Function[1] |

               if($f.name->isEmpty() || !isPackageOk($f),
                |
                  processLambdaFunction($f, $paramPaths, $vars, $processed, $config, $visitedFunctions, $debug, $addFunctionParamClasses);,
                |
                  let funcParamNodes =  if($addFunctionParamClasses,
                                          |  let funcParamClasses = $f->functionType().parameters.genericType.rawType ->cast(@Class);
                                              list($funcParamClasses -> map(c|$c->dummyNodeForClass()));,
                                          | [] 
                                        );

                  if($visitedFunctions->get($f->meta::pure::functions::meta::elementToPath())->isNotEmpty(),
                  |
                    let acutalBodyResult = resolveDummyNodes($visitedFunctions->get($f->meta::pure::functions::meta::elementToPath())->toOne(), $paramPaths);
                    makeScanPropertiesState($acutalBodyResult.current, $paramPaths.result->concatenate($acutalBodyResult.result)->concatenate($funcParamNodes), $visitedFunctions);,
                  |
                    let dummyPaths = range($paramPaths->size())->map(ind | list(^DummyPropertyPathNode(id = $ind, class = meta::pure::lineage::scanProperties::DummyClass, property = getDummyProperty())->cast(@PropertyPathNode)));

                    let newVars     = $f->functionType().parameters.name->zip($dummyPaths)->newMap();
                    let allVars     = $vars->putAll($newVars)->cast(@Map>);
                    let expressions = $f->match([l:FunctionDefinition[1]| $l.expressionSequence, n:NativeFunction[1]| []]);

                    let body = $expressions->processExpressionSequence($allVars, $processed->concatenate($f), $config, $visitedFunctions, $debug);
                    let updatedMap = $visitedFunctions->put($f->meta::pure::functions::meta::elementToPath(), ^$body(visitedFunctions = ^Map()));

                    let acutalResult = resolveDummyNodes($body, $paramPaths);
                                      
                    makeScanPropertiesState($acutalResult.current, $paramPaths.result->concatenate($acutalResult.result)->concatenate($funcParamNodes), $updatedMap);
                  );
               ); 
          }
      ]);

}

function <> meta::pure::lineage::scanProperties::replaceDummyNodesWithParamsPropertyPathNodes(propertyPathNode: PropertyPathNode[0..1], paramPaths:ScanPropertiesState[*]):PropertyPathNode[*]
{
  if(!$propertyPathNode->isEmpty() && $propertyPathNode->toOne()->instanceOf(DummyPropertyPathNode),
  |
    let paramIndex = $propertyPathNode->toOne()->cast(@DummyPropertyPathNode).id;
    $paramPaths->at($paramIndex).current.values;,
  | $propertyPathNode;
  );
}

function <> meta::pure::lineage::scanProperties::resolveDummyNodes(dummyRes:ScanPropertiesState[1], paramPaths:ScanPropertiesState[*]):ScanPropertiesState[1]
{
    let currentPropertyPathNodes = $dummyRes.current.values;
    let current = list(replaceDummyNodesWithParamsPropertyPathNodes($currentPropertyPathNodes->first(), $paramPaths)->concatenate($currentPropertyPathNodes->slice(1, $currentPropertyPathNodes->size())));
    let result = $dummyRes.result->map(list | list(replaceDummyNodesWithParamsPropertyPathNodes($list.values->first(), $paramPaths)->concatenate($list.values->slice(1,$list.values->size()))););
    makeScanPropertiesState($current, $result, ^Map());
}

function <> meta::pure::lineage::scanProperties::processExpressionSequence(expressions:ValueSpecification[*], vars:Map>[1], processed:Function[*], config:ScanConfig[1], visitedFunctions:Map[1],  debug:DebugContext[1]):ScanPropertiesState[1]
{
   print(if($debug.debug,|$debug.space+'  - vars '+$vars->varsToString()+'\n',|''));

   $expressions->fold(
      {e, p |
         let r = $p.first;
         let v = $p.second;

         let ePaths  = $e->internal_scanProperties($processed, $v, $config, $r.visitedFunctions, ^$debug(space=$debug.space+'    '));
         let newVars = if($e->instanceOf(FunctionExpression) && $e->cast(@FunctionExpression).func == letFunction_String_1__T_m__T_m_,
                          {|
                             let varName  = $e->cast(@FunctionExpression).parametersValues->evaluateAndDeactivate()->at(0)->cast(@InstanceValue).values->at(0)->cast(@String);
                             let varValue = $ePaths.current->orElseEmptyPath();
                             print(if($debug.debug,|$debug.space+'  - +var '+varToString($varName, $varValue)+'\n',|''));
                             $v->put($varName, $varValue);
                          },
                          | $v
                       );
         let newRes  = makeScanPropertiesState(
                          if($ePaths->isEmpty(), |emptyPath(), |$ePaths->toOne().current),
                          $r.result->concatenate($ePaths.result),
                          $ePaths->getVisitedFunctions()
                       );
         pair($newRes, $newVars);
      },
      pair(makeScanPropertiesState(emptyPath(), [], $visitedFunctions), $vars)
   ).first;
}

function <> meta::pure::lineage::scanProperties::varsToString(vars:Map>[1]):String[1]
{
   $vars->keyValues()->map(kv| varToString($kv.first, $kv.second))->joinStrings('[', ' , ', ']')
}

function <> meta::pure::lineage::scanProperties::varToString(name:String[1], value:List[1]):String[1]
{
   $name+':'+$value->printPropertyNodeLists()->replace('\n', ';');
}

function <> meta::pure::lineage::scanProperties::processProperty(p:Property[1], srcClass:Class[1], list:List[1], append:Boolean[1]):List[1]
{
   let property = ^PropertyPathNode(
                        class = $srcClass,
                        property=$p
                  );
   if($append,|^$list(values = $list.values->concatenate($property)),|^$list(values = $property->concatenate($list.values)));
}

function <> meta::pure::lineage::scanProperties::processQualifier(q:QualifiedProperty[1], srcClass:Class[1], parameters:ValueSpecification[*], list:List[1], processed:Function[*], vars:Map>[1], append:Boolean[1], config:ScanConfig[1], visitedFunctions:Map[1], debug:DebugContext[1]):List[1]
{
   let paramPaths    = $parameters->map(v|$v->internal_scanProperties($processed, $vars, $config, $visitedFunctions, ^$debug(space=$debug.space+'    ')));
   let qualifierVars = $q->functionType().parameters->tail().name->zip($paramPaths.current)->newMap();
    
  let property = if($q->isMilestonedGeneratedQualifiedProperty() && $config.explodeMilestonedProperties==false,
       | if(hasSufficientValuesForPropertyParameters( $q,$parameters),
            | ^PropertyPathNode(
                          class                 = $srcClass,
                          property              = $q,
                          nestedQualifier       = [],
                          parameters            = $parameters,
                          nestedQualifierReturn = []
                       ),
            | ^PropertyPathNode(
                          class                 = $srcClass,
                          property              = $q->switchToNoArgMilestonedGeneratedQualifiedProperty()->toOne(),
                          nestedQualifier       = [],
                          parameters            = [],
                          nestedQualifierReturn = []
                       )
         ),
      | let qualifierBody = $q.expressionSequence->processExpressionSequence($qualifierVars, $processed->concatenate($q), $config, $visitedFunctions, $debug);
          ^PropertyPathNode(
                                class                 = $srcClass,
                                property              = $q,
                                nestedQualifier       = $qualifierBody.result,
                                parameters            = $parameters,
                                nestedQualifierReturn = $qualifierBody->last().current
                            );
     );
   if($append,|^$list(values = $list.values->concatenate($property)),|^$list(values = $property->concatenate($list.values)));
}

function <> meta::pure::lineage::scanProperties::hasSufficientValuesForPropertyParameters(q:QualifiedProperty[1], parameters:ValueSpecification[*]):Boolean[1]
{
   $q->functionType().parameters->evaluateAndDeactivate()->tail()->size()  ==  $parameters->size();
}

function <> meta::pure::lineage::scanProperties::scanPath(p:Path[1], list:List[1], processed:Function[*], vars:Map>[1], config:ScanConfig[1], visitedFunctions:Map[1], debug:DebugContext[1]):List[*]
{
   $p.path->fold({pe,s|$pe->match([
                                    p:PropertyPathElement[1]|pair($p.property->functionReturnType().rawType->toOne(),
                                                                  $p.property->match(
                                                                                       [
                                                                                          p:Property[1]| processProperty($p, $s.first->cast(@Class), $s.second, true),
                                                                                          q:QualifiedProperty[1]| processQualifier($q, $s.first->cast(@Class), $p.parameters, $s.second, $processed, $vars, true, $config, $visitedFunctions,$debug)
                                                                                       ]
                                                                               )
                                                            ),
                                    c:CastPathElement[1]| pair($c.type.rawType->toOne(), $s.second)
                                  ]
                                 )
                 }, pair($p.start.rawType->toOne(), $list)
               ).second;
}


function <> meta::pure::lineage::scanProperties::scanGraphFetchTree(g:GraphFetchTree[1], type: Class[1], list:List[1], processed:Function[*], vars:Map>[1],config:ScanConfig[1],  visitedFunctions:Map[1], debug:DebugContext[1]):List[*]
{
   $g.subTrees->cast(@PropertyGraphFetchTree)->map({st | 
      let res = $st.property->match([
         p:Property[1]| processProperty($p, $type, $list, true),
         q:QualifiedProperty[1]| processQualifier($q, $type, $st.parameters, $list, $processed, $vars, true, $config, $visitedFunctions, $debug)
      ]); 
      if($st.subTrees->isNotEmpty(),
         | scanGraphFetchTree($st, $st->typeFromGraphFetchTree()->cast(@Class), $res, $processed, $vars, $config, $visitedFunctions, $debug),
         | $res
      );
  });
}

function <> meta::pure::lineage::scanProperties::findFunction(vs:ValueSpecification[1]):Function[0..1]
{
   $vs->findFunctions()->first();
}

function <> meta::pure::lineage::scanProperties::findFunctions(vs:ValueSpecification[1]):Function[*]
{
   $vs->match([
      ervs:ExtendedRoutedValueSpecification[1] | $ervs.value->findFunction(),
      frvs:FunctionRoutedValueSpecification[1] | $frvs.value->findFunction(),
      iv  :InstanceValue[1] | $iv.values->map(v | $v->match([f:Function[1]|$f, a:Any[*]|[]])),
      a   :Any[*]           | []
   ]);
}

function <> meta::pure::lineage::scanProperties::noVars():Map>[1]
{
   ^Map>();
}

function <> meta::pure::lineage::scanProperties::removeEmptyResults(res:ScanPropertiesState[0..1]):ScanPropertiesState[0..1]
{
   $res->map(r| ^$r(result=$r.result->removeEmptyPaths()));
}

function <> meta::pure::lineage::scanProperties::removeEmptyPaths(paths:List[*]):List[*]
{
   $paths->filter(l| $l.values->isNotEmpty());
}

function <> meta::pure::lineage::scanProperties::orElseEmptyPath(maybeRes:ScanPropertiesState[0..1]):ScanPropertiesState[1]
{
   $maybeRes->match([
      r0:ScanPropertiesState[0] | makeScanPropertiesState(emptyPath(), [], ^Map()),
      r1:ScanPropertiesState[1] | $r1
   ]);
}

function <> meta::pure::lineage::scanProperties::orElseEmptyPath(maybePath:List[0..1]):List[1]
{
   $maybePath->match([
      l0:List[0] | emptyPath(),
      l1:List[1] | $l1
   ]);
}

function meta::pure::lineage::scanProperties::emptyPath():List[1]
{
   ^List();
}

function meta::pure::lineage::scanProperties::printPropertyNodeLists(properyLists:List[*]):String[1]
{
   $properyLists->printPropertyNodeLists('');
}

function meta::pure::lineage::scanProperties::printPropertyNodeLists(properyLists:List[*], space:String[1]):String[1]
{
   $properyLists->map(l|$l.values->map(v|$v.class.name->toOne()+'->'+$v.property.name->toOne())->joinStrings(', '))->sort()->removeDuplicates()->joinStrings('\n'+$space);
}

function <> meta::pure::lineage::scanProperties::propertyTree::recurseBuildTree(list:List[*]):PropertyPathTree[*]
{
   let m = $list->filter(l|!$l.values->isEmpty())->groupBy(l|$l.values->at(0));
   $m->keys()->map(
                  k|^PropertyPathTree(display = $k.property->match([p:Property[1]|'',q:QualifiedProperty[1]|'(Q)'])+$k.property.name->toOne(),
                                      value = $k,
                                      children = $m->get($k).values->map(l|^$l(values=$l.values->slice(1, $l.values->size())))->recurseBuildTree(),
                                      qualifierSubTree = $k.nestedQualifier->buildPropertyTree()
                                      )
               );
}

function <> meta::pure::lineage::scanProperties::propertyTree::srcClass(property:AbstractProperty[1]):Type[1]
{
   $property->match([
                     q:QualifiedProperty[1]|$q->functionType().parameters->evaluateAndDeactivate()->at(0).genericType.rawType->toOne(),
                     p:Property[1]|$p.classifierGenericType.typeArguments->at(0).rawType->toOne()
                    ]);
}

function meta::pure::lineage::scanProperties::propertyTree::buildPropertyTree(executes:Execute[*]):PropertyPathTree[1]
{
   let properyLists = $executes->map(exec|$exec.funcEntryPoint->scanProperties(emptyPath(), [], []););
   $properyLists.result->buildPropertyTree();
}

function meta::pure::lineage::scanProperties::propertyTree::buildPropertyTree(properyLists:List[*]):PropertyPathTree[1]
{
   let filteredPropertyLists = $properyLists->filter(f|$f.values->isNotEmpty());

   let grpByClass = $filteredPropertyLists->groupBy(v|$v.values->at(0).class);

   let propertyTree = ^PropertyPathTree(display='root',
                                        value='root',
                                        children = $grpByClass->keys()->map(c|^PropertyPathTree(display=$c.name->toOne(), value=$c, children=$grpByClass->get($c).values->recurseBuildTree()))//filteredPropertyLists->recurse()
                                       );
}

function meta::pure::lineage::scanProperties::propertyTree::printTree(t:PropertyPathTree[1], space:String[1]):String[1]
{
   $space+$t.value->match([c:Class[1]|'c_'+$c.name->toOne()+'\n',
                            a:String[1]|$a+'\n',
                            p:PropertyPathNode[1]|'p_'+$p.class.name->toOne()+'.'+$p.property.name->toOne()+'\n'+if($p.nestedQualifier->isEmpty(),|'',|$p.nestedQualifier->buildPropertyTree()->printTree($space+'   __ '));
                          ])+
   $t.children->sort({a,b| if($a.value->instanceOf(PropertyPathNode) && $b.value->instanceOf(PropertyPathNode),
                             |let c = $a.value->cast(@PropertyPathNode);
                              let d = $b.value->cast(@PropertyPathNode);
                              let e = $c.class.name->toOne()+'.'+$c.property.name->toOne();
                              let f = $d.class.name->toOne()+'.'+$d.property.name->toOne();
                              $e->compare($f);,
                             |0
                          );
                     })
              ->map(c|$c->printTree($space+' '))->joinStrings('');
}

function meta::pure::lineage::scanProperties::inlineQualifiedPropertyNodes(p: PropertyPathTree[1]):PropertyPathTree[1]
{
  meta::pure::lineage::scanProperties::inlineQualifiedPropertyNodes($p, defaultScanConfig());
}

function meta::pure::lineage::scanProperties::inlineQualifiedPropertyNodes(p: PropertyPathTree[1], config:ScanConfig[1]):PropertyPathTree[1]
{
   $p->recurse_inlineQualifiedPropertyNodes($config)->toOne();
}

function <> meta::pure::lineage::scanProperties::recurse_inlineQualifiedPropertyNodes(p: PropertyPathTree[1], config:ScanConfig[1]):PropertyPathTree[*]
{
   let newTrees = if($p.value->instanceOf(PropertyPathNode) && $p.value->cast(@PropertyPathNode).property->instanceOf(QualifiedProperty)
                              && ! $p.value->cast(@PropertyPathNode).property->isMilestonedGeneratedQualifiedProperty(),
                     {|  
                        if($p.children->isEmpty(),
                           | $p.qualifierSubTree.children.children,
                           {| 
                              let qual = $p.value->cast(@PropertyPathNode).property->cast(@QualifiedProperty);
                              let qualifierTreeCurrent = $qual.expressionSequence->evaluateAndDeactivate()->toOne()->internal_scanProperties([], noVars(), $config,^Map(), noDebug()).current;
                              let qualifierTree = $p.qualifierSubTree.children->toOne();
                              findAndAddChildrenAtTheEnd($qualifierTree, $qualifierTreeCurrent.values, $p.children).children;
                           }
                        )->map(nt| $nt->recurse_inlineQualifiedPropertyNodes($config));
                     },
                     | $p
                  );

   $newTrees->map(t| ^$t(children=$t.children->map(c| $c->recurse_inlineQualifiedPropertyNodes($config))));
}

function meta::pure::lineage::scanProperties::propertyTree::buildMultiLevelPropertyTrees(vs:ValueSpecification[1], mappings:Mapping[*], extensions:meta::pure::extension::Extension[*]):PropertyPathTree[*]
{
    $vs->buildMultiLevelPropertyTrees($mappings, $extensions, noDebug())
}

function meta::pure::lineage::scanProperties::propertyTree::buildMultiLevelPropertyTrees(vs:ValueSpecification[1], mappings:Mapping[*], extensions:meta::pure::extension::Extension[*], debug:DebugContext[1]):PropertyPathTree[*]
{
   let res = ^Pair>(first=$vs, second=^List(values=[$vs->scanProperties( $debug).result->buildPropertyTree()]));

   $mappings->fold({a, b| let reprocessedQuery = $b.first->cast(@FunctionExpression)->meta::pure::mapping::modelToModel::chain::allReprocess([], $a, $extensions, $debug).res;
                          let newPropertyTree = $reprocessedQuery->scanProperties($debug).result->buildPropertyTree();
                          ^$b(first = $reprocessedQuery,
                              second = ^List(values=$b.second.values->concatenate($newPropertyTree)));
                       },
                   $res).second.values;
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy