/// <summary> /// Constructor to create an SapIDocDefinition when the segment contents are already known /// Note: The c-Header and the exported Structure do not contain an IDoc description. The description is only /// found in the XSD (which does not contain enough information to create an SapIDocDefinition). /// </summary> /// <param name="name">Name of IDoc type</param> /// <param name="controlRecord">The Control Record Segment</param> /// <param name="dataRecord">The Data Record Segment</param> /// <param name="segments">The Segments of the IDoc</param> private SapIDocDefinition( string name, SapIDocSegment controlRecord, SapIDocSegment dataRecord, Dictionary <string, SapIDocSegment> segments) { Logger.DebugFormat("========== Creating {0} with dictionary of Segments ==========", GetType().Name); #region Argument Check if (string.IsNullOrEmpty(name)) { throw new ArgumentException("IDoc name cannot be empty.", "name"); } if (controlRecord == null) { throw new ArgumentNullException("controlRecord"); } if (dataRecord == null) { throw new ArgumentNullException("dataRecord"); } if (segments == null) { throw new ArgumentNullException("segments"); } #endregion this.Name = name; this.ControlRecord = controlRecord; this.DataRecord = dataRecord; this.Segments = segments; }
///// <summary> ///// The IDoc description e.g. "BAPI Outbound;Meter Asset Standing Data Updates". ///// Note that this property can only be set when an instance of the class is created. ///// Note that this property can be found in the XSD definition, but not in the C-Header definition or the ///// Structure definition. ///// </summary> ////public string Description ////{ //// get { return _idocDescription; } //// private set //// { //// if (String.IsNullOrEmpty(value)) //// { //// throw new SapIDocException("IDoc description cannot be empty."); // This might cause problems if they haven't bothered to enter a description. //// } //// _log.Debug("IDoc description: {0}", value); //// _idocDescription = value; //// } ////} #endregion #region Public Methods /////// <summary> /////// Create an IDoc definition from an XSD file that has been exported from SAP. /////// </summary> /////// <param name="path">The XSD filename</param> /////// <returns>Returns a new IDocDefinion object.</returns> ////public static SapIDocDefinition LoadXsd(string path) ////{ //// // Read file contents //// XElement definition; //// try //// { //// definition = XElement.Load(path); //// } //// catch (Exception e) //// { //// string msg = String.Format("Problem reading file {0}", path); //// throw new SapIDocException(msg, e); //// } //// return new SapIDocDefinition(definition); ////} /// <summary> /// Reads an IDoc Definition (C-Header) from the file system, and creates an SapIDocDefinition object. /// </summary> /// <param name="path">A file containing the IDoc Definition as a C Header.</param> /// <returns>A new SapIDocDefinition object</returns> public static SapIDocDefinition LoadHeader(string path) { Logger.DebugFormat("Calling {0} with file: {1}", MethodBase.GetCurrentMethod().GetQualifiedName(), path); SapIDocSegment controlRecord = null; SapIDocSegment dataRecord = null; var segments = new Dictionary <string, SapIDocSegment>(); if (!File.Exists(path)) { throw new SapIDocException(string.Format("Could not find C Header file: {0}", path)); } Logger.DebugFormat("C Header file contents:{0}{1}", Environment.NewLine, File.ReadAllText(path)); // Convert the C code to XML (srcml) so it can be easily processed. var srcml = ConvertCToSrcml(path); // The src2srcml command adds "src:" namespace prefixes to all elements. var nsm = new XmlNamespaceManager(new NameTable()); nsm.AddNamespace("src", "http://www.sdml.info/srcML/src"); //// Note that XPathSelectElement does not use the default namespace (""), will always return NULL if you query an XML document that has a default namespace. nsm.AddNamespace("cpp", "http://www.sdml.info/srcML/cpp"); // Extract the field definitions from each IDoc segment (typedef/struct). var structsXml = srcml.XPathSelectElements("/src:typedef", nsm); foreach (var seg in structsXml) { var segmentName = seg.XPathSelectElement("./src:name", nsm).Value.ToUpper(); var tmpSegDescComment = seg.XPathSelectElement("./src:type/src:struct/src:comment", nsm).Value; var segmentDescription = tmpSegDescComment.Substring(3, tmpSegDescComment.Length - (3 + 2)).TrimEnd(); //// remove start/end block-comment, and any trailing whitespace. var currentSegment = new SapIDocSegment(segmentName, segmentDescription); //// must Add() each field to this separately. var fieldsXml = seg.XPathSelectElements("./src:type/src:struct/src:block/src:decl_stmt", nsm).ToArray(); var commentsXml = seg.XPathSelectElements("./src:type/src:struct/src:block/src:comment", nsm).ToArray(); if (fieldsXml.Count() != commentsXml.Count()) { throw new SapIDocException( "Each field definition in the C-Header should have a matching comment."); } int fieldStartPos; // The starting position changes if it is a data segment. switch (segmentDescription) { case "IDoc Control Record for Interface to External System": fieldStartPos = 0; controlRecord = currentSegment; break; case "IDoc Data Record for Interface to External System": fieldStartPos = 0; dataRecord = currentSegment; break; case "IDoc Status Record for Interface to External System": Logger.Debug("Skipping Status Record definition, as this is not needed to read an IDoc."); continue; default: fieldStartPos = dataRecord.EnsureNotNull()["SDATA"].StartPosition; segments.Add(segmentName, currentSegment); break; } foreach (var fld in fieldsXml.Zip(commentsXml, (field, comment) => new { field, comment })) { var fieldName = fld.field.XPathSelectElement("./src:decl/src:name/src:name", nsm).Value.ToUpperInvariant(); var fieldLength = Convert.ToInt32( fld.field.XPathSelectElement("./src:decl/src:name/src:index/src:expr", nsm).Value); var tmpFldDescComment = fld.comment.Value; var fieldDescription = tmpFldDescComment .Substring(3, tmpFldDescComment.Length - (3 + 2)) .TrimEnd(); //// remove start/end block-comment, and any trailing whitespace. currentSegment.Add(new SapIDocField(fieldName, fieldDescription, fieldStartPos, fieldLength)); fieldStartPos += fieldLength; } } //// To get the IDoc name, look for the comment that says "/* Segment structures for IDoc type ZISUPODMAS_BAPIZBUSMASSENDEM01 */". //// Note that there is no IDoc description in the C-Header or the exported Structure. var tmpIdocName = srcml .XPathSelectElement("//src:comment[contains(., 'Segment structures for IDoc type')]", nsm) .Value; var idocName = tmpIdocName.Substring(36, tmpIdocName.Length - (36 + 2)).TrimEnd(); //// remove start/end block-comment, and any trailing whitespace. return(new SapIDocDefinition(idocName, controlRecord, dataRecord, segments)); }