// functions to turn a string into a DOORs Filter /* The original concept for this code is Reik Schroeder's Reik Schröder SIEMENS AG Com MN PG R D C5 BLN - S50 - 21 13623 Berlin Reik.Schroeder@siemens.com Modified by Richard Good (rick.good@virgin.net) as follows: - I have rewritten all the code except for the parseSimpleFilter function and Regexps it now utilises an array and can handle NOTs in the middle of statments and brackets or other escape characters in the attribute names and enumerations I have moved back to the dual logic situation (the way that telelogic implement it). Its just a great deal easier to understand it this way. My users have huge modules and commonly specify extremely complicated filters about 600 chars long with brackets in the enumerations and attribute names, they want to be able to tweek the filter for a new view rather than spending hours reconstructing a multi layered filter. This tool should be used for these sort of changes rather than specifying from scratch it is also good for applying one modules filter to another. I use a microsoft regexp addin to do the replace because DOORS regexps and escape chars simply don't work in some cases. I have left some debugging code in due to the number of things that can go wrong */ //The Main Data structure. I will store id in first col (the way to key into it) //the rest of the stuff will be as in the following constants Array FilterArray const int OPERATOR = 0 const int LEVEL = 1 const int PARENT = 2 const int FILTER = 3 const int STRING = 4 const int SIMPLE = 5 //constants for compound filters const int NONE = 0 const int NOTop = 1 const int ANDop = 2 const int ORop = 3 string Ops[]={"NONE", "NOT", "AND", "OR"} //general globals int DeepestLevel int FilterCount string WholeThing //This stuff is for tokenizing/ detokenizing Regexp DodgyChars = regexp "(\"|\\(|\\)|\\[|\\]|\\.|\\^|\\||\\$|\\+|\\*|\\\\)" Skip TokenSkip int NumberOfTokens bool ProcessTokens #include string tempVersion = doorsVersion() Regexp versionReg = regexp "(DOORS )([^\\.].*\\.[^\\.].*)" versionReg tempVersion string thisVersion = tempVersion[match 2] real currVersion = realOf(thisVersion) bool nineTwoFilter = (currVersion >= 9.2) //(Attribute 'Object Text' contains 'deleted') AND (Has in or out links through *) //Object Text Contains 'deleted' //Attribute 'Object Text' contains 'if' (case sensitive) //Attribute 'Object Text' contains 'if' (case sensitive) //Object Text Contains 'if' //(((Attribute 'SCE Design ID' contains ',' (case sensitive)) OR (Attribute 'SCE Design ID' contains ':' (case sensitive))) OR (Attribute 'SCE Design ID' contains ';' (case sensitive))) OR (Attribute 'SCE Design ID' contains ' ' (case sensitive)) Regexp REcolumnFilter Regexp REcontainsFilter Regexp REcontentsFilter Regexp REmultiValueFilter Regexp REemptyFilter Regexp REcompareFilter Regexp REcurrentObjectFilter Regexp REleavesFilter Regexp RElinksFilter // regular Expressions needed to recognize "simple" Filters if(nineTwoFilter) { print "nine two version" REcolumnFilter = regexp "Column '(.*)' contains '(.*)'(.*)" REcontainsFilter = regexp "Attribute '([^'].*)' contains '([^'].*)'(.*)" REcontentsFilter = regexp "(Contains) '(.*)'(.*)" REmultiValueFilter = regexp "(.*) (excludes|includes) (.*)" REemptyFilter = regexp "(.*) is (not |)empty" REcompareFilter = regexp "(.*) (>=|>|<=|<|!=|==) (.*)" REcurrentObjectFilter = regexp "(Exclude|Include) Object (.*)" REleavesFilter = regexp "(|Non-)Leaves" RElinksFilter = regexp "Has (no |)(in-|out-|in or out )links through (.*)" } else { REcolumnFilter = regexp "Column '(.*)' contains \"(.*)\"(.*)" REcontainsFilter = regexp "(.*) (CONTAINS|Contains) '(.*)' (.*)" REcontentsFilter = regexp "(CONTAINS|Contains) '(.*)'(.*)" REmultiValueFilter = regexp "(.*) (excludes|includes) (.*)" REemptyFilter = regexp "(.*) is (not |)empty" REcompareFilter = regexp "(.*) (>=|>|<=|<|!=|==) (.*)" REcurrentObjectFilter = regexp "(Exclude|Include) Object (.*)" REleavesFilter = regexp "(|Non-)Leaves" RElinksFilter = regexp "Has (no |)(in-|out-|in or out )links through (.*)" } bool FilterError void printFilterError (string input, string err){ ack "ERROR on parsing filter string '"input"' (please redo your filter): \n"err"\n" FilterError = true } void DeTokenizeString(string &inp) { string Tok, OrigStr for OrigStr in TokenSkip do { Tok = (string key TokenSkip) inp = VBReplace(inp, Tok, OrigStr,false) } } // function to parse a "simple" Filter, that means, which is not combined of other Filters Filter parseSimpleFilter (string input){ if(ProcessTokens) DeTokenizeString(input) //print input "\n" if (REcolumnFilter input){ // any type of "Column" filters string params = input[match 3] string colName = input[match 1] string val = input[match 2] if (null params) return column (colName, val, false, false) else if (params == " (with regexps and case sensitive)") return column (colName, val, true, true) else if (params == " (with regexps)") return column (colName, val, false, true) else if (params == " (case sensitive)") return column (colName, val, true, false) else { printFilterError (input, "unrecognized parameter to Column Filter >>"params"<<\n"//- "possible values are : '(with regexps)', '(case sensitive)', '(with regexps and case sensitive)' or nothing") } } else if (REcontainsFilter input){ // any type of Attribute contains filter //differrent if its a 9.2 or above filter if(nineTwoFilter) { string params = input[match 3] string atName = input[match 1] string val = input[match 2] if (null params) return contains (attribute atName, val, false, false) else if (params == " (with regexps and case sensitive)") return contains (attribute atName, val, true, true) else if (params == " (with regexps)") return contains (attribute atName, val, false, true) else if (params == " (case sensitive)") return contains (attribute atName, val, true, false) else { printFilterError (input, "unrecognized parameter to Column Filter >>"params"<<\n"//- "possible values are : '(with regexps)', '(case sensitive)', '(with regexps and case sensitive)' or nothing") } } else { if (input[match 4] == "[with regexps]") return contains (attribute input[match 1], input[match 3]) else if (null input[match 4]) return contains (attribute input[match 1], input[match 3], input[match 2] == "Contains") else{ printFilterError (input, "unrecognized parameter to Contains Filter >>"input[match 4]"<<\n"//- "possible values are : '[with regexps]' or nothing") } } } else if (REcontentsFilter input){ // any type of "Any Text Attribute" contains filter if (input[match 3] == " [with regexps]") return contents (input[match 2]) else if (null input[match 3]) return contents (input[match 2], input[match 1] == "Contains") else{ printFilterError (input, "unrecognized parameter to Contains in any Text Attribute Filter >>"input[match 3]"<<\n"//- "possible values are : '[with regexps]' or nothing") } } else if (REmultiValueFilter input){ // both types of filter for multivalued attributes if (input[match 2] == "excludes") return excludes (attribute input[match 1], input[match 3]) else return includes (attribute input[match 1], input[match 3]) } else if (REemptyFilter input){ // is Empty and is not Empty filters if (null input[match 2]) return isNull (attribute input[match 1]) else return notNull (attribute input[match 1]) } else if (REcompareFilter input){ // any type of comarison filters string cmd = input[match 2] string thisAtt = input[match 1] if (thisAtt == "Object Number" or exists attribute thisAtt) { if (cmd == ">=") return (attribute input[match 1] >= input[match 3]) else if (cmd == ">") return (attribute input[match 1] > input[match 3]) else if (cmd == "<=") return (attribute input[match 1] <= input[match 3]) else if (cmd == "<") return (attribute input[match 1] < input[match 3]) else if (cmd == "!=") return (attribute input[match 1] != input[match 3]) else if (cmd == "==") return (attribute input[match 1] == input[match 3]) } else { printFilterError (input, "Attribute '" thisAtt "' does not exist") } } else if (REcurrentObjectFilter input){ // filter that includes/excludes current Object respectively any Object Object ThisObj =object (intOf (input[match 2])) if (null ThisObj) { printFilterError (input, "Object " input[match 2] " no longer exists.") } current = ThisObj if (input[match 1] == "Exclude") return excludeCurrent else return includeCurrent } else if (REleavesFilter input){ // filter to show only leaves if (null input[match 1]) return includeLeaves else return excludeLeaves } else if (RElinksFilter input){ // any Link related filters LinkFilter lf if (input[match 2] == "in-") lf = linkFilterIncoming else if(input[match 2] == "out-") lf = linkFilterOutgoing else lf = linkFilterBoth if (null input[match 1]) return hasLinks (lf, input[match 3]) else return hasNoLinks (lf, input[match 3]) } else printFilterError (input, "unrecognized Filter string") return null } Filter ProcessFilterParts() { int i,j,k, temp string str, FiltString bool b int ParentArray[FilterCount] // Stream out = write "C:\\Temp\\Debug.tsv" // out << "String\tOperator\tParent\tID\tSimple\tLevel\n" for(i=0;i0;i--) { for(j=0;j0;k--) { for(i=0;i0) { FirstPart =(Filter get(FilterArray,i+1,FILTER)) int TempLev= (int get(FilterArray,i+1,LEVEL)) if((null FirstPart) or (TempLev-1!=ThisLev)) { ack "null filter passed can't recover" halt } } if(ThisOp == ANDop or ThisOp == ORop) { while(j0 && i < stopAt){ if (b[i] == '\'' && (i == 0 || b[i-1] != '\\')) isInString = !isInString if (!isInString){ if (b[i] == '(') bCount++ if (b[i] == ')') bCount-- } i++ } //add the string to the array put(FilterArray, Thelevel, FilterCount, LEVEL) put(FilterArray, b[StartAt+1:i-2], FilterCount, STRING) put(FilterArray, false, FilterCount, SIMPLE) put(FilterArray, Parent, FilterCount, PARENT) put(FilterArray, null, FilterCount, FILTER) //look for NOT this is only relevant at the start of the string if (b[0:2] == "NOT") { put(FilterArray, NOTop, FilterCount, OPERATOR) } //look for an AND/OR after the brackets elseif (i < stopAt-2 && (b[i:i+2]== "AND" or b[i+1:i+3] == "AND")){ put(FilterArray, ANDop, FilterCount, OPERATOR) } elseif (i < stopAt-2 && (b[i:i+1] == "OR" or b[i+1:i+2] == "OR")){ // priority 4 put(FilterArray, ORop, FilterCount, OPERATOR) } else { put(FilterArray, NONE, FilterCount, OPERATOR) } FilterCount++ //recurse to next level ProcessFilter(b[StartAt+1:i-2],Thelevel+1, FilterCount-1) } else i++ } else i++ } if (NoBrackets){ //Simple filter put(FilterArray, NONE, FilterCount, OPERATOR) put(FilterArray, b, FilterCount, STRING) put(FilterArray, Thelevel, FilterCount, LEVEL) put(FilterArray, Parent, FilterCount, PARENT) put(FilterArray, true, FilterCount, SIMPLE) put(FilterArray, null, FilterCount, FILTER) FilterCount++ } if(Thelevel>DeepestLevel) DeepestLevel = Thelevel } Filter parseFilter (string input){ FilterCount=0 DeepestLevel=0 FilterArray = create(1,1) WholeThing = input TokenSkip = createString NumberOfTokens=0 ProcessTokens=false TokenizeString(input) //print input ProcessFilter (input, 1, -1) Filter F = ProcessFilterParts delete TokenSkip delete FilterArray return F } delete FilterArra