/// <summary> /// Gets the content from the SIFElement for the specified version of SIF. Only /// elements that apply to the requested version of SIF will be returned. /// </summary> /// <param name="element">The element to retrieve content from</param> /// <param name="version"></param> /// <returns></returns> public virtual IList <Element> GetContent(SifElement element, SifVersion version) { List <Element> returnValue = new List <Element>(); ICollection <SimpleField> fields = element.GetFields(); foreach (SimpleField val in fields) { IElementDef def = val.ElementDef; if (def.IsSupported(version) && !def.IsAttribute(version) && def.Field) { returnValue.Add(val); } } IList <SifElement> children = element.GetChildList(); foreach (SifElement val in children) { IElementDef def = val.ElementDef; if (def.IsSupported(version)) { returnValue.Add(val); } } MergeSort.Sort <Element>(returnValue, ElementSorter <Element> .GetInstance(version)); //returnValue.Sort( ElementSorter<Element>.GetInstance( version ) ); return(returnValue); }
/// <summary> /// Gets the content from the SIFElement for the specified version of SIF. Only /// elements that apply to the requested version of SIF will be returned. /// </summary> /// <param name="element">The element to retrieve content from</param> /// <param name="version"></param> /// <returns></returns> public override IList <Element> GetContent(SifElement element, SifVersion version) { List <Element> returnValue = new List <Element>(); ICollection <SimpleField> fields = element.GetFields(); foreach (SimpleField val in fields) { IElementDef def = val.ElementDef; if (def.IsSupported(version) && !def.IsAttribute(version) && def.Field) { returnValue.Add(val); } } IList <SifElement> children = element.GetChildList(); foreach (SifElement val in children) { IElementDef def = val.ElementDef; if (def.IsSupported(version)) { if (def.IsCollapsed(version)) { ICollection <Element> subElements = GetContent(val, version); // FIXUP the ElementDef for this version of SIF. // for example, StudentPersonal/EmailList/Email needs it's // ElementDef set to "StudentPersonal_Email" foreach (Element e in subElements) { IElementDef subElementDef = e.ElementDef; if (version.CompareTo(subElementDef.EarliestVersion) >= 0) { String tag = subElementDef.Tag(Adk.SifVersion); IElementDef restoredDef = Adk.Dtd.LookupElementDef(element.ElementDef, tag); if (restoredDef != null) { e.ElementDef = restoredDef; } returnValue.Add(e); } } } else { returnValue.Add(val); } } } MergeSort.Sort <Element>(returnValue, ElementSorter <Element> .GetInstance(version)); //returnValue.Sort(ElementSorter<Element>.GetInstance(version)); return(returnValue); }
/// <summary> /// Parses the source of the XmlReader into a SifElement object. /// </summary> /// <param name="reader">The reader containing the Xml data to be parsed</param> /// <param name="zone">The Zone from which the message was received, or null if /// not applicable or not known</param> /// <param name="flags">The flags to use for parsing</param> /// <param name="version">The version of SIF that will be associated with the /// returned object.</param> /// <returns> A SifElement object encapsulating the message payload (e.g. /// a OpenADK.Library.us.Student.StudentPersonal object)</returns> /// <exception cref="OpenADK.Library.AdkParsingException">AdkParsingException is thrown if unable to /// parse the message</exception> /// <exception cref="System.IO.IOException"> IOException is thrown if an error is reported while reading /// the message content</exception> public SifElement Parse(XmlReader reader, IZone zone, SifParserFlags flags, SifVersion version) { try { reader.MoveToContent(); if (reader.LocalName == "SIF_Message") { SifElement element = ReadSIFMessageElement(reader, Adk.Dtd, zone, flags, version); return(element.GetChildList()[0]); } else { version = ParseVersion(reader, Adk.Dtd, zone, flags, version); return(ParseElementStream(reader, version, Adk.Dtd, zone, flags)); } } catch (XmlException xmle) { throw new AdkParsingException(xmle.Message, zone, xmle); } }
/// <summary> /// Gets the content from the SIFElement for the specified version of SIF. Only /// elements that apply to the requested version of SIF will be returned. /// </summary> /// <param name="element">The element to retrieve content from</param> /// <param name="version"></param> /// <returns></returns> public virtual IList<Element> GetContent( SifElement element, SifVersion version ) { List<Element> returnValue = new List<Element>(); ICollection<SimpleField> fields = element.GetFields(); foreach (SimpleField val in fields) { IElementDef def = val.ElementDef; if (def.IsSupported(version) && !def.IsAttribute(version) && def.Field) { returnValue.Add(val); } } IList<SifElement> children = element.GetChildList(); foreach(SifElement val in children ) { IElementDef def = val.ElementDef; if (def.IsSupported(version)) { returnValue.Add(val); } } MergeSort.Sort<Element>(returnValue, ElementSorter<Element>.GetInstance(version)); //returnValue.Sort( ElementSorter<Element>.GetInstance( version ) ); return returnValue; }
/// <summary> /// Gets the content from the SIFElement for the specified version of SIF. Only /// elements that apply to the requested version of SIF will be returned. /// </summary> /// <param name="element">The element to retrieve content from</param> /// <param name="version"></param> /// <returns></returns> public override IList<Element> GetContent(SifElement element, SifVersion version) { List<Element> returnValue = new List<Element>(); ICollection<SimpleField> fields = element.GetFields(); foreach (SimpleField val in fields) { IElementDef def = val.ElementDef; if (def.IsSupported(version) && !def.IsAttribute(version) && def.Field ) { returnValue.Add(val); } } IList<SifElement> children = element.GetChildList(); foreach (SifElement val in children) { IElementDef def = val.ElementDef; if (def.IsSupported(version)) { if (def.IsCollapsed(version)) { ICollection<Element> subElements = GetContent(val, version); // FIXUP the ElementDef for this version of SIF. // for example, StudentPersonal/EmailList/Email needs it's // ElementDef set to "StudentPersonal_Email" foreach (Element e in subElements) { IElementDef subElementDef = e.ElementDef; if (version.CompareTo(subElementDef.EarliestVersion) >= 0) { String tag = subElementDef.Tag(Adk.SifVersion); IElementDef restoredDef = Adk.Dtd.LookupElementDef(element.ElementDef, tag); if (restoredDef != null) { e.ElementDef = restoredDef; } returnValue.Add(e); } } } else { returnValue.Add(val); } } } MergeSort.Sort<Element>(returnValue, ElementSorter<Element>.GetInstance(version)); //returnValue.Sort(ElementSorter<Element>.GetInstance(version)); return returnValue; }
/// <summary> Recursively parse an XPath-like query.</summary> /// <param name="relativeTo">The SifElement this iteration is relative to. For the /// first call to this method, the <i>relativeTo</i> parameter is usually /// a SifDataObject such as StudentPersonal to which the XPath query /// string is relative. With each subsequent call it is the SifElement /// or SimpleField that was previously processed.</param> /// <param name="curSegment">The current segment of the path that is being processed. /// For the first call to this method, the <i>curSegment</i> should be /// the portion of the XPath query string up to the first forward slash, /// exclusive.</param> /// <param name="nextSegment">The remaining portion of the path to be processed. /// For the first call to this method, the <i>nextSegment</i> should be /// the portion of the XPath query string following the first forward /// slash.</param> /// <returns> The Element satisfying the query, or <c>null</c> if no /// match was found (unless the <i>create</i> parameter is true). If the /// query resolves to an attribute, a SimpleField object is returned. If /// it resolves to an element, a SifElement object is returned. In both /// cases the caller can obtain the text value of the attribute or /// element by calling its <c>getTextValue</c> method. /// </returns> private Element _xpath(SifElement relativeTo, string curSegment, string nextSegment) { SifElement nextEle = null; int attr = curSegment.IndexOf('@'); int bracket = curSegment.IndexOf('['); if (bracket != -1) { if (attr == -1) { throw new AdkSchemaException("Invalid query: \"" + curSegment + "\" must be in the form [@Attribute='value']"); } string subEleTag = curSegment.Substring(0, (bracket) - (0)); SifElement subEle = relativeTo.GetChild(subEleTag); if (subEle == null) { return null; } int endBracket = curSegment.IndexOf(']', bracket); if (bracket == -1) { throw new AdkSchemaException("Invalid query: \"" + curSegment + "\" must be in the form [@Attribute='value']"); } List<SearchCond> condsList = new List<SearchCond>(10); string conds = curSegment.Substring(bracket + 1, (endBracket) - (bracket + 1)); string[] tokens = conds.Split(','); foreach (string thisTok in tokens) { if (thisTok[0] != '@') { throw new AdkSchemaException("Attribute names must be preceded with the @ character: " + thisTok); } int eq = thisTok.IndexOf('='); if (eq == -1) { throw new AdkSchemaException("Attribute value must be in the form [@Attribute='value']: " + thisTok); } // Lookup the referenced attribute SimpleField attrEle = subEle.GetField(thisTok.Substring(1, (eq) - (1))); if (attrEle == null) { return null; } // Add the attribute/value to the list string aval = _attrValue(thisTok.Substring(eq + 1)); SearchCond sc = new SearchCond(attrEle.ElementDef, aval); condsList.Add(sc); } // Search the parent's subEleTag children for matching attributes. // All attributes in the condsList must match. SifElementList ch = relativeTo.GetChildList(subEleTag); int chLen = ch.Count; for (int i = 0; i < chLen && nextEle == null; i++) { SifElement cmpEle; cmpEle = ch[i]; int matched = 0; // Compare the attributes for (int x = 0; x < condsList.Count; x++) { SearchCond sc = condsList[x]; SimpleField atr = cmpEle.GetField(sc.fAttr); if (atr == null) { break; } if (atr.TextValue.Equals(sc.fValue)) { matched++; } } // If all attributes matched, this is a match if (matched == condsList.Count) { nextEle = cmpEle; // Continue the search if nextSegment has a value if (nextSegment != null && nextSegment.Length > 0) { int ii = nextSegment.IndexOf('/'); Element ee = _xpath(nextEle, ii == -1 ? nextSegment : nextSegment.Substring(0, (ii) - (0)), ii == -1 ? null : nextSegment.Substring(ii + 1)); if (ee != null) { return ee; } else { nextEle = null; } } } } if (nextEle == null) { return null; } } else { // Search for the named attribute/element if (attr != -1) { return relativeTo.GetField(curSegment.Substring(1)); } else { nextEle = relativeTo.GetChild(curSegment); if (nextEle == null) { if (nextSegment == null || (nextSegment.Length > 0 && nextSegment[0] == '@')) { return relativeTo.GetField(curSegment); } return null; } } } // Continue the search if nextSegment has a value if ((nextSegment != null && nextSegment.Length > 0)) { int i = nextSegment.IndexOf('/'); return _xpath(nextEle, i == -1 ? nextSegment : nextSegment.Substring(0, (i) - (0)), i == -1 ? null : nextSegment.Substring(i + 1)); } return nextEle; }
/// <summary> Recursively builds elements and attributes relative to a SifElement.</summary> /// <param name="relativeTo">The SifElement this iteration is relative to. For the /// first call to this method, the <i>relativeTo</i> parameter is usually /// a SifDataObject such as StudentPersonal to which the XPath query /// string is relative. With each subsequent call it is the SifElement /// or SimpleField that was previously processed.</param> /// <param name="path">A running path of the segments processed thus far; an empty /// StringBuffer should be passed to this parameter the first time the /// method is called </param> /// <param name="curSegment">The current segment of the path that is being processed. /// For the first call to this method, the <i>curSegment</i> should be /// the portion of the XPath query string up to the first forward slash, /// exclusive.</param> /// <param name="nextSegment">The remaining portion of the path to be processed. /// For the first call to this method, the <i>nextSegment</i> should be /// the portion of the XPath query string following the first forward /// slash.</param> /// <param name="prevAttributes">An optional array of attribute values that were /// used to construct an Element in the processing of the last segment. /// The array is comprised of attribute name and value pairs such that /// element N is an attribute name and N+1 is its value. For the first /// call to this method, the array should be null. For subsequent calls, /// it should be null unless an Element was constructed from attribute /// values.</param> /// <param name="version">The version of SIF for which this mapping operation is being evaluated</param> /// <param name="textFormatter">The SIFFormatter instance used to parse strings into strongly-typed data values. /// For many uses of this API, this formatter is equivalent to ADK.TextFormatter</param> /// <param name="pathFormatter">The SIFFormatter instance used for setting child SIFElements on their parents. /// This formatter may be different than the text formatter. The text formatter is, for /// compatibility's sake defaulted to SIF 1.x. However, the path formatter must be /// correct for the mappings path being evaluated.</param> /// <param name="valueBuilder"></param> /// <returns> The Element satisfying the query, or <c>null</c> if no /// match was found (unless the <i>create</i> parameter is true). If the /// query resolves to an attribute, a SimpleField object is returned. If /// it resolves to an element, a SifElement object is returned. In both /// cases the caller can obtain the text value of the attribute or /// element by calling its <c>TextValue</c> Property. /// </returns> private Element _xpathBuild( SifElement relativeTo, StringBuilder path, string curSegment, string nextSegment, string[] prevAttributes, IValueBuilder valueBuilder, SifVersion version, SifFormatter textFormatter, SifFormatter pathFormatter) { string[] _prevAttributes = null; SifElement nextEle = null; int asgnEq = curSegment.LastIndexOf('='); int attr = curSegment.LastIndexOf('@', asgnEq == -1 ? curSegment.Length - 1 : asgnEq - 1); int bracket = curSegment.IndexOf('['); if (bracket != -1) { if (attr == -1) { throw new AdkSchemaException("Invalid query: \"" + curSegment + "\" must be in the form [@Attr='value1','value2',...]"); } string subEleTag = curSegment.Substring(0, (bracket) - (0)); int lastBracket = curSegment.LastIndexOf(']'); string[] attrList = curSegment.Substring(bracket + 1, (lastBracket) - (bracket + 1)).Split(','); _prevAttributes = new string[attrList.Length * 2]; int _prevI = 0; for (int a = 0; a < attrList.Length; a++) { string _curSegment = attrList[a]; // Determine the value of the attribute int eq = _curSegment.IndexOf("="); string val = _curSegment.Substring(eq + 1); string v = null; if (val[0] == '\'') { int end = val.IndexOf('\'', 1); if (end != -1) { v = valueBuilder == null ? val.Substring(1, (end) - (1)) : valueBuilder.Evaluate(val.Substring(1, (end) - (1))); } } if (v == null) { throw new AdkSchemaException("Attribute value (" + val + ") must be in the form @Attribute='value'"); } string attrName = _curSegment.Substring(1, (eq) - (1)); _prevAttributes[_prevI++] = attrName; _prevAttributes[_prevI++] = v; if (nextEle == null) { // // Look at all of the peer elements to determine if any have the // attribute value set. If so, return it; otherwise create a new // instance of the element with the attribute set. For example, if // curSegment is "Address[@Type='M']", we must look at all of the // Address children of relativeTo in order to determine if any // currently exist with a Type field set to a value of 'M'. If one // does, then it already exists and there is nothing to do; if // not found, however, a new Address child must be added with a // Type field of 'M'. // // Lookup the IElementDef of relativeTo IElementDef subEleDef = Adk.Dtd.LookupElementDef(relativeTo.ElementDef, subEleTag); if (subEleDef == null) { subEleDef = Adk.Dtd.LookupElementDef(subEleTag); if (subEleDef == null) { throw new AdkSchemaException(subEleTag + " is not a recognized attribute of " + relativeTo.Tag); } } bool repeatable = subEleDef.IsRepeatable(relativeTo.SifVersion); SifElementList peers = relativeTo.GetChildList(subEleDef); if (curSegment.IndexOf("+]") == -1) { // // Determine if relatSifElementListiveTo has any children that already // define this attribute/value; if not, create a new instance. // If subEleDef is not repeatable, however, we cannot add // another instance of it regardless. // for (int i = 0; i < peers.Count && nextEle == null; i++) { SimpleField ftest = peers[i].GetField(attrName); if (ftest != null && ftest.TextValue.Equals(v)) { nextEle = peers[i]; } } } if (nextEle == null) { if (!(peers.Count > 0 && !repeatable)) { nextEle = _createChild(relativeTo, subEleTag, valueBuilder, version, textFormatter, pathFormatter); } else { // // subEleDef is not repeatable, so we need to back up // and add this attribute/value to a fresh instance of // relativeTo if possible. First use _xpath(path) to try // to select that instance in case it already exists (otherwise // we'd create a new instance each iteration, which is // not the desired result.) // string _tmp; if (path.Length > 0) { _tmp = path + "/" + curSegment; } else { _tmp = curSegment; } if (XPATH_DEBUG) { Console.Out.Write("Searching for path relative to " + relativeTo.Root.ElementDef.Name + ": " + _tmp); } int _del = _tmp.IndexOf('/'); nextEle = (SifElement) _xpath((SifElement)relativeTo.Root, _tmp.Substring(0, (_del == -1 ? _tmp.Length : _del) - (0)), _del == -1 ? null : _tmp.Substring(_del + 1)); if (XPATH_DEBUG) { if (nextEle == null) { Console.Out.WriteLine("; not found, a new instance will be created"); } else { Console.Out.WriteLine("; found"); } } if (nextEle == null) { if (relativeTo.ElementDef.IsRepeatable(relativeTo.SifVersion)) { // Clone relativeTo SifElement grandParent = (SifElement)relativeTo.Parent; nextEle = SifElement.Create(grandParent, relativeTo.ElementDef); pathFormatter.AddChild(grandParent, nextEle, version); // Clone subEleDef; this now becomes nextEle SifElement newEle = SifElement.Create(nextEle, subEleDef); pathFormatter.AddChild(nextEle, newEle, version); _copyAttributes(nextEle, prevAttributes); nextEle = newEle; } else { throw new AdkSchemaException( "It is not possible to create the element or attribute identified by this path: " + _tmp + (nextSegment == null ? "" : "/" + nextSegment) + ". The element or attribute is either undefined in this version of SIF, " + "or an attempt is being made to create another instance of an element that is not Repeatable."); } } } } } if (nextEle != null) { _createField(nextEle, attrName, v); } if (a == attrList.Length && nextEle == null) { return null; } } } else { // Search for the named attribute/element if (attr != -1) { SimpleField ff = relativeTo.GetField(curSegment.Substring(1)); if (ff == null) { ff = _createField(relativeTo, curSegment.Substring(1), null); } return ff; } else { string _tag = curSegment; int eq = curSegment.IndexOf('='); if (eq != -1) { _tag = curSegment.Substring(0, (eq) - (0)); } nextEle = relativeTo.GetChild(_tag); if (nextEle == null) { // The curSegment element does not exist as a child of the relativeTo // object, so create it. nextEle = _createChild(relativeTo, curSegment, valueBuilder, version, textFormatter, pathFormatter); if (nextEle == null) { if (nextSegment == null) { return relativeTo.GetField(_tag); } return null; } } } } // Continue the search if nextSegment has a value if (nextEle != null && (nextSegment != null && nextSegment.Length > 0)) { int i = nextSegment.IndexOf('/'); if (path.Length > 0) { path.Append("/"); } path.Append(curSegment); return _xpathBuild( nextEle, path, i == -1 ? nextSegment : nextSegment.Substring(0, (i)), i == -1 ? null : nextSegment.Substring(i + 1), _prevAttributes, valueBuilder, version, textFormatter, pathFormatter); } if (nextSegment == null && nextEle != null && (nextEle.TextValue == null || nextEle.TextValue.Length == 0)) { int eq2 = curSegment.LastIndexOf('='); if (eq2 != -1) { if (bracket == -1 || (curSegment.LastIndexOf(']') < eq2)) { // // An equals sign in the final segment indicates there is // a value constant or expression following the XPath // (e.g. "OtherId[@Type='06']=@pad($(PERMNUM),0,5)" ). Use // the user-supplied ValueBuilder to evaluate it. // string str = curSegment.Substring(eq2 + 1); nextEle.TextValue = valueBuilder == null ? str : valueBuilder.Evaluate(str); } } } return nextEle; }