├── .gitignore ├── EdiTools.Tests ├── EdiComponentTest.cs ├── EdiDocumentTest.cs ├── EdiElementTest.cs ├── EdiMappingTest.cs ├── EdiRepetitionTest.cs ├── EdiSegmentTest.cs ├── EdiTools.Tests.csproj └── EdiValueTest.cs ├── EdiTools.sln ├── EdiTools.vsmdi ├── EdiTools ├── EdiComponent.cs ├── EdiDocument.cs ├── EdiElement.cs ├── EdiMapping.cs ├── EdiOptions.cs ├── EdiRepetition.cs ├── EdiSegment.cs ├── EdiTools.csproj ├── EdiTransactionSet.cs └── EdiValue.cs ├── License.txt ├── Local.testsettings ├── Readme.md └── TraceAndTestImpact.testsettings /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | 11 | [Dd]ebug/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | [Bb]in/ 16 | [Oo]bj/ 17 | 18 | # MSTest test Results 19 | [Tt]est[Rr]esult*/ 20 | [Bb]uild[Ll]og.* 21 | 22 | *_i.c 23 | *_p.c 24 | *.ilk 25 | *.meta 26 | *.obj 27 | *.pch 28 | *.pdb 29 | *.pgc 30 | *.pgd 31 | *.rsp 32 | *.sbr 33 | *.tlb 34 | *.tli 35 | *.tlh 36 | *.tmp 37 | *.tmp_proj 38 | *.log 39 | *.vspscc 40 | *.vssscc 41 | .builds 42 | *.pidb 43 | *.log 44 | *.scc 45 | 46 | # Visual C++ cache files 47 | ipch/ 48 | *.aps 49 | *.ncb 50 | *.opensdf 51 | *.sdf 52 | *.cachefile 53 | 54 | # Visual Studio profiler 55 | *.psess 56 | *.vsp 57 | *.vspx 58 | 59 | # Guidance Automation Toolkit 60 | *.gpState 61 | 62 | # ReSharper is a .NET coding add-in 63 | _ReSharper*/ 64 | *.[Rr]e[Ss]harper 65 | 66 | # TeamCity is a build add-in 67 | _TeamCity* 68 | 69 | # DotCover is a Code Coverage Tool 70 | *.dotCover 71 | 72 | # NCrunch 73 | *.ncrunch* 74 | .*crunch*.local.xml 75 | 76 | # Installshield output folder 77 | [Ee]xpress/ 78 | 79 | # DocProject is a documentation generator add-in 80 | DocProject/buildhelp/ 81 | DocProject/Help/*.HxT 82 | DocProject/Help/*.HxC 83 | DocProject/Help/*.hhc 84 | DocProject/Help/*.hhk 85 | DocProject/Help/*.hhp 86 | DocProject/Help/Html2 87 | DocProject/Help/html 88 | 89 | # Click-Once directory 90 | publish/ 91 | 92 | # Publish Web Output 93 | *.Publish.xml 94 | *.pubxml 95 | 96 | # NuGet Packages Directory 97 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line 98 | #packages/ 99 | 100 | # Windows Azure Build Output 101 | csx 102 | *.build.csdef 103 | 104 | # Windows Store app package directory 105 | AppPackages/ 106 | 107 | # Others 108 | sql/ 109 | *.Cache 110 | ClientBin/ 111 | [Ss]tyle[Cc]op.* 112 | ~$* 113 | *~ 114 | *.dbmdl 115 | *.[Pp]ublish.xml 116 | *.pfx 117 | *.publishsettings 118 | 119 | # RIA/Silverlight projects 120 | Generated_Code/ 121 | 122 | # Backup & report files from converting an old project file to a newer 123 | # Visual Studio version. Backup files are not needed, because we have git ;-) 124 | _UpgradeReport_Files/ 125 | Backup*/ 126 | UpgradeLog*.XML 127 | UpgradeLog*.htm 128 | 129 | # SQL Server files 130 | App_Data/*.mdf 131 | App_Data/*.ldf 132 | 133 | # ========================= 134 | # Windows detritus 135 | # ========================= 136 | 137 | # Windows image file caches 138 | Thumbs.db 139 | ehthumbs.db 140 | 141 | # Folder config file 142 | Desktop.ini 143 | 144 | # Recycle Bin used on file shares 145 | $RECYCLE.BIN/ 146 | 147 | # Mac crap 148 | .DS_Store 149 | 150 | .svn/ 151 | .vs/ 152 | -------------------------------------------------------------------------------- /EdiTools.Tests/EdiComponentTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | 4 | namespace EdiTools.Tests 5 | { 6 | [TestClass] 7 | public class EdiComponentTest 8 | { 9 | [TestMethod] 10 | public void ConvertingAComponentToAString() 11 | { 12 | var component = new EdiComponent("value"); 13 | Assert.AreEqual("value", component.ToString()); 14 | } 15 | 16 | [TestMethod] 17 | [ExpectedException(typeof (FormatException))] 18 | public void StringifyingAComponentContainingADefaultSeparator() 19 | { 20 | var component = new EdiComponent("a>b"); 21 | Assert.AreEqual("a>b", component.ToString()); 22 | } 23 | 24 | [TestMethod] 25 | [ExpectedException(typeof (FormatException))] 26 | public void StringifyingAComponentContainingASpecificSeparator() 27 | { 28 | var component = new EdiComponent("a>b"); 29 | var options = new EdiOptions {ComponentSeparator = '>'}; 30 | Assert.AreEqual("a>b", component.ToString(options)); 31 | } 32 | 33 | [TestMethod] 34 | public void StringifyingAComponentContainerSeparatorsWithAReleaseCharacter() 35 | { 36 | var component = new EdiComponent(":+.? '"); 37 | var options = new EdiOptions 38 | { 39 | ComponentSeparator = ':', 40 | ElementSeparator = '+', 41 | ReleaseCharacter = '?', 42 | SegmentTerminator = '\'' 43 | }; 44 | Assert.AreEqual("?:?+.?? ?'", component.ToString(options)); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /EdiTools.Tests/EdiDocumentTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using System.Xml.Linq; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | 8 | namespace EdiTools.Tests 9 | { 10 | [TestClass] 11 | public class EdiDocumentTest 12 | { 13 | [TestMethod] 14 | public void ReadingASegment() 15 | { 16 | var options = new EdiOptions 17 | { 18 | ElementSeparator = '*', 19 | SegmentTerminator = '~' 20 | }; 21 | EdiDocument document = EdiDocument.Parse("ST*997*0001~", options); 22 | 23 | Assert.AreEqual(1, document.Segments.Count); 24 | Assert.AreEqual("ST", document.Segments[0].Id); 25 | Assert.AreEqual(2, document.Segments[0].Elements.Count); 26 | Assert.AreEqual("997", document.Segments[0].Elements[0].Value); 27 | Assert.AreEqual("0001", document.Segments[0].Elements[1].Value); 28 | } 29 | 30 | [TestMethod] 31 | public void ReadingTwoSegments() 32 | { 33 | var options = new EdiOptions 34 | { 35 | ElementSeparator = '*', 36 | SegmentTerminator = '~' 37 | }; 38 | EdiDocument document = EdiDocument.Parse("ST*997*0001~SE*1*0001~", options); 39 | 40 | Assert.AreEqual(2, document.Segments.Count); 41 | Assert.AreEqual("ST", document.Segments[0].Id); 42 | Assert.AreEqual(2, document.Segments[0].Elements.Count); 43 | Assert.AreEqual("997", document.Segments[0].Elements[0].Value); 44 | Assert.AreEqual("0001", document.Segments[0].Elements[1].Value); 45 | Assert.AreEqual("SE", document.Segments[1].Id); 46 | Assert.AreEqual(2, document.Segments[1].Elements.Count); 47 | Assert.AreEqual("1", document.Segments[1].Elements[0].Value); 48 | Assert.AreEqual("0001", document.Segments[1].Elements[1].Value); 49 | } 50 | 51 | [TestMethod] 52 | public void GuessingSeparators() 53 | { 54 | EdiDocument document = EdiDocument.Parse("ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER *120101*0000*U*00401*000000001*0*P*>~IEA*0*000000001~"); 55 | 56 | Assert.AreEqual(2, document.Segments.Count); 57 | Assert.AreEqual("ISA", document.Segments[0].Id); 58 | Assert.AreEqual(16, document.Segments[0].Elements.Count); 59 | Assert.AreEqual("SENDER ", document.Segments[0].Elements[5].Value); 60 | Assert.AreEqual(">", document.Segments[0].Elements[15].Value); 61 | Assert.AreEqual("IEA", document.Segments[1].Id); 62 | Assert.AreEqual(2, document.Segments[1].Elements.Count); 63 | Assert.AreEqual("0", document.Segments[1].Elements[0].Value); 64 | Assert.AreEqual("000000001", document.Segments[1].Elements[1].Value); 65 | } 66 | 67 | [TestMethod] 68 | public void IgnoringExtraWhiteSpace() 69 | { 70 | string edi = new StringBuilder() 71 | .AppendLine("ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER *120101*0000*U*00401*000000001*0*P*>~") 72 | .AppendLine("IEA*0*000000001~") 73 | .ToString(); 74 | EdiDocument document = EdiDocument.Parse(edi); 75 | 76 | Assert.AreEqual(2, document.Segments.Count); 77 | Assert.AreEqual("ISA", document.Segments[0].Id); 78 | Assert.AreEqual(16, document.Segments[0].Elements.Count); 79 | Assert.AreEqual("SENDER ", document.Segments[0].Elements[5].Value); 80 | Assert.AreEqual(">", document.Segments[0].Elements[15].Value); 81 | Assert.AreEqual("IEA", document.Segments[1].Id); 82 | Assert.AreEqual(2, document.Segments[1].Elements.Count); 83 | Assert.AreEqual("0", document.Segments[1].Elements[0].Value); 84 | Assert.AreEqual("000000001", document.Segments[1].Elements[1].Value); 85 | } 86 | 87 | [TestMethod] 88 | public void ReadingComponents() 89 | { 90 | EdiDocument document = EdiDocument.Parse("ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER *120101*0000*U*00401*000000001*0*P*>~SEG*COMPONENT1>COMPONENT2*COMPONENT3>COMPONENT4>COMPONENT5~"); 91 | 92 | Assert.AreEqual(2, document.Segments[1].Elements.Count); 93 | Assert.AreEqual(2, document.Segments[1].Elements[0].Components.Count); 94 | Assert.AreEqual("COMPONENT1", document.Segments[1].Elements[0].Components[0].Value); 95 | Assert.AreEqual("COMPONENT2", document.Segments[1].Elements[0].Components[1].Value); 96 | Assert.AreEqual(3, document.Segments[1].Elements[1].Components.Count); 97 | Assert.AreEqual("COMPONENT3", document.Segments[1].Elements[1].Components[0].Value); 98 | Assert.AreEqual("COMPONENT4", document.Segments[1].Elements[1].Components[1].Value); 99 | Assert.AreEqual("COMPONENT5", document.Segments[1].Elements[1].Components[2].Value); 100 | } 101 | 102 | [TestMethod] 103 | public void ReadingRepetitions() 104 | { 105 | EdiDocument document = EdiDocument.Parse("ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER *120101*0000*^*00402*000000001*0*P*>~SEG*REPETITION1^COMPONENT1>COMPONENT2~"); 106 | 107 | Assert.AreEqual(1, document.Segments[1].Elements.Count); 108 | Assert.AreEqual(2, document.Segments[1].Elements[0].Repetitions.Count); 109 | Assert.AreEqual("REPETITION1", document.Segments[1].Elements[0].Repetitions[0].Value); 110 | Assert.AreEqual(2, document.Segments[1].Elements[0].Repetitions[1].Components.Count); 111 | Assert.AreEqual("COMPONENT1", document.Segments[1].Elements[0].Repetitions[1].Components[0].Value); 112 | Assert.AreEqual("COMPONENT2", document.Segments[1].Elements[0].Repetitions[1].Components[1].Value); 113 | } 114 | 115 | [TestMethod] 116 | public void IgnoringRepetitionSeparatorWhenLessThanVersion4020() 117 | { 118 | EdiDocument document = EdiDocument.Parse("ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER *120101*0000*^*00401*000000001*0*P*>~SEG*REPETITION1^COMPONENT1>COMPONENT2~"); 119 | 120 | Assert.AreEqual(1, document.Segments[1].Elements.Count); 121 | Assert.AreEqual(2, document.Segments[1].Elements[0].Components.Count); 122 | Assert.AreEqual("REPETITION1^COMPONENT1", document.Segments[1].Elements[0].Components[0].Value); 123 | Assert.AreEqual("COMPONENT2", document.Segments[1].Elements[0].Components[1].Value); 124 | } 125 | 126 | [TestMethod] 127 | public void IgnoringAlphaNumericRepetitionSeparator() 128 | { 129 | EdiDocument document = EdiDocument.Parse("ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER *120101*0000*U*00402*000000001*0*P*>~SEG*VALUE1>VALUE2~"); 130 | 131 | Assert.AreEqual(1, document.Segments[1].Elements.Count); 132 | Assert.AreEqual(2, document.Segments[1].Elements[0].Components.Count); 133 | Assert.AreEqual("VALUE1", document.Segments[1].Elements[0].Components[0].Value); 134 | Assert.AreEqual("VALUE2", document.Segments[1].Elements[0].Components[1].Value); 135 | } 136 | 137 | [TestMethod] 138 | public void Saving() 139 | { 140 | const string edi = "ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER *120101*0000*^*00402*000000001*0*P*>~SEG*REPETITION1^COMPONENT1>COMPONENT2~"; 141 | EdiDocument document = EdiDocument.Parse(edi); 142 | var buffer = new StringWriter(); 143 | document.Save(buffer); 144 | 145 | Assert.AreEqual(edi, buffer.ToString()); 146 | } 147 | 148 | [TestMethod] 149 | public void DetectingRepetitionAndComponentSeparatorsWhenSaving() 150 | { 151 | string edi = new StringBuilder() 152 | .Append("ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER *120101*0000*^*00402*000000001*0*P*>~") 153 | .Append("SEG*REPETITION1^COMPONENT1>COMPONENT2~") 154 | .Append("ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER *120101*0000*|*00402*000000001*0*P*<~") 155 | .Append("SEG*REPETITION1|COMPONENT1~") 193 | .AppendLine("SEG*REPETITION1^COMPONENT1>COMPONENT2~") 194 | .ToString(); 195 | EdiDocument document = EdiDocument.Parse(edi); 196 | document.Options.AddLineBreaks = true; 197 | var buffer = new StringWriter(); 198 | document.Save(buffer); 199 | 200 | Assert.AreEqual(edi, buffer.ToString()); 201 | } 202 | 203 | [TestMethod] 204 | public void SavingToAStreamLeavesTheStreamOpen() 205 | { 206 | var stream = new MemoryStream(); 207 | new EdiDocument().Save(stream); 208 | 209 | Assert.IsTrue(stream.CanWrite); 210 | } 211 | 212 | [TestMethod] 213 | public void GettingTransactionSets() 214 | { 215 | string edi = new StringBuilder() 216 | .AppendLine("ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER *120101*0000*U*00401*000000001*0*P*>~") 217 | .AppendLine("GS********1~") 218 | .AppendLine("ST**0001~") 219 | .AppendLine("AK1~") 220 | .AppendLine("SE~") 221 | .AppendLine("SE~") 222 | .AppendLine("GE~") 223 | .AppendLine("IEA~") 224 | .AppendLine("ST**0002~") 225 | .AppendLine("SE~") 226 | .AppendLine("ISA*00* *00* *ZZ*SENDER *ZZ*RECEIVER *120101*0000*U*00401*000000002*0*P*>~") 227 | .AppendLine("GS********2~") 228 | .AppendLine("AK2~") 229 | .AppendLine("ST**0003~") 230 | .AppendLine("AK3~") 231 | .AppendLine("ST**0004~") 232 | .AppendLine("GE~") 233 | .AppendLine("IEA~") 234 | .ToString(); 235 | EdiDocument document = EdiDocument.Parse(edi); 236 | IList transactionSets = document.TransactionSets; 237 | 238 | Assert.AreEqual(4, transactionSets.Count); 239 | 240 | Assert.AreEqual("000000001", transactionSets[0].InterchangeHeader[13]); 241 | Assert.AreEqual("1", transactionSets[0].FunctionalGroupHeader[8]); 242 | Assert.AreEqual(3, transactionSets[0].Segments.Count); 243 | Assert.AreEqual("0001", transactionSets[0].Segments[0][02]); 244 | Assert.AreEqual("AK1", transactionSets[0].Segments[1].Id); 245 | Assert.AreEqual("SE", transactionSets[0].Segments[2].Id); 246 | 247 | Assert.IsNull(transactionSets[1].InterchangeHeader); 248 | Assert.IsNull(transactionSets[1].FunctionalGroupHeader); 249 | Assert.AreEqual(2, transactionSets[1].Segments.Count); 250 | Assert.AreEqual("0002", transactionSets[1].Segments[0][02]); 251 | Assert.AreEqual("SE", transactionSets[1].Segments[1].Id); 252 | 253 | Assert.AreEqual("000000002", transactionSets[2].InterchangeHeader[13]); 254 | Assert.AreEqual("2", transactionSets[2].FunctionalGroupHeader[8]); 255 | Assert.AreEqual(2, transactionSets[2].Segments.Count); 256 | Assert.AreEqual("0003", transactionSets[2].Segments[0][02]); 257 | Assert.AreEqual("AK3", transactionSets[2].Segments[1].Id); 258 | 259 | Assert.AreEqual("000000002", transactionSets[3].InterchangeHeader[13]); 260 | Assert.AreEqual("2", transactionSets[3].FunctionalGroupHeader[8]); 261 | Assert.AreEqual(3, transactionSets[3].Segments.Count); 262 | Assert.AreEqual("0004", transactionSets[3].Segments[0][02]); 263 | Assert.AreEqual("GE", transactionSets[3].Segments[1].Id); 264 | Assert.AreEqual("IEA", transactionSets[3].Segments[2].Id); 265 | } 266 | 267 | [TestMethod] 268 | public void LoadingAnXml() 269 | { 270 | XDocument xml = XDocument.Parse(new StringBuilder() 271 | .AppendLine("") 272 | .AppendLine(" ") 273 | .AppendLine(" " + 99.7 + "") 274 | .AppendLine(" ") 275 | .AppendLine(" 850") 276 | .AppendLine(" 810") 277 | .AppendLine(" ") 278 | .AppendLine(" ") 279 | .AppendLine(" " + 1.234 + "") 280 | .AppendLine(" 5678") 281 | .AppendLine(" ") 282 | .AppendLine(" 123") 283 | .AppendLine(" ") 284 | .AppendLine("") 285 | .ToString()); 286 | EdiDocument document = EdiDocument.LoadXml(xml); 287 | document.Options.SegmentTerminator = '~'; 288 | document.Options.ElementSeparator = '*'; 289 | document.Options.ComponentSeparator = '>'; 290 | document.Options.RepetitionSeparator = '^'; 291 | string edi = document.ToString(); 292 | 293 | Assert.AreEqual("ST*997^850>810*1234>>5678**123~", edi); 294 | } 295 | 296 | [TestMethod] 297 | public void ReadingEdifactSegments() 298 | { 299 | EdiDocument document = EdiDocument.Parse("UNA:+.? 'UNB+IATB:1+6XPPC+LHPPC+940101:0950+1'UNH+1+PAORES:93:1:IA'"); 300 | 301 | Assert.AreEqual(3, document.Segments.Count); 302 | Assert.AreEqual("UNA", document.Segments[0].Id); 303 | Assert.AreEqual(1, document.Segments[0].Elements.Count); 304 | Assert.AreEqual(":+.? ", document.Segments[0].Elements[0].Value); 305 | Assert.AreEqual("UNB", document.Segments[1].Id); 306 | Assert.AreEqual(5, document.Segments[1].Elements.Count); 307 | Assert.AreEqual(2, document.Segments[1].Elements[3].Components.Count); 308 | Assert.AreEqual("940101", document.Segments[1].Elements[3].Components[0].Value); 309 | Assert.AreEqual("0950", document.Segments[1].Elements[3].Components[1].Value); 310 | Assert.AreEqual("UNH", document.Segments[2].Id); 311 | Assert.AreEqual(2, document.Segments[2].Elements.Count); 312 | Assert.AreEqual("1", document.Segments[2].Elements[0].Value); 313 | } 314 | 315 | [TestMethod] 316 | public void ReadingSegmentsWithReleaseCharacters() 317 | { 318 | EdiDocument document = EdiDocument.Parse("UNA:+.? 'UNB+??IATB?:1?+6XPPC+LHPPC+940101:0950+1?'UNH+1+PAORES:93:1:IA'"); 319 | 320 | Assert.AreEqual(2, document.Segments.Count); 321 | Assert.AreEqual("UNB", document.Segments[1].Id); 322 | Assert.AreEqual(6, document.Segments[1].Elements.Count); 323 | Assert.AreEqual("?IATB:1+6XPPC", document.Segments[1].Elements[0].Value); 324 | } 325 | 326 | [TestMethod] 327 | public void GettingEdifactTransactionSets() 328 | { 329 | string edi = new StringBuilder() 330 | .AppendLine("UNA:+.? '") 331 | .AppendLine("UNB+1'") 332 | .AppendLine("UNH+2'") 333 | .AppendLine("MSG'") 334 | .AppendLine("IFT'") 335 | .AppendLine("UNT'") 336 | .AppendLine("UNZ'") 337 | .AppendLine("UNB+3'") 338 | .AppendLine("UNG+4'") 339 | .AppendLine("UNH+5'") 340 | .AppendLine("UNT'") 341 | .AppendLine("UNE'") 342 | .AppendLine("UNZ'") 343 | .ToString(); 344 | EdiDocument document = EdiDocument.Parse(edi); 345 | IList transactionSets = document.TransactionSets; 346 | 347 | Assert.AreEqual(2, transactionSets.Count); 348 | 349 | Assert.AreEqual("1", transactionSets[0].InterchangeHeader[01]); 350 | Assert.IsNull(transactionSets[0].FunctionalGroupHeader); 351 | Assert.AreEqual(4, transactionSets[0].Segments.Count); 352 | Assert.AreEqual("2", transactionSets[0].Segments[0][01]); 353 | Assert.AreEqual("MSG", transactionSets[0].Segments[1].Id); 354 | Assert.AreEqual("IFT", transactionSets[0].Segments[2].Id); 355 | Assert.AreEqual("UNT", transactionSets[0].Segments[3].Id); 356 | 357 | Assert.AreEqual("3", transactionSets[1].InterchangeHeader[01]); 358 | Assert.AreEqual("4", transactionSets[1].FunctionalGroupHeader[01]); 359 | Assert.AreEqual(2, transactionSets[1].Segments.Count); 360 | Assert.AreEqual("5", transactionSets[1].Segments[0][01]); 361 | Assert.AreEqual("UNT", transactionSets[1].Segments[1].Id); 362 | } 363 | } 364 | } -------------------------------------------------------------------------------- /EdiTools.Tests/EdiElementTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | namespace EdiTools.Tests 4 | { 5 | [TestClass] 6 | public class EdiElementTest 7 | { 8 | [TestMethod] 9 | public void CreatingAnElement() 10 | { 11 | var element = new EdiElement(); 12 | element[02] = "1234"; 13 | element[04] = "5678"; 14 | 15 | Assert.IsNull(element[01]); 16 | Assert.AreEqual("1234", element[02]); 17 | Assert.IsNull(element[03]); 18 | Assert.AreEqual("5678", element[04]); 19 | Assert.IsNull(element[05]); 20 | Assert.AreEqual(1234, element.Component(02).RealValue); 21 | Assert.IsNull(element.Component(03)); 22 | } 23 | 24 | [TestMethod] 25 | public void UpdatingComponents() 26 | { 27 | var element = new EdiElement(); 28 | element[01] = "ORIGINAL01"; 29 | element[02] = "ORIGINAL02"; 30 | element[03] = "ORIGINAL03"; 31 | element[01] = "UPDATE01"; 32 | element.Component(02).Value = "UPDATE02"; 33 | element[03] = null; 34 | 35 | Assert.AreEqual("UPDATE01", element[01]); 36 | Assert.AreEqual("UPDATE02", element[02]); 37 | Assert.IsNull(element[03]); 38 | } 39 | 40 | [TestMethod] 41 | public void ConvertingAnElementToAString() 42 | { 43 | var element = new EdiElement(); 44 | element.Repetitions.Add(new EdiRepetition("1234")); 45 | element.Repetitions.Add(new EdiRepetition("5678")); 46 | 47 | Assert.AreEqual("1234^5678", element.ToString()); 48 | } 49 | 50 | [TestMethod] 51 | public void ConvertingAnElementToAStringWithSpecificSeparators() 52 | { 53 | var element = new EdiElement(); 54 | element.Repetitions.Add(new EdiRepetition("1234")); 55 | element.Repetitions.Add(new EdiRepetition("5678")); 56 | var options = new EdiOptions {RepetitionSeparator = '^'}; 57 | 58 | Assert.AreEqual("1234^5678", element.ToString(options)); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /EdiTools.Tests/EdiMappingTest.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | using System.Xml.Linq; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace EdiTools.Tests 6 | { 7 | [TestClass] 8 | public class EdiMappingTest 9 | { 10 | [TestMethod] 11 | public void MappingBlankEdiWithBlankMapping() 12 | { 13 | var segments = new EdiSegment[0]; 14 | EdiMapping mapping = EdiMapping.Parse(""); 15 | XDocument actual = mapping.Map(segments); 16 | 17 | XDocument expected = XDocument.Parse(""); 18 | Assert.IsTrue(XNode.DeepEquals(expected, actual)); 19 | } 20 | 21 | [TestMethod] 22 | public void MappingOneSegmentWithBlankMapping() 23 | { 24 | EdiDocument edi = EdiDocument.Parse("ST*997^850>810*1234>>5678**123~", new EdiOptions {RepetitionSeparator = '^', ComponentSeparator = '>'}); 25 | EdiMapping mapping = EdiMapping.Parse(""); 26 | XDocument actual = mapping.Map(edi.Segments); 27 | 28 | XDocument expected = XDocument.Parse(new StringBuilder() 29 | .AppendLine("") 30 | .AppendLine(" ") 31 | .AppendLine(" 997") 32 | .AppendLine(" ") 33 | .AppendLine(" 850") 34 | .AppendLine(" 810") 35 | .AppendLine(" ") 36 | .AppendLine(" ") 37 | .AppendLine(" 1234") 38 | .AppendLine(" 5678") 39 | .AppendLine(" ") 40 | .AppendLine(" 123") 41 | .AppendLine(" ") 42 | .AppendLine("") 43 | .ToString()); 44 | Assert.IsTrue(XNode.DeepEquals(expected, actual)); 45 | } 46 | 47 | [TestMethod] 48 | public void MappingAnExpectedSegment() 49 | { 50 | EdiDocument edi = EdiDocument.Parse("ST*997^850>810*1234>>5678**123~", new EdiOptions {RepetitionSeparator = '^', ComponentSeparator = '>'}); 51 | EdiMapping mapping = EdiMapping.Parse(new StringBuilder() 52 | .AppendLine("") 53 | .AppendLine(" ") 54 | .AppendLine(" ") 55 | .AppendLine(" ") 56 | .AppendLine(" ") 57 | .AppendLine(" ") 58 | .AppendLine(" ") 59 | .AppendLine(" ") 60 | .AppendLine(" ") 61 | .AppendLine(" ") 62 | .AppendLine(" ") 63 | .AppendLine(" ") 64 | .AppendLine("") 65 | .ToString()); 66 | XDocument actual = mapping.Map(edi.Segments); 67 | 68 | XDocument expected = XDocument.Parse(new StringBuilder() 69 | .AppendLine("") 70 | .AppendLine(" ") 71 | .AppendLine(" 99.7") 72 | .AppendLine(" ") 73 | .AppendLine(" 850") 74 | .AppendLine(" 810") 75 | .AppendLine(" ") 76 | .AppendLine(" ") 77 | .AppendLine(" 1.234") 78 | .AppendLine(" 5678") 79 | .AppendLine(" ") 80 | .AppendLine(" 123") 81 | .AppendLine(" ") 82 | .AppendLine("") 83 | .ToString()); 84 | Assert.IsTrue(XNode.DeepEquals(expected, actual)); 85 | } 86 | 87 | [TestMethod] 88 | public void MappingAHierarchy() 89 | { 90 | EdiDocument edi = EdiDocument.Parse(new StringBuilder() 91 | .AppendLine("ST~") 92 | .AppendLine("HL***S~") 93 | .AppendLine("TD1~") 94 | .AppendLine("N1~") 95 | .AppendLine("N3~") 96 | .AppendLine("N4~") 97 | .AppendLine("N1~") 98 | .AppendLine("N1~") 99 | .AppendLine("HL***O~") 100 | .AppendLine("PRF~") 101 | .AppendLine("TD1~") 102 | .AppendLine("N1~") 103 | .AppendLine("HL***I~") 104 | .AppendLine("HL***I~") 105 | .AppendLine("HL***O~") 106 | .AppendLine("PRF~") 107 | .AppendLine("TD1~") 108 | .AppendLine("N1~") 109 | .AppendLine("HL***I~") 110 | .AppendLine("CTT~") 111 | .AppendLine("SE~") 112 | .ToString(), 113 | new EdiOptions 114 | { 115 | ElementSeparator = '*', 116 | RepetitionSeparator = '^', 117 | ComponentSeparator = '>' 118 | }); 119 | EdiMapping mapping = EdiMapping.Parse(new StringBuilder() 120 | .AppendLine("") 121 | .AppendLine(" ") 122 | .AppendLine(" ") 123 | .AppendLine(" ") 124 | .AppendLine(" ") 125 | .AppendLine(" ") 126 | .AppendLine(" ") 127 | .AppendLine(" ") 128 | .AppendLine(" ") 129 | .AppendLine(" ") 130 | .AppendLine(" ") 131 | .AppendLine(" ") 132 | .AppendLine(" ") 133 | .AppendLine(" ") 134 | .AppendLine(" ") 135 | .AppendLine(" ") 136 | .AppendLine(" ") 137 | .AppendLine(" ") 138 | .AppendLine(" ") 139 | .AppendLine(" ") 140 | .AppendLine(" ") 141 | .AppendLine(" ") 142 | .AppendLine(" ") 143 | .AppendLine(" ") 144 | .AppendLine(" ") 145 | .AppendLine(" ") 146 | .AppendLine(" ") 147 | .AppendLine("") 148 | .ToString()); 149 | XDocument actual = mapping.Map(edi.Segments); 150 | 151 | XDocument expected = XDocument.Parse(new StringBuilder() 152 | .AppendLine("") 153 | .AppendLine(" ") 154 | .AppendLine(" ") 155 | .AppendLine(" ") 156 | .AppendLine(" S") 157 | .AppendLine(" ") 158 | .AppendLine(" ") 159 | .AppendLine(" ") 160 | .AppendLine(" ") 161 | .AppendLine(" ") 162 | .AppendLine(" ") 163 | .AppendLine(" ") 164 | .AppendLine(" ") 165 | .AppendLine(" ") 166 | .AppendLine(" ") 167 | .AppendLine(" ") 168 | .AppendLine(" ") 169 | .AppendLine(" ") 170 | .AppendLine(" ") 171 | .AppendLine(" ") 172 | .AppendLine(" ") 173 | .AppendLine(" O") 174 | .AppendLine(" ") 175 | .AppendLine(" ") 176 | .AppendLine(" ") 177 | .AppendLine(" ") 178 | .AppendLine(" ") 179 | .AppendLine(" ") 180 | .AppendLine(" ") 181 | .AppendLine(" I") 182 | .AppendLine(" ") 183 | .AppendLine(" ") 184 | .AppendLine(" ") 185 | .AppendLine(" ") 186 | .AppendLine(" I") 187 | .AppendLine(" ") 188 | .AppendLine(" ") 189 | .AppendLine(" ") 190 | .AppendLine(" ") 191 | .AppendLine(" O") 192 | .AppendLine(" ") 193 | .AppendLine(" ") 194 | .AppendLine(" ") 195 | .AppendLine(" ") 196 | .AppendLine(" ") 197 | .AppendLine(" ") 198 | .AppendLine(" ") 199 | .AppendLine(" I") 200 | .AppendLine(" ") 201 | .AppendLine(" ") 202 | .AppendLine(" ") 203 | .AppendLine(" ") 204 | .AppendLine("") 205 | .ToString()); 206 | Assert.IsTrue(XNode.DeepEquals(expected, actual)); 207 | } 208 | } 209 | } -------------------------------------------------------------------------------- /EdiTools.Tests/EdiRepetitionTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | namespace EdiTools.Tests 4 | { 5 | [TestClass] 6 | public class EdiRepetitionTest 7 | { 8 | [TestMethod] 9 | public void CreatingARepetition() 10 | { 11 | var repetition = new EdiRepetition(); 12 | repetition[02] = "1234"; 13 | repetition[04] = "5678"; 14 | 15 | Assert.IsNull(repetition[01]); 16 | Assert.AreEqual("1234", repetition[02]); 17 | Assert.IsNull(repetition[03]); 18 | Assert.AreEqual("5678", repetition[04]); 19 | Assert.IsNull(repetition[05]); 20 | Assert.AreEqual(1234, repetition.Component(02).RealValue); 21 | Assert.IsNull(repetition.Component(03)); 22 | } 23 | 24 | [TestMethod] 25 | public void UpdatingComponents() 26 | { 27 | var repetition = new EdiRepetition(); 28 | repetition[01] = "ORIGINAL01"; 29 | repetition[02] = "ORIGINAL02"; 30 | repetition[03] = "ORIGINAL03"; 31 | repetition[01] = "UPDATE01"; 32 | repetition.Component(02).Value = "UPDATE02"; 33 | repetition[03] = null; 34 | 35 | Assert.AreEqual("UPDATE01", repetition[01]); 36 | Assert.AreEqual("UPDATE02", repetition[02]); 37 | Assert.IsNull(repetition[03]); 38 | } 39 | 40 | [TestMethod] 41 | public void ConvertingARepetitionToAString() 42 | { 43 | var repetition = new EdiRepetition(); 44 | repetition[02] = "1234"; 45 | repetition[04] = "5678"; 46 | 47 | Assert.AreEqual(">1234>>5678", repetition.ToString()); 48 | } 49 | 50 | [TestMethod] 51 | public void ConvertingARepetitionToAStringWithSpecificSeparators() 52 | { 53 | var repetition = new EdiRepetition(); 54 | repetition[02] = "1234"; 55 | repetition[04] = "5678"; 56 | var options = new EdiOptions {ComponentSeparator = '<'}; 57 | 58 | Assert.AreEqual("<1234<<5678", repetition.ToString(options)); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /EdiTools.Tests/EdiSegmentTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | 3 | namespace EdiTools.Tests 4 | { 5 | [TestClass] 6 | public class EdiSegmentTest 7 | { 8 | [TestMethod] 9 | public void CreatingASegment() 10 | { 11 | var seg = new EdiSegment("SEG"); 12 | seg[02] = "1234"; 13 | seg.Element(04, new EdiElement("5678")); 14 | 15 | Assert.IsNull(seg[01]); 16 | Assert.AreEqual("1234", seg[02]); 17 | Assert.IsNull(seg[03]); 18 | Assert.AreEqual("5678", seg[04]); 19 | Assert.IsNull(seg[05]); 20 | Assert.AreEqual(1234, seg.Element(02).RealValue); 21 | Assert.IsNull(seg.Element(03)); 22 | } 23 | 24 | [TestMethod] 25 | public void UpdatingElements() 26 | { 27 | var seg = new EdiSegment("SEG"); 28 | seg[01] = "ORIGINAL01"; 29 | seg[02] = "ORIGINAL02"; 30 | seg[03] = "ORIGINAL03"; 31 | seg[04] = "ORIGINAL04"; 32 | seg[05] = "ORIGINAL05"; 33 | seg[01] = "UPDATE01"; 34 | seg.Element(02, new EdiElement("UPDATE02")); 35 | seg.Element(03).Value = "UPDATE03"; 36 | seg[04] = null; 37 | seg.Element(05, null); 38 | 39 | Assert.AreEqual("UPDATE01", seg[01]); 40 | Assert.AreEqual("UPDATE02", seg[02]); 41 | Assert.AreEqual("UPDATE03", seg[03]); 42 | Assert.IsNull(seg[04]); 43 | Assert.IsNull(seg[05]); 44 | } 45 | 46 | [TestMethod] 47 | public void ConvertingASegmentToAString() 48 | { 49 | var segment = new EdiSegment("SEG"); 50 | segment[02] = "1234"; 51 | segment[04] = "5678"; 52 | 53 | Assert.AreEqual("SEG**1234**5678\r", segment.ToString()); 54 | } 55 | 56 | [TestMethod] 57 | public void ConvertingASegmentToAStringWithSpecificSeparators() 58 | { 59 | var segment = new EdiSegment("SEG"); 60 | segment[02] = "1234"; 61 | segment[04] = "5678"; 62 | var options = new EdiOptions {SegmentTerminator = '~'}; 63 | 64 | Assert.AreEqual("SEG**1234**5678~", segment.ToString(options)); 65 | } 66 | 67 | [TestMethod] 68 | public void ConvertingAUnaSegmentToAString() 69 | { 70 | var segment = new EdiSegment("UNA"); 71 | var options = new EdiOptions 72 | { 73 | ComponentSeparator = ':', 74 | ElementSeparator = '+', 75 | ReleaseCharacter = '?', 76 | SegmentTerminator = '\'' 77 | }; 78 | 79 | Assert.AreEqual("UNA:+.? '", segment.ToString(options)); 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /EdiTools.Tests/EdiTools.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | bin 6 | 1.0.0 7 | EdiTools.Tests 8 | David Peng 9 | Unit tests for EDI Tools. 10 | https://github.com/davidpeng/editools 11 | https://github.com/davidpeng/editools 12 | true 13 | Library 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /EdiTools.Tests/EdiValueTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Threading; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace EdiTools.Tests 7 | { 8 | [TestClass] 9 | public class EdiValueTest 10 | { 11 | [ClassInitialize] 12 | public static void SetUp(TestContext context) 13 | { 14 | SetCulture("en"); 15 | } 16 | 17 | private static void SetCulture(String culture) 18 | { 19 | Thread.CurrentThread.CurrentCulture = new CultureInfo(culture); 20 | } 21 | 22 | [TestMethod] 23 | public void GettingTheDateValueOfAnElement() 24 | { 25 | Assert.AreEqual(new DateTime(2013, 2, 23), new EdiElement("130223").DateValue); 26 | Assert.AreEqual(new DateTime(2013, 2, 23), new EdiElement("20130223").DateValue); 27 | } 28 | 29 | [TestMethod] 30 | public void GettingTheTimeValueOfAnElement() 31 | { 32 | Assert.AreEqual(DateTime.Parse("3:24"), new EdiElement("0324").TimeValue); 33 | Assert.AreEqual(DateTime.Parse("3:24:22"), new EdiElement("032422").TimeValue); 34 | Assert.AreEqual(DateTime.Parse("3:24:22.150"), new EdiElement("03242215").TimeValue); 35 | } 36 | 37 | [TestMethod] 38 | public void GettingTheNumericValueOfAnElement() 39 | { 40 | Assert.AreEqual(123, new EdiElement("123").NumericValue(0)); 41 | Assert.AreEqual(-123, new EdiElement("-123").NumericValue(0)); 42 | Assert.AreEqual(12.3m, new EdiElement("123").NumericValue(1)); 43 | Assert.AreEqual(-12.3m, new EdiElement("-123").NumericValue(1)); 44 | Assert.AreEqual(1.23m, new EdiElement("123").NumericValue(2)); 45 | Assert.AreEqual(0.123m, new EdiElement("123").NumericValue(3)); 46 | Assert.AreEqual(-0.123m, new EdiElement("-123").NumericValue(3)); 47 | } 48 | 49 | [TestMethod] 50 | public void GettingTheNumericValueOfAnElementInNonEnCulture() 51 | { 52 | SetCulture("fr"); 53 | Assert.AreEqual(123, new EdiElement("123").NumericValue(0)); 54 | Assert.AreEqual(-123, new EdiElement("-123").NumericValue(0)); 55 | Assert.AreEqual(12.3m, new EdiElement("123").NumericValue(1)); 56 | Assert.AreEqual(-12.3m, new EdiElement("-123").NumericValue(1)); 57 | Assert.AreEqual(1.23m, new EdiElement("123").NumericValue(2)); 58 | Assert.AreEqual(0.123m, new EdiElement("123").NumericValue(3)); 59 | Assert.AreEqual(-0.123m, new EdiElement("-123").NumericValue(3)); 60 | } 61 | 62 | [TestMethod] 63 | public void GettingTheRealValueOfAnElement() 64 | { 65 | Assert.AreEqual(123, new EdiElement("123").RealValue); 66 | Assert.AreEqual(-123, new EdiElement("-123").RealValue); 67 | Assert.AreEqual(12.3m, new EdiElement("12.3").RealValue); 68 | Assert.AreEqual(-12.3m, new EdiElement("-12.3").RealValue); 69 | Assert.AreEqual(1.23m, new EdiElement("1.23").RealValue); 70 | Assert.AreEqual(0.123m, new EdiElement(".123").RealValue); 71 | Assert.AreEqual(-0.123m, new EdiElement("-.123").RealValue); 72 | Assert.AreEqual(1.23m, new EdiElement("1,23").RealValue); 73 | } 74 | 75 | [TestMethod] 76 | public void GettingTheRealValueOfAnElementInNonEnCulture() 77 | { 78 | SetCulture("fr"); 79 | Assert.AreEqual(123, new EdiElement("123").RealValue); 80 | Assert.AreEqual(-123, new EdiElement("-123").RealValue); 81 | Assert.AreEqual(12.3m, new EdiElement("12.3").RealValue); 82 | Assert.AreEqual(-12.3m, new EdiElement("-12.3").RealValue); 83 | Assert.AreEqual(1.23m, new EdiElement("1.23").RealValue); 84 | Assert.AreEqual(0.123m, new EdiElement(".123").RealValue); 85 | Assert.AreEqual(-0.123m, new EdiElement("-.123").RealValue); 86 | Assert.AreEqual(1.23m, new EdiElement("1,23").RealValue); 87 | } 88 | 89 | [TestMethod] 90 | public void GettingTheIsoDateOfAnElement() 91 | { 92 | Assert.AreEqual("2013-02-23", new EdiElement("130223").IsoDate); 93 | Assert.AreEqual("2013-02-23", new EdiElement("20130223").IsoDate); 94 | } 95 | 96 | [TestMethod] 97 | public void GettingTheIsoTimeOfAnElement() 98 | { 99 | Assert.AreEqual("03:24", new EdiElement("0324").IsoTime); 100 | Assert.AreEqual("03:24:22", new EdiElement("032422").IsoTime); 101 | Assert.AreEqual("03:24:22.15", new EdiElement("03242215").IsoTime); 102 | } 103 | 104 | [TestMethod] 105 | public void FormattingADate() 106 | { 107 | var date = new DateTime(2013, 2, 23); 108 | Assert.AreEqual("130223", EdiValue.Date(6, date)); 109 | Assert.AreEqual("20130223", EdiValue.Date(8, date)); 110 | } 111 | 112 | [TestMethod] 113 | public void FormattingATime() 114 | { 115 | DateTime time = DateTime.Parse("3:24:22.150"); 116 | Assert.AreEqual("0324", EdiValue.Time(4, time)); 117 | Assert.AreEqual("032422", EdiValue.Time(6, time)); 118 | Assert.AreEqual("03242215", EdiValue.Time(8, time)); 119 | } 120 | 121 | [TestMethod] 122 | public void FormattingANumeric() 123 | { 124 | Assert.AreEqual("123", EdiValue.Numeric(0, 123)); 125 | Assert.AreEqual("-123", EdiValue.Numeric(0, -123)); 126 | Assert.AreEqual("1230", EdiValue.Numeric(1, 123)); 127 | Assert.AreEqual("-1230", EdiValue.Numeric(1, -123)); 128 | Assert.AreEqual("123", EdiValue.Numeric(1, 12.3m)); 129 | Assert.AreEqual("-123", EdiValue.Numeric(1, -12.3m)); 130 | Assert.AreEqual("1230", EdiValue.Numeric(2, 12.3m)); 131 | Assert.AreEqual("-1230", EdiValue.Numeric(2, -12.3m)); 132 | Assert.AreEqual("12", EdiValue.Numeric(0, 12.3m)); 133 | Assert.AreEqual("-12", EdiValue.Numeric(0, -12.3m)); 134 | Assert.AreEqual("0", EdiValue.Numeric(2, 0)); 135 | Assert.AreEqual("1", EdiValue.Numeric(2, 0.01m)); 136 | Assert.AreEqual("-1", EdiValue.Numeric(2, -0.01m)); 137 | } 138 | 139 | [TestMethod] 140 | public void FormattingANumericInNonEnCulture() 141 | { 142 | SetCulture("fr"); 143 | Assert.AreEqual("123", EdiValue.Numeric(0, 123)); 144 | Assert.AreEqual("-123", EdiValue.Numeric(0, -123)); 145 | Assert.AreEqual("1230", EdiValue.Numeric(1, 123)); 146 | Assert.AreEqual("-1230", EdiValue.Numeric(1, -123)); 147 | Assert.AreEqual("123", EdiValue.Numeric(1, 12.3m)); 148 | Assert.AreEqual("-123", EdiValue.Numeric(1, -12.3m)); 149 | Assert.AreEqual("1230", EdiValue.Numeric(2, 12.3m)); 150 | Assert.AreEqual("-1230", EdiValue.Numeric(2, -12.3m)); 151 | Assert.AreEqual("12", EdiValue.Numeric(0, 12.3m)); 152 | Assert.AreEqual("-12", EdiValue.Numeric(0, -12.3m)); 153 | Assert.AreEqual("0", EdiValue.Numeric(2, 0)); 154 | Assert.AreEqual("1", EdiValue.Numeric(2, 0.01m)); 155 | Assert.AreEqual("-1", EdiValue.Numeric(2, -0.01m)); 156 | } 157 | 158 | [TestMethod] 159 | public void FormattingAReal() 160 | { 161 | Assert.AreEqual("123", EdiValue.Real(123)); 162 | Assert.AreEqual("-123", EdiValue.Real(-123)); 163 | Assert.AreEqual("12.3", EdiValue.Real(12.3m)); 164 | Assert.AreEqual("-12.3", EdiValue.Real(-12.3m)); 165 | Assert.AreEqual("0.123", EdiValue.Real(0.123m)); 166 | Assert.AreEqual("-0.123", EdiValue.Real(-0.123m)); 167 | } 168 | } 169 | } -------------------------------------------------------------------------------- /EdiTools.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EdiTools", "EdiTools\EdiTools.csproj", "{7119112D-6717-4A35-8C9E-6D558725C741}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EdiTools.Tests", "EdiTools.Tests\EdiTools.Tests.csproj", "{58A94E2A-CB65-418A-A6B2-76C4B1F3770A}" 7 | EndProject 8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{5A42EA8D-76DA-483C-A975-C7C2CA72EC43}" 9 | ProjectSection(SolutionItems) = preProject 10 | EdiTools.vsmdi = EdiTools.vsmdi 11 | License.txt = License.txt 12 | Local.testsettings = Local.testsettings 13 | Readme.md = Readme.md 14 | TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings 15 | EndProjectSection 16 | EndProject 17 | Global 18 | GlobalSection(SubversionScc) = preSolution 19 | Svn-Managed = True 20 | Manager = AnkhSVN - Subversion Support for Visual Studio 21 | EndGlobalSection 22 | GlobalSection(TestCaseManagementSettings) = postSolution 23 | CategoryFile = EdiTools.vsmdi 24 | EndGlobalSection 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {7119112D-6717-4A35-8C9E-6D558725C741}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {7119112D-6717-4A35-8C9E-6D558725C741}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {7119112D-6717-4A35-8C9E-6D558725C741}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {7119112D-6717-4A35-8C9E-6D558725C741}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {58A94E2A-CB65-418A-A6B2-76C4B1F3770A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {58A94E2A-CB65-418A-A6B2-76C4B1F3770A}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {58A94E2A-CB65-418A-A6B2-76C4B1F3770A}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {58A94E2A-CB65-418A-A6B2-76C4B1F3770A}.Release|Any CPU.Build.0 = Release|Any CPU 38 | EndGlobalSection 39 | GlobalSection(SolutionProperties) = preSolution 40 | HideSolutionNode = FALSE 41 | EndGlobalSection 42 | EndGlobal 43 | -------------------------------------------------------------------------------- /EdiTools.vsmdi: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /EdiTools/EdiComponent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace EdiTools 4 | { 5 | /// 6 | /// Represents an EDI component element. 7 | /// 8 | public class EdiComponent : EdiValue 9 | { 10 | private string _value; 11 | 12 | /// 13 | /// Initializes a new instance of the EdiComponent class with the specified value. 14 | /// 15 | /// The initial value of the component element. 16 | public EdiComponent(string value) 17 | { 18 | _value = value; 19 | } 20 | 21 | /// 22 | /// Gets or sets the value of the component element. 23 | /// 24 | public override string Value 25 | { 26 | get { return _value; } 27 | set { _value = value; } 28 | } 29 | 30 | /// 31 | /// Returns the EDI for this component element. 32 | /// 33 | /// A string containing the EDI. 34 | public override string ToString() 35 | { 36 | return ToString(null); 37 | } 38 | 39 | /// 40 | /// Returns the EDI for this component element, optionally specifying separator characters. 41 | /// 42 | /// An EdiOptions that specifies separator characters. 43 | /// A string containing the EDI. 44 | public string ToString(EdiOptions options) 45 | { 46 | char segmentTerminator = options != null && options.SegmentTerminator.HasValue ? options.SegmentTerminator.Value : EdiOptions.DefaultSegmentTerminator; 47 | char elementSeparator = options != null && options.ElementSeparator.HasValue ? options.ElementSeparator.Value : EdiOptions.DefaultElementSeparator; 48 | char componentSeparator = options != null && options.ComponentSeparator.HasValue ? options.ComponentSeparator.Value : EdiOptions.DefaultComponentSeparator; 49 | if (options != null && options.ReleaseCharacter.HasValue) 50 | { 51 | return _value.Replace(options.ReleaseCharacter.ToString(), options.ReleaseCharacter.ToString() + options.ReleaseCharacter.ToString()) 52 | .Replace(segmentTerminator.ToString(), options.ReleaseCharacter.ToString() + segmentTerminator) 53 | .Replace(elementSeparator.ToString(), options.ReleaseCharacter.ToString() + elementSeparator) 54 | .Replace(componentSeparator.ToString(), options.ReleaseCharacter.ToString() + componentSeparator); 55 | } 56 | if (_value.IndexOf(segmentTerminator) != -1) 57 | throw new FormatException(string.Format("'{0}' contains the segment terminator.", _value)); 58 | if (_value.IndexOf(elementSeparator) != -1) 59 | throw new FormatException(string.Format("'{0}' contains the element separator.", _value)); 60 | if (options != null && options.RepetitionSeparator.HasValue && _value.IndexOf(options.RepetitionSeparator.Value) != -1) 61 | throw new FormatException(string.Format("'{0}' contains the repetition separator.", _value)); 62 | if (_value.IndexOf(componentSeparator) != -1) 63 | throw new FormatException(string.Format("'{0}' contains the component separator.", _value)); 64 | return _value; 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /EdiTools/EdiDocument.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text.RegularExpressions; 5 | using System.Xml.Linq; 6 | 7 | namespace EdiTools 8 | { 9 | /// 10 | /// Represents an EDI document. 11 | /// 12 | public class EdiDocument 13 | { 14 | /// 15 | /// Initializes a new instance of class EdiDocument, optionally specifying separator characters. 16 | /// 17 | /// An EdiOptions containing separator characters to use when saving this document. 18 | public EdiDocument(EdiOptions options = null) 19 | { 20 | Options = options == null ? new EdiOptions() : new EdiOptions(options); 21 | Segments = new List(); 22 | } 23 | 24 | private EdiDocument(string edi, EdiOptions options) 25 | { 26 | if (options == null) 27 | { 28 | options = new EdiOptions(); 29 | Options = options; 30 | } 31 | else 32 | { 33 | Options = new EdiOptions(options); 34 | options = new EdiOptions(options); 35 | } 36 | if (!options.SegmentTerminator.HasValue) 37 | options.SegmentTerminator = GuessSegmentTerminator(edi); 38 | if (!options.ElementSeparator.HasValue) 39 | options.ElementSeparator = GuessElementSeparator(edi); 40 | if (!options.ReleaseCharacter.HasValue) 41 | options.ReleaseCharacter = GuessReleaseCharacter(edi); 42 | 43 | Segments = new List(); 44 | string[] rawSegments = SplitEdi(edi, options.SegmentTerminator.Value, options.ReleaseCharacter); 45 | for (int i = 0; i < rawSegments.Length; i++) 46 | { 47 | string rawSegment = rawSegments[i]; 48 | if (i == rawSegments.Length - 1 && (rawSegment == null || rawSegment.Trim() == string.Empty)) 49 | break; 50 | EdiSegment segment = null; 51 | if (rawSegment.StartsWith("UNA", StringComparison.OrdinalIgnoreCase)) 52 | { 53 | segment = new EdiSegment(rawSegment.Substring(0, 3)); 54 | segment.Elements.Add(new EdiElement(rawSegment.Substring(3, 5))); 55 | options.ComponentSeparator = rawSegment[3]; 56 | options.DecimalIndicator = rawSegment[5]; 57 | } 58 | else 59 | { 60 | string[] rawElements = SplitEdi(rawSegment.TrimStart(), options.ElementSeparator.Value, options.ReleaseCharacter); 61 | segment = new EdiSegment(rawElements[0]); 62 | for (int j = 1; j < rawElements.Length; j++) 63 | { 64 | if (segment.Id.Equals("ISA", StringComparison.OrdinalIgnoreCase)) 65 | { 66 | if (j == 16) 67 | { 68 | options.ComponentSeparator = rawElements[j][0]; 69 | segment.Elements.Add(new EdiElement(rawElements[j])); 70 | continue; 71 | } 72 | 73 | if (j == 11) 74 | { 75 | if (string.CompareOrdinal(rawElements[12], "00402") >= 0 && 76 | !char.IsLetterOrDigit(rawElements[j][0])) 77 | { 78 | options.RepetitionSeparator = rawElements[j][0]; 79 | segment.Elements.Add(new EdiElement(rawElements[j])); 80 | continue; 81 | } 82 | options.RepetitionSeparator = null; 83 | } 84 | } 85 | segment.Elements.Add(rawElements[j] != string.Empty ? ParseElement(rawElements[j], options) : null); 86 | } 87 | } 88 | Segments.Add(segment); 89 | } 90 | } 91 | 92 | private EdiDocument(XDocument xml) 93 | { 94 | Options = new EdiOptions(); 95 | Segments = new List(); 96 | LoadLoop(xml.Root); 97 | } 98 | 99 | /// 100 | /// Gets an EdiOptions containing separator characters used when loading or saving this document. 101 | /// 102 | public EdiOptions Options { get; private set; } 103 | 104 | /// 105 | /// Gets a list of segments belonging to this document. 106 | /// 107 | public IList Segments { get; private set; } 108 | 109 | /// 110 | /// Gets a list of transaction sets belonging to this document. 111 | /// 112 | public IList TransactionSets 113 | { 114 | get 115 | { 116 | var transactionSets = new List(); 117 | EdiTransactionSet transactionSet = null; 118 | EdiSegment interchangeHeader = null; 119 | EdiSegment functionalGroupHeader = null; 120 | foreach (EdiSegment segment in Segments) 121 | { 122 | switch (segment.Id.ToUpper()) 123 | { 124 | case "ISA": 125 | case "UNB": 126 | interchangeHeader = segment; 127 | break; 128 | case "GS": 129 | case "UNG": 130 | functionalGroupHeader = segment; 131 | break; 132 | case "ST": 133 | case "UNH": 134 | transactionSet = new EdiTransactionSet(interchangeHeader, functionalGroupHeader); 135 | transactionSets.Add(transactionSet); 136 | break; 137 | case "GE": 138 | case "UNE": 139 | functionalGroupHeader = null; 140 | break; 141 | case "IEA": 142 | case "UNZ": 143 | interchangeHeader = null; 144 | break; 145 | } 146 | if (transactionSet == null) 147 | continue; 148 | transactionSet.Segments.Add(segment); 149 | if (segment.Id.Equals("SE", StringComparison.OrdinalIgnoreCase) || 150 | segment.Id.Equals("UNT", StringComparison.OrdinalIgnoreCase)) 151 | { 152 | transactionSet = null; 153 | } 154 | } 155 | return transactionSets; 156 | } 157 | } 158 | 159 | /// 160 | /// Creates a new EdiDocument from a string, optionally specifying separator characters. 161 | /// 162 | /// A string that contains EDI. 163 | /// An EdiOptions containing separator characters to use when saving this document. 164 | /// An EdiDocument populated from the string that contains EDI. 165 | public static EdiDocument Parse(string edi, EdiOptions options = null) 166 | { 167 | return new EdiDocument(edi, options); 168 | } 169 | 170 | /// 171 | /// Creates a new EdiDocument from a file, optionally specifying separator characters. 172 | /// 173 | /// A file name that references the file to load into a new EdiDocument. 174 | /// An EdiOptions containing separator characters to use when saving this document. 175 | /// An EdiDocument that contains the contents of the specified file. 176 | public static EdiDocument Load(string fileName, EdiOptions options = null) 177 | { 178 | string edi = File.ReadAllText(fileName); 179 | return new EdiDocument(edi, options); 180 | } 181 | 182 | /// 183 | /// Creates a new EdiDocument from a TextReader, optionally specifying separator characters. 184 | /// 185 | /// A TextReader that contains the content for the EdiDocument. 186 | /// An EdiOptions containing separator characters to use when saving this document. 187 | /// An EdiDocument that contains the contents of the specified TextReader. 188 | public static EdiDocument Load(TextReader reader, EdiOptions options = null) 189 | { 190 | string edi = reader.ReadToEnd(); 191 | return new EdiDocument(edi, options); 192 | } 193 | 194 | /// 195 | /// Creates a new EdiDocument instance by using the specified stream, optionally specifying separator characters. 196 | /// 197 | /// The stream that contains the EDI data. 198 | /// An EdiOptions containing separator characters to use when saving this document. 199 | /// An EdiDocument object that reads the data that is contained in the stream. 200 | public static EdiDocument Load(Stream stream, EdiOptions options = null) 201 | { 202 | using (var reader = new StreamReader(stream)) 203 | { 204 | string edi = reader.ReadToEnd(); 205 | return new EdiDocument(edi, options); 206 | } 207 | } 208 | 209 | /// 210 | /// Creates a new EdiDocument from a string containing XML. 211 | /// 212 | /// A string that contains XML. 213 | /// An EdiDocument populated from the string that contains XML. 214 | public static EdiDocument ParseXml(string text) 215 | { 216 | XDocument xml = XDocument.Parse(text); 217 | return new EdiDocument(xml); 218 | } 219 | 220 | /// 221 | /// Creates a new EdiDocument from an XDocument. 222 | /// 223 | /// The XDocument that contains the EDI data. 224 | /// An EdiDocument object that reads the data that is contained in the XDocument. 225 | public static EdiDocument LoadXml(XDocument xml) 226 | { 227 | return new EdiDocument(xml); 228 | } 229 | 230 | /// 231 | /// Creates a new EdiDocument from an XML file. 232 | /// 233 | /// A file name that references the file to load into a new EdiDocument. 234 | /// An EdiDocument that contains the contents of the specified file. 235 | public static EdiDocument LoadXml(string fileName) 236 | { 237 | XDocument xml = XDocument.Load(fileName); 238 | return new EdiDocument(xml); 239 | } 240 | 241 | /// 242 | /// Creates a new EdiDocument from a TextReader. 243 | /// 244 | /// A TextReader that contains the content for the EdiDocument. 245 | /// An EdiDocument that contains the contents of the specified TextReader. 246 | public static EdiDocument LoadXml(TextReader reader) 247 | { 248 | XDocument xml = XDocument.Load(reader); 249 | return new EdiDocument(xml); 250 | } 251 | 252 | /// 253 | /// Creates a new EdiDocument instance by using the specified stream. 254 | /// 255 | /// The stream that contains the EDI data. 256 | /// An EdiDocument object that reads the data that is contained in the stream. 257 | public static EdiDocument LoadXml(Stream stream) 258 | { 259 | using (var reader = new StreamReader(stream)) 260 | { 261 | XDocument xml = XDocument.Load(reader); 262 | return new EdiDocument(xml); 263 | } 264 | } 265 | 266 | private char GuessElementSeparator(string edi) 267 | { 268 | if (edi.StartsWith("UNA", StringComparison.OrdinalIgnoreCase)) 269 | return edi[4]; 270 | Match match = Regex.Match(edi, "[^A-Z0-9]", RegexOptions.IgnoreCase); 271 | if (!match.Success) 272 | throw new Exception("Could not guess the element separator."); 273 | return match.Value[0]; 274 | } 275 | 276 | private char GuessSegmentTerminator(string edi) 277 | { 278 | if (edi.StartsWith("ISA", StringComparison.OrdinalIgnoreCase)) 279 | return edi[105]; 280 | if (edi.StartsWith("UNA", StringComparison.OrdinalIgnoreCase)) 281 | return edi[8]; 282 | Match match = Regex.Match(edi, @"([\x00-\x1f~])\s*$"); 283 | if (!match.Success) 284 | throw new Exception("Could not guess the segment terminator."); 285 | return match.Groups[1].Value[0]; 286 | } 287 | 288 | private char? GuessReleaseCharacter(string edi) 289 | { 290 | if (edi.StartsWith("UNA", StringComparison.OrdinalIgnoreCase) && edi[6] != ' ') 291 | return edi[6]; 292 | return null; 293 | } 294 | 295 | private EdiElement ParseElement(string rawElement, EdiOptions options) 296 | { 297 | var element = new EdiElement(); 298 | string[] repetitions = options.RepetitionSeparator.HasValue ? SplitEdi(rawElement, options.RepetitionSeparator.Value, options.ReleaseCharacter) : new[] {rawElement}; 299 | foreach (string rawRepetition in repetitions) 300 | { 301 | if (rawRepetition != string.Empty) 302 | element.Repetitions.Add(ParseRepetition(rawRepetition, options)); 303 | } 304 | return element; 305 | } 306 | 307 | private EdiRepetition ParseRepetition(string rawRepetition, EdiOptions options) 308 | { 309 | var repetition = new EdiRepetition(); 310 | string[] components = options.ComponentSeparator.HasValue ? SplitEdi(rawRepetition, options.ComponentSeparator.Value, options.ReleaseCharacter) : new[] {rawRepetition}; 311 | foreach (string rawComponent in components) 312 | { 313 | if (rawComponent != string.Empty) 314 | repetition.Components.Add(new EdiComponent(options.ReleaseCharacter.HasValue ? UnescapeEdi(rawComponent, options.ReleaseCharacter.Value) : rawComponent)); 315 | else 316 | repetition.Components.Add(null); 317 | } 318 | return repetition; 319 | } 320 | 321 | private string[] SplitEdi(string edi, char separator, char? releaseCharacter) 322 | { 323 | if (releaseCharacter.HasValue) 324 | return Regex.Split(edi, "(? 437 | /// Serialize this EdiDocument to a file, overwriting an existing file, if it exists. 438 | /// 439 | /// A string that contains the name of the file. 440 | public void Save(string fileName) 441 | { 442 | using (var writer = new StreamWriter(fileName)) 443 | { 444 | Save(writer); 445 | } 446 | } 447 | 448 | /// 449 | /// Outputs this EdiDocument to the specified Stream. 450 | /// 451 | /// The stream to output this EdiDocument to. 452 | public void Save(Stream stream) 453 | { 454 | var writer = new StreamWriter(stream); 455 | Save(writer); 456 | writer.Flush(); 457 | } 458 | 459 | /// 460 | /// Serialize this EdiDocument to a TextWriter. 461 | /// 462 | /// A TextWriter that the EdiDocument will be written to. 463 | public void Save(TextWriter writer) 464 | { 465 | var options = new EdiOptions(Options); 466 | foreach (EdiSegment segment in Segments) 467 | { 468 | if (segment.Id.Equals("ISA", StringComparison.OrdinalIgnoreCase)) 469 | { 470 | if (segment[11] != null && string.CompareOrdinal(segment[12], "00402") >= 0 && !char.IsLetterOrDigit(segment[11][0])) 471 | options.RepetitionSeparator = segment[11][0]; 472 | else 473 | options.RepetitionSeparator = null; 474 | if (segment[16] != null) 475 | options.ComponentSeparator = segment[16][0]; 476 | } 477 | writer.Write(segment.ToString(options)); 478 | if (options.AddLineBreaks) 479 | writer.WriteLine(); 480 | } 481 | writer.Flush(); 482 | } 483 | 484 | /// 485 | /// Returns the EDI for this EdiDocument. 486 | /// 487 | /// A string containing the EDI. 488 | public override string ToString() 489 | { 490 | var buffer = new StringWriter(); 491 | Save(buffer); 492 | return buffer.ToString(); 493 | } 494 | } 495 | } -------------------------------------------------------------------------------- /EdiTools/EdiElement.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Text; 3 | 4 | namespace EdiTools 5 | { 6 | /// 7 | /// Represents an EDI element. 8 | /// 9 | public class EdiElement : EdiValue 10 | { 11 | /// 12 | /// Initializes a new instance of the EdiElement class. 13 | /// 14 | public EdiElement() 15 | { 16 | Repetitions = new List(); 17 | } 18 | 19 | /// 20 | /// Initializes a new instance of the EdiElement class with the specified value. 21 | /// 22 | /// The initial value of the element. 23 | public EdiElement(string value) 24 | { 25 | Repetitions = new List {new EdiRepetition(value)}; 26 | } 27 | 28 | /// 29 | /// Gets a list of the repetitions in this element. 30 | /// 31 | public IList Repetitions { get; private set; } 32 | 33 | /// 34 | /// Gets the value of the first repetition or sets the value of the whole element. 35 | /// 36 | public override string Value 37 | { 38 | get { return Repetitions[0].Value; } 39 | 40 | set 41 | { 42 | Repetitions.Clear(); 43 | Repetitions.Add(new EdiRepetition(value)); 44 | } 45 | } 46 | 47 | /// 48 | /// Gets a list of the component elements in the first repetition of this element. 49 | /// 50 | public IList Components 51 | { 52 | get 53 | { 54 | if (Repetitions.Count == 0) 55 | Repetitions.Add(new EdiRepetition()); 56 | return Repetitions[0].Components; 57 | } 58 | } 59 | 60 | /// 61 | /// Gets or sets the value of the component element at the specified position. 62 | /// 63 | /// The position of the component element, starting at 1. 64 | /// A string containing the value of the component element. 65 | public string this[int position] 66 | { 67 | get 68 | { 69 | int index = position - 1; 70 | if (Components.Count <= index || Components[index] == null) 71 | return null; 72 | return Components[index].Value; 73 | } 74 | 75 | set 76 | { 77 | int index = position - 1; 78 | if (!string.IsNullOrEmpty(value)) 79 | { 80 | while (Components.Count <= index) 81 | Components.Add(null); 82 | Components[index] = new EdiComponent(value); 83 | } 84 | else if (Components.Count > index) 85 | Components[index] = null; 86 | } 87 | } 88 | 89 | /// 90 | /// Returns the component element at the specified position. 91 | /// 92 | /// The position of the component element, starting at 1. 93 | /// An EdiComponent representing the component element. 94 | public EdiComponent Component(int position) 95 | { 96 | int index = position - 1; 97 | return Components.Count <= index ? null : Components[index]; 98 | } 99 | 100 | /// 101 | /// Returns the EDI for this element. 102 | /// 103 | /// A string containing the EDI. 104 | public override string ToString() 105 | { 106 | return ToString(null); 107 | } 108 | 109 | /// 110 | /// Returns the EDI for this element, optionally specifying separator characters. 111 | /// 112 | /// An EdiOptions that specifies separator characters. 113 | /// A string containing the EDI. 114 | public string ToString(EdiOptions options) 115 | { 116 | var edi = new StringBuilder(); 117 | for (int i = 0; i < Repetitions.Count; i++) 118 | { 119 | if (i > 0) 120 | edi.Append(options != null && options.RepetitionSeparator.HasValue ? options.RepetitionSeparator.Value : EdiOptions.DefaultRepetitionSeparator); 121 | edi.Append(Repetitions[i].ToString(options)); 122 | } 123 | return edi.ToString(); 124 | } 125 | } 126 | } -------------------------------------------------------------------------------- /EdiTools/EdiMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text.RegularExpressions; 7 | using System.Xml.Linq; 8 | 9 | namespace EdiTools 10 | { 11 | /// 12 | /// Represents an EDI to XML mapping. 13 | /// 14 | public class EdiMapping 15 | { 16 | private readonly Loop _root; 17 | 18 | private EdiMapping(XDocument xml) 19 | { 20 | if (xml.Root == null) 21 | throw new Exception("XML is missing a root element."); 22 | Errors = new List(); 23 | _root = ReadLoop(xml.Root); 24 | } 25 | 26 | /// 27 | /// Gets a list of errors encountered while loading or using this EdiMapping. 28 | /// 29 | public IList Errors { get; private set; } 30 | 31 | /// 32 | /// Creates a new EdiMapping from a string. 33 | /// 34 | /// A string that contains an EDI to XML mapping. 35 | /// An EdiMapping populated from the string that contains the EDI to XML mapping. 36 | public static EdiMapping Parse(string text) 37 | { 38 | XDocument xml = XDocument.Parse(text); 39 | return new EdiMapping(xml); 40 | } 41 | 42 | /// 43 | /// Creates a new EdiMapping from an XDocument. 44 | /// 45 | /// An XDocument that contains an EDI to XML mapping. 46 | /// An EdiMapping populated from the XDocument that contains the EDI to XML mapping. 47 | public static EdiMapping Load(XDocument xml) 48 | { 49 | return new EdiMapping(xml); 50 | } 51 | 52 | /// 53 | /// Creates a new EdiMapping from a file. 54 | /// 55 | /// A file name that references the file to load into a new EdiMapping. 56 | /// An EdiMapping that contains the contents of the specified file. 57 | public static EdiMapping Load(string fileName) 58 | { 59 | XDocument xml = XDocument.Load(fileName); 60 | return new EdiMapping(xml); 61 | } 62 | 63 | /// 64 | /// Creates a new EdiMapping from a TextReader. 65 | /// 66 | /// A TextReader that contains the content for the EdiMapping. 67 | /// An EdiMapping that contains the contents of the specified TextReader. 68 | public static EdiMapping Load(TextReader reader) 69 | { 70 | XDocument xml = XDocument.Load(reader); 71 | return new EdiMapping(xml); 72 | } 73 | 74 | /// 75 | /// Creates a new EdiMapping instance by using the specified stream. 76 | /// 77 | /// The stream that contains the EDI to XML mapping. 78 | /// An EdiMapping object that reads the EDI to XML mapping that is contained in the stream. 79 | public static EdiMapping Load(Stream stream) 80 | { 81 | using (var reader = new StreamReader(stream)) 82 | { 83 | XDocument xml = XDocument.Load(reader); 84 | return new EdiMapping(xml); 85 | } 86 | } 87 | 88 | private Node ReadNode(XElement xml) 89 | { 90 | if (xml.Name.LocalName.EndsWith("loop")) 91 | return ReadLoop(xml); 92 | return ReadSegment(xml); 93 | } 94 | 95 | private Loop ReadLoop(XElement xml) 96 | { 97 | var loop = new Loop(xml.Name.LocalName); 98 | foreach (XElement element in xml.Elements()) 99 | loop.Nodes.Add(ReadNode(element)); 100 | return loop; 101 | } 102 | 103 | private Segment ReadSegment(XElement xml) 104 | { 105 | var segment = new Segment(xml.Name.LocalName); 106 | foreach (XElement element in xml.Elements()) 107 | { 108 | int elementIndex = GetElementIndex(element.Name.LocalName); 109 | while (segment.Elements.Count <= elementIndex) 110 | segment.Elements.Add(null); 111 | if (segment.Elements[elementIndex] != null) 112 | { 113 | Errors.Add(string.Format("Element '{0}' occupies a position in the segment already taken by element '{1}'.", 114 | element.Name.LocalName, segment.Elements[elementIndex].Id)); 115 | continue; 116 | } 117 | segment.Elements[elementIndex] = ReadElement(element); 118 | } 119 | return segment; 120 | } 121 | 122 | private int GetElementIndex(string elementId) 123 | { 124 | int position; 125 | if (elementId.Length < 2 || !int.TryParse(elementId.Substring(elementId.Length - 2), out position)) 126 | { 127 | Errors.Add(string.Format("Element '{0}' does not have a valid segment position.", elementId)); 128 | return -1; 129 | } 130 | return position - 1; 131 | } 132 | 133 | private Element ReadElement(XElement xml) 134 | { 135 | string type = GetElementType(xml); 136 | bool restrict = GetElementRestrict(xml); 137 | var element = new Element(xml.Name.LocalName, type, restrict); 138 | foreach (XElement xmlElement in xml.Elements()) 139 | { 140 | if (xmlElement.Name.LocalName == "option") 141 | { 142 | string option = xmlElement.Value; 143 | if (element.Options.Contains(option)) 144 | { 145 | Errors.Add(string.Format("Option '{0}' is already defined in the element.", option)); 146 | continue; 147 | } 148 | element.Options[option] = GetOptionDefinition(xmlElement); 149 | continue; 150 | } 151 | 152 | int componentIndex = GetElementIndex(xmlElement.Name.LocalName); 153 | while (element.Components.Count <= componentIndex) 154 | element.Components.Add(null); 155 | if (element.Components[componentIndex] != null) 156 | { 157 | Errors.Add(string.Format("Component '{0}' occupies a position in the element already taken by component '{1}'.", 158 | xmlElement.Name.LocalName, element.Components[componentIndex].Id)); 159 | continue; 160 | } 161 | element.Components[componentIndex] = ReadComponent(xmlElement); 162 | } 163 | return element; 164 | } 165 | 166 | private string GetElementType(XElement xml) 167 | { 168 | XAttribute typeAttribute = xml.Attribute("type"); 169 | if (typeAttribute == null) 170 | return null; 171 | if (IsValidElementType(typeAttribute.Value)) 172 | return typeAttribute.Value; 173 | Errors.Add(string.Format("'{0}' is not a valid type.", typeAttribute.Value)); 174 | return null; 175 | } 176 | 177 | private bool IsValidElementType(string type) 178 | { 179 | return Regex.IsMatch(type, "^id|an|dt|tm|n[0-9]|r$"); 180 | } 181 | 182 | private bool GetElementRestrict(XElement xml) 183 | { 184 | XAttribute restrictAttribute = xml.Attribute("restrict"); 185 | if (restrictAttribute == null) 186 | return false; 187 | return restrictAttribute.Value == "true"; 188 | } 189 | 190 | private string GetOptionDefinition(XElement xml) 191 | { 192 | XAttribute definitionAttribute = xml.Attribute("definition"); 193 | if (definitionAttribute == null) 194 | return null; 195 | return definitionAttribute.Value; 196 | } 197 | 198 | private Component ReadComponent(XElement xml) 199 | { 200 | string type = GetElementType(xml); 201 | bool restrict = GetElementRestrict(xml); 202 | var component = new Component(xml.Name.LocalName, type, restrict); 203 | foreach (XElement element in xml.Elements()) 204 | { 205 | if (element.Name.LocalName != "option") 206 | continue; 207 | string option = element.Value; 208 | if (component.Options.Contains(option)) 209 | { 210 | Errors.Add(string.Format("Option '{0}' is already defined in the component.", option)); 211 | continue; 212 | } 213 | component.Options[option] = GetOptionDefinition(element); 214 | } 215 | return component; 216 | } 217 | 218 | /// 219 | /// Converts the specified list of EdiSegments into an XDocument using this EdiMapping. 220 | /// 221 | /// A list of EdiSegments to convert into an XDocument. 222 | /// An XDocument containing the XML representations of the EdiSegments. 223 | public XDocument Map(IList segments) 224 | { 225 | var mapState = new MapState(segments); 226 | XElement rootElement = Map(mapState, _root); 227 | return new XDocument(rootElement); 228 | } 229 | 230 | private XElement Map(MapState mapState, Loop loop) 231 | { 232 | var xml = new XElement(loop.Id); 233 | string previousSegmentId = null; 234 | var loopState = new MapState.LoopState(loop); 235 | mapState.LoopStates.Push(loopState); 236 | while (mapState.SegmentIndex < mapState.Segments.Count) 237 | { 238 | EdiSegment segment = mapState.Segments[mapState.SegmentIndex]; 239 | if (loopState.VisitedSegmentIds.Contains(segment.Id)) 240 | break; 241 | Node mapping = loop.FindMatchingNode(segment); 242 | if (mapping == null && SegmentWasUnvisitedInOuterLoops(segment, mapState)) 243 | break; 244 | if (mapping is Loop) 245 | xml.Add(Map(mapState, (Loop) mapping)); 246 | else 247 | { 248 | xml.Add(MapSegment(segment, (Segment) mapping)); 249 | mapState.SegmentIndex++; 250 | } 251 | if (previousSegmentId == null) 252 | { 253 | if (loop != _root) 254 | loopState.VisitedSegmentIds.Add(segment.Id); 255 | } 256 | else if (previousSegmentId != segment.Id) 257 | loopState.VisitedSegmentIds.Add(previousSegmentId); 258 | previousSegmentId = segment.Id; 259 | } 260 | mapState.LoopStates.Pop(); 261 | return xml; 262 | } 263 | 264 | private bool SegmentWasUnvisitedInOuterLoops(EdiSegment segment, MapState mapState) 265 | { 266 | return mapState.LoopStates.Any(state => !state.VisitedSegmentIds.Contains(segment.Id) && 267 | state.Loop.FindMatchingNode(segment) != null); 268 | } 269 | 270 | private XElement MapSegment(EdiSegment segment, Segment mapping) 271 | { 272 | var xml = new XElement(mapping != null ? mapping.Id : segment.Id); 273 | for (int i = 0; i < segment.Elements.Count; i++) 274 | { 275 | EdiElement element = segment.Elements[i]; 276 | if (element == null) 277 | continue; 278 | Element elementMapping = null; 279 | string segmentId; 280 | if (mapping != null) 281 | { 282 | if (mapping.Elements.Count > i) 283 | elementMapping = mapping.Elements[i]; 284 | segmentId = mapping.Id; 285 | } 286 | else 287 | segmentId = segment.Id; 288 | string defaultElementId = segmentId + (i + 1).ToString("d2"); 289 | xml.Add(MapElement(element, elementMapping, defaultElementId)); 290 | } 291 | return xml; 292 | } 293 | 294 | private IEnumerable MapElement(EdiElement element, Element mapping, string defaultElementId) 295 | { 296 | var repetitions = new List(); 297 | foreach (EdiRepetition repetition in element.Repetitions) 298 | { 299 | var xml = new XElement(mapping != null ? mapping.Id : defaultElementId); 300 | if (repetition.Components.Count == 1) 301 | { 302 | if (mapping != null) 303 | { 304 | if (mapping.Components.Count == 0) 305 | { 306 | string mappedValue; 307 | if (mapping.Type == null || !MapValue(repetition, mapping.Type, out mappedValue)) 308 | xml.Value = repetition.Value; 309 | else 310 | { 311 | xml.SetAttributeValue("type", mapping.Type); 312 | xml.Value = mappedValue; 313 | } 314 | if (mapping.Options.Contains(repetition.Value)) 315 | { 316 | string definition = mapping.Options[repetition.Value]; 317 | if (definition != null && definition.Trim() != string.Empty) 318 | xml.SetAttributeValue("definition", definition); 319 | } 320 | } 321 | else 322 | xml.Add(MapComponent(repetition.Components[0], mapping.Components[0], mapping.Id + "01")); 323 | } 324 | else 325 | xml.Value = repetition.Value; 326 | } 327 | else 328 | { 329 | for (int i = 0; i < repetition.Components.Count; i++) 330 | { 331 | EdiComponent component = repetition.Components[i]; 332 | if (component == null) 333 | continue; 334 | Component componentMapping = null; 335 | string elementId; 336 | if (mapping != null) 337 | { 338 | if (mapping.Components.Count > i) 339 | componentMapping = mapping.Components[i]; 340 | elementId = mapping.Id; 341 | } 342 | else 343 | elementId = defaultElementId; 344 | string defaultComponentId = elementId + (i + 1).ToString("d2"); 345 | xml.Add(MapComponent(component, componentMapping, defaultComponentId)); 346 | } 347 | } 348 | repetitions.Add(xml); 349 | } 350 | return repetitions; 351 | } 352 | 353 | private XElement MapComponent(EdiComponent component, Component mapping, string defaultComponentId) 354 | { 355 | if (mapping != null) 356 | { 357 | var xml = new XElement(mapping.Id); 358 | string mappedValue; 359 | if (mapping.Type == null || !MapValue(component, mapping.Type, out mappedValue)) 360 | xml.Value = component.Value; 361 | else 362 | { 363 | xml.SetAttributeValue("type", mapping.Type); 364 | xml.Value = mappedValue; 365 | } 366 | if (mapping.Options.Contains(component.Value)) 367 | { 368 | string definition = mapping.Options[component.Value]; 369 | xml.SetAttributeValue("definition", definition); 370 | } 371 | return xml; 372 | } 373 | return new XElement(defaultComponentId, component.Value); 374 | } 375 | 376 | private bool MapValue(EdiValue node, string type, out string mappedValue) 377 | { 378 | try 379 | { 380 | switch (type) 381 | { 382 | case "id": 383 | case "an": 384 | mappedValue = node.Value; 385 | return true; 386 | case "dt": 387 | mappedValue = node.IsoDate; 388 | return true; 389 | case "tm": 390 | mappedValue = node.IsoTime; 391 | return true; 392 | case "r": 393 | mappedValue = node.RealValue.ToString(CultureInfo.InvariantCulture); 394 | return true; 395 | default: 396 | int decimals = int.Parse(type.Substring(1)); 397 | mappedValue = node.NumericValue(decimals).ToString(CultureInfo.InvariantCulture); 398 | return true; 399 | } 400 | } 401 | catch (FormatException) 402 | { 403 | Errors.Add(string.Format("'{0}' is not a valid value of type '{1}'.", node.Value, type)); 404 | mappedValue = null; 405 | return false; 406 | } 407 | } 408 | 409 | #region Nested type: Component 410 | 411 | private class Component 412 | { 413 | private readonly bool _restrict; 414 | 415 | public Component(string id, string type, bool restrict) 416 | { 417 | Id = id; 418 | Type = type; 419 | Options = new Options(); 420 | _restrict = restrict; 421 | } 422 | 423 | public string Id { get; private set; } 424 | public string Type { get; private set; } 425 | public Options Options { get; private set; } 426 | 427 | public bool IsMatch(EdiComponent component) 428 | { 429 | return !_restrict || (component != null && Options.Contains(component.Value)); 430 | } 431 | } 432 | 433 | #endregion 434 | 435 | #region Nested type: Element 436 | 437 | private class Element 438 | { 439 | private readonly bool _restrict; 440 | 441 | public Element(string id, string type, bool restrict) 442 | { 443 | Id = id; 444 | Type = type; 445 | Components = new List(); 446 | Options = new Options(); 447 | _restrict = restrict; 448 | } 449 | 450 | public string Id { get; private set; } 451 | public string Type { get; private set; } 452 | public IList Components { get; private set; } 453 | public Options Options { get; private set; } 454 | 455 | public bool IsMatch(EdiElement element) 456 | { 457 | if (_restrict && element == null) 458 | return false; 459 | foreach (EdiRepetition repetition in element.Repetitions) 460 | { 461 | if (_restrict && !Options.Contains(repetition.Value)) 462 | return false; 463 | if (repetition.Components.Where((t, i) => Components.Count > i && Components[i] != null && 464 | !Components[i].IsMatch(t)).Any()) 465 | { 466 | return false; 467 | } 468 | } 469 | return true; 470 | } 471 | } 472 | 473 | #endregion 474 | 475 | #region Nested type: Loop 476 | 477 | private class Loop : Node 478 | { 479 | public Loop(string id) 480 | { 481 | Id = id; 482 | Nodes = new List(); 483 | } 484 | 485 | public IList Nodes { get; private set; } 486 | 487 | public Node FindMatchingNode(EdiSegment segment) 488 | { 489 | foreach (Node node in Nodes) 490 | { 491 | if (node is Segment) 492 | { 493 | var segmentMapping = (Segment) node; 494 | if (segmentMapping.IsMatch(segment)) 495 | return node; 496 | } 497 | else 498 | { 499 | var loop = (Loop) node; 500 | Segment segmentMapping = loop.GetFirstSegment(); 501 | if (segmentMapping != null && segmentMapping.IsMatch(segment)) 502 | return node; 503 | } 504 | } 505 | return null; 506 | } 507 | 508 | private Segment GetFirstSegment() 509 | { 510 | if (Nodes.Count == 0) 511 | return null; 512 | var firstSegment = Nodes[0] as Segment; 513 | if (firstSegment != null) 514 | return firstSegment; 515 | var loop = (Loop) Nodes[0]; 516 | return loop.GetFirstSegment(); 517 | } 518 | } 519 | 520 | #endregion 521 | 522 | #region Nested type: MapState 523 | 524 | private class MapState 525 | { 526 | public MapState(IList segments) 527 | { 528 | Segments = segments; 529 | LoopStates = new Stack(); 530 | } 531 | 532 | public IList Segments { get; private set; } 533 | public int SegmentIndex { get; set; } 534 | public Stack LoopStates { get; private set; } 535 | 536 | #region Nested type: LoopState 537 | 538 | public class LoopState 539 | { 540 | public LoopState(Loop loop) 541 | { 542 | Loop = loop; 543 | VisitedSegmentIds = new HashSet(); 544 | } 545 | 546 | public Loop Loop { get; private set; } 547 | public HashSet VisitedSegmentIds { get; private set; } 548 | } 549 | 550 | #endregion 551 | } 552 | 553 | #endregion 554 | 555 | #region Nested type: Node 556 | 557 | private abstract class Node 558 | { 559 | public string Id { get; protected set; } 560 | } 561 | 562 | #endregion 563 | 564 | #region Nested type: Options 565 | 566 | private class Options 567 | { 568 | private readonly IList