Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/SharpFM.Model/ClipTypes/IClipTypeStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ public interface IClipTypeStrategy

/// <summary>
/// Produce a starter XML body for a fresh clip with the given name. Used by
/// "new clip" flows in the host and by plugins.
/// "new clip" flows in the host and by plugins. Build with
/// <see cref="System.Xml.Linq.XElement"/> rather than string concatenation so
/// XML metacharacters in <paramref name="clipName"/> are escaped by the framework.
/// </summary>
string DefaultXml(string clipName);

Expand Down
14 changes: 9 additions & 5 deletions src/SharpFM.Model/ClipTypes/TableClipStrategy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
using System.Xml.Linq;
using SharpFM.Model.Parsing;
using SharpFM.Model.Schema;
using SharpFM.Model.Scripting;

namespace SharpFM.Model.ClipTypes;

Expand Down Expand Up @@ -65,10 +64,15 @@ public ClipParseResult Parse(string xml)
return new ParseSuccess(new TableClipModel(table), report);
}

public string DefaultXml(string clipName) =>
_wrapsBaseTable
? $"<fmxmlsnippet type=\"FMObjectList\"><BaseTable name=\"{XmlHelpers.XmlEscape(clipName)}\"></BaseTable></fmxmlsnippet>"
: "<fmxmlsnippet type=\"FMObjectList\"></fmxmlsnippet>";
public string DefaultXml(string clipName)
{
var snippet = new XElement("fmxmlsnippet", new XAttribute("type", "FMObjectList"));
if (_wrapsBaseTable)
{
snippet.Add(new XElement("BaseTable", new XAttribute("name", clipName)));
}
return snippet.ToString(SaveOptions.DisableFormatting);
}

public string? TryGetSourceName(string xml)
{
Expand Down
14 changes: 7 additions & 7 deletions src/SharpFM.Model/Schema/FmField.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ public XElement ToXml()
// Calculation
if (!string.IsNullOrEmpty(Calculation))
{
var calcEl = XElement.Parse($"<Calculation><![CDATA[{Calculation}]]></Calculation>");
var calcEl = new XElement("Calculation", new XCData(Calculation));
if (AlwaysEvaluate)
calcEl.Add(new XAttribute("alwaysEvaluate", "True"));
if (!string.IsNullOrEmpty(CalculationContext))
Expand Down Expand Up @@ -222,10 +222,10 @@ public XElement ToXml()
new XAttribute("nextSerialNumber", AutoEnterValue ?? "1")));
break;
case AutoEnterType.ConstantData:
autoEl.Add(XElement.Parse($"<ConstantData><![CDATA[{AutoEnterValue ?? ""}]]></ConstantData>"));
autoEl.Add(new XElement("ConstantData", new XCData(AutoEnterValue ?? "")));
break;
case AutoEnterType.Calculation:
autoEl.Add(XElement.Parse($"<Calculation><![CDATA[{AutoEnterValue ?? ""}]]></Calculation>"));
autoEl.Add(new XElement("Calculation", new XCData(AutoEnterValue ?? "")));
break;
}

Expand All @@ -249,16 +249,16 @@ public XElement ToXml()
{
var rangeEl = new XElement("Range");
if (RangeMin != null)
rangeEl.Add(XElement.Parse($"<MinimumValue><![CDATA[{RangeMin}]]></MinimumValue>"));
rangeEl.Add(new XElement("MinimumValue", new XCData(RangeMin)));
if (RangeMax != null)
rangeEl.Add(XElement.Parse($"<MaximumValue><![CDATA[{RangeMax}]]></MaximumValue>"));
rangeEl.Add(new XElement("MaximumValue", new XCData(RangeMax)));
valEl.Add(rangeEl);
}

if (ValidationCalculation != null)
valEl.Add(XElement.Parse($"<StrictValidation><![CDATA[{ValidationCalculation}]]></StrictValidation>"));
valEl.Add(new XElement("StrictValidation", new XCData(ValidationCalculation)));
if (ErrorMessage != null)
valEl.Add(XElement.Parse($"<ErrorMessage><![CDATA[{ErrorMessage}]]></ErrorMessage>"));
valEl.Add(new XElement("ErrorMessage", new XCData(ErrorMessage)));

el.Add(valEl);
}
Expand Down
2 changes: 1 addition & 1 deletion src/SharpFM.Model/Scripting/Values/Calculation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public sealed record Calculation(string Text)
/// common case in FileMaker script XML.
/// </summary>
public XElement ToXml(string elementName = "Calculation") =>
XElement.Parse($"<{elementName}><![CDATA[{Text}]]></{elementName}>");
new(elementName, new XCData(Text));

/// <summary>
/// Parse the text body of the element (CDATA is transparent to
Expand Down
8 changes: 0 additions & 8 deletions src/SharpFM.Model/Scripting/XmlHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@ namespace SharpFM.Model.Scripting;

public static class XmlHelpers
{
public static string XmlEscape(string s)
{
return s.Replace("&", "&amp;")
.Replace("<", "&lt;")
.Replace(">", "&gt;")
.Replace("\"", "&quot;");
}

public static string Unquote(string s)
{
if (s.Length >= 2 && s[0] == '"' && s[^1] == '"')
Expand Down
1 change: 1 addition & 0 deletions tests/SharpFM.Tests/ClipTypes/TableClipStrategyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public void DefaultXml_ProducesParseableSnippet()
[InlineData("My \"favorite\" stuff")]
[InlineData("A & B")]
[InlineData("<Angle>")]
[InlineData("O'Brien")]
public void Table_DefaultXml_EscapesPunctuationInName(string clipName)
{
var seed = TableClipStrategy.Table.DefaultXml(clipName);
Expand Down
13 changes: 13 additions & 0 deletions tests/SharpFM.Tests/Scripting/Values/CalculationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,17 @@ public void RoundTrip_PreservesComplexExpression()

Assert.Equal(expr, roundTripped.Value);
}

[Theory]
[InlineData("a < b & c > d")]
[InlineData("Quote(\"hello\")")]
[InlineData("If ( name = \"O'Brien\" ; 1 ; 0 )")]
public void RoundTrip_PreservesXmlMetacharacters(string expr)
{
var emitted = new Calculation(expr).ToXml();
var serialized = emitted.ToString();
var reparsed = Calculation.FromXml(XElement.Parse(serialized));

Assert.Equal(expr, reparsed.Text);
}
}
Loading