Esempio n. 1
0
        internal object Run(AtomMaterializer materializer, AtomEntry entry, Type expectedType)
        {
            Debug.Assert(materializer != null, "materializer != null");
            Debug.Assert(entry != null, "entry != null");

            return this.Plan(materializer, entry, expectedType);
        }
Esempio n. 2
0
        private void ParseCurrentLink(AtomEntry targetEntry)
        {
            Debug.Assert(targetEntry != null, "targetEntry != null");
            Debug.Assert(
                this.reader.NodeType == XmlNodeType.Element,
                "this.reader.NodeType == XmlNodeType.Element -- otherwise we shouldn't try to parse a link");
            Debug.Assert(
                this.reader.LocalName == "link",
                "this.reader.LocalName == 'link' -- otherwise we shouldn't try to parse a link");

            string relation = this.reader.GetAttribute(XmlConstants.AtomLinkRelationAttributeName);

            if (relation == null)
            {
                return;
            }

            if (relation == XmlConstants.AtomEditRelationAttributeValue && targetEntry.EditLink == null)
            {
                string href = this.reader.GetAttribute(XmlConstants.AtomHRefAttributeName);
                if (String.IsNullOrEmpty(href))
                {
                    throw Error.InvalidOperation(Strings.Context_MissingEditLinkInResponseBody);
                }

                targetEntry.EditLink = this.ConvertHRefAttributeValueIntoURI(href);
            }
            else if (relation == XmlConstants.AtomSelfRelationAttributeValue && targetEntry.QueryLink == null)
            {
                string href = this.reader.GetAttribute(XmlConstants.AtomHRefAttributeName);
                if (String.IsNullOrEmpty(href))
                {
                    throw Error.InvalidOperation(Strings.Context_MissingSelfLinkInResponseBody);
                }

                targetEntry.QueryLink = this.ConvertHRefAttributeValueIntoURI(href);
            }
            else if (relation == XmlConstants.AtomEditMediaRelationAttributeValue && targetEntry.MediaEditUri == null)
            {
                string href = this.reader.GetAttribute(XmlConstants.AtomHRefAttributeName);
                if (String.IsNullOrEmpty(href))
                {
                    throw Error.InvalidOperation(Strings.Context_MissingEditMediaLinkInResponseBody);
                }

                targetEntry.MediaEditUri   = this.ConvertHRefAttributeValueIntoURI(href);
                targetEntry.StreamETagText = this.reader.GetAttribute(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace);
            }

            if (!this.reader.IsEmptyElement)
            {
                string propertyName = UriUtil.GetNameFromAtomLinkRelationAttribute(relation);
                if (propertyName == null)
                {
                    return;
                }

                string propertyValueText = this.reader.GetAttribute(XmlConstants.AtomTypeAttributeName);
                bool   isFeed;

                if (!IsAllowedLinkType(propertyValueText, out isFeed))
                {
                    return;
                }

                if (!ReadChildElement(this.reader, XmlConstants.AtomInlineElementName, XmlConstants.DataWebMetadataNamespace))
                {
                    return;
                }

                bool   emptyInlineCollection = this.reader.IsEmptyElement;
                object propertyValue         = null;

                if (!emptyInlineCollection)
                {
                    AtomFeed         nestedFeed  = null;
                    AtomEntry        nestedEntry = null;
                    List <AtomEntry> feedEntries = null;

                    Debug.Assert(this.reader is Xml.XmlWrappingReader, "reader must be a instance of XmlWrappingReader");
                    string    readerBaseUri = this.reader.BaseURI;
                    XmlReader nestedReader  = Xml.XmlWrappingReader.CreateReader(readerBaseUri, this.reader.ReadSubtree());
                    nestedReader.Read();
                    Debug.Assert(nestedReader.LocalName == "inline", "nestedReader.LocalName == 'inline'");

                    AtomParser nested = new AtomParser(nestedReader, this.entryCallback, this.typeScheme, this.currentDataNamespace);
                    while (nested.Read())
                    {
                        switch (nested.DataKind)
                        {
                        case AtomDataKind.Feed:
                            feedEntries   = new List <AtomEntry>();
                            nestedFeed    = nested.CurrentFeed;
                            propertyValue = nestedFeed;
                            break;

                        case AtomDataKind.Entry:
                            nestedEntry = nested.CurrentEntry;
                            if (feedEntries != null)
                            {
                                feedEntries.Add(nestedEntry);
                            }
                            else
                            {
                                propertyValue = nestedEntry;
                            }

                            break;

                        case AtomDataKind.PagingLinks:
                            break;

                        default:
                            throw new InvalidOperationException(Strings.AtomParser_UnexpectedContentUnderExpandedLink);
                        }
                    }

                    if (nestedFeed != null)
                    {
                        Debug.Assert(
                            nestedFeed.Entries == null,
                            "nestedFeed.Entries == null -- otherwise someone initialized this for us");
                        nestedFeed.Entries = feedEntries;
                    }
                }

                AtomContentProperty property = new AtomContentProperty();
                property.Name = propertyName;

                if (emptyInlineCollection || propertyValue == null)
                {
                    property.IsNull = true;
                    if (isFeed)
                    {
                        property.Feed         = new AtomFeed();
                        property.Feed.Entries = Enumerable.Empty <AtomEntry>();
                    }
                    else
                    {
                        property.Entry        = new AtomEntry();
                        property.Entry.IsNull = true;
                    }
                }
                else
                {
                    property.Feed  = propertyValue as AtomFeed;
                    property.Entry = propertyValue as AtomEntry;
                }

                targetEntry.DataValues.Add(property);
            }
        }
Esempio n. 3
0
        internal bool Read()
        {
            if (this.DataKind == AtomDataKind.Finished)
            {
                return(false);
            }

            while (this.reader.Read())
            {
                if (ShouldIgnoreNode(this.reader))
                {
                    continue;
                }

                Debug.Assert(
                    this.reader.NodeType == XmlNodeType.Element || this.reader.NodeType == XmlNodeType.EndElement,
                    "this.reader.NodeType == XmlNodeType.Element || this.reader.NodeType == XmlNodeType.EndElement -- otherwise we should have ignored or thrown");

                AtomDataKind readerData = ParseStateForReader(this.reader);

                if (this.reader.NodeType == XmlNodeType.EndElement)
                {
                    break;
                }

                switch (readerData)
                {
                case AtomDataKind.Custom:
                    if (this.DataKind == AtomDataKind.None)
                    {
                        this.kind = AtomDataKind.Custom;
                        return(true);
                    }
                    else
                    {
                        MaterializeAtom.SkipToEnd(this.reader);
                        continue;
                    }

                case AtomDataKind.Entry:
                    this.kind = AtomDataKind.Entry;
                    this.ParseCurrentEntry(out this.entry);
                    return(true);

                case AtomDataKind.Feed:
                    if (this.DataKind == AtomDataKind.None)
                    {
                        this.feed = new AtomFeed();
                        this.kind = AtomDataKind.Feed;
                        return(true);
                    }

                    throw new InvalidOperationException(Strings.AtomParser_FeedUnexpected);

                case AtomDataKind.FeedCount:
                    this.ParseCurrentFeedCount();
                    break;

                case AtomDataKind.PagingLinks:
                    if (this.feed == null)
                    {
                        throw new InvalidOperationException(Strings.AtomParser_PagingLinkOutsideOfFeed);
                    }

                    this.kind = AtomDataKind.PagingLinks;
                    this.ParseCurrentFeedPagingLinks();
                    return(true);

                default:
                    Debug.Assert(false, "Atom Parser is in a wrong state...Did you add a new AtomDataKind?");
                    break;
                }
            }

            this.kind  = AtomDataKind.Finished;
            this.entry = null;
            return(false);
        }
        /// <summary>Applies all accumulated changes to the associated data context.</summary>
        /// <remarks>The log should be cleared after this method successfully executed.</remarks>
        internal void ApplyToContext()
        {
            Debug.Assert(
                this.mergeOption != MergeOption.OverwriteChanges || this.foundEntriesWithMedia.Count == 0,
                "mergeOption != MergeOption.OverwriteChanges || foundEntriesWithMedia.Count == 0 - we only use the 'entries-with-media' lookaside when we're not in overwrite mode, otherwise we track everything through identity stack");

            if (!this.Tracking)
            {
                return;
            }

            foreach (KeyValuePair <String, AtomEntry> entity in this.identityStack)
            {
                AtomEntry entry = entity.Value;
                if (entry.CreatedByMaterializer ||
                    entry.ResolvedObject == this.insertRefreshObject ||
                    entry.ShouldUpdateFromPayload)
                {
                    // Create a new descriptor and try to attach, if one already exists, get the existing reference instead.
                    EntityDescriptor descriptor = new EntityDescriptor(entity.Key, entry.QueryLink, entry.EditLink, entry.ResolvedObject, null, null, null, entry.ETagText, EntityStates.Unchanged);
                    descriptor = this.context.InternalAttachEntityDescriptor(descriptor, false);

                    // we should always reset descriptor's state to Unchanged (old v1 behaviour)
                    descriptor.State = EntityStates.Unchanged;

                    this.ApplyMediaEntryInformation(entry, descriptor);
                    descriptor.ServerTypeName = entry.TypeName;
                }
                else
                {
                    // Refresh the entity state indirectly by calling TryGetEntity.
                    EntityStates state;
                    this.context.TryGetEntity(entity.Key, entry.ETagText, this.mergeOption, out state);
                }
            }

            // Regardless of the merge mode, media link information should
            // always be applied to the context.
            foreach (AtomEntry entry in this.foundEntriesWithMedia.Values)
            {
                Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise it wasn't found");
                EntityDescriptor descriptor = this.context.GetEntityDescriptor(entry.ResolvedObject);
                this.ApplyMediaEntryInformation(entry, descriptor);
            }

            foreach (LinkDescriptor link in this.links)
            {
                if (EntityStates.Added == link.State)
                {
                    // Added implies collection
                    if ((EntityStates.Deleted == this.context.GetEntityDescriptor(link.Target).State) ||
                        (EntityStates.Deleted == this.context.GetEntityDescriptor(link.Source).State))
                    {
                        this.context.DeleteLink(link.Source, link.SourceProperty, link.Target);
                    }
                    else
                    {
                        this.context.AttachLink(link.Source, link.SourceProperty, link.Target, this.mergeOption);
                    }
                }
                else if (EntityStates.Modified == link.State)
                {
                    // Modified implies reference
                    object target = link.Target;
                    if (MergeOption.PreserveChanges == this.mergeOption)
                    {
                        LinkDescriptor end = this.context.GetLinks(link.Source, link.SourceProperty).FirstOrDefault();
                        if (null != end && null == end.Target)
                        {
                            // leave the SetLink(link.Source, link.SourceProperty, null)
                            continue;
                        }

                        if ((null != target) && (EntityStates.Deleted == this.context.GetEntityDescriptor(target).State) ||
                            (EntityStates.Deleted == this.context.GetEntityDescriptor(link.Source).State))
                        {
                            target = null;
                        }
                    }

                    this.context.AttachLink(link.Source, link.SourceProperty, target, this.mergeOption);
                }
                else
                {
                    // detach link
                    Debug.Assert(EntityStates.Detached == link.State, "not detached link");
                    this.context.DetachLink(link.Source, link.SourceProperty, link.Target);
                }
            }
        }
Esempio n. 5
0
        /// <summary>Consumes the next chunk of content from the underlying XML reader.</summary>
        /// <returns>
        /// true if another piece of content is available, identified by DataKind.
        /// false if there is no more content.
        /// </returns>
        internal bool Read()
        {
            // When an external caller 'insists', we'll come all the way down (which is the 'most local'
            // scope at which this is known), and unwind as a no-op.
            if (this.DataKind == AtomDataKind.Finished)
            {
                return(false);
            }

            while (this.reader.Read())
            {
                if (ShouldIgnoreNode(this.reader))
                {
                    continue;
                }

                Debug.Assert(
                    this.reader.NodeType == XmlNodeType.Element || this.reader.NodeType == XmlNodeType.EndElement,
                    "this.reader.NodeType == XmlNodeType.Element || this.reader.NodeType == XmlNodeType.EndElement -- otherwise we should have ignored or thrown");

                AtomDataKind readerData = ParseStateForReader(this.reader);

                if (this.reader.NodeType == XmlNodeType.EndElement)
                {
                    // The only case in which we expect to see an end-element at the top level
                    // is for a feed. Custom elements and entries should be consumed by
                    // their own parsing methods. However we are tolerant of additional EndElements,
                    // which at this point mean we have nothing else to consume.
                    break;
                }

                switch (readerData)
                {
                case AtomDataKind.Custom:
                    if (this.DataKind == AtomDataKind.None)
                    {
                        this.kind = AtomDataKind.Custom;
                        return(true);
                    }
                    else
                    {
                        MaterializeAtom.SkipToEnd(this.reader);
                        continue;
                    }

                case AtomDataKind.Entry:
                    this.kind = AtomDataKind.Entry;
                    this.ParseCurrentEntry(out this.entry);
                    return(true);

                case AtomDataKind.Feed:
                    if (this.DataKind == AtomDataKind.None)
                    {
                        this.feed = new AtomFeed();
                        this.kind = AtomDataKind.Feed;
                        return(true);
                    }

                    throw new InvalidOperationException(Strings.AtomParser_FeedUnexpected);

                case AtomDataKind.FeedCount:
                    this.ParseCurrentFeedCount();
                    break;

                case AtomDataKind.PagingLinks:
                    if (this.feed == null)
                    {
                        // paging link outside of feed?
                        throw new InvalidOperationException(Strings.AtomParser_PagingLinkOutsideOfFeed);
                    }

                    this.kind = AtomDataKind.PagingLinks;
                    this.ParseCurrentFeedPagingLinks();
                    return(true);

                default:
                    Debug.Assert(false, "Atom Parser is in a wrong state...Did you add a new AtomDataKind?");
                    break;
                }
            }

            this.kind  = AtomDataKind.Finished;
            this.entry = null;
            return(false);
        }
Esempio n. 6
0
        /// <summary>
        /// Parses the current reader into a new <paramref name="targetEntry"/> 
        /// instance.
        /// </summary>
        /// <param name="targetEntry">
        /// After invocation, the target entry that was created as a result
        /// of parsing the current reader.
        /// </param>
        private void ParseCurrentEntry(out AtomEntry targetEntry)
        {
            Debug.Assert(this.reader.NodeType == XmlNodeType.Element, "this.reader.NodeType == XmlNodeType.Element");

            // Push reader.
            var callbackResult = this.entryCallback(this.reader);
            Debug.Assert(callbackResult.Key != null, "callbackResult.Key != null");
            this.readers.Push(this.reader);
            this.reader = callbackResult.Key;

            this.reader.Read();
            Debug.Assert(this.reader.LocalName == "entry", "this.reader.LocalName == 'entry' - otherwise we're not reading the subtree");

            bool hasContent = false;
            targetEntry = new AtomEntry();
            targetEntry.DataValues = new List<AtomContentProperty>();
            targetEntry.Tag = callbackResult.Value;
            targetEntry.ETagText = this.reader.GetAttribute(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace);

            while (this.reader.Read())
            {
                if (ShouldIgnoreNode(this.reader))
                {
                    continue;
                }

                if (this.reader.NodeType == XmlNodeType.Element)
                {
                    int depth = this.reader.Depth;
                    string elementName = this.reader.LocalName;
                    string namespaceURI = this.reader.NamespaceURI;
                    if (namespaceURI == XmlConstants.AtomNamespace)
                    {
                        if (elementName == XmlConstants.AtomCategoryElementName && targetEntry.TypeName == null)
                        {
                            string text = this.reader.GetAttributeEx(XmlConstants.AtomCategorySchemeAttributeName, XmlConstants.AtomNamespace);
                            if (text == this.typeScheme)
                            {
                                targetEntry.TypeName = this.reader.GetAttributeEx(XmlConstants.AtomCategoryTermAttributeName, XmlConstants.AtomNamespace);
                            }
                        }
                        else if (elementName == XmlConstants.AtomContentElementName)
                        {
                            hasContent = true;
                            this.ParseCurrentContent(targetEntry);
                        }
                        else if (elementName == XmlConstants.AtomIdElementName && targetEntry.Identity == null)
                        {
                            // The .Identity == null check ensures that only the first id element is processed.
                            string idText = ReadElementStringForText(this.reader);
                            idText = Util.ReferenceIdentity(idText);

                            // here we could just assign idText to Identity
                            // however we used to check for AbsoluteUri, thus we need to 
                            // convert string to Uri and check for absoluteness
                            Uri idUri = Util.CreateUri(idText, UriKind.RelativeOrAbsolute);
                            if (!idUri.IsAbsoluteUri)
                            {
                                throw Error.InvalidOperation(Strings.Context_TrackingExpectsAbsoluteUri);
                            }

                            targetEntry.Identity = idText;
                        }
                        else if (elementName == XmlConstants.AtomLinkElementName)
                        {
                            this.ParseCurrentLink(targetEntry);
                        }
                    }
                    else if (namespaceURI == XmlConstants.DataWebMetadataNamespace)
                    {
                        if (elementName == XmlConstants.AtomPropertiesElementName)
                        {
                            if (targetEntry.MediaLinkEntry.HasValue && !targetEntry.MediaLinkEntry.Value)
                            {
                                // This means we saw a non-empty <atom:Content> element but now we have a Properties element
                                // that also carries properties
                                throw Error.InvalidOperation(Strings.Deserialize_ContentPlusPropertiesNotAllowed);
                            }

                            targetEntry.MediaLinkEntry = true;

                            if (!this.reader.IsEmptyElement)
                            {
                                this.ReadCurrentProperties(targetEntry.DataValues);
                            }
                        }
                    }

                    SkipToEndAtDepth(this.reader, depth);
                }
            }

            if (targetEntry.Identity == null)
            {
                throw Error.InvalidOperation(Strings.Deserialize_MissingIdElement);
            }

            if (!hasContent)
            {
                // Content is expected for the GetResponse operation
                throw Error.BatchStreamContentExpected(BatchStreamState.GetResponse);
            }

            this.reader = this.readers.Pop();
        }
Esempio n. 7
0
        /// <summary>
        /// Parses the content on the reader into the specified <paramref name="targetEntry"/>.
        /// </summary>
        /// <param name="targetEntry">Target to read values into.</param>
        private void ParseCurrentContent(AtomEntry targetEntry)
        {
            Debug.Assert(targetEntry != null, "targetEntry != null");
            Debug.Assert(this.reader.NodeType == XmlNodeType.Element, "this.reader.NodeType == XmlNodeType.Element");

            string propertyValue = this.reader.GetAttributeEx(XmlConstants.AtomContentSrcAttributeName, XmlConstants.AtomNamespace);
            if (propertyValue != null)
            {
                // This is a media link entry
                // Note that in this case we don't actually use this URL (or the link/edit-media URL)
                // for editing. We rely on the Astoria URL convention (/propname/$value or just /$value)
                if (!this.reader.IsEmptyElement)
                {
                    throw Error.InvalidOperation(Strings.Deserialize_ExpectedEmptyMediaLinkEntryContent);
                }

                targetEntry.MediaLinkEntry = true;
                targetEntry.MediaContentUri = new Uri(propertyValue, UriKind.RelativeOrAbsolute);
            }
            else
            {
                // This is a regular (non-media link) entry
                if (targetEntry.MediaLinkEntry.HasValue && targetEntry.MediaLinkEntry.Value)
                {
                    // This means we saw a <m:Properties> element but now we have a Content element
                    // that's not just a media link entry pointer (src)
                    throw Error.InvalidOperation(Strings.Deserialize_ContentPlusPropertiesNotAllowed);
                }

                targetEntry.MediaLinkEntry = false;

                propertyValue = this.reader.GetAttributeEx(XmlConstants.AtomTypeAttributeName, XmlConstants.AtomNamespace);
                if (AtomParser.IsAllowedContentType(propertyValue))
                {
                    if (this.reader.IsEmptyElement)
                    {
                        return;
                    }

                    if (ReadChildElement(this.reader, XmlConstants.AtomPropertiesElementName, XmlConstants.DataWebMetadataNamespace))
                    {
                        this.ReadCurrentProperties(targetEntry.DataValues);
                    }
                    else if (this.reader.NodeType != XmlNodeType.EndElement)
                    {
                        throw Error.InvalidOperation(Strings.Deserialize_NotApplicationXml);
                    }
                }
            }
        }
Esempio n. 8
0
        private void ParseCurrentContent(AtomEntry targetEntry)
        {
            Debug.Assert(targetEntry != null, "targetEntry != null");
            Debug.Assert(this.reader.NodeType == XmlNodeType.Element, "this.reader.NodeType == XmlNodeType.Element");
            
            string propertyValue = this.reader.GetAttributeEx(XmlConstants.AtomContentSrcAttributeName, XmlConstants.AtomNamespace);
            if (propertyValue != null)
            {
                if (!this.reader.IsEmptyElement)
                {
                    throw Error.InvalidOperation(Strings.Deserialize_ExpectedEmptyMediaLinkEntryContent);
                }

                targetEntry.MediaLinkEntry = true;
                targetEntry.MediaContentUri = new Uri(propertyValue, UriKind.RelativeOrAbsolute);
            }
            else
            {
                if (targetEntry.MediaLinkEntry.HasValue && targetEntry.MediaLinkEntry.Value)
                {
                    throw Error.InvalidOperation(Strings.Deserialize_ContentPlusPropertiesNotAllowed);
                }

                targetEntry.MediaLinkEntry = false;

                propertyValue = this.reader.GetAttributeEx(XmlConstants.AtomTypeAttributeName, XmlConstants.AtomNamespace);
                if (AtomParser.IsAllowedContentType(propertyValue))
                {
                    if (this.reader.IsEmptyElement)
                    {
                        return;
                    }

                    if (ReadChildElement(this.reader, XmlConstants.AtomPropertiesElementName, XmlConstants.DataWebMetadataNamespace))
                    {
                        this.ReadCurrentProperties(targetEntry.DataValues);
                    }
                    else
                    if (this.reader.NodeType != XmlNodeType.EndElement)
                    {
                        throw Error.InvalidOperation(Strings.Deserialize_NotApplicationXml);
                    }
                }
            }
        }
Esempio n. 9
0
        /// <summary>
        /// Invoke this method to notify the log that a new instance
        /// was created.
        /// </summary>
        /// <param name="entry">Entry for the created instance.</param>
        internal void CreatedInstance(AtomEntry entry)
        {
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise, what did we create?");
            Debug.Assert(entry.CreatedByMaterializer, "entry.CreatedByMaterializer -- otherwise we shouldn't be calling this");

            if (ShouldTrackWithContext(entry))
            {
                this.identityStack.Add(entry.Identity, entry);
                if (this.mergeOption == MergeOption.AppendOnly)
                {
                    this.appendOnlyEntries.Add(entry.Identity, entry);
                }
            }
        }
Esempio n. 10
0
        /// <summary>
        /// Invoke this method to notify the log that a new link was
        /// added to a collection.
        /// </summary>
        /// <param name="source">
        /// Instance with the collection to which <paramref name="target"/> 
        /// was added.
        /// </param>
        /// <param name="propertyName">Property name for collection.</param>
        /// <param name="target">Object which was added.</param>
        internal void AddedLink(AtomEntry source, string propertyName, object target)
        {
            Debug.Assert(source != null, "source != null");
            Debug.Assert(propertyName != null, "propertyName != null");

            if (!this.Tracking)
            {
                return;
            }

            if (ShouldTrackWithContext(source) && ShouldTrackWithContext(target))
            {
                LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Added);
                this.links.Add(item);
            }
        }
Esempio n. 11
0
        /// <summary>Attempts to resolve an entry from those tracked in the log.</summary>
        /// <param name="entry">Entry to resolve.</param>
        /// <param name="existingEntry">
        /// After invocation, an existing entry with the same identity as 
        /// <paramref name="entry"/>; possibly null.
        /// </param>
        /// <returns>true if an existing entry was found; false otherwise.</returns>
        internal bool TryResolve(AtomEntry entry, out AtomEntry existingEntry)
        {
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(entry.Identity != null, "entry.Identity != null");

            if (this.identityStack.TryGetValue(entry.Identity, out existingEntry))
            {
                return true;
            }

            if (this.appendOnlyEntries.TryGetValue(entry.Identity, out existingEntry))
            {
                // The AppendOnly entries are valid only as long as they were not modified
                // between calls to .MoveNext().
                EntityStates state;
                this.context.TryGetEntity(entry.Identity, entry.ETagText, this.mergeOption, out state);
                if (state == EntityStates.Unchanged)
                {
                    return true;
                }
                else
                {
                    this.appendOnlyEntries.Remove(entry.Identity);
                }
            }

            existingEntry = null;
            return false;
        }
Esempio n. 12
0
        /// <summary>
        /// Invoke this method to notify the log that the
        /// target instance of a "directed" update was found.
        /// </summary>
        /// <param name="entry">Entry found.</param>
        /// <remarks>
        /// The target instance is typically the object that we
        /// expect will get refreshed by the response from a POST
        /// method.
        /// 
        /// For example if a create a Customer and POST it to
        /// a service, the response of the POST will return the
        /// re-serialized instance, with (important!) server generated
        /// values and URIs.
        /// </remarks>
        internal void FoundTargetInstance(AtomEntry entry)
        {
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise this is not a target");

            if (ShouldTrackWithContext(entry))
            {
                this.context.AttachIdentity(entry.Identity, entry.QueryLink, entry.EditLink, entry.ResolvedObject, entry.ETagText);
                this.identityStack.Add(entry.Identity, entry);
                this.insertRefreshObject = entry.ResolvedObject;
            }
        }
Esempio n. 13
0
        /// <summary>
        /// Invoke this method to notify the log that an existing
        /// instance was found while resolving an object.
        /// </summary>
        /// <param name="entry">Entry for instance.</param>
        internal void FoundExistingInstance(AtomEntry entry)
        {
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(ShouldTrackWithContext(entry), "Existing entries should be entity");

            if (this.mergeOption == MergeOption.OverwriteChanges)
            {
                this.identityStack[entry.Identity] = entry;
            }
            else if (this.Tracking && entry.MediaLinkEntry == true)
            {
                this.foundEntriesWithMedia[entry.Identity] = entry;
            }
        }
Esempio n. 14
0
        internal void ApplyToContext()
        {
            Debug.Assert(
                this.mergeOption != MergeOption.OverwriteChanges || this.foundEntriesWithMedia.Count == 0,
                "mergeOption != MergeOption.OverwriteChanges || foundEntriesWithMedia.Count == 0 - we only use the 'entries-with-media' lookaside when we're not in overwrite mode, otherwise we track everything through identity stack");

            if (!this.Tracking)
            {
                return;
            }

            foreach (KeyValuePair <String, AtomEntry> entity in this.identityStack)
            {
                AtomEntry entry = entity.Value;
                if (entry.CreatedByMaterializer ||
                    entry.ResolvedObject == this.insertRefreshObject ||
                    entry.ShouldUpdateFromPayload)
                {
                    EntityDescriptor descriptor = new EntityDescriptor(entity.Key, entry.QueryLink, entry.EditLink, entry.ResolvedObject, null, null, null, entry.ETagText, EntityStates.Unchanged);
                    descriptor = this.context.InternalAttachEntityDescriptor(descriptor, false);

                    descriptor.State = EntityStates.Unchanged;

                    this.ApplyMediaEntryInformation(entry, descriptor);
                    descriptor.ServerTypeName = entry.TypeName;
                }
                else
                {
                    EntityStates state;
                    this.context.TryGetEntity(entity.Key, entry.ETagText, this.mergeOption, out state);
                }
            }

            foreach (AtomEntry entry in this.foundEntriesWithMedia.Values)
            {
                Debug.Assert(entry.ResolvedObject != null, "entry.ResolvedObject != null -- otherwise it wasn't found");
                EntityDescriptor descriptor = this.context.GetEntityDescriptor(entry.ResolvedObject);
                this.ApplyMediaEntryInformation(entry, descriptor);
            }

            foreach (LinkDescriptor link in this.links)
            {
                if (EntityStates.Added == link.State)
                {
                    if ((EntityStates.Deleted == this.context.GetEntityDescriptor(link.Target).State) ||
                        (EntityStates.Deleted == this.context.GetEntityDescriptor(link.Source).State))
                    {
                        this.context.DeleteLink(link.Source, link.SourceProperty, link.Target);
                    }
                    else
                    {
                        this.context.AttachLink(link.Source, link.SourceProperty, link.Target, this.mergeOption);
                    }
                }
                else if (EntityStates.Modified == link.State)
                {
                    object target = link.Target;
                    if (MergeOption.PreserveChanges == this.mergeOption)
                    {
                        LinkDescriptor end = this.context.GetLinks(link.Source, link.SourceProperty).FirstOrDefault();
                        if (null != end && null == end.Target)
                        {
                            continue;
                        }

                        if ((null != target) && (EntityStates.Deleted == this.context.GetEntityDescriptor(target).State) ||
                            (EntityStates.Deleted == this.context.GetEntityDescriptor(link.Source).State))
                        {
                            target = null;
                        }
                    }

                    this.context.AttachLink(link.Source, link.SourceProperty, target, this.mergeOption);
                }
                else
                {
                    Debug.Assert(EntityStates.Detached == link.State, "not detached link");
                    this.context.DetachLink(link.Source, link.SourceProperty, link.Target);
                }
            }
        }
Esempio n. 15
0
        private void ParseCurrentEntry(out AtomEntry targetEntry)
        {
            Debug.Assert(this.reader.NodeType == XmlNodeType.Element, "this.reader.NodeType == XmlNodeType.Element");

            var callbackResult = this.entryCallback(this.reader);

            Debug.Assert(callbackResult.Key != null, "callbackResult.Key != null");
            this.readers.Push(this.reader);
            this.reader = callbackResult.Key;

            this.reader.Read();
            Debug.Assert(this.reader.LocalName == "entry", "this.reader.LocalName == 'entry' - otherwise we're not reading the subtree");

            bool hasContent = false;

            targetEntry            = new AtomEntry();
            targetEntry.DataValues = new List <AtomContentProperty>();
            targetEntry.Tag        = callbackResult.Value;
            targetEntry.ETagText   = this.reader.GetAttribute(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace);

            while (this.reader.Read())
            {
                if (ShouldIgnoreNode(this.reader))
                {
                    continue;
                }

                if (this.reader.NodeType == XmlNodeType.Element)
                {
                    int    depth        = this.reader.Depth;
                    string elementName  = this.reader.LocalName;
                    string namespaceURI = this.reader.NamespaceURI;
                    if (namespaceURI == XmlConstants.AtomNamespace)
                    {
                        if (elementName == XmlConstants.AtomCategoryElementName && targetEntry.TypeName == null)
                        {
                            string text = this.reader.GetAttributeEx(XmlConstants.AtomCategorySchemeAttributeName, XmlConstants.AtomNamespace);
                            if (text == this.typeScheme)
                            {
                                targetEntry.TypeName = this.reader.GetAttributeEx(XmlConstants.AtomCategoryTermAttributeName, XmlConstants.AtomNamespace);
                            }
                        }
                        else if (elementName == XmlConstants.AtomContentElementName)
                        {
                            hasContent = true;
                            this.ParseCurrentContent(targetEntry);
                        }
                        else if (elementName == XmlConstants.AtomIdElementName && targetEntry.Identity == null)
                        {
                            string idText = ReadElementStringForText(this.reader);
                            idText = Util.ReferenceIdentity(idText);

                            Uri idUri = Util.CreateUri(idText, UriKind.RelativeOrAbsolute);
                            if (!idUri.IsAbsoluteUri)
                            {
                                throw Error.InvalidOperation(Strings.Context_TrackingExpectsAbsoluteUri);
                            }

                            targetEntry.Identity = idText;
                        }
                        else if (elementName == XmlConstants.AtomLinkElementName)
                        {
                            this.ParseCurrentLink(targetEntry);
                        }
                    }
                    else if (namespaceURI == XmlConstants.DataWebMetadataNamespace)
                    {
                        if (elementName == XmlConstants.AtomPropertiesElementName)
                        {
                            if (targetEntry.MediaLinkEntry.HasValue && !targetEntry.MediaLinkEntry.Value)
                            {
                                throw Error.InvalidOperation(Strings.Deserialize_ContentPlusPropertiesNotAllowed);
                            }

                            targetEntry.MediaLinkEntry = true;

                            if (!this.reader.IsEmptyElement)
                            {
                                this.ReadCurrentProperties(targetEntry.DataValues);
                            }
                        }
                    }

                    SkipToEndAtDepth(this.reader, depth);
                }
            }

            if (targetEntry.Identity == null)
            {
                throw Error.InvalidOperation(Strings.Deserialize_MissingIdElement);
            }

            if (!hasContent)
            {
                throw Error.BatchStreamContentExpected(BatchStreamState.GetResponse);
            }

            this.reader = this.readers.Pop();
        }
Esempio n. 16
0
        internal bool Read()
        {
            if (this.DataKind == AtomDataKind.Finished)
            {
                return false;
            }

            while (this.reader.Read())
            {
                if (ShouldIgnoreNode(this.reader))
                {
                    continue;
                }

                Debug.Assert(
                    this.reader.NodeType == XmlNodeType.Element || this.reader.NodeType == XmlNodeType.EndElement,
                    "this.reader.NodeType == XmlNodeType.Element || this.reader.NodeType == XmlNodeType.EndElement -- otherwise we should have ignored or thrown");

                AtomDataKind readerData = ParseStateForReader(this.reader);

                if (this.reader.NodeType == XmlNodeType.EndElement)
                {
                    break;
                }

                switch (readerData)
                {
                    case AtomDataKind.Custom:
                        if (this.DataKind == AtomDataKind.None)
                        {
                            this.kind = AtomDataKind.Custom;
                            return true;
                        }
                        else
                        {
                            MaterializeAtom.SkipToEnd(this.reader);
                            continue;
                        }

                    case AtomDataKind.Entry:
                        this.kind = AtomDataKind.Entry;
                        this.ParseCurrentEntry(out this.entry);
                        return true;

                    case AtomDataKind.Feed:
                        if (this.DataKind == AtomDataKind.None)
                        {
                            this.feed = new AtomFeed();
                            this.kind = AtomDataKind.Feed;
                            return true;
                        }

                        throw new InvalidOperationException(Strings.AtomParser_FeedUnexpected);

                    case AtomDataKind.FeedCount:
                        this.ParseCurrentFeedCount();
                        break;

                    case AtomDataKind.PagingLinks:
                        if (this.feed == null)
                        {
                            throw new InvalidOperationException(Strings.AtomParser_PagingLinkOutsideOfFeed);
                        }

                        this.kind = AtomDataKind.PagingLinks;
                        this.ParseCurrentFeedPagingLinks();
                        return true;

                    default:
                        Debug.Assert(false, "Atom Parser is in a wrong state...Did you add a new AtomDataKind?");
                        break;
                }
            }

            this.kind = AtomDataKind.Finished;
            this.entry = null;            
            return false;
        }
Esempio n. 17
0
        /// <summary>
        /// Invoke this method to notify the log that a link was set on
        /// a property.
        /// </summary>
        /// <param name="source">Entry for source object.</param>
        /// <param name="propertyName">Name of property set.</param>
        /// <param name="target">Target object.</param>
        internal void SetLink(AtomEntry source, string propertyName, object target)
        {
            Debug.Assert(source != null, "source != null");
            Debug.Assert(propertyName != null, "propertyName != null");

            if (!this.Tracking)
            {
                return;
            }

            if (ShouldTrackWithContext(source) && ShouldTrackWithContext(target))
            {
                Debug.Assert(this.Tracking, "this.Tracking -- otherwise there's an 'if' missing (it happens to be that the assert holds for all current callers");
                LinkDescriptor item = new LinkDescriptor(source.ResolvedObject, propertyName, target, EntityStates.Modified);
                this.links.Add(item);
            }
        }
Esempio n. 18
0
        /// <summary>Consumes the next chunk of content from the underlying XML reader.</summary>
        /// <returns>
        /// true if another piece of content is available, identified by DataKind.
        /// false if there is no more content.
        /// </returns>
        internal bool Read()
        {
            // When an external caller 'insists', we'll come all the way down (which is the 'most local'
            // scope at which this is known), and unwind as a no-op.
            if (this.DataKind == AtomDataKind.Finished)
            {
                return false;
            }

            while (this.reader.Read())
            {
                if (ShouldIgnoreNode(this.reader))
                {
                    continue;
                }

                Debug.Assert(
                    this.reader.NodeType == XmlNodeType.Element || this.reader.NodeType == XmlNodeType.EndElement,
                    "this.reader.NodeType == XmlNodeType.Element || this.reader.NodeType == XmlNodeType.EndElement -- otherwise we should have ignored or thrown");

                AtomDataKind readerData = ParseStateForReader(this.reader);

                if (this.reader.NodeType == XmlNodeType.EndElement)
                {
                    // The only case in which we expect to see an end-element at the top level
                    // is for a feed. Custom elements and entries should be consumed by
                    // their own parsing methods. However we are tolerant of additional EndElements,
                    // which at this point mean we have nothing else to consume.
                    break;
                }

                switch (readerData)
                {
                    case AtomDataKind.Custom:
                        if (this.DataKind == AtomDataKind.None)
                        {
                            this.kind = AtomDataKind.Custom;
                            return true;
                        }
                        else
                        {
                            MaterializeAtom.SkipToEnd(this.reader);
                            continue;
                        }

                    case AtomDataKind.Entry:
                        this.kind = AtomDataKind.Entry;
                        this.ParseCurrentEntry(out this.entry);
                        return true;

                    case AtomDataKind.Feed:
                        if (this.DataKind == AtomDataKind.None)
                        {
                            this.feed = new AtomFeed();
                            this.kind = AtomDataKind.Feed;
                            return true;
                        }

                        throw new InvalidOperationException(Strings.AtomParser_FeedUnexpected);

                    case AtomDataKind.FeedCount:
                        this.ParseCurrentFeedCount();
                        break;

                    case AtomDataKind.PagingLinks:
                        if (this.feed == null)
                        {
                            // paging link outside of feed?
                            throw new InvalidOperationException(Strings.AtomParser_PagingLinkOutsideOfFeed);
                        }

                        this.kind = AtomDataKind.PagingLinks;
                        this.ParseCurrentFeedPagingLinks();
                        return true;

                    default:
                        Debug.Assert(false, "Atom Parser is in a wrong state...Did you add a new AtomDataKind?");
                        break;
                }
            }

            this.kind = AtomDataKind.Finished;
            this.entry = null;
            return false;
        }
Esempio n. 19
0
 /// <summary>
 /// Returns true if we should track this entry with context
 /// </summary>
 /// <param name="entry">The atom entry</param>
 /// <returns>true if entry should be tracked</returns>
 private static bool ShouldTrackWithContext(AtomEntry entry)
 {
     Debug.Assert(entry.ActualType != null, "Entry with no type added to log");
     return entry.ActualType.IsEntityType;
 }
Esempio n. 20
0
        private void ParseCurrentLink(AtomEntry targetEntry)
        {
            Debug.Assert(targetEntry != null, "targetEntry != null");
            Debug.Assert(
                this.reader.NodeType == XmlNodeType.Element, 
                "this.reader.NodeType == XmlNodeType.Element -- otherwise we shouldn't try to parse a link");
            Debug.Assert(
                this.reader.LocalName == "link",
                "this.reader.LocalName == 'link' -- otherwise we shouldn't try to parse a link");

            string relation = this.reader.GetAttribute(XmlConstants.AtomLinkRelationAttributeName);
            if (relation == null)
            {
                return;
            }

            if (relation == XmlConstants.AtomEditRelationAttributeValue && targetEntry.EditLink == null)
            {
                // Only process the first link that has @rel='edit'.
                string href = this.reader.GetAttribute(XmlConstants.AtomHRefAttributeName);
                if (String.IsNullOrEmpty(href))
                {
                    throw Error.InvalidOperation(Strings.Context_MissingEditLinkInResponseBody);
                }

                targetEntry.EditLink = this.ConvertHRefAttributeValueIntoURI(href);
            }
            else if (relation == XmlConstants.AtomSelfRelationAttributeValue && targetEntry.QueryLink == null)
            {
                // Only process the first link that has @rel='self'.
                string href = this.reader.GetAttribute(XmlConstants.AtomHRefAttributeName);
                if (String.IsNullOrEmpty(href))
                {
                    throw Error.InvalidOperation(Strings.Context_MissingSelfLinkInResponseBody);
                }

                targetEntry.QueryLink = this.ConvertHRefAttributeValueIntoURI(href);
            }
            else if (relation == XmlConstants.AtomEditMediaRelationAttributeValue && targetEntry.MediaEditUri == null)
            {
                string href = this.reader.GetAttribute(XmlConstants.AtomHRefAttributeName);
                if (String.IsNullOrEmpty(href))
                {
                    throw Error.InvalidOperation(Strings.Context_MissingEditMediaLinkInResponseBody);
                }

                targetEntry.MediaEditUri = this.ConvertHRefAttributeValueIntoURI(href);
                targetEntry.StreamETagText = this.reader.GetAttribute(XmlConstants.AtomETagAttributeName, XmlConstants.DataWebMetadataNamespace);
            }

            if (!this.reader.IsEmptyElement)
            {
                string propertyName = UriUtil.GetNameFromAtomLinkRelationAttribute(relation);
                if (propertyName == null)
                {
                    return;
                }

                string propertyValueText = this.reader.GetAttribute(XmlConstants.AtomTypeAttributeName);
                bool isFeed;

                if (!IsAllowedLinkType(propertyValueText, out isFeed))
                {
                    return;
                }

                if (!ReadChildElement(this.reader, XmlConstants.AtomInlineElementName, XmlConstants.DataWebMetadataNamespace))
                {
                    return;
                }

                bool emptyInlineCollection = this.reader.IsEmptyElement;
                object propertyValue = null;

                if (!emptyInlineCollection)
                {
                    AtomFeed nestedFeed = null;
                    AtomEntry nestedEntry = null;
                    List<AtomEntry> feedEntries = null;

                    Debug.Assert(this.reader is Xml.XmlWrappingReader, "reader must be a instance of XmlWrappingReader");
                    string readerBaseUri = this.reader.BaseURI;
                    XmlReader nestedReader = Xml.XmlWrappingReader.CreateReader(readerBaseUri, this.reader.ReadSubtree());
                    nestedReader.Read();
                    Debug.Assert(nestedReader.LocalName == "inline", "nestedReader.LocalName == 'inline'");

                    AtomParser nested = new AtomParser(nestedReader, this.entryCallback, this.typeScheme, this.currentDataNamespace);
                    while (nested.Read())
                    {
                        switch (nested.DataKind)
                        {
                            case AtomDataKind.Feed:
                                feedEntries = new List<AtomEntry>();
                                nestedFeed = nested.CurrentFeed;
                                propertyValue = nestedFeed;
                                break;
                            case AtomDataKind.Entry:
                                nestedEntry = nested.CurrentEntry;
                                if (feedEntries != null)
                                {
                                    feedEntries.Add(nestedEntry);
                                }
                                else
                                {
                                    propertyValue = nestedEntry;
                                }

                                break;
                            case AtomDataKind.PagingLinks:
                                // Here the inner feed parser found a paging link, and stored it on nestedFeed.NextPageLink
                                // we are going to add it into a link table and associate
                                // with the collection at AtomMaterializer::Materialize()
                                // Do nothing for now.
                                break;
                            default:
                                throw new InvalidOperationException(Strings.AtomParser_UnexpectedContentUnderExpandedLink);
                        }
                    }

                    if (nestedFeed != null)
                    {
                        Debug.Assert(
                            nestedFeed.Entries == null,
                            "nestedFeed.Entries == null -- otherwise someone initialized this for us");
                        nestedFeed.Entries = feedEntries;
                    }
                }

                AtomContentProperty property = new AtomContentProperty();
                property.Name = propertyName;

                if (emptyInlineCollection || propertyValue == null)
                {
                    property.IsNull = true;
                    if (isFeed)
                    {
                        property.Feed = new AtomFeed();
                        property.Feed.Entries = Enumerable.Empty<AtomEntry>();
                    }
                    else
                    {
                        property.Entry = new AtomEntry();
                        property.Entry.IsNull = true;
                    }
                }
                else
                {
                    property.Feed = propertyValue as AtomFeed;
                    property.Entry = propertyValue as AtomEntry;
                }

                targetEntry.DataValues.Add(property);
            }
        }
Esempio n. 21
0
        /// <summary>
        /// Applies the media entry information (if any) to the specified 
        /// <paramref name="descriptor"/>.
        /// </summary>
        /// <param name="entry">Entry with (potential) media entry information to apply.</param>
        /// <param name="descriptor">Descriptor to update.</param>
        private void ApplyMediaEntryInformation(AtomEntry entry, EntityDescriptor descriptor)
        {
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(descriptor != null, "descriptor != null");

            if (entry.MediaEditUri != null || entry.MediaContentUri != null)
            {
                // 

                if (entry.MediaEditUri != null)
                {
                    descriptor.EditStreamUri = new Uri(this.context.BaseUriWithSlash, entry.MediaEditUri);
                }

                if (entry.MediaContentUri != null)
                {
                    descriptor.ReadStreamUri = new Uri(this.context.BaseUriWithSlash, entry.MediaContentUri);
                }

                descriptor.StreamETag = entry.StreamETagText;
            }
        }
 /// <summary>
 /// Returns true if we should track this entry with context
 /// </summary>
 /// <param name="entry">The atom entry</param>
 /// <returns>true if entry should be tracked</returns>
 private static bool ShouldTrackWithContext(AtomEntry entry)
 {
     Debug.Assert(entry.ActualType != null, "Entry with no type added to log");
     return(entry.ActualType.IsEntityType);
 }
Esempio n. 23
0
        internal bool TryResolve(AtomEntry entry, out AtomEntry existingEntry)
        {
            Debug.Assert(entry != null, "entry != null");
            Debug.Assert(entry.Identity != null, "entry.Identity != null");

            if (this.identityStack.TryGetValue(entry.Identity, out existingEntry))
            {
                return true;
            }

            if (this.appendOnlyEntries.TryGetValue(entry.Identity, out existingEntry))
            {
                EntityStates state;
                this.context.TryGetEntity(entry.Identity, entry.ETagText, this.mergeOption, out state);
                if (state == EntityStates.Unchanged)
                {
                    return true;
                }
                else
                {
                    this.appendOnlyEntries.Remove(entry.Identity);
                }
            }

            existingEntry = null;
            return false;
        }