Skip to content

Commit 08c9b64

Browse files
committed
XMP : Anchor namespace declarations to custom nodes + XmlArray unit tests
1 parent 692268a commit 08c9b64

File tree

11 files changed

+337
-35
lines changed

11 files changed

+337
-35
lines changed

ATL.unit-test/Misc/XmlArrayTest.cs

+218
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
using ATL.AudioData;
2+
using ATL.AudioData.IO;
3+
using System.IO;
4+
5+
namespace ATL.test
6+
{
7+
[TestClass]
8+
public class XmlArrayTest
9+
{
10+
private static string getFrom(string name)
11+
{
12+
var dataPath = TestUtils.GetResourceLocationRoot() + "_Xml" + Path.DirectorySeparatorChar + name;
13+
using (var source = new FileStream(dataPath, FileMode.Open))
14+
{
15+
var reader = new StreamReader(source);
16+
return reader.ReadToEnd().ReplaceLineEndings("").Replace("\t", "");
17+
}
18+
}
19+
20+
[TestMethod]
21+
public void XmlArray_writeBasic()
22+
{
23+
var xmlArray = new XmlArray(
24+
"root",
25+
"test",
26+
_ => false,
27+
_ => false
28+
);
29+
30+
TagHolder holder = new TagHolder();
31+
var data = new Dictionary<string, string>();
32+
data["test.one"] = "aaa";
33+
data["test.two"] = "bbb";
34+
holder.AdditionalFields = data;
35+
36+
var memStream = new MemoryStream();
37+
xmlArray.ToStream(memStream, holder);
38+
var reader = new StreamReader(memStream);
39+
memStream.Position = 0;
40+
string result = reader.ReadToEnd();
41+
42+
Assert.IsTrue(result.Length > 0);
43+
44+
var expected = getFrom("basic.xml");
45+
Assert.AreEqual(expected, result);
46+
}
47+
48+
[TestMethod]
49+
public void XmlArray_writeAttributes()
50+
{
51+
var xmlArray = new XmlArray(
52+
"root",
53+
"test",
54+
_ => false,
55+
_ => false
56+
);
57+
xmlArray.setStructuralAttributes(new HashSet<string> { "hey", "PIP" });
58+
59+
TagHolder holder = new TagHolder();
60+
var data = new Dictionary<string, string>();
61+
data["test.one.hey"] = "ho";
62+
data["test.one"] = "aaa";
63+
data["test.two.pip"] = "boy";
64+
data["test.two"] = "bbb";
65+
holder.AdditionalFields = data;
66+
67+
var memStream = new MemoryStream();
68+
xmlArray.ToStream(memStream, holder);
69+
var reader = new StreamReader(memStream);
70+
memStream.Position = 0;
71+
string result = reader.ReadToEnd();
72+
73+
Assert.IsTrue(result.Length > 0);
74+
75+
var expected = getFrom("attributes.xml");
76+
Assert.AreEqual(expected, result);
77+
}
78+
79+
[TestMethod]
80+
public void XmlArray_writeCollections()
81+
{
82+
var xmlArray = new XmlArray(
83+
"root",
84+
"test",
85+
e => e.EndsWith("LIST", StringComparison.OrdinalIgnoreCase),
86+
_ => false
87+
);
88+
89+
TagHolder holder = new TagHolder();
90+
var data = new Dictionary<string, string>();
91+
data["test.one"] = "aaa";
92+
data["test.two"] = "bbb";
93+
data["test.theList.elt[0].value"] = "11";
94+
data["test.theList.elt[1].value"] = "22";
95+
data["test.theList.elt[2].value"] = "33";
96+
holder.AdditionalFields = data;
97+
98+
var memStream = new MemoryStream();
99+
xmlArray.ToStream(memStream, holder);
100+
var reader = new StreamReader(memStream);
101+
memStream.Position = 0;
102+
string result = reader.ReadToEnd();
103+
104+
Assert.IsTrue(result.Length > 0);
105+
106+
var expected = getFrom("collection.xml");
107+
Assert.AreEqual(expected, result);
108+
}
109+
110+
[TestMethod]
111+
public void XmlArray_writeRootNs()
112+
{
113+
IDictionary<string, string> DEFAULT_NAMESPACES = new Dictionary<string, string> { { "pap", "test:ns:meta/" } };
114+
var xmlArray = new XmlArray(
115+
"root",
116+
"test",
117+
_ => false,
118+
_ => false
119+
);
120+
// No namespace anchors (=> default anchor goes to root)
121+
xmlArray.setDefaultNamespaces(DEFAULT_NAMESPACES);
122+
123+
TagHolder holder = new TagHolder();
124+
var data = new Dictionary<string, string>();
125+
data["test.pap:one"] = "aaa";
126+
data["test.pap:two"] = "bbb";
127+
holder.AdditionalFields = data;
128+
129+
var memStream = new MemoryStream();
130+
xmlArray.ToStream(memStream, holder);
131+
var reader = new StreamReader(memStream);
132+
memStream.Position = 0;
133+
string result = reader.ReadToEnd();
134+
135+
Assert.IsTrue(result.Length > 0);
136+
137+
var expected = getFrom("rootNs.xml");
138+
Assert.AreEqual(expected, result);
139+
140+
141+
// All namespaces explicitly anchored to root
142+
IDictionary<string, ISet<string>> NAMESPACE_ANCHORS = new Dictionary<string, ISet<string>> { { "root", new HashSet<string> { "" } } };
143+
xmlArray.setNamespaceAnchors(NAMESPACE_ANCHORS);
144+
145+
memStream = new MemoryStream();
146+
xmlArray.ToStream(memStream, holder);
147+
reader = new StreamReader(memStream);
148+
memStream.Position = 0;
149+
result = reader.ReadToEnd();
150+
151+
Assert.IsTrue(result.Length > 0);
152+
153+
Assert.AreEqual(expected, result);
154+
155+
156+
// Specific namespace explicitly anchored to root
157+
IDictionary<string, ISet<string>> NAMESPACE_ANCHORS2 = new Dictionary<string, ISet<string>> { { "root", new HashSet<string> { "pap" } } };
158+
xmlArray.setNamespaceAnchors(NAMESPACE_ANCHORS2);
159+
160+
memStream = new MemoryStream();
161+
xmlArray.ToStream(memStream, holder);
162+
reader = new StreamReader(memStream);
163+
memStream.Position = 0;
164+
result = reader.ReadToEnd();
165+
166+
Assert.IsTrue(result.Length > 0);
167+
168+
Assert.AreEqual(expected, result);
169+
}
170+
171+
[TestMethod]
172+
public void XmlArray_writeAnchoredNs()
173+
{
174+
IDictionary<string, string> DEFAULT_NAMESPACES = new Dictionary<string, string> { { "pap", "test:ns:meta/" } };
175+
// Anchor one specific ns to one specific node
176+
IDictionary<string, ISet<string>> NAMESPACE_ANCHORS = new Dictionary<string, ISet<string>> { { "container", new HashSet<string> { "pap" } } };
177+
var xmlArray = new XmlArray(
178+
"root",
179+
"test",
180+
_ => false,
181+
_ => false
182+
);
183+
xmlArray.setDefaultNamespaces(DEFAULT_NAMESPACES);
184+
xmlArray.setNamespaceAnchors(NAMESPACE_ANCHORS);
185+
186+
TagHolder holder = new TagHolder();
187+
var data = new Dictionary<string, string>();
188+
data["test.container.pap:one"] = "aaa";
189+
data["test.container.pap:two"] = "bbb";
190+
holder.AdditionalFields = data;
191+
192+
var memStream = new MemoryStream();
193+
xmlArray.ToStream(memStream, holder);
194+
var reader = new StreamReader(memStream);
195+
memStream.Position = 0;
196+
string result = reader.ReadToEnd();
197+
198+
Assert.IsTrue(result.Length > 0);
199+
200+
var expected = getFrom("anchoredNs.xml");
201+
Assert.AreEqual(expected, result);
202+
203+
// Anchor all ns'es to one specific node
204+
IDictionary<string, ISet<string>> NAMESPACE_ANCHORS2 = new Dictionary<string, ISet<string>> { { "container", new HashSet<string> { "" } } };
205+
xmlArray.setNamespaceAnchors(NAMESPACE_ANCHORS2);
206+
207+
memStream = new MemoryStream();
208+
xmlArray.ToStream(memStream, holder);
209+
reader = new StreamReader(memStream);
210+
memStream.Position = 0;
211+
result = reader.ReadToEnd();
212+
213+
Assert.IsTrue(result.Length > 0);
214+
215+
Assert.AreEqual(expected, result);
216+
}
217+
}
218+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<root>
3+
<container xmlns:pap="test:ns:meta/">
4+
<pap:one>aaa</pap:one>
5+
<pap:two>bbb</pap:two>
6+
</container>
7+
</root>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<root>
3+
<one hey="ho">aaa</one>
4+
<two pip="boy">bbb</two>
5+
</root>
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<root>
3+
<one>aaa</one>
4+
<two>bbb</two>
5+
</root>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<root>
3+
<one>aaa</one>
4+
<two>bbb</two>
5+
<theList>
6+
<elt><value>11</value></elt>
7+
<elt><value>22</value></elt>
8+
<elt><value>33</value></elt>
9+
</theList>
10+
</root>
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<root xmlns:pap="test:ns:meta/">
3+
<pap:one>aaa</pap:one>
4+
<pap:two>bbb</pap:two>
5+
</root>

ATL/AudioData/IO/Helpers/IXmlTag.cs

+7-7
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,24 @@ public static bool IsDataEligible(MetaDataHolder meta)
3434
return WavHelper.IsDataEligible(meta, "ixml.");
3535
}
3636

37-
public static int ToStream(BinaryWriter w, bool isLittleEndian, MetaDataHolder meta)
37+
public static int ToStream(Stream w, bool isLittleEndian, MetaDataHolder meta)
3838
{
3939
w.Write(Utils.Latin1Encoding.GetBytes(CHUNK_IXML));
4040

41-
long sizePos = w.BaseStream.Position;
42-
w.Write(0); // Placeholder for chunk size that will be rewritten at the end of the method
41+
long sizePos = w.Position;
42+
w.Write(StreamUtils.EncodeInt32(0)); // Placeholder for chunk size that will be rewritten at the end of the method
4343

4444
XmlArray xmlArray = createXmlArray();
4545
int result = xmlArray.ToStream(w, meta);
4646

47-
long finalPos = w.BaseStream.Position;
47+
long finalPos = w.Position;
4848

4949
// Add the extra padding byte if needed
5050
long paddingSize = (finalPos - sizePos) % 2;
51-
if (paddingSize > 0) w.BaseStream.WriteByte(0);
51+
if (paddingSize > 0) w.WriteByte(0);
5252

53-
w.BaseStream.Seek(sizePos, SeekOrigin.Begin);
54-
if (isLittleEndian) w.Write((int)(finalPos - sizePos - 4));
53+
w.Seek(sizePos, SeekOrigin.Begin);
54+
if (isLittleEndian) w.Write(StreamUtils.EncodeInt32((int)(finalPos - sizePos - 4)));
5555
else w.Write(StreamUtils.EncodeBEInt32((int)(finalPos - sizePos - 4)));
5656

5757
return result;

ATL/AudioData/IO/Helpers/XmpTag.cs

+15-7
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ internal static class XmpTag
5757
{"tscHS", "http://www.techsmith.com/xmp/tscHS/"},
5858
};
5959

60+
// Namespace anchors
61+
private static readonly IDictionary<string, ISet<string>> NAMESPACE_ANCHORS = new Dictionary<string, ISet<string>>
62+
{
63+
{ "x:xmpmeta", new HashSet<string>{ "x" } },
64+
{ "rdf:RDF", new HashSet<string> { "" } }
65+
};
66+
6067

6168
private static XmlArray createXmlArray()
6269
{
@@ -72,6 +79,7 @@ private static XmlArray createXmlArray()
7279
);
7380
result.setStructuralAttributes(ATTRIBUTES);
7481
result.setDefaultNamespaces(DEFAULT_NAMESPACES);
82+
result.setNamespaceAnchors(NAMESPACE_ANCHORS);
7583
return result;
7684
}
7785

@@ -86,25 +94,25 @@ public static bool IsDataEligible(MetaDataHolder meta)
8694
return WavHelper.IsDataEligible(meta, "xmp.");
8795
}
8896

89-
public static int ToStream(BinaryWriter w, MetaDataHolder meta, bool isLittleEndian = false, bool wavEmbed = false)
97+
public static int ToStream(Stream w, MetaDataHolder meta, bool isLittleEndian = false, bool wavEmbed = false)
9098
{
9199
if (wavEmbed) w.Write(Utils.Latin1Encoding.GetBytes(CHUNK_XMP));
92100

93-
long sizePos = w.BaseStream.Position;
101+
long sizePos = w.Position;
94102
// Placeholder for chunk size that will be rewritten at the end of the method
95-
if (wavEmbed) w.Write(0);
103+
if (wavEmbed) w.Write(StreamUtils.EncodeInt32(0));
96104

97105
XmlArray xmlArray = createXmlArray();
98106
int result = xmlArray.ToStream(w, meta);
99107

100108
if (wavEmbed) // Add the extra padding byte if needed
101109
{
102-
long finalPos = w.BaseStream.Position;
110+
long finalPos = w.Position;
103111
long paddingSize = (finalPos - sizePos) % 2;
104-
if (paddingSize > 0) w.BaseStream.WriteByte(0);
112+
if (paddingSize > 0) w.WriteByte(0);
105113

106-
w.BaseStream.Seek(sizePos, SeekOrigin.Begin);
107-
if (isLittleEndian) w.Write((int)(finalPos - sizePos - 4));
114+
w.Seek(sizePos, SeekOrigin.Begin);
115+
if (isLittleEndian) w.Write(StreamUtils.EncodeInt32((int)(finalPos - sizePos - 4)));
108116
else w.Write(StreamUtils.EncodeBEInt32((int)(finalPos - sizePos - 4)));
109117
}
110118

ATL/AudioData/IO/MP4.cs

+1-2
Original file line numberDiff line numberDiff line change
@@ -2675,8 +2675,7 @@ private static int writeUuidFrame(TagData tag, string key, BinaryWriter w)
26752675
if (keyNominal.Equals(XmpTag.UUID_XMP, StringComparison.OrdinalIgnoreCase))
26762676
{
26772677
using var mem = new MemoryStream();
2678-
using var memW = new BinaryWriter(mem);
2679-
XmpTag.ToStream(memW, new TagHolder(tag));
2678+
XmpTag.ToStream(mem, new TagHolder(tag));
26802679
data = mem.ToArray();
26812680
}
26822681
else

ATL/AudioData/IO/WAV.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -594,8 +594,8 @@ private int write(BinaryWriter w, MetaDataHolder tag, string zone)
594594
}
595595
else if (zone.Equals(CHUNK_DISP + ".0") && DispTag.IsDataEligible(tag)) result += DispTag.ToStream(w, isLittleEndian, tag); // Process the 1st position as a whole
596596
else if (zone.Equals(CHUNK_BEXT) && BextTag.IsDataEligible(tag)) result += BextTag.ToStream(w, isLittleEndian, tag);
597-
else if (zone.Equals(CHUNK_IXML) && IXmlTag.IsDataEligible(tag)) result += IXmlTag.ToStream(w, isLittleEndian, tag);
598-
else if (zone.Equals(CHUNK_XMP) && XmpTag.IsDataEligible(tag)) result += XmpTag.ToStream(w, tag, isLittleEndian, true);
597+
else if (zone.Equals(CHUNK_IXML) && IXmlTag.IsDataEligible(tag)) result += IXmlTag.ToStream(w.BaseStream, isLittleEndian, tag);
598+
else if (zone.Equals(CHUNK_XMP) && XmpTag.IsDataEligible(tag)) result += XmpTag.ToStream(w.BaseStream, tag, isLittleEndian, true);
599599
else if (zone.Equals(CHUNK_CART) && CartTag.IsDataEligible(tag)) result += CartTag.ToStream(w, isLittleEndian, tag);
600600

601601
break;

0 commit comments

Comments
 (0)