Goals:
   facilitate users writing queries that return data in a directly usable format
   allow users to access data with as low a barrier of entry as possible
                remove need for user to launch local server (requires access to binary data path, etc.)
                no server restart needed
                zero development environment setup
                dynamic requests
   support supplying parameters
   optimize memory performance in a multi-user environment (server)
   minimize network traffic
   allow app code to be effectively contributed to the server in a safe way

kinds of objects and allowed hops between levels:
   Branch <-> Branch
   Branch <-> Artifact
   Artifact <-> Artifact
   Transaction -> Artifact

EBNF used below:
ruleName : __expression__
* 0 or more times
+ 1 or more times
? 0 or 1 times (means optional)
| separates alternatives (i.e. means or) 
() groups for purpose of multiplicity suffix (i.e. *, +, ?)

grammar oseeScript;

script                  : script_version? (SL_COMMENT | osee_statement)+ EOF ;
script_version          : 'script-version' SEMANTIC_VERSION ;
osee_statement          : orcs_query | parameter | action;

orcs_query              : 'start' 'from' starting_point orcs_clause+ with_tx_comment? ;
starting_point          : 'branch' branch_start | 'tx' tx_start ;
branch_start            : int_param | '*' | 'where' branch_criteria ;
branch_criteria         : branch_criterion ('and' branch_criterion)* ;
branch_criterion        : branch_numeric_criterion | branch_name_criterion | branch_archived_criterion | branch_type_criterion | branch_state_criterion ;

branch_numeric_criterion: ('branch-id' | 'parent-id' | 'parent-tx' | 'baseline-tx-id') (equality_operator | numeric_operator) int_param_list ;
branch_name_criterion   : 'name' string_operator string_regex_param_list ;
branch_archived_criterion: 'archived' equality_operator ('true' | 'false') ;

branch_type_criterion   : 'type' equality_operator branch_type_list ;
branch_type_list        : branch_type | '[' branch_type (',' branch_type)* ']' ;
branch_type             : 'baseline' | 'working' | 'merge' | 'system-root' | 'port' | param_ref ;

branch_state_criterion  : 'state' equality_operator branch_state_list ;
branch_state_list       : branch_state | '[' branch_state (',' branch_state)* ']' ;
branch_state            : 'created' | 'modified' | 'committed' | 'rebaselined' | 'deleted' | 'rebaseline_in_progress' | 'commit_in_progress' | 'creation_in_progress' | 'delete_in_progress' | 'purge_in_progress' | param_ref ;

tx_start                : int_param | '*' | 'where' tx_criteria ;
tx_criteria             : tx_criterion ('and' tx_criterion)* ;
tx_criterion            : tx_type_criterion | tx_comment_criterion | tx_numeric_criterion ;
tx_type_criterion       : 'type' equality_operator ('baseline' | 'non-baseline') ;
tx_comment_criterion    : 'tx-comment' string_operator string_regex_param_list ;
tx_numeric_criterion    : ('tx-id' | 'branch-id' | 'author-id' | 'commit-artifact-id' | 'time') (equality_operator | numeric_operator) int_param_list ;

orcs_clause             : find_clause | collect_clause | follow_clause | parameter | set_attribute_clause | set_relation_clause | delete_artifact_clause | delete_attribute_clause | delete_relation_clause ;

find_clause             : 'find' (find_artifacts | find_types);
find_artifacts          : 'artifacts' ('*' | 'where' item_criteria) ;
find_types              : ('attribute' | 'artifact' | 'relation') 'types' ('*' | 'where' type_criteria) ;
type_criteria           : type_criterion ('and' type_criterion)* ;
type_criterion          : 'super-type' equality_operator type_list ;
item_criteria           : item_criterion ('and' item_criterion)* ;
item_criterion          : artifact_criterion | attribute_criterion | relation_criterion ;
artifact_criterion      : 'art-mod-type' equality_operator mod_type_list | 'art-id' equality_operator int_param_list | 'art-type' equality_operator type_list ;
attribute_criterion     : attribute_spec (existence_operator | operator param_list) ;

relation_criterion      : 'relation' type_list_wild (existence_operator | ('.' side_id_label equality_operator int_param_list)) ;
side_id_label           : 'side-A-id' | 'side-B-id' ;
mod_type_list           : mod_type | '[' mod_type (',' mod_type)* ']' ;
mod_type                : 'new' | 'modified' | 'deleted' | 'merged' | 'artifact_deleted' | 'introduced' | 'undeleted' | param_ref ;

collect_clause          : 'collect' alias? collect_body limit? order_by?;
collect_body            : '*' | exp alias? (',' exp alias?)* ;
limit                   : 'limit' POS_INT ;
order_by                : 'sort' ('ascending' | 'descending') field (',' field)* ;
exp                     : field | param_list | function_call ;
field                   : branch_field | tx_field | artifact_field | attribute_spec | relation_spec ;
branch_field            : 'name' | 'branch-id' | 'type' | 'parent-id' | 'parent-tx' | 'archived' | 'state' | 'baseline-tx-id' | 'inherit-access-control' ;
tx_field                : 'tx-id' | 'type' | 'branch-id' | 'tx-comment' | 'time' | 'author-id' | 'commit-artifact-id' ;
artifact_field          : 'art-id' | 'art-type' | 'art-version-id' | 'art-mod-type' | 'tx-id' | 'art-url' | 'art-count';
attribute_spec          : 'attribute' type_list_wild ('.' attribute_field)? ;
attribute_field         : 'id' | 'version-id' | 'mod-type' | 'int-url' | 'ext-url' | 'raw-value' | 'pretty-value' | 'count';   //  the raw value of a date attribute is a ms as a long but its pretty value is date formatted string
relation_spec           : 'relation' type_list_wild ('.' relation_field)? ;
relation_field          : 'id' | side_id_label | 'rationale' | 'version-id' | 'mod-type' | 'count' ;
function_call           : 'replace' '(' exp ',' exp')' | 'toLowerCase' '(' exp ')' | 'toUpperCase' '(' exp ')' | 'format' '(' exp (',' exp)+ ')' | 'average' '(' exp ')' | 'sum' '(' exp ')' | 'substring' '(' exp ',' exp')' | 'if' '(' exp ',' exp ',' exp')';

follow_clause           : 'follow' (follow_relation | follow_branch | follow_attribute | follow_tx) ;
follow_relation         : 'relation' type_list_wild 'to' ('side-A' | 'side-B') follow_depth? ('where' item_criteria)? ;
follow_branch           : 'branch' 'to' ('parent' | 'children') follow_depth? ('where' branch_criteria)? ;
follow_attribute        : follow_attribute_branch | follow_attribute_art ;
follow_attribute_branch : 'attribute' type_list 'to' 'branch' ('where' branch_criteria)? ;
follow_attribute_art    : 'attribute' type_list 'to' 'artifact' ('where' item_criteria)? ;

follow_tx               : 'tx' 'to' ('changed-artifacts' | 'branch-id' | 'parent-id');
follow_depth            : 'depth' (POS_INT | 'max' | 'max' '-'  POS_INT) ;

operator                : numeric_operator | string_operator | token_operator ;
numeric_operator        : '<' | '>' | '<=' | '>=' ;
string_operator         : 'contains' | equality_operator ;
equality_operator       : '!=' | '=' ;
existence_operator      : 'exists' | 'not exists' ;

token_operator          : '=' '[' eq_option (',' eq_option)* ']' ;
eq_option               : eq_case_sensitivity | eq_token_count | eq_token_delim | eq_token_order ;
eq_case_sensitivity     : 'match-case' | 'ignore-case' ;
eq_token_count          : 'match-token-count' | 'ignore-token-count' ;
eq_token_delim          : 'exact-delim' | 'whitespace-delim' | 'any-delim' ;
eq_token_order          : 'any-order' | 'match-order' ;

delete_artifact_clause  : 'delete' 'artifact' 'where' item_criteria ;
delete_attribute_clause : 'delete' 'attribute' 'where' item_criteria ;
delete_relation_clause  : 'delete' 'relation' ('where' item_criteria)? ;
set_attribute_clause    : 'set' 'attribute' type_list '=' param 'where' item_criteria ;
set_relation_clause     : 'set' 'relation' 'rationale' '=' string_param 'where' item_criteria ;

with_tx_comment         : 'with' 'tx-comment' string_param ;

alias                   : 'as' ID ;
param_list              : param | '[' param (',' param)* ']' ;
param                   : '-'? POS_INT | REAL | STRING | REGEX | param_ref ;
string_param            : STRING | param_ref ;
string_regex_param      : string_param | REGEX ;
string_regex_param_list : string_regex_param | '[' string_regex_param (',' string_regex_param)* ']' ;
int_param               : POS_INT | param_ref ;
int_param_list          : int_param | '[' int_param (',' int_param)* ']' ;
param_ref               : ID ;
type_list_wild          : type_list | '{' '*' '}' ;
type_list               : type | '[' type (',' type)* ']' ;
type                    : POS_INT | TYPE_NAME | param_ref ;

POS_INT                 : [1-9][0-9]* ;
SEMANTIC_VERSION        : [0-9]+ '.' [0-9]+ '.' [0-9] ;
REAL                    : '-'? [0-9]+ '.' [0-9]+ ; 
ID                      : [a-zA-Z][A-Za-z0-9_-]* ;
REGEX                   : '/' ~('/')+ '/' ;
STRING                  : '\'' ~('\'')+ '\'' ;
TYPE_NAME               : '{' ~('}')+ '}' ;

SL_COMMENT              : '//' ~[\n\r]*; 
WS                      : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines

parameter               : 'var' parameter_type '[]'? param_ref ':' (parameter_details (':' (exp  | orcs_query))? | exp  | orcs_query);
parameter_details       : parameter_detail (',' parameter_detail)* ;
parameter_type          : 'string'| 'boolean'| 'file'| 'branch'| 'artifact-type'| 'attribute-type'| 'relation-type'| 'artifact'| 'int'| 'real' | 'script' | 'object';
parameter_detail        : 'label' '=' STRING | 'value' '=' (exp  | orcs_query) | 'description' '=' STRING | 'dependency' '=' param_ref | 'choices' '=' param ;

action                  : 'action' STRING? action_method ;
action_method           : 'osee-script' | ('url' http_verb? | 'java-script') (STRING | param_ref)+ ;   // the tokens after the action_method are method specific, for 'url' they are concatenated together.  the param_ref are used as a key into the OseeAppParams prior to concatenation
http_verb               : 'get' | 'put' | 'post' | 'delete' | 'head' ;   // if the http-verb is omitted, 'get' is used

// if the label is omitted on a parameter or action, then it is not visually represented

// Defaults
// find artifacts implicit exp: art-mod-type != deleted
// parameter branch: choices = start from branch where type = [baseline, working] and archived = false
// art-mod-type != deleted is implicit in each find artifacts unless explicitly specified otherwise
// if the label for an action is not specified then the action is not visually represented
  

/*
Notes:
1.  Aliases in the same scope need to be unambiguous
2.  Name of nested collection is the side name if alias is not specified
3.  The clause(s) after the follow (if exists) are applied at each level.
	 To skip levels, first use a follow with no clause, then a follow with desired clause
4.  An attribute spec with only the first argument (type) gets the value
5.  Content is returned in the order it was captured
6.  An artifact type criteria involving a super type means matches that type or any of its subtypes
7.  Attribute types with media type JSON will be properly nested in search result JSON
8.  In future specify rest end points including for non-OSEE data sources
9.  a single top-level JSON object is always returned '{' '}'
10. if nothing is collected, return '{' '}'
11. collected artifacts are given the key  "artifacts" unless a different key is provided by the collect alias
12. if only one attribute/relation spec is collected (i.e. collect attribute{Name}.id), then an array directly containing its instances is returned
13. if more than attribute/relation spec is collected, then an array of objects each containing a key-value pair for each spec is returned
14. aggregate values such art art-count are returned as a a key-value pair directly within the collect object
15. a useful action  "action java-script oseeAppStart(getOseeAppParams());"
*/

== Use Cases ==

script-version 1.0.0


// 1.  get names of all baseline branches and their children
start from branch where type = system-root
	follow branch to children depth max
	collect branch-id, name

//  "params": {}
//   results:
// { "branches":  
//   [  {"branch-id": 3458, "name": "SAW 1",
//         "children": 
//         [  {"branch-id": 7856, "name": "SAW 2",
//               "children":
//                  {"branch-id": 7856, "name": "SAW 3", "children": []}
//            }
//         ],
//                 {"branch-id": 456, "name": "CSW 1",
//         "children": {...}
//      },
//   ]
// }

// 2.  TeamWorldSearchItem.performSearch()
var branch ATS             : value = 570
var int[] team-def-id-list : value = [453,6456,7567]
var string state-exp       : value = 'Implement;<727536>'

start from branch ATS
find artifacts where art-type = {Version} and attribute{Name} = ['SAW 1', 'SAW 2']

var int[] version-ids : value = art-id

find artifacts where attribute{Team Definition} = team-def-id-list 
	and relation{TeamWorkflowTargetedForVersion}.side-A-id = version-ids 
	and attribute{ats.Current State} contains state-exp and
	//and attr-type in ("Name", "ats.Current State")
	collect art-type, art-id, attribute{Name}, attribute{ats.Current State} as cur-state
	follow relation {TeamWorkflowTargetedForVersion} to side-B
	collect as version art-id, attribute{Name}

// params:
//   {  "team-def-id-list": [453,6456,7567],
//      "ATS" : 570,
//      "state-exp"       : "Implement;<727536>"
//   }
// results:
// { "artifacts": 
//   [  {"art-type": "Workflow", "art-id": 3485634, "Name": "RPCR 8125", "cur-state": "Implement;<727536>",
//         "version": {"art-id": 45685, "Name": "FTB 2"}
//      },                                            ...
//                 {"art-type": "Workflow", "art-id": 9523468, "Name": "RPCR 9215", "cur-state": "Implement;<727536>",
//         "version": {"art-id": 635673, "Name": "FTB 4"}
//      },
//   ]
// }

// 3.  AccountResource.unsubscribe()
start from branch common
	find artifacts where art-id = [user-id, group-id]
	collect as names attribute{Name}
	delete relation where relation{Users}.side-B-id = user-id
	with tx-comment 'User unsubscribed self from group'

//   params:
//   {  "common" : 570,
//      "user-id" : 50,
//      "group-id": 368195
//   }
//   results:
// { "names":
//   ["Ryan D. Brooks", "Experimental Group"]
// }


// 4.  RequirementStreamingOutput.write()
start from branch common 
	find artifacts where art-id = doc-template-id
	collect as templates attribute{General String Data}
// this example 
//   params:
//   {  "common" : 570,
//      "doc-template-id": 7635248
//   }
//   results: 
// { "templates":
//   ["String1", "String2", String3"]
// }

// 5.  Promote Items for build scripts
start from branch ATS
	find artifacts where relation {TeamWorkflowTargetedForVersion}.side-B-id = verionArtId and art-type = {Team Workflow}
	collect as workflows attribute{ats.Legacy PCR Id} as pcr-id, attribute{promoteItem} as items

//   params:
//   {  "ATS" : 570,
//      "verionArtId": 64652
//   }
//   results:
//   {"workflows":
//     [ {"pcr-id": 8125, "items": [{embeddedJson1}, {embeddedJson2}]},
//       {"pcr-id": 8156, "items": []}
//     ]
//   }


// 6.  UserRelatedToAtsObjectSearch
start from branch ATS
	find artifacts where attribute[{Current State}, {State}, {Log}] =[ignore-case, any-order] userId
	collect as touched-arts userId as user, art-id as artids
	find artifacts where art-id = userId
	collect as related-arts relation[{TeamLead}, {TeamMember}, {FavoriteUser}, {SubscribedUser}, {PrivilegedMember}].side-B-id as artids

//   params:
//   {  "ATS" : 570,
//      "userId": 50
//   }
//   results: 
//   { "touched-arts": {
//        "user": 50,
//         "artids": [345, 5467, 34, 45, 64, 56, 3456]
//      },
//     "related-arts": {
//        "artids": [76, 2346, 24, 794, 45]
//     }
//   }


// 7.  List recent transactions authored by me with nicely formatted date and include name of corresponding branch 
start from tx where author-id = 50
	collect tx-id, format('%1$tm %1$te,%1$tY', time), tx-comment limit 100 sort descending tx-id
	follow tx to branch-id
	collect name


// 8.  Find all Subsystem Requirements that don't have an allocation relation
start from branch SAW_1
	find artifacts where art-type={Abstract Subsystem Requirement} and relation{Allocation} not exists 
	collect art-count, attribute{Name}, art-url

//   params:
//   {  "SAW_1" : 14976,
//   }
//   results: 
//   { "art-count": 306,
//     "artifacts": [
//        {"Name": "Standards", "art-url": "http://..."},
//                          ...
//        {"Name": "Logistics", "art-url": "http://..."}
//     ]
//   }


// 9. find all duplicate Paragraph Numbers
start from branch branchId 
	find artifacts where attribute{Paragraph Number}.count > 1
	collect art-url


// 10. find all attributes with value ' ' on all branches
start from branch * 
	find artifacts where attribute{*} = ' '
	collect attribute{*}.count

//  "params": {}
//   results: 
//   { "attribute{*}.count": 5352
//   }


// 11.  count all artifacts on a branch
start from branch myBranchId
	find artifacts *
	collect art-count

//  "params": {}
//   results: 
//   { "attribute{*}.count": 5352
//   }


// 12.  Detailed Test Status
start from branch myBranchId
	find artifacts where art-type = {Abstract Software Requirement}
	collect art-type, attribute[{Name}, {Subsystem}, {Partition}, {Qualification Method}]
	follow relation {Verification} to side-B
	collect attribute{Name}, toLowerCase ( attribute{Category} )


// 13.  OSEE an app that returns workflows for a specific team and version

var script teamQuery : start from branch ATS find artifacts where art-type = {Team Definition}
var script verionQuery : start from branch ATS find artifacts where art-type = {Version}

var artifact Team_Definition : label = 'Team Definition(s)', choices = teamQuery
var artifact Versions : label = 'Version(s)', dependency = Team_Definition, choices = verionQuery
var boolean closed : label = 'Closed', description='Include items in the closed or completed'
action 'Search' osee-script

start from branch ATS
	find artifacts where attribute{Team Definition} = Team_Definition 
	and relation{TeamWorkflowTargetedForVersion}.side-A-id = Versions 
	and attribute{ats.Current State} contains ['Closed', 'Cancelled']
collect  art-url

// 14.  WcafeCheckFaultsOperation
var branch branchId: label = 'Branch'
action 'Start' osee-script

start from branch branchId 
	find artifacts where art-type = {Fault} 
	and attribute{Name} >= 800 
	and attribute{Name} < 1500 
	and attribute{wcafe.Name} = /(?i)aiu.*/ 
var object faults : attribute{*}
//   results: 
//   { "faults": 
//       [ {"Name": 800, "Name": "AUI tho",...},
//                       ...
//         {"Name": 1326, "Name": "AUI chi",...}
//       ]
//   }
