/// <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="iText.Kernel.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", XMPError.BADXPATH); } String rootProp = VerifyXPathRoot(schemaNS, pos.path.JSubstring(pos.stepBegin, pos.stepEnd)); XMPAliasInfo aliasInfo = XMPMetaFactory.GetSchemaRegistry().FindAlias(rootProp); if (aliasInfo == null) { // add schema xpath step expandedXPath.Add(new XMPPathSegment(schemaNS, XMPPath.SCHEMA_NODE)); XMPPathSegment rootStep = new XMPPathSegment(rootProp, XMPPath.STRUCT_FIELD_STEP); expandedXPath.Add(rootStep); } else { // add schema xpath step and base step of alias expandedXPath.Add(new XMPPathSegment(aliasInfo.GetNamespace(), XMPPath.SCHEMA_NODE)); XMPPathSegment rootStep = new XMPPathSegment(VerifyXPathRoot(aliasInfo.GetNamespace(), aliasInfo.GetPropName ()), XMPPath.STRUCT_FIELD_STEP); rootStep.SetAlias(true); rootStep.SetAliasForm(aliasInfo.GetAliasForm().GetOptions()); expandedXPath.Add(rootStep); if (aliasInfo.GetAliasForm().IsArrayAltText()) { XMPPathSegment qualSelectorStep = new XMPPathSegment("[?xml:lang='x-default']", XMPPath.QUAL_SELECTOR_STEP ); qualSelectorStep.SetAlias(true); qualSelectorStep.SetAliasForm(aliasInfo.GetAliasForm().GetOptions()); expandedXPath.Add(qualSelectorStep); } else { if (aliasInfo.GetAliasForm().IsArray()) { XMPPathSegment indexStep = new XMPPathSegment("[1]", XMPPath.ARRAY_INDEX_STEP); indexStep.SetAlias(true); indexStep.SetAliasForm(aliasInfo.GetAliasForm().GetOptions()); expandedXPath.Add(indexStep); } } } }
// empty /// <summary> /// Split an XMPPath expression apart at the conceptual steps, adding the /// root namespace prefix to the first property component. /// </summary> /// <remarks> /// Split an XMPPath expression apart at the conceptual steps, adding the /// root namespace prefix to the first property component. The schema URI is /// put in the first (0th) slot in the expanded XMPPath. Check if the top /// level component is an alias, but don't resolve it. /// <para /> /// In the most verbose case steps are separated by '/', and each step can be /// of these forms: /// <dl> /// <dt>prefix:name /// <dd> A top level property or struct field. /// <dt>[index] /// <dd> An element of an array. /// <dt>[last()] /// <dd> The last element of an array. /// <dt>[fieldName="value"] /// <dd> An element in an array of structs, chosen by a field value. /// <dt>[@xml:lang="value"] /// <dd> An element in an alt-text array, chosen by the xml:lang qualifier. /// <dt>[?qualName="value"] /// <dd> An element in an array, chosen by a qualifier value. /// <dt>@xml:lang /// <dd> An xml:lang qualifier. /// <dt>?qualName /// <dd> A general qualifier. /// </dl> /// <para /> /// The logic is complicated though by shorthand for arrays, the separating /// '/' and leading '*' are optional. These are all equivalent: array/*[2] /// array/[2] array*[2] array[2] All of these are broken into the 2 steps /// "array" and "[2]". /// <para /> /// The value portion in the array selector forms is a string quoted by ''' /// or '"'. The value may contain any character including a doubled quoting /// character. The value may be empty. /// <para /> /// The syntax isn't checked, but an XML name begins with a letter or '_', /// and contains letters, digits, '.', '-', '_', and a bunch of special /// non-ASCII Unicode characters. An XML qualified name is a pair of names /// separated by a colon. /// </remarks> /// <param name="schemaNS">schema namespace</param> /// <param name="path">property name</param> /// <returns>Returns the expandet XMPPath.</returns> /// <exception cref="iText.Kernel.XMP.XMPException">Thrown if the format is not correct somehow.</exception> public static XMPPath ExpandXPath(String schemaNS, String path) { if (schemaNS == null || path == null) { throw new XMPException("Parameter must not be null", XMPError.BADPARAM); } XMPPath expandedXPath = new XMPPath(); PathPosition pos = new PathPosition(); pos.path = path; // Pull out the first component and do some special processing on it: add the schema // namespace prefix and and see if it is an alias. The start must be a "qualName". ParseRootNode(schemaNS, pos, expandedXPath); // Now continue to process the rest of the XMPPath string. while (pos.stepEnd < path.Length) { pos.stepBegin = pos.stepEnd; SkipPathDelimiter(path, pos); pos.stepEnd = pos.stepBegin; XMPPathSegment segment; if (path[pos.stepBegin] != '[') { // A struct field or qualifier. segment = ParseStructSegment(pos); } else { // One of the array forms. segment = ParseIndexSegment(pos); } if (segment.GetKind() == XMPPath.STRUCT_FIELD_STEP) { if (segment.GetName()[0] == '@') { segment.SetName("?" + segment.GetName().Substring(1)); if (!"?xml:lang".Equals(segment.GetName())) { throw new XMPException("Only xml:lang allowed with '@'", XMPError.BADXPATH); } } if (segment.GetName()[0] == '?') { pos.nameStart++; segment.SetKind(XMPPath.QUALIFIER_STEP); } VerifyQualName(pos.path.JSubstring(pos.nameStart, pos.nameEnd)); } else { if (segment.GetKind() == XMPPath.FIELD_SELECTOR_STEP) { if (segment.GetName()[1] == '@') { segment.SetName("[?" + segment.GetName().Substring(2)); if (!segment.GetName().StartsWith("[?xml:lang=")) { throw new XMPException("Only xml:lang allowed with '@'", XMPError.BADXPATH); } } if (segment.GetName()[1] == '?') { pos.nameStart++; segment.SetKind(XMPPath.QUAL_SELECTOR_STEP); VerifyQualName(pos.path.JSubstring(pos.nameStart, pos.nameEnd)); } } } expandedXPath.Add(segment); } return(expandedXPath); }