private void parseInnerSyntaxOfResponseTemplate(Dictionary<string, ParserItem> root, Dictionary<string, string> customVariables, List<ParserError> errors) { List<string> keys = new List<string>(root.Keys); ParserCompareDefinition pcd; foreach (string key in keys) { if (root[key].value is string) { // find out the exact comparing definiton from the value and save it along so we will be able to compare with actual received JSON response root[key] = parseCompareRules(root[key], customVariables, errors); continue; } if (root[key].value is Dictionary<string, ParserItem>) // Dictionary type means that original JSON had an object at this position { // call the same function recursively for the entire sub-node parseInnerSyntaxOfResponseTemplate((Dictionary<string, ParserItem>)root[key].value, customVariables, errors); continue; } if (root[key].value is List<ParserItem>) // List type means that original JSON had an array at this position ... { List<ParserItem> list = (List<ParserItem>)root[key].value; if (list.Count > 0) { if (list[0].value is string) // if at least one item is present and it is string ... root[key] = parseCompareRules(list[0], customVariables, errors); // ... parse compare rules for entire array from this field } continue; } // simple type // make parser compare definition for strict comparison and assingn it to this item pcd = new ParserCompareDefinition(); pcd.value = root[key].value; root[key].comp_def = pcd; } }
private ParserItem parseCompareRules(ParserItem item, Dictionary<string, string> customVariables, List<ParserError> errors) { string value = (string)item.value; int original_error_count = errors.Count; // find out all positions of MAIN_SYNTAX_CHARACTERs in the string value List<int> occurrences = new List<int>(); int index = 0; do { index = value.IndexOf(MAIN_SYNTAX_CHARACTER, index); // ... so, find all occurrences of function characters in given string value if (index != -1) { if (index == 0 || value[index - 1] != ESCAPE_CHARACTER) // either first character or unescaped one occurrences.Add(index); index++; } } while (index != -1); List<int> original_occurrences = new List<int>(occurrences); // when we notify the user about parser error we want to refer to the positions from original template // take care of possibility that we got a plain string without any MAIN_SYNTAX_CHARACTERs if (occurrences.Count == 0) { item.comp_def = new ParserCompareDefinition(); item.comp_def.type = typeof(string); item.comp_def.value = value; return item; } // take care of possible errors if (occurrences.Count < 5) // not enough MAIN_SYNTAX_CHARACTERs errors.Add(new ParserError(item.line, item.position + original_occurrences.Last(), string.Format(ERR_MSG_EXPECTING_CHARACTER, MAIN_SYNTAX_CHARACTER), SOURCE_TEMPLATE)); if (occurrences.Count > 5) // too many MAIN_SYNTAX_CHARACTERs errors.Add(new ParserError(item.line, item.position + original_occurrences[5], string.Format(ERR_MSG_INVALID_CHARACTER, MAIN_SYNTAX_CHARACTER), SOURCE_TEMPLATE)); if (occurrences.First() != 0) // illegal characters before first MAIN_SYNTAX_CHARACTER errors.Add(new ParserError(item.line, item.position, string.Format(ERR_MSG_INVALID_CHARACTER, value[0]), SOURCE_TEMPLATE)); if (occurrences.Last() != value.Length - 1) // illegal characters after last MAIN_SYNTAX_CHARACTER errors.Add(new ParserError(item.line, item.position + original_occurrences.Last(), string.Format(ERR_MSG_INVALID_CHARACTER, value[original_occurrences.Last() + 1]), SOURCE_TEMPLATE)); // if there are any new syntax errors we can't parse this item - return it without any change if (errors.Count > original_error_count) return item; // now we can replace all escaped MAIN_SYNTAX_CHARACTER for the real ones // note: all concerned indices in occurrences list must be modified accordingly, since ESCAPE_CHARACTERs are being taken // out from the original string index = 0; do { index = value.IndexOf("" + ESCAPE_CHARACTER + MAIN_SYNTAX_CHARACTER, index); // find escaped MAIN_SYNTAX_CHARACTER if (index != -1) { value = value.Remove(index, 1); // remove ESCAPE_CHARACTER from the string // all occurrences with further placement than index of escaped character must be corrected by one shift_occurrences(occurrences, index, -1); index++; } } while (index != -1); // // retrieve individual values between MAIN_SYNTAX_CHARACTERs and save them to the item structure // string type = value.Substring(occurrences[0] + 1, occurrences[1] - occurrences[0] - 1); // type string[] ops = value.Substring(occurrences[1] + 1, occurrences[2] - occurrences[1] - 1).Split(new char[] {RESPONSE_OPERATION_SEPARATOR}, StringSplitOptions.RemoveEmptyEntries); // operation with possible modifiers string val = value.Substring(occurrences[2] + 1, occurrences[3] - occurrences[2] - 1); // value string var = value.Substring(occurrences[3] + 1, occurrences[4] - occurrences[3] - 1); // variable ParserCompareDefinition pcd = new ParserCompareDefinition(); // type if (type.Length > 0) { switch (type) { case RESPONSE_TYPE_STRING: pcd.type = typeof(string); break; case RESPONSE_TYPE_INTEGER: pcd.type = typeof(int); break; case RESPONSE_TYPE_FLOAT: pcd.type = typeof(float); break; case RESPONSE_TYPE_BOOLEAN: pcd.type = typeof(bool); break; case RESPONSE_TYPE_OBJECT: pcd.type = typeof(object); break; case RESPONSE_TYPE_ARRAY: pcd.type = typeof(Array); break; default: // urecognized type errors.Add(new ParserError(item.line, item.position + 1, string.Format(ERR_MSG_UNKNOWN_TYPE, type), SOURCE_TEMPLATE)); break; } } // operation + modifiers bool use_var = false; // use variable modifier bool if_present = false; // if present modifier string op = null; // operation if (ops.Length > 0) { foreach (string ops_item in ops) { switch (ops_item) { case RESPONSE_OPERATION_MODIFIER_USE_VARIABLE: use_var = true; break; case RESPONSE_OPERATION_MODIFIER_IF_PRESENT: if_present = true; break; case RESPONSE_OPERATION_EQUAL: if (RESPONSE_OPERATION_EQUAL_ALLOWED_TYPES.Contains(type)) op = ops_item; else errors.Add(new ParserError(item.line, item.position + original_occurrences[1] + 1, string.Format(ERR_MSG_OPERATION_DOESNT_SUPPORT_TYPE, ops_item, type), SOURCE_TEMPLATE)); break; case RESPONSE_OPERATION_NOT_EQUAL: if (RESPONSE_OPERATION_NOT_EQUAL_ALLOWED_TYPES.Contains(type)) op = ops_item; else errors.Add(new ParserError(item.line, item.position + original_occurrences[1] + 1, string.Format(ERR_MSG_OPERATION_DOESNT_SUPPORT_TYPE, ops_item, type), SOURCE_TEMPLATE)); break; case RESPONSE_OPERATION_LESS_THAN: if (RESPONSE_OPERATION_LESS_THAN_ALLOWED_TYPES.Contains(type)) op = ops_item; else errors.Add(new ParserError(item.line, item.position + original_occurrences[1] + 1, string.Format(ERR_MSG_OPERATION_DOESNT_SUPPORT_TYPE, ops_item, type), SOURCE_TEMPLATE)); break; case RESPONSE_OPERATION_LESS_THAN_OR_EQUAL: if (RESPONSE_OPERATION_LESS_THAN_OR_EQUAL_ALLOWED_TYPES.Contains(type)) op = ops_item; else errors.Add(new ParserError(item.line, item.position + original_occurrences[1] + 1, string.Format(ERR_MSG_OPERATION_DOESNT_SUPPORT_TYPE, ops_item, type), SOURCE_TEMPLATE)); break; case RESPONSE_OPERATION_GREATER_THAN: if (RESPONSE_OPERATION_GREATER_THAN_ALLOWED_TYPES.Contains(type)) op = ops_item; else errors.Add(new ParserError(item.line, item.position + original_occurrences[1] + 1, string.Format(ERR_MSG_OPERATION_DOESNT_SUPPORT_TYPE, ops_item, type), SOURCE_TEMPLATE)); break; case RESPONSE_OPERATION_GREATER_THAN_OR_EQUAL: if (RESPONSE_OPERATION_GREATER_THAN_OR_EQUAL_ALLOWED_TYPES.Contains(type)) op = ops_item; else errors.Add(new ParserError(item.line, item.position + original_occurrences[1] + 1, string.Format(ERR_MSG_OPERATION_DOESNT_SUPPORT_TYPE, ops_item, type), SOURCE_TEMPLATE)); break; case RESPONSE_OPERATION_MATCHING_REGEXP_PATTERN: if (RESPONSE_OPERATION_MATCHING_REGEXP_PATTERN_ALLOWED_TYPES.Contains(type)) op = ops_item; else errors.Add(new ParserError(item.line, item.position + original_occurrences[1] + 1, string.Format(ERR_MSG_OPERATION_DOESNT_SUPPORT_TYPE, ops_item, type), SOURCE_TEMPLATE)); break; default: // urecognized operation type errors.Add(new ParserError(item.line, item.position + original_occurrences[1] + 1, string.Format(ERR_MSG_UNKNOWN_OPERATION, ops_item), SOURCE_TEMPLATE)); break; } } // assign operation and modifiers into parser compare definition object pcd.use_variable = use_var; pcd.if_present = if_present; pcd.operation = op; // if operation was specified, type must be also specified if (pcd.type == null && pcd.operation != null) errors.Add(new ParserError(item.line, item.position + 1, ERR_MSG_MISING_TYPE_FOR_OPERATION, SOURCE_TEMPLATE)); } // value if (val.Length > 0) { if (use_var) // do we want to replace variable name in value field by it's actual value? { if (customVariables.ContainsKey(val)) // search for desired custom variable ... { // try to convert value of parsed variable name to desired type; if that is not possible add an error pcd.value = convertToDesiredType(pcd.type, customVariables[val], item, errors, original_occurrences); } else // ... and add error if it doesn't exist errors.Add(new ParserError(item.line, item.position + original_occurrences[2] + 1, string.Format(ERR_MSG_UNKNOWN_CUSTOM_VARIABLE, val), SOURCE_TEMPLATE)); } else { // try to convert parsed value to desired type; if that is not possible add an error pcd.value = convertToDesiredType(pcd.type, val, item, errors, original_occurrences); } } else { // in case that every part of possible information from syntax string is missing, let's presume empty string if (occurrences[4] - occurrences[0] == 4) pcd.value = ""; } // variable if (var.Length > 0) pcd.var_name = var; // if there are any new syntax errors we can't parse this item - return it without any change if (errors.Count > original_error_count) return item; // assign newly constructed parser compare definition object into item and return the item item.comp_def = pcd; return item; }