in src/StructuredLogger/Construction/ItemGroupParser.cs [17:195]
public static BaseNode ParsePropertyOrItemList(string message, string prefix, StringCache stringTable, bool isOutputItem = false)
{
if (!TextUtilities.ContainsLineBreak(message))
{
var nameValue = TextUtilities.ParseNameValue(message, trimFromStart: prefix.Length);
if (!isOutputItem)
{
var property = new Property
{
Name = stringTable.Intern(nameValue.Key),
Value = stringTable.Intern(nameValue.Value)
};
return property;
}
else
{
var singleItem = new AddItem { Name = stringTable.Intern(nameValue.Key) };
var item = new Item { Text = stringTable.Intern(nameValue.Value) };
singleItem.AddChild(item);
return singleItem;
}
}
// Can't use a field initializer with ThreadStatic.
if (lineSpans == null)
{
lineSpans = new List<Span>(10240);
}
lineSpans.Clear();
message.CollectLineSpans(lineSpans, includeLineBreakInSpan: false);
NamedNode parameter = isOutputItem ? new AddItem() : new Parameter();
if (lineSpans[0].Length > prefix.Length)
{
// we have a weird case of multi-line value
var nameValue = TextUtilities.ParseNameValue(message, lineSpans[0].Skip(prefix.Length));
parameter.Name = stringTable.Intern(nameValue.Key);
parameter.AddChild(new Item
{
Text = stringTable.Intern(nameValue.Value)
});
for (int i = 1; i < lineSpans.Count; i++)
{
parameter.AddChild(new Item
{
Text = stringTable.Intern(message.Substring(lineSpans[i]))
});
}
return parameter;
}
Item currentItem = null;
Property currentProperty = null;
foreach (var lineSpan in lineSpans)
{
if (TextUtilities.IsWhitespace(message, lineSpan))
{
continue;
}
var numberOfLeadingSpaces = TextUtilities.GetNumberOfLeadingSpaces(message, lineSpan);
switch (numberOfLeadingSpaces)
{
case 4:
if (message[lineSpan.End - 1] == '=')
{
parameter.Name = stringTable.Intern(message.Substring(lineSpan.Start + 4, lineSpan.Length - 5));
}
break;
case 8:
var skip8 = message.Substring(lineSpan.Skip(8));
var equals = skip8.IndexOf('=');
if (equals != -1)
{
var kvp = TextUtilities.ParseNameValueWithEqualsPosition(skip8, equals);
currentProperty = new Property
{
Name = stringTable.Intern(kvp.Key),
Value = stringTable.Intern(kvp.Value)
};
parameter.AddChild(currentProperty);
currentItem = null;
}
else
{
currentItem = new Item
{
Text = stringTable.Intern(skip8)
};
parameter.AddChild(currentItem);
currentProperty = null;
}
break;
case 16:
if (currentItem == null && currentProperty != null)
{
// we incorrectly interpreted the previous line as Property, not Item (because it had '=')
// and so we created a property out of name/value.
// Fix this by turning it into an Item.
if (parameter.LastChild == currentProperty)
{
currentItem = new Item
{
Text = stringTable.Intern(currentProperty.Name + "=" + currentProperty.Value)
};
parameter.Children.RemoveAt(parameter.Children.Count - 1);
currentProperty = null;
parameter.AddChild(currentItem);
}
}
if (currentItem != null)
{
var span16 = lineSpan.Skip(16);
var equals16 = message.IndexOf(span16, '=');
if (equals16 == -1)
{
// must be a continuation of the metadata value from the previous line
if (currentItem.HasChildren)
{
var metadata = currentItem.Children[currentItem.Children.Count - 1] as Metadata;
if (metadata != null)
{
var currentLine = message.Substring(span16);
if (!string.IsNullOrEmpty(metadata.Value))
{
metadata.Value = metadata.Value + currentLine;
}
else
{
metadata.Value = currentLine;
}
}
}
}
else
{
var nameValue = TextUtilities.ParseNameValueWithEqualsPosition(message, span16, equals16);
var metadata = new Metadata
{
Name = stringTable.Intern(nameValue.Key),
Value = stringTable.Intern(nameValue.Value)
};
currentItem.AddChild(metadata);
}
}
break;
default:
var line = message.Substring(lineSpan);
if (numberOfLeadingSpaces == 0 && line == prefix)
{
continue;
}
// must be a continuation of a multi-line value
if (currentProperty != null)
{
currentProperty.Value += "\n" + line;
}
else if (currentItem != null && currentItem.HasChildren)
{
var metadata = currentItem.Children[currentItem.Children.Count - 1] as Metadata;
if (metadata != null)
{
metadata.Value = (metadata.Value ?? "") + line;
}
}
break;
}
}
return parameter;
}