/// <summary> /// After processing by ExpandXPath, a step can be of these forms: /// <ul> /// <li>qualName - A top level property or struct field. /// <li>[index] - An element of an array. /// <li>[last()] - The last element of an array. /// <li>[qualName="value"] - An element in an array of structs, chosen by a field value. /// <li>[?qualName="value"] - An element in an array, chosen by a qualifier value. /// <li>?qualName - A general qualifier. /// </ul> /// Find the appropriate child node, resolving aliases, and optionally creating nodes. /// </summary> /// <param name="parentNode"> the node to start to start from </param> /// <param name="nextStep"> the xpath segment </param> /// <param name="createNodes"> </param> /// <returns> returns the found or created XmpPath node </returns> /// <exception cref="XmpException"> </exception> private static XmpNode FollowXPathStep(XmpNode parentNode, XmpPathSegment nextStep, bool createNodes) { XmpNode nextNode = null; uint stepKind = nextStep.Kind; if (stepKind == XmpPath.STRUCT_FIELD_STEP) { nextNode = FindChildNode(parentNode, nextStep.Name, createNodes); } else if (stepKind == XmpPath.QUALIFIER_STEP) { nextNode = FindQualifierNode(parentNode, nextStep.Name.Substring(1), createNodes); } else { // This is an array indexing step. First get the index, then get the node. int index; if (!parentNode.Options.Array) { throw new XmpException("Indexing applied to non-array", XmpError.BADXPATH); } if (stepKind == XmpPath.ARRAY_INDEX_STEP) { index = FindIndexedItem(parentNode, nextStep.Name, createNodes); } else if (stepKind == XmpPath.ARRAY_LAST_STEP) { index = parentNode.ChildrenLength; } else if (stepKind == XmpPath.FIELD_SELECTOR_STEP) { string[] result = Utils.SplitNameAndValue(nextStep.Name); string fieldName = result[0]; string fieldValue = result[1]; index = LookupFieldSelector(parentNode, fieldName, fieldValue); } else if (stepKind == XmpPath.QUAL_SELECTOR_STEP) { string[] result = Utils.SplitNameAndValue(nextStep.Name); string qualName = result[0]; string qualValue = result[1]; index = LookupQualSelector(parentNode, qualName, qualValue, nextStep.AliasForm); } else { throw new XmpException("Unknown array indexing step in FollowXPathStep", XmpError.INTERNALFAILURE); } if (1 <= index && index <= parentNode.ChildrenLength) { nextNode = parentNode.GetChild(index); } } return(nextNode); }
/// <summary> /// After processing by ExpandXPath, a step can be of these forms: /// </summary> /// <remarks> /// After processing by ExpandXPath, a step can be of these forms: /// <list type="bullet"> /// <item>qualName - A top level property or struct field.</item> /// <item>[index] - An element of an array.</item> /// <item>[last()] - The last element of an array.</item> /// <item>[qualName="value"] - An element in an array of structs, chosen by a field value.</item> /// <item>[?qualName="value"] - An element in an array, chosen by a qualifier value.</item> /// <item>?qualName - A general qualifier.</item> /// </list> /// Find the appropriate child node, resolving aliases, and optionally creating nodes. /// </remarks> /// <param name="parentNode">the node to start to start from</param> /// <param name="nextStep">the xpath segment</param> /// <param name="createNodes"></param> /// <returns>returns the found or created XMPPath node</returns> /// <exception cref="XmpException"></exception> private static XmpNode FollowXPathStep(XmpNode parentNode, XmpPathSegment nextStep, bool createNodes) { XmpNode nextNode = null; var stepKind = nextStep.Kind; switch (stepKind) { case XmpPathStepType.StructFieldStep: nextNode = FindChildNode(parentNode, nextStep.Name, createNodes); break; case XmpPathStepType.QualifierStep: nextNode = FindQualifierNode(parentNode, nextStep.Name.Substring(1), createNodes); break; default: // This is an array indexing step. First get the index, then get the node. if (!parentNode.Options.IsArray) { throw new XmpException("Indexing applied to non-array", XmpErrorCode.BadXPath); } int index; switch (stepKind) { case XmpPathStepType.ArrayIndexStep: index = FindIndexedItem(parentNode, nextStep.Name, createNodes); break; case XmpPathStepType.ArrayLastStep: index = parentNode.GetChildrenLength(); break; case XmpPathStepType.FieldSelectorStep: Utils.SplitNameAndValue(nextStep.Name, out string fieldName, out string fieldValue); index = LookupFieldSelector(parentNode, fieldName, fieldValue); break; case XmpPathStepType.QualSelectorStep: Utils.SplitNameAndValue(nextStep.Name, out string qualName, out string qualValue); index = LookupQualSelector(parentNode, qualName, qualValue, nextStep.AliasForm); break; default: throw new XmpException("Unknown array indexing step in FollowXPathStep", XmpErrorCode.InternalFailure); } if (1 <= index && index <= parentNode.GetChildrenLength()) { nextNode = parentNode.GetChild(index); } break; } return(nextNode); }
/// <summary> /// Parses the root node of an XMP Path, checks if namespace and prefix fit together /// and resolve the property to the base property if it is an alias. /// </summary> /// <param name="schemaNs">the root namespace</param> /// <param name="pos">the parsing position helper</param> /// <param name="expandedXPath">the path to contribute to</param> /// <exception cref="XmpException">If the path is not valid.</exception> private static void ParseRootNode(string schemaNs, PathPosition pos, XmpPath expandedXPath) { while (pos.StepEnd < pos.Path.Length && "/[*".IndexOf(pos.Path[pos.StepEnd]) < 0) { pos.StepEnd++; } if (pos.StepEnd == pos.StepBegin) { throw new XmpException("Empty initial XMPPath step", XmpErrorCode.BadXPath); } var rootProp = VerifyXPathRoot(schemaNs, pos.Path.Substring(pos.StepBegin, pos.StepEnd - pos.StepBegin)); var aliasInfo = XmpMetaFactory.SchemaRegistry.FindAlias(rootProp); if (aliasInfo == null) { // add schema xpath step expandedXPath.Add(new XmpPathSegment(schemaNs, XmpPathStepType.SchemaNode)); var rootStep = new XmpPathSegment(rootProp, XmpPathStepType.StructFieldStep); expandedXPath.Add(rootStep); return; } // add schema xpath step and base step of alias expandedXPath.Add(new XmpPathSegment(aliasInfo.Namespace, XmpPathStepType.SchemaNode)); expandedXPath.Add(new XmpPathSegment(VerifyXPathRoot(aliasInfo.Namespace, aliasInfo.PropName), XmpPathStepType.StructFieldStep) { IsAlias = true, AliasForm = aliasInfo.AliasForm.GetOptions() }); if (aliasInfo.AliasForm.IsArrayAltText) { expandedXPath.Add(new XmpPathSegment("[?xml:lang='x-default']", XmpPathStepType.QualSelectorStep) { IsAlias = true, AliasForm = aliasInfo.AliasForm.GetOptions() }); } else if (aliasInfo.AliasForm.IsArray) { expandedXPath.Add(new XmpPathSegment("[1]", XmpPathStepType.ArrayIndexStep) { IsAlias = true, AliasForm = aliasInfo.AliasForm.GetOptions() }); } }
/// <summary>Parses an array index segment.</summary> /// <param name="pos">the xmp path</param> /// <returns>Returns the segment or an error</returns> /// <exception cref="XmpException">thrown on xmp path errors</exception> private static XmpPathSegment ParseIndexSegment(PathPosition pos) { XmpPathSegment segment; pos.StepEnd++; // Look at the character after the leading '['. if ('0' <= pos.Path[pos.StepEnd] && pos.Path[pos.StepEnd] <= '9') { // A numeric (decimal integer) array index. while (pos.StepEnd < pos.Path.Length && '0' <= pos.Path[pos.StepEnd] && pos.Path[pos.StepEnd] <= '9') { pos.StepEnd++; } segment = new XmpPathSegment(null, XmpPath.ArrayIndexStep); } else { // Could be "[last()]" or one of the selector forms. Find the ']' or '='. while (pos.StepEnd < pos.Path.Length && pos.Path[pos.StepEnd] != ']' && pos.Path[pos.StepEnd] != '=') { pos.StepEnd++; } if (pos.StepEnd >= pos.Path.Length) { throw new XmpException("Missing ']' or '=' for array index", XmpErrorCode.BadXPath); } if (pos.Path[pos.StepEnd] == ']') { if (!"[last()".Equals(pos.Path.Substring(pos.StepBegin, pos.StepEnd - pos.StepBegin))) { throw new XmpException("Invalid non-numeric array index", XmpErrorCode.BadXPath); } segment = new XmpPathSegment(null, XmpPath.ArrayLastStep); } else { pos.NameStart = pos.StepBegin + 1; pos.NameEnd = pos.StepEnd; pos.StepEnd++; // Absorb the '=', remember the quote. var quote = pos.Path[pos.StepEnd]; if (quote != '\'' && quote != '"') { throw new XmpException("Invalid quote in array selector", XmpErrorCode.BadXPath); } pos.StepEnd++; // Absorb the leading quote. while (pos.StepEnd < pos.Path.Length) { if (pos.Path[pos.StepEnd] == quote) { // check for escaped quote if (pos.StepEnd + 1 >= pos.Path.Length || pos.Path[pos.StepEnd + 1] != quote) { break; } pos.StepEnd++; } pos.StepEnd++; } if (pos.StepEnd >= pos.Path.Length) { throw new XmpException("No terminating quote for array selector", XmpErrorCode.BadXPath); } pos.StepEnd++; // Absorb the trailing quote. // ! Touch up later, also changing '@' to '?'. segment = new XmpPathSegment(null, XmpPath.FieldSelectorStep); } } if (pos.StepEnd >= pos.Path.Length || pos.Path[pos.StepEnd] != ']') { throw new XmpException("Missing ']' for array index", XmpErrorCode.BadXPath); } pos.StepEnd++; segment.Name = pos.Path.Substring(pos.StepBegin, pos.StepEnd - pos.StepBegin); return(segment); }
/// <summary> /// Parses the root node of an XMP Path, checks if namespace and prefix fit together /// and resolve the property to the base property if it is an alias. /// </summary> /// <param name="schemaNs">the root namespace</param> /// <param name="pos">the parsing position helper</param> /// <param name="expandedXPath">the path to contribute to</param> /// <exception cref="XmpException">If the path is not valid.</exception> private static void ParseRootNode(string schemaNs, PathPosition pos, XmpPath expandedXPath) { while (pos.StepEnd < pos.Path.Length && "/[*".IndexOf(pos.Path[pos.StepEnd]) < 0) pos.StepEnd++; if (pos.StepEnd == pos.StepBegin) throw new XmpException("Empty initial XMPPath step", XmpErrorCode.BadXPath); var rootProp = VerifyXPathRoot(schemaNs, pos.Path.Substring (pos.StepBegin, pos.StepEnd - pos.StepBegin)); var aliasInfo = XmpMetaFactory.SchemaRegistry.FindAlias(rootProp); if (aliasInfo == null) { // add schema xpath step expandedXPath.Add(new XmpPathSegment(schemaNs, XmpPath.SchemaNode)); var rootStep = new XmpPathSegment(rootProp, XmpPath.StructFieldStep); expandedXPath.Add(rootStep); } else { // add schema xpath step and base step of alias expandedXPath.Add(new XmpPathSegment(aliasInfo.Namespace, XmpPath.SchemaNode)); var rootStep = new XmpPathSegment(VerifyXPathRoot(aliasInfo.Namespace, aliasInfo.PropName), XmpPath.StructFieldStep); rootStep.IsAlias = true; rootStep.AliasForm = aliasInfo.AliasForm.GetOptions(); expandedXPath.Add(rootStep); if (aliasInfo.AliasForm.IsArrayAltText) { var qualSelectorStep = new XmpPathSegment("[?xml:lang='x-default']", XmpPath.QualSelectorStep); qualSelectorStep.IsAlias = true; qualSelectorStep.AliasForm = aliasInfo.AliasForm.GetOptions(); expandedXPath.Add(qualSelectorStep); } else if (aliasInfo.AliasForm.IsArray) { var indexStep = new XmpPathSegment("[1]", XmpPath.ArrayIndexStep); indexStep.IsAlias = true; indexStep.AliasForm = aliasInfo.AliasForm.GetOptions(); expandedXPath.Add(indexStep); } } }
/// <summary>Parses an array index segment.</summary> /// <param name="pos">the xmp path</param> /// <returns>Returns the segment or an error</returns> /// <exception cref="XmpException">thrown on xmp path errors</exception> private static XmpPathSegment ParseIndexSegment(PathPosition pos) { XmpPathSegment segment; pos.StepEnd++; // Look at the character after the leading '['. if ('0' <= pos.Path[pos.StepEnd] && pos.Path[pos.StepEnd] <= '9') { // A numeric (decimal integer) array index. while (pos.StepEnd < pos.Path.Length && '0' <= pos.Path[pos.StepEnd] && pos.Path[pos.StepEnd] <= '9') pos.StepEnd++; segment = new XmpPathSegment(null, XmpPath.ArrayIndexStep); } else { // Could be "[last()]" or one of the selector forms. Find the ']' or '='. while (pos.StepEnd < pos.Path.Length && pos.Path[pos.StepEnd] != ']' && pos.Path[pos.StepEnd] != '=') pos.StepEnd++; if (pos.StepEnd >= pos.Path.Length) throw new XmpException("Missing ']' or '=' for array index", XmpErrorCode.BadXPath); if (pos.Path[pos.StepEnd] == ']') { if (!"[last()".Equals(pos.Path.Substring(pos.StepBegin, pos.StepEnd - pos.StepBegin))) throw new XmpException("Invalid non-numeric array index", XmpErrorCode.BadXPath); segment = new XmpPathSegment(null, XmpPath.ArrayLastStep); } else { pos.NameStart = pos.StepBegin + 1; pos.NameEnd = pos.StepEnd; pos.StepEnd++; // Absorb the '=', remember the quote. var quote = pos.Path[pos.StepEnd]; if (quote != '\'' && quote != '"') throw new XmpException("Invalid quote in array selector", XmpErrorCode.BadXPath); pos.StepEnd++; // Absorb the leading quote. while (pos.StepEnd < pos.Path.Length) { if (pos.Path[pos.StepEnd] == quote) { // check for escaped quote if (pos.StepEnd + 1 >= pos.Path.Length || pos.Path[pos.StepEnd + 1] != quote) break; pos.StepEnd++; } pos.StepEnd++; } if (pos.StepEnd >= pos.Path.Length) throw new XmpException("No terminating quote for array selector", XmpErrorCode.BadXPath); pos.StepEnd++; // Absorb the trailing quote. // ! Touch up later, also changing '@' to '?'. segment = new XmpPathSegment(null, XmpPath.FieldSelectorStep); } } if (pos.StepEnd >= pos.Path.Length || pos.Path[pos.StepEnd] != ']') throw new XmpException("Missing ']' for array index", XmpErrorCode.BadXPath); pos.StepEnd++; segment.Name = pos.Path.Substring (pos.StepBegin, pos.StepEnd - pos.StepBegin); return segment; }
/// <summary> /// After processing by ExpandXPath, a step can be of these forms: /// </summary> /// <remarks> /// After processing by ExpandXPath, a step can be of these forms: /// <list type="bullet"> /// <item>qualName - A top level property or struct field.</item> /// <item>[index] - An element of an array.</item> /// <item>[last()] - The last element of an array.</item> /// <item>[qualName="value"] - An element in an array of structs, chosen by a field value.</item> /// <item>[?qualName="value"] - An element in an array, chosen by a qualifier value.</item> /// <item>?qualName - A general qualifier.</item> /// </list> /// Find the appropriate child node, resolving aliases, and optionally creating nodes. /// </remarks> /// <param name="parentNode">the node to start to start from</param> /// <param name="nextStep">the xpath segment</param> /// <param name="createNodes"></param> /// <returns>returns the found or created XMPPath node</returns> /// <exception cref="XmpException"></exception> private static XmpNode FollowXPathStep(XmpNode parentNode, XmpPathSegment nextStep, bool createNodes) { XmpNode nextNode = null; var stepKind = nextStep.Kind; if (stepKind == XmpPath.StructFieldStep) { nextNode = FindChildNode(parentNode, nextStep.Name, createNodes); } else { if (stepKind == XmpPath.QualifierStep) { nextNode = FindQualifierNode(parentNode, nextStep.Name.Substring(1), createNodes); } else { // This is an array indexing step. First get the index, then get the node. if (!parentNode.Options.IsArray) { throw new XmpException("Indexing applied to non-array", XmpErrorCode.BadXPath); } var index = 0; if (stepKind == XmpPath.ArrayIndexStep) { index = FindIndexedItem(parentNode, nextStep.Name, createNodes); } else { if (stepKind == XmpPath.ArrayLastStep) { index = parentNode.GetChildrenLength(); } else { if (stepKind == XmpPath.FieldSelectorStep) { var result = Utils.SplitNameAndValue(nextStep.Name); var fieldName = result[0]; var fieldValue = result[1]; index = LookupFieldSelector(parentNode, fieldName, fieldValue); } else { if (stepKind == XmpPath.QualSelectorStep) { var result = Utils.SplitNameAndValue(nextStep.Name); var qualName = result[0]; var qualValue = result[1]; index = LookupQualSelector(parentNode, qualName, qualValue, nextStep.AliasForm); } else { throw new XmpException("Unknown array indexing step in FollowXPathStep", XmpErrorCode.InternalFailure); } } } } if (1 <= index && index <= parentNode.GetChildrenLength()) { nextNode = parentNode.GetChild(index); } } } return(nextNode); }