private void Validate( ref LiteralToken literal, DefinitionInfo definition) { // Legal var literal2 = literal; if (definition.Get <ScalarDefinition>().Any(x => x.IsMatch(literal2))) { return; } // Not a string, convert if (literal.Type != TokenType.String) { var stringToken = new StringToken(literal.FileId, literal.Line, literal.Column, literal.ToString()); // Legal if (definition.Get <StringDefinition>().Any(x => x.IsMatch(stringToken))) { literal = stringToken; return; } } // Illegal m_context.Error(literal, TemplateStrings.UnexpectedValue(literal)); }
private String GetErrorPrefix( Int32?fileId, Int32?line, Int32?column) { if (fileId != null) { var fileName = GetFileName(fileId.Value); if (line != null && column != null) { return($"{fileName} {TemplateStrings.LineColumn(line, column)}:"); } else { return($"{fileName}:"); } } else if (line != null && column != null) { return($"{TemplateStrings.LineColumn(line, column)}:"); } else { return(String.Empty); } }
internal void IncrementEvents() { if (m_events++ >= m_maxEvents) { throw new InvalidOperationException(TemplateStrings.MaxTemplateEventsExceeded()); } }
internal void IncrementDepth() { if (m_depth++ >= m_maxDepth) { throw new InvalidOperationException(TemplateStrings.MaxObjectDepthExceeded()); } }
private static Boolean MatchesDirective( String trimmed, String directive, Int32 expectedParameters, out List <String> parameters, out Exception ex) { if (trimmed.StartsWith(directive, StringComparison.Ordinal) && (trimmed.Length == directive.Length || Char.IsWhiteSpace(trimmed[directive.Length]))) { parameters = new List <String>(); var startIndex = directive.Length; var inString = false; var parens = 0; for (var i = startIndex; i < trimmed.Length; i++) { var c = trimmed[i]; if (Char.IsWhiteSpace(c) && !inString && parens == 0) { if (startIndex < i) { parameters.Add(trimmed.Substring(startIndex, i - startIndex)); } startIndex = i + 1; } else if (c == '\'') { inString = !inString; } else if (c == '(' && !inString) { parens++; } else if (c == ')' && !inString) { parens--; } } if (startIndex < trimmed.Length) { parameters.Add(trimmed.Substring(startIndex)); } if (expectedParameters != parameters.Count) { ex = new ArgumentException(TemplateStrings.ExpectedNParametersFollowingDirective(expectedParameters, directive, parameters.Count)); parameters = null; return(false); } ex = null; return(true); } ex = null; parameters = null; return(false); }
private void HandleMappingWithAllLooseProperties( DefinitionInfo mappingDefinition, DefinitionInfo keyDefinition, DefinitionInfo valueDefinition, MappingToken mapping) { TemplateToken nextValue; var keys = new HashSet <String>(StringComparer.OrdinalIgnoreCase); while (m_objectReader.AllowLiteral(out LiteralToken rawLiteral)) { var nextKeyScalar = ParseScalar(rawLiteral, mappingDefinition.AllowedContext); // Expression if (nextKeyScalar is ExpressionToken) { // Legal if (mappingDefinition.AllowedContext.Length > 0) { m_memory.AddBytes(nextKeyScalar); nextValue = ReadValue(valueDefinition); mapping.Add(nextKeyScalar, nextValue); } // Illegal else { m_context.Error(nextKeyScalar, TemplateStrings.ExpressionNotAllowed()); SkipValue(); } continue; } // Not a string, convert if (!(nextKeyScalar is StringToken nextKey)) { nextKey = new StringToken(nextKeyScalar.FileId, nextKeyScalar.Line, nextKeyScalar.Column, nextKeyScalar.ToString()); } // Duplicate if (!keys.Add(nextKey.Value)) { m_context.Error(nextKey, TemplateStrings.ValueAlreadyDefined(nextKey.Value)); SkipValue(); continue; } // Validate Validate(nextKey, keyDefinition); m_memory.AddBytes(nextKey); // Add the pair nextValue = ReadValue(valueDefinition); mapping.Add(nextKey, nextValue); } ExpectMappingEnd(); }
private void SkipValue(Boolean error = false) { m_memory.IncrementEvents(); // Scalar if (m_objectReader.AllowLiteral(out LiteralToken literal)) { if (error) { m_context.Error(literal, TemplateStrings.UnexpectedValue(literal)); } return; } // Sequence if (m_objectReader.AllowSequenceStart(out SequenceToken sequence)) { m_memory.IncrementDepth(); if (error) { m_context.Error(sequence, TemplateStrings.UnexpectedSequenceStart()); } while (!m_objectReader.AllowSequenceEnd()) { SkipValue(); } m_memory.DecrementDepth(); return; } // Mapping if (m_objectReader.AllowMappingStart(out MappingToken mapping)) { m_memory.IncrementDepth(); if (error) { m_context.Error(mapping, TemplateStrings.UnexpectedMappingStart()); } while (!m_objectReader.AllowMappingEnd()) { SkipValue(); SkipValue(); } m_memory.DecrementDepth(); return; } // Unexpected throw new InvalidOperationException(TemplateStrings.ExpectedScalarSequenceOrMapping()); }
internal void AddBytes(Int32 bytes) { checked { m_currentBytes += bytes; } if (m_currentBytes > m_maxBytes) { throw new InvalidOperationException(TemplateStrings.MaxObjectSizeExceeded()); } }
private void HandleMappingWithAllLooseProperties( DefinitionInfo mappingDefinition, DefinitionInfo keyDefinition, DefinitionInfo valueDefinition, MappingToken mapping) { var keys = new HashSet <String>(StringComparer.OrdinalIgnoreCase); while (m_unraveler.AllowScalar(mappingDefinition.Expand, out ScalarToken nextKeyScalar)) { // Expression if (nextKeyScalar is ExpressionToken) { if (nextKeyScalar is BasicExpressionToken) { mapping.Add(nextKeyScalar, Evaluate(valueDefinition)); } else { var anyDefinition = new DefinitionInfo(mappingDefinition, TemplateConstants.Any); mapping.Add(nextKeyScalar, Evaluate(anyDefinition)); } continue; } // Not a string if (!(nextKeyScalar is StringToken nextKey)) { nextKey = new StringToken(nextKeyScalar.FileId, nextKeyScalar.Line, nextKeyScalar.Column, nextKeyScalar.ToString()); } // Duplicate if (!keys.Add(nextKey.Value)) { m_context.Error(nextKey, TemplateStrings.ValueAlreadyDefined(nextKey.Value)); m_unraveler.SkipMappingValue(); continue; } // Validate Validate(nextKey, keyDefinition); // Add the pair var nextValue = Evaluate(valueDefinition); mapping.Add(nextKey, nextValue); } m_unraveler.ReadMappingEnd(); }
private void Validate( ref ScalarToken scalar, DefinitionInfo definition) { switch (scalar.Type) { case TokenType.Null: case TokenType.Boolean: case TokenType.Number: case TokenType.String: var literal = scalar as LiteralToken; // Legal if (definition.Get <ScalarDefinition>().Any(x => x.IsMatch(literal))) { return; } // Not a string, convert if (literal.Type != TokenType.String) { literal = new StringToken(literal.FileId, literal.Line, literal.Column, literal.ToString()); // Legal if (definition.Get <StringDefinition>().Any(x => x.IsMatch(literal))) { scalar = literal; return; } } // Illegal m_context.Error(literal, TemplateStrings.UnexpectedValue(literal)); break; case TokenType.BasicExpression: // Illegal if (definition.AllowedContext.Length == 0) { m_context.Error(scalar, TemplateStrings.ExpressionNotAllowed()); } break; default: m_context.Error(scalar, TemplateStrings.UnexpectedValue(scalar)); break; } }
private ExpressionToken ParseExpression( Int32?line, Int32?column, String value, String[] allowedContext, out Exception ex) { var trimmed = value.Trim(); // Check if the value is empty if (String.IsNullOrEmpty(trimmed)) { ex = new ArgumentException(TemplateStrings.ExpectedExpression()); return(null); } // Try to find a matching directive List <String> parameters; if (MatchesDirective(trimmed, TemplateConstants.InsertDirective, 0, out parameters, out ex)) { return(new InsertExpressionToken(m_fileId, line, column)); } else if (ex != null) { return(null); } // Check if the value is an expression if (!ExpressionToken.IsValidExpression(trimmed, allowedContext, out ex)) { return(null); } // Return the expression return(new BasicExpressionToken(m_fileId, line, column, trimmed)); }
private TemplateToken Evaluate(DefinitionInfo definition) { // Scalar if (m_unraveler.AllowScalar(definition.Expand, out ScalarToken scalar)) { if (scalar is LiteralToken literal) { Validate(ref literal, definition); return(literal); } else { return(scalar); } } // Sequence start if (m_unraveler.AllowSequenceStart(definition.Expand, out SequenceToken sequence)) { var sequenceDefinition = definition.Get <SequenceDefinition>().FirstOrDefault(); // Legal if (sequenceDefinition != null) { var itemDefinition = new DefinitionInfo(definition, sequenceDefinition.ItemType); // Add each item while (!m_unraveler.AllowSequenceEnd(definition.Expand)) { var item = Evaluate(itemDefinition); sequence.Add(item); } } // Illegal else { // Error m_context.Error(sequence, TemplateStrings.UnexpectedSequenceStart()); // Skip each item while (!m_unraveler.AllowSequenceEnd(expand: false)) { m_unraveler.SkipSequenceItem(); } } return(sequence); } // Mapping if (m_unraveler.AllowMappingStart(definition.Expand, out MappingToken mapping)) { var mappingDefinitions = definition.Get <MappingDefinition>().ToList(); // Legal if (mappingDefinitions.Count > 0) { if (mappingDefinitions.Count > 1 || m_schema.HasProperties(mappingDefinitions[0]) || String.IsNullOrEmpty(mappingDefinitions[0].LooseKeyType)) { HandleMappingWithWellKnownProperties(definition, mappingDefinitions, mapping); } else { var keyDefinition = new DefinitionInfo(definition, mappingDefinitions[0].LooseKeyType); var valueDefinition = new DefinitionInfo(definition, mappingDefinitions[0].LooseValueType); HandleMappingWithAllLooseProperties(definition, keyDefinition, valueDefinition, mapping); } } // Illegal else { m_context.Error(mapping, TemplateStrings.UnexpectedMappingStart()); while (!m_unraveler.AllowMappingEnd(expand: false)) { m_unraveler.SkipMappingKey(); m_unraveler.SkipMappingValue(); } } return(mapping); } throw new ArgumentException(TemplateStrings.ExpectedScalarSequenceOrMapping()); }
private void HandleMappingWithWellKnownProperties( DefinitionInfo definition, List <MappingDefinition> mappingDefinitions, MappingToken mapping) { // Check if loose properties are allowed String looseKeyType = null; String looseValueType = null; DefinitionInfo?looseKeyDefinition = null; DefinitionInfo?looseValueDefinition = null; if (!String.IsNullOrEmpty(mappingDefinitions[0].LooseKeyType)) { looseKeyType = mappingDefinitions[0].LooseKeyType; looseValueType = mappingDefinitions[0].LooseValueType; } var keys = new HashSet <String>(StringComparer.OrdinalIgnoreCase); var hasExpressionKey = false; while (m_objectReader.AllowLiteral(out LiteralToken rawLiteral)) { var nextKeyScalar = ParseScalar(rawLiteral, definition.AllowedContext); // Expression if (nextKeyScalar is ExpressionToken) { hasExpressionKey = true; // Legal if (definition.AllowedContext.Length > 0) { m_memory.AddBytes(nextKeyScalar); var anyDefinition = new DefinitionInfo(definition, TemplateConstants.Any); mapping.Add(nextKeyScalar, ReadValue(anyDefinition)); } // Illegal else { m_context.Error(nextKeyScalar, TemplateStrings.ExpressionNotAllowed()); SkipValue(); } continue; } // Not a string, convert if (!(nextKeyScalar is StringToken nextKey)) { nextKey = new StringToken(nextKeyScalar.FileId, nextKeyScalar.Line, nextKeyScalar.Column, nextKeyScalar.ToString()); } // Duplicate if (!keys.Add(nextKey.Value)) { m_context.Error(nextKey, TemplateStrings.ValueAlreadyDefined(nextKey.Value)); SkipValue(); continue; } // Well known if (m_schema.TryMatchKey(mappingDefinitions, nextKey.Value, out String nextValueType)) { m_memory.AddBytes(nextKey); var nextValueDefinition = new DefinitionInfo(definition, nextValueType); var nextValue = ReadValue(nextValueDefinition); mapping.Add(nextKey, nextValue); continue; } // Loose if (looseKeyType != null) { if (looseKeyDefinition == null) { looseKeyDefinition = new DefinitionInfo(definition, looseKeyType); looseValueDefinition = new DefinitionInfo(definition, looseValueType); } Validate(nextKey, looseKeyDefinition.Value); m_memory.AddBytes(nextKey); var nextValue = ReadValue(looseValueDefinition.Value); mapping.Add(nextKey, nextValue); continue; } // Error m_context.Error(nextKey, TemplateStrings.UnexpectedValue(nextKey.Value)); SkipValue(); } // Only one if (mappingDefinitions.Count > 1) { var hitCount = new Dictionary <String, Int32>(); foreach (MappingDefinition mapdef in mappingDefinitions) { foreach (String key in mapdef.Properties.Keys) { if (!hitCount.TryGetValue(key, out Int32 value)) { hitCount.Add(key, 1); } else { hitCount[key] = value + 1; } } } List <String> nonDuplicates = new List <String>(); foreach (String key in hitCount.Keys) { if (hitCount[key] == 1) { nonDuplicates.Add(key); } } nonDuplicates.Sort(); String listToDeDuplicate = String.Join(", ", nonDuplicates); m_context.Error(mapping, TemplateStrings.UnableToDetermineOneOf(listToDeDuplicate)); } else if (mappingDefinitions.Count == 1 && !hasExpressionKey) { foreach (var property in mappingDefinitions[0].Properties) { if (property.Value.Required) { if (!keys.Contains(property.Key)) { m_context.Error(mapping, $"Required property is missing: {property.Key}"); } } } } ExpectMappingEnd(); }
private void HandleMappingWithWellKnownProperties( DefinitionInfo definition, List <MappingDefinition> mappingDefinitions, MappingToken mapping) { // Check if loose properties are allowed String looseKeyType = null; String looseValueType = null; DefinitionInfo?looseKeyDefinition = null; DefinitionInfo?looseValueDefinition = null; if (!String.IsNullOrEmpty(mappingDefinitions[0].LooseKeyType)) { looseKeyType = mappingDefinitions[0].LooseKeyType; looseValueType = mappingDefinitions[0].LooseValueType; } var keys = new HashSet <String>(StringComparer.OrdinalIgnoreCase); while (m_unraveler.AllowScalar(definition.Expand, out ScalarToken nextKeyScalar)) { // Expression if (nextKeyScalar is ExpressionToken) { var anyDefinition = new DefinitionInfo(definition, TemplateConstants.Any); mapping.Add(nextKeyScalar, Evaluate(anyDefinition)); continue; } // Not a string, convert if (!(nextKeyScalar is StringToken nextKey)) { nextKey = new StringToken(nextKeyScalar.FileId, nextKeyScalar.Line, nextKeyScalar.Column, nextKeyScalar.ToString()); } // Duplicate if (!keys.Add(nextKey.Value)) { m_context.Error(nextKey, TemplateStrings.ValueAlreadyDefined(nextKey.Value)); m_unraveler.SkipMappingValue(); continue; } // Well known if (m_schema.TryMatchKey(mappingDefinitions, nextKey.Value, out String nextValueType)) { var nextValueDefinition = new DefinitionInfo(definition, nextValueType); var nextValue = Evaluate(nextValueDefinition); mapping.Add(nextKey, nextValue); continue; } // Loose if (looseKeyType != null) { if (looseKeyDefinition == null) { looseKeyDefinition = new DefinitionInfo(definition, looseKeyType); looseValueDefinition = new DefinitionInfo(definition, looseValueType); } Validate(nextKey, looseKeyDefinition.Value); var nextValue = Evaluate(looseValueDefinition.Value); mapping.Add(nextKey, nextValue); continue; } // Error m_context.Error(nextKey, TemplateStrings.UnexpectedValue(nextKey.Value)); m_unraveler.SkipMappingValue(); } // Only one if (mappingDefinitions.Count > 1) { var hitCount = new Dictionary <String, Int32>(); foreach (MappingDefinition mapdef in mappingDefinitions) { foreach (String key in mapdef.Properties.Keys) { if (!hitCount.TryGetValue(key, out Int32 value)) { hitCount.Add(key, 1); } else { hitCount[key] = value + 1; } } } List <String> nonDuplicates = new List <String>(); foreach (String key in hitCount.Keys) { if (hitCount[key] == 1) { nonDuplicates.Add(key); } } nonDuplicates.Sort(); String listToDeDuplicate = String.Join(", ", nonDuplicates); m_context.Error(mapping, TemplateStrings.UnableToDetermineOneOf(listToDeDuplicate)); } m_unraveler.ReadMappingEnd(); }
public TemplateValidationException(IEnumerable <TemplateValidationError> errors) : this(TemplateStrings.TemplateNotValidWithErrors(string.Join(",", (errors ?? Enumerable.Empty <TemplateValidationError>()).Select(e => e.Message)))) { m_errors = new List <TemplateValidationError>(errors ?? Enumerable.Empty <TemplateValidationError>()); }
public TemplateValidationException() : this(TemplateStrings.TemplateNotValid()) { }
private TemplateToken ReadValue(DefinitionInfo definition) { m_memory.IncrementEvents(); // Scalar if (m_objectReader.AllowLiteral(out LiteralToken literal)) { var scalar = ParseScalar(literal, definition.AllowedContext); Validate(ref scalar, definition); m_memory.AddBytes(scalar); return(scalar); } // Sequence if (m_objectReader.AllowSequenceStart(out SequenceToken sequence)) { m_memory.IncrementDepth(); m_memory.AddBytes(sequence); var sequenceDefinition = definition.Get <SequenceDefinition>().FirstOrDefault(); // Legal if (sequenceDefinition != null) { var itemDefinition = new DefinitionInfo(definition, sequenceDefinition.ItemType); // Add each item while (!m_objectReader.AllowSequenceEnd()) { var item = ReadValue(itemDefinition); sequence.Add(item); } } // Illegal else { // Error m_context.Error(sequence, TemplateStrings.UnexpectedSequenceStart()); // Skip each item while (!m_objectReader.AllowSequenceEnd()) { SkipValue(); } } m_memory.DecrementDepth(); return(sequence); } // Mapping if (m_objectReader.AllowMappingStart(out MappingToken mapping)) { m_memory.IncrementDepth(); m_memory.AddBytes(mapping); var mappingDefinitions = definition.Get <MappingDefinition>().ToList(); // Legal if (mappingDefinitions.Count > 0) { if (mappingDefinitions.Count > 1 || m_schema.HasProperties(mappingDefinitions[0]) || String.IsNullOrEmpty(mappingDefinitions[0].LooseKeyType)) { HandleMappingWithWellKnownProperties(definition, mappingDefinitions, mapping); } else { var keyDefinition = new DefinitionInfo(definition, mappingDefinitions[0].LooseKeyType); var valueDefinition = new DefinitionInfo(definition, mappingDefinitions[0].LooseValueType); HandleMappingWithAllLooseProperties(definition, keyDefinition, valueDefinition, mapping); } } // Illegal else { m_context.Error(mapping, TemplateStrings.UnexpectedMappingStart()); while (!m_objectReader.AllowMappingEnd()) { SkipValue(); SkipValue(); } } m_memory.DecrementDepth(); return(mapping); } throw new InvalidOperationException(TemplateStrings.ExpectedScalarSequenceOrMapping()); }
private ScalarToken ParseScalar( LiteralToken token, String[] allowedContext) { // Not a string if (token.Type != TokenType.String) { return(token); } // Check if the value is definitely a literal var raw = token.ToString(); Int32 startExpression; if (String.IsNullOrEmpty(raw) || (startExpression = raw.IndexOf(TemplateConstants.OpenExpression)) < 0) // Doesn't contain ${{ { return(token); } // Break the value into segments of LiteralToken and ExpressionToken var segments = new List <ScalarToken>(); var i = 0; while (i < raw.Length) { // An expression starts here: if (i == startExpression) { // Find the end of the expression - i.e. }} startExpression = i; var endExpression = -1; var inString = false; for (i += TemplateConstants.OpenExpression.Length; i < raw.Length; i++) { if (raw[i] == '\'') { inString = !inString; // Note, this handles escaped single quotes gracefully. Ex. 'foo''bar' } else if (!inString && raw[i] == '}' && raw[i - 1] == '}') { endExpression = i; i++; break; } } // Check if not closed if (endExpression < startExpression) { m_context.Error(token, TemplateStrings.ExpressionNotClosed()); return(token); } // Parse the expression var rawExpression = raw.Substring( startExpression + TemplateConstants.OpenExpression.Length, endExpression - startExpression + 1 - TemplateConstants.OpenExpression.Length - TemplateConstants.CloseExpression.Length); var expression = ParseExpression(token.Line, token.Column, rawExpression, allowedContext, out Exception ex); // Check for error if (ex != null) { m_context.Error(token, ex); return(token); } // Check if a directive was used when not allowed if (!String.IsNullOrEmpty(expression.Directive) && ((startExpression != 0) || (i < raw.Length))) { m_context.Error(token, TemplateStrings.DirectiveNotAllowedInline(expression.Directive)); return(token); } // Add the segment segments.Add(expression); // Look for the next expression startExpression = raw.IndexOf(TemplateConstants.OpenExpression, i); } // The next expression is further ahead: else if (i < startExpression) { // Append the segment AddString(segments, token.Line, token.Column, raw.Substring(i, startExpression - i)); // Adjust the position i = startExpression; } // No remaining expressions: else { AddString(segments, token.Line, token.Column, raw.Substring(i)); break; } } // Check if can convert to a literal // For example, the escaped expression: ${{ '{{ this is a literal }}' }} if (segments.Count == 1 && segments[0] is BasicExpressionToken basicExpression && IsExpressionString(basicExpression.Expression, out String str)) { return(new StringToken(m_fileId, token.Line, token.Column, str)); } // Check if only ony segment if (segments.Count == 1) { return(segments[0]); } // Build the new expression, using the format function var format = new StringBuilder(); var args = new StringBuilder(); var argIndex = 0; foreach (var segment in segments) { if (segment is StringToken literal) { var text = ExpressionUtility.StringEscape(literal.Value) // Escape quotes .Replace("{", "{{") // Escape braces .Replace("}", "}}"); format.Append(text); } else { format.Append("{" + argIndex.ToString(CultureInfo.InvariantCulture) + "}"); // Append formatter argIndex++; var expression = segment as BasicExpressionToken; args.Append(", "); args.Append(expression.Expression); } } return(new BasicExpressionToken(m_fileId, token.Line, token.Column, $"format('{format}'{args})")); }