Representates an XMP XmpPath with segment accessor methods. @since 28.02.2006
Ejemplo n.º 1
0
        /**
         * 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.
         * @param schemaNs the root namespace
         * @param pos the parsing position helper
         * @param expandedXPath  the path to contribute to
         * @throws XmpException If the path is not valid.
         */

        internal 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.Substring(pos.StepBegin, pos.StepEnd - pos.StepBegin));
            IXmpAliasInfo aliasInfo = XmpMetaFactory.SchemaRegistry.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.Namespace, XmpPath.SCHEMA_NODE));
                XmpPathSegment rootStep = new XmpPathSegment(VerifyXPathRoot(aliasInfo.Namespace, aliasInfo.PropName),
                                                             XmpPath.STRUCT_FIELD_STEP);
                rootStep.Alias     = true;
                rootStep.AliasForm = aliasInfo.AliasForm.Options;
                expandedXPath.Add(rootStep);

                if (aliasInfo.AliasForm.ArrayAltText)
                {
                    XmpPathSegment qualSelectorStep = new XmpPathSegment("[?xml:lang='x-default']",
                                                                         XmpPath.QUAL_SELECTOR_STEP);
                    qualSelectorStep.Alias     = true;
                    qualSelectorStep.AliasForm = aliasInfo.AliasForm.Options;
                    expandedXPath.Add(qualSelectorStep);
                }
                else if (aliasInfo.AliasForm.Array)
                {
                    XmpPathSegment indexStep = new XmpPathSegment("[1]", XmpPath.ARRAY_INDEX_STEP);
                    indexStep.Alias     = true;
                    indexStep.AliasForm = aliasInfo.AliasForm.Options;
                    expandedXPath.Add(indexStep);
                }
            }
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Constructor with optionsl initial values. If <code>propName</code> is provided, 
        /// <code>schemaNs</code> has also be provided. </summary>
        /// <param name="xmp"> the iterated metadata object. </param>
        /// <param name="schemaNs"> the iteration is reduced to this schema (optional) </param>
        /// <param name="propPath"> the iteration is redurce to this property within the <code>schemaNs</code> </param>
        /// <param name="options"> advanced iteration options, see <seealso cref="IteratorOptions"/> </param>
        /// <exception cref="XmpException"> If the node defined by the paramters is not existing.  </exception>
        public XmpIteratorImpl(XmpMetaImpl xmp, string schemaNs, string propPath, IteratorOptions options) {
            // make sure that options is defined at least with defaults
            _options = options ?? new IteratorOptions();

            // the start node of the iteration depending on the schema and property filter
            XmpNode startNode;
            string initialPath = null;
            bool baseSchema = !String.IsNullOrEmpty(schemaNs);
            bool baseProperty = !String.IsNullOrEmpty(propPath);

            if (!baseSchema && !baseProperty) {
                // complete tree will be iterated
                startNode = xmp.Root;
            }
            else if (baseSchema && baseProperty) {
                // Schema and property node provided
                XmpPath path = XmpPathParser.ExpandXPath(schemaNs, propPath);

                // base path is the prop path without the property leaf
                XmpPath basePath = new XmpPath();
                for (int i = 0; i < path.Size() - 1; i++) {
                    basePath.Add(path.GetSegment(i));
                }

                startNode = XmpNodeUtils.FindNode(xmp.Root, path, false, null);
                _baseNs = schemaNs;
                initialPath = basePath.ToString();
            }
            else if (baseSchema && !baseProperty) {
                // Only Schema provided
                startNode = XmpNodeUtils.FindSchemaNode(xmp.Root, schemaNs, false);
            }
            else // !baseSchema  &&  baseProperty
            {
                // No schema but property provided -> error
                throw new XmpException("Schema namespace URI is required", XmpError.BADSCHEMA);
            }


            // create iterator
            if (startNode != null) {
                _nodeIterator = (!_options.JustChildren)
                                    ? new NodeIterator(this, startNode, initialPath, 1)
                                    : new NodeIteratorChildren(this, startNode, initialPath);
            }
            else {
                // create null iterator
                _nodeIterator = EmptyList.GetEnumerator();
            }
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Follow an expanded path expression to find or create a node.
        /// </summary>
        /// <param name="xmpTree"> the node to begin the search. </param>
        /// <param name="xpath"> the complete xpath </param>
        /// <param name="createNodes"> flag if nodes shall be created 
        /// 			(when called by <code>setProperty()</code>) </param>
        /// <param name="leafOptions"> the options for the created leaf nodes (only when
        ///			<code>createNodes == true</code>). </param>
        /// <returns> Returns the node if found or created or <code>null</code>. </returns>
        /// <exception cref="XmpException"> An exception is only thrown if an error occurred, 
        /// 			not if a node was not found. </exception>
        internal static XmpNode FindNode(XmpNode xmpTree, XmpPath xpath, bool createNodes, PropertyOptions leafOptions) {
            // check if xpath is set.
            if (xpath == null || xpath.Size() == 0) {
                throw new XmpException("Empty XmpPath", XmpError.BADXPATH);
            }

            // Root of implicitly created subtree to possible delete it later. 
            // Valid only if leaf is new.
            XmpNode rootImplicitNode = null;

            // resolve schema step
            XmpNode currNode = FindSchemaNode(xmpTree, xpath.GetSegment((int) XmpPath.STEP_SCHEMA).Name, createNodes);
            if (currNode == null) {
                return null;
            }
            if (currNode.Implicit) {
                currNode.Implicit = false; // Clear the implicit node bit.
                rootImplicitNode = currNode; // Save the top most implicit node.
            }


            // Now follow the remaining steps of the original XmpPath.
            try {
                for (int i = 1; i < xpath.Size(); i++) {
                    currNode = FollowXPathStep(currNode, xpath.GetSegment(i), createNodes);
                    if (currNode == null) {
                        if (createNodes) {
                            // delete implicitly created nodes
                            DeleteNode(rootImplicitNode);
                        }
                        return null;
                    }
                    if (currNode.Implicit) {
                        // clear the implicit node flag
                        currNode.Implicit = false;

                        // if node is an ALIAS (can be only in root step, auto-create array 
                        // when the path has been resolved from a not simple alias type
                        if (i == 1 && xpath.GetSegment(i).Alias && xpath.GetSegment(i).AliasForm != 0) {
                            currNode.Options.SetOption(xpath.GetSegment(i).AliasForm, true);
                        }
                            // "CheckImplicitStruct" in C++
                        else if (i < xpath.Size() - 1 && xpath.GetSegment(i).Kind == XmpPath.STRUCT_FIELD_STEP &&
                                 !currNode.Options.CompositeProperty) {
                            currNode.Options.Struct = true;
                        }

                        if (rootImplicitNode == null) {
                            rootImplicitNode = currNode; // Save the top most implicit node.
                        }
                    }
                }
            }
            catch (XmpException e) {
                // if new notes have been created prior to the error, delete them
                if (rootImplicitNode != null) {
                    DeleteNode(rootImplicitNode);
                }
                throw e;
            }


            if (rootImplicitNode != null) {
                // set options only if a node has been successful created
                currNode.Options.MergeWith(leafOptions);
                currNode.Options = currNode.Options;
            }

            return currNode;
        }
Ejemplo n.º 4
0
        /**
	     * 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.
	     * <p>
	     * 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=&quot;value&quot;]
	     * <dd> An element in an array of structs, chosen by a field value.
	     * <dt>[@xml:lang=&quot;value&quot;]
	     * <dd> An element in an alt-text array, chosen by the xml:lang qualifier.
	     * <dt>[?qualName=&quot;value&quot;]
	     * <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>
	     * <p>
	     * 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]".
	     * <p>
	     * 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.
	     * <p>
	     * 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.
	     * @param schemaNs
	     *            schema namespace
	     * @param path
	     *            property name
	     * @return Returns the expandet XmpPath.
	     * @throws XmpException
	     *             Thrown if the format is not correct somehow.
	     * 
	     */

        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 = path[pos.StepBegin] != '[' ? ParseStructSegment(pos) : ParseIndexSegment(pos);

                if (segment.Kind == XmpPath.STRUCT_FIELD_STEP) {
                    if (segment.Name[0] == '@') {
                        segment.Name = "?" + segment.Name.Substring(1);
                        if (!"?xml:lang".Equals(segment.Name)) {
                            throw new XmpException("Only xml:lang allowed with '@'", XmpError.BADXPATH);
                        }
                    }
                    if (segment.Name[0] == '?') {
                        pos.NameStart++;
                        segment.Kind = XmpPath.QUALIFIER_STEP;
                    }

                    VerifyQualName(pos.Path.Substring(pos.NameStart, pos.NameEnd - pos.NameStart));
                }
                else if (segment.Kind == XmpPath.FIELD_SELECTOR_STEP) {
                    if (segment.Name[1] == '@') {
                        segment.Name = "[?" + segment.Name.Substring(2);
                        if (!segment.Name.StartsWith("[?xml:lang=")) {
                            throw new XmpException("Only xml:lang allowed with '@'", XmpError.BADXPATH);
                        }
                    }

                    if (segment.Name[1] == '?') {
                        pos.NameStart++;
                        segment.Kind = XmpPath.QUAL_SELECTOR_STEP;
                        VerifyQualName(pos.Path.Substring(pos.NameStart, pos.NameEnd - pos.NameStart));
                    }
                }

                expandedXPath.Add(segment);
            }
            return expandedXPath;
        }
Ejemplo n.º 5
0
        /**
	     * 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. 
	     * @param schemaNs the root namespace
	     * @param pos the parsing position helper
	     * @param expandedXPath  the path to contribute to
	     * @throws XmpException If the path is not valid.
	     */

        internal 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.Substring(pos.StepBegin, pos.StepEnd - pos.StepBegin));
            IXmpAliasInfo aliasInfo = XmpMetaFactory.SchemaRegistry.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.Namespace, XmpPath.SCHEMA_NODE));
                XmpPathSegment rootStep = new XmpPathSegment(VerifyXPathRoot(aliasInfo.Namespace, aliasInfo.PropName),
                                                             XmpPath.STRUCT_FIELD_STEP);
                rootStep.Alias = true;
                rootStep.AliasForm = aliasInfo.AliasForm.Options;
                expandedXPath.Add(rootStep);

                if (aliasInfo.AliasForm.ArrayAltText) {
                    XmpPathSegment qualSelectorStep = new XmpPathSegment("[?xml:lang='x-default']",
                                                                         XmpPath.QUAL_SELECTOR_STEP);
                    qualSelectorStep.Alias = true;
                    qualSelectorStep.AliasForm = aliasInfo.AliasForm.Options;
                    expandedXPath.Add(qualSelectorStep);
                }
                else if (aliasInfo.AliasForm.Array) {
                    XmpPathSegment indexStep = new XmpPathSegment("[1]", XmpPath.ARRAY_INDEX_STEP);
                    indexStep.Alias = true;
                    indexStep.AliasForm = aliasInfo.AliasForm.Options;
                    expandedXPath.Add(indexStep);
                }
            }
        }
Ejemplo n.º 6
0
        /**
         * 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.
         * <p>
         * 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=&quot;value&quot;]
         * <dd> An element in an array of structs, chosen by a field value.
         * <dt>[@xml:lang=&quot;value&quot;]
         * <dd> An element in an alt-text array, chosen by the xml:lang qualifier.
         * <dt>[?qualName=&quot;value&quot;]
         * <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>
         * <p>
         * 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]".
         * <p>
         * 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.
         * <p>
         * 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.
         * @param schemaNs
         *            schema namespace
         * @param path
         *            property name
         * @return Returns the expandet XmpPath.
         * @throws XmpException
         *             Thrown if the format is not correct somehow.
         *
         */

        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 = path[pos.StepBegin] != '[' ? ParseStructSegment(pos) : ParseIndexSegment(pos);

                if (segment.Kind == XmpPath.STRUCT_FIELD_STEP)
                {
                    if (segment.Name[0] == '@')
                    {
                        segment.Name = "?" + segment.Name.Substring(1);
                        if (!"?xml:lang".Equals(segment.Name))
                        {
                            throw new XmpException("Only xml:lang allowed with '@'", XmpError.BADXPATH);
                        }
                    }
                    if (segment.Name[0] == '?')
                    {
                        pos.NameStart++;
                        segment.Kind = XmpPath.QUALIFIER_STEP;
                    }

                    VerifyQualName(pos.Path.Substring(pos.NameStart, pos.NameEnd - pos.NameStart));
                }
                else if (segment.Kind == XmpPath.FIELD_SELECTOR_STEP)
                {
                    if (segment.Name[1] == '@')
                    {
                        segment.Name = "[?" + segment.Name.Substring(2);
                        if (!segment.Name.StartsWith("[?xml:lang="))
                        {
                            throw new XmpException("Only xml:lang allowed with '@'", XmpError.BADXPATH);
                        }
                    }

                    if (segment.Name[1] == '?')
                    {
                        pos.NameStart++;
                        segment.Kind = XmpPath.QUAL_SELECTOR_STEP;
                        VerifyQualName(pos.Path.Substring(pos.NameStart, pos.NameEnd - pos.NameStart));
                    }
                }

                expandedXPath.Add(segment);
            }
            return(expandedXPath);
        }