/// <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="Com.Adobe.Xmp.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", XMPErrorConstants.Badxpath); } string rootProp = VerifyXPathRoot(schemaNS, Sharpen.Runtime.Substring(pos.path, pos.stepBegin, pos.stepEnd)); XMPAliasInfo aliasInfo = XMPMetaFactory.GetSchemaRegistry().FindAlias(rootProp); if (aliasInfo == null) { // add schema xpath step expandedXPath.Add(new XMPPathSegment(schemaNS, XMPPath.SchemaNode)); XMPPathSegment rootStep = new XMPPathSegment(rootProp, XMPPath.StructFieldStep); expandedXPath.Add(rootStep); } else { // add schema xpath step and base step of alias expandedXPath.Add(new XMPPathSegment(aliasInfo.GetNamespace(), XMPPath.SchemaNode)); XMPPathSegment rootStep = new XMPPathSegment(VerifyXPathRoot(aliasInfo.GetNamespace(), aliasInfo.GetPropName()), XMPPath.StructFieldStep); rootStep.SetAlias(true); rootStep.SetAliasForm(aliasInfo.GetAliasForm().GetOptions()); expandedXPath.Add(rootStep); if (aliasInfo.GetAliasForm().IsArrayAltText()) { XMPPathSegment qualSelectorStep = new XMPPathSegment("[?xml:lang='x-default']", XMPPath.QualSelectorStep); qualSelectorStep.SetAlias(true); qualSelectorStep.SetAliasForm(aliasInfo.GetAliasForm().GetOptions()); expandedXPath.Add(qualSelectorStep); } else { if (aliasInfo.GetAliasForm().IsArray()) { XMPPathSegment indexStep = new XMPPathSegment("[1]", XMPPath.ArrayIndexStep); indexStep.SetAlias(true); indexStep.SetAliasForm(aliasInfo.GetAliasForm().GetOptions()); expandedXPath.Add(indexStep); } } } }
/// <summary>Parses a struct segment</summary> /// <param name="pos">the current position in the path</param> /// <returns>Retusn the segment or an errror</returns> /// <exception cref="Com.Adobe.Xmp.XMPException">If the sement is empty</exception> private static XMPPathSegment ParseStructSegment(PathPosition pos) { pos.nameStart = pos.stepBegin; while (pos.stepEnd < pos.path.Length && "/[*".IndexOf(pos.path[pos.stepEnd]) < 0) { pos.stepEnd++; } pos.nameEnd = pos.stepEnd; if (pos.stepEnd == pos.stepBegin) { throw new XMPException("Empty XMPPath segment", XMPErrorConstants.Badxpath); } // ! Touch up later, also changing '@' to '?'. XMPPathSegment segment = new XMPPathSegment(Sharpen.Runtime.Substring(pos.path, pos.stepBegin, pos.stepEnd), XMPPath.StructFieldStep); return(segment); }
/// <summary>Parses an array index segment.</summary> /// <param name="pos">the xmp path</param> /// <returns>Returns the segment or an error</returns> /// <exception cref="Com.Adobe.Xmp.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", XMPErrorConstants.Badxpath); } if (pos.path[pos.stepEnd] == ']') { if (!"[last()".Equals(Sharpen.Runtime.Substring(pos.path, pos.stepBegin, pos.stepEnd))) { throw new XMPException("Invalid non-numeric array index", XMPErrorConstants.Badxpath); } segment = new XMPPathSegment(null, XMPPath.ArrayLastStep); } else { pos.nameStart = pos.stepBegin + 1; pos.nameEnd = pos.stepEnd; pos.stepEnd++; // Absorb the '=', remember the quote. char quote = pos.path[pos.stepEnd]; if (quote != '\'' && quote != '"') { throw new XMPException("Invalid quote in array selector", XMPErrorConstants.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", XMPErrorConstants.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", XMPErrorConstants.Badxpath); } pos.stepEnd++; segment.SetName(Sharpen.Runtime.Substring(pos.path, pos.stepBegin, pos.stepEnd)); return(segment); }
/// <summary> /// After processing by ExpandXPath, a step can be of these forms: /// <ul> /// <li>qualName - A top level property or struct field. /// </summary> /// <remarks> /// 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. /// </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="Com.Adobe.Xmp.XMPException"></exception> private static XMPNode FollowXPathStep(XMPNode parentNode, XMPPathSegment nextStep, bool createNodes) { XMPNode nextNode = null; int index = 0; int stepKind = nextStep.GetKind(); if (stepKind == XMPPath.StructFieldStep) { nextNode = FindChildNode(parentNode, nextStep.GetName(), createNodes); } else { if (stepKind == XMPPath.QualifierStep) { nextNode = FindQualifierNode(parentNode, Sharpen.Runtime.Substring(nextStep.GetName(), 1), createNodes); } else { // This is an array indexing step. First get the index, then get the node. if (!parentNode.GetOptions().IsArray()) { throw new XMPException("Indexing applied to non-array", XMPErrorConstants.Badxpath); } if (stepKind == XMPPath.ArrayIndexStep) { index = FindIndexedItem(parentNode, nextStep.GetName(), createNodes); } else { if (stepKind == XMPPath.ArrayLastStep) { index = parentNode.GetChildrenLength(); } else { if (stepKind == XMPPath.FieldSelectorStep) { string[] result = Utils.SplitNameAndValue(nextStep.GetName()); string fieldName = result[0]; string fieldValue = result[1]; index = LookupFieldSelector(parentNode, fieldName, fieldValue); } else { if (stepKind == XMPPath.QualSelectorStep) { string[] result = Utils.SplitNameAndValue(nextStep.GetName()); string qualName = result[0]; string qualValue = result[1]; index = LookupQualSelector(parentNode, qualName, qualValue, nextStep.GetAliasForm()); } else { throw new XMPException("Unknown array indexing step in FollowXPathStep", XMPErrorConstants.Internalfailure); } } } } if (1 <= index && index <= parentNode.GetChildrenLength()) { nextNode = parentNode.GetChild(index); } } } return nextNode; }
// Bits for XPathStepInfo options. // /// <summary>Append a path segment</summary> /// <param name="segment">the segment to add</param> public virtual void Add(XMPPathSegment segment) { segments.Add(segment); }
/// <summary>Parses an array index segment.</summary> /// <param name="pos">the xmp path</param> /// <returns>Returns the segment or an error</returns> /// <exception cref="Com.Adobe.Xmp.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", XMPErrorConstants.Badxpath); } if (pos.path[pos.stepEnd] == ']') { if (!"[last()".Equals(Sharpen.Runtime.Substring(pos.path, pos.stepBegin, pos.stepEnd))) { throw new XMPException("Invalid non-numeric array index", XMPErrorConstants.Badxpath); } segment = new XMPPathSegment(null, XMPPath.ArrayLastStep); } else { pos.nameStart = pos.stepBegin + 1; pos.nameEnd = pos.stepEnd; pos.stepEnd++; // Absorb the '=', remember the quote. char quote = pos.path[pos.stepEnd]; if (quote != '\'' && quote != '"') { throw new XMPException("Invalid quote in array selector", XMPErrorConstants.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", XMPErrorConstants.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", XMPErrorConstants.Badxpath); } pos.stepEnd++; segment.SetName(Sharpen.Runtime.Substring(pos.path, pos.stepBegin, pos.stepEnd)); return segment; }
/// <summary>Parses a struct segment</summary> /// <param name="pos">the current position in the path</param> /// <returns>Retusn the segment or an errror</returns> /// <exception cref="Com.Adobe.Xmp.XMPException">If the sement is empty</exception> private static XMPPathSegment ParseStructSegment(PathPosition pos) { pos.nameStart = pos.stepBegin; while (pos.stepEnd < pos.path.Length && "/[*".IndexOf(pos.path[pos.stepEnd]) < 0) { pos.stepEnd++; } pos.nameEnd = pos.stepEnd; if (pos.stepEnd == pos.stepBegin) { throw new XMPException("Empty XMPPath segment", XMPErrorConstants.Badxpath); } // ! Touch up later, also changing '@' to '?'. XMPPathSegment segment = new XMPPathSegment(Sharpen.Runtime.Substring(pos.path, pos.stepBegin, pos.stepEnd), XMPPath.StructFieldStep); return segment; }