Exemplo n.º 1
0
        /// <summary>
        /// Resolve the path into zero or more concrete object references.
        /// Never returns null. The returned list is sorted by ordinal.
        /// </summary>
        public List <Reference> Resolve(Reference context, Resolver resolver)
        {
            try
            {
                resolver.Trace("Path={0} context={1} line {2}:{3}", this, context, _lineNumber, _linePosition);
                if (this == Empty)
                {
                    return(new List <Reference>());
                }

                //	Start at the path's origin object. If the origin is the context
                //	but the context is null or empty, then set the origin to the root.
                Origin origin = _origin;
                if (origin == Origin.Context && (context == Reference.Null || context == Reference.Empty))
                {
                    origin = Origin.Root;
                }
                Reference start = null;
                switch (origin)
                {
                case Origin.Context:
                    start = context;
                    break;

                case Origin.Root:
                    start = Reference.Create(ContentSourceType.None, resolver.DocumentId, true);
                    break;
                }

                //	Follow the first step from the start reference. Each step will
                //	discover a set of zero or more resolved references, and will
                //	then follow its own next step starting at each of those resolved
                //	references. This will build up a set of resolved references, each
                //	of which is a result of following the full path from the single
                //	start position. But if we have no steps at all then resolve to
                //	the start object.
                List <Reference> resolved = null;
                if (_firstStep != null)
                {
                    resolved = _firstStep.Follow(start, resolver);
                }
                else
                {
                    resolved = new List <Reference>();
                    resolved.Add(start);
                }

                resolver.Trace("Resolved {0} objects:", resolved.Count);
                foreach (Reference reference in resolved)
                {
                    resolver.Trace("{0}", reference);
                }

                return(resolved);
            }
            catch (PathException ex)
            {
                ex.Path    = this.ToString();
                ex.Context = context.ToString();
                throw;
                //TODO: Since framework 4.5 (I think) this naked throw statement
                //loses the exception's call stack and shows the exception
                //originating here. There are messy ways to preserve it, but
                //can we find a clean way? 4.5 introduces ExceptionDispatchInfo
                //to handle this kind of thing (although it's really designed
                //for async continuations) but it has some side effects. First,
                //the compiler won't recognise ExceptionDispatchInfo.Throw as
                //being a throw (because it's not) and so may insist on having
                //a return value in the method - no big deal. But what makes this
                //unusable for us is that ExceptionDispatchInfo can't be marshaled
                //across app domain boundaries, and we may want to use app domains
                //to support multiple versions of assemblies for backwards
                //compatibility.
            }
        }
Exemplo n.º 2
0
        /// <summary>
        /// Find the concrete objects, based on the target template object, with this
        /// step's relationship to the start object. Throws ArgumentException if the
        /// start reference is not resolved, or if the step's target is already resolved.
        /// The returned list is sorted by ordinal.
        /// </summary>
        /// <exception cref="ArgumentException"></exception>
        internal List <Reference> Follow(Reference start, Resolver resolver)
        {
            if (!start.IsResolved)
            {
                throw new ArgumentException($"Start object {start} is not resolved.");
            }
            if (_reference.IsResolved)
            {
                throw new ArgumentException($"Target reference {_reference} is already resolved.");
            }

            List <Reference> found = new List <Reference>();

            //	If the start reference is an instance of the path reference then
            //	return it directly and don't look any further up/down
            if (resolver.IsInstanceOf(start, _reference))
            {
                found.Add(start);
                return(found);
            }

            switch (_navigation.Direction)
            {
            case Direction.Up:
                Reference ancestor = FollowUp(start, resolver);
                if (ancestor != null)
                {
                    found.Add(ancestor);
                }
                break;

            case Direction.Down:
                List <Reference> descendants = FollowDown(start, resolver);
                foreach (Reference descendant in descendants)
                {
                    if (descendant != null)
                    {
                        found.Add(descendant);
                    }
                }
                break;
            }

            //	If we're the last step in the path then our result set is the
            //	full result set for this fork of the path.
            if (_next == null)
            {
                return(found);
            }

            //	If we're not the last step then use each of our resolved references
            //	as the starting point for a fork at the next step.
            List <Reference> result = new List <Reference>();

            foreach (Reference reference in found)
            {
                List <Reference> forks = _next.Follow(reference, resolver);
                result.AddRange(forks);
            }
            return(result);
        }