/// <summary>
        /// This creates a new DvMediaItem instance that refers
        /// to this instance, by simply specifying the intended ID.
        /// Generally, public programmers should use the
        /// <see cref="DvMediaContainer.AddReference"/> method
        /// to prevent object ID collisions.
        /// </summary>
        /// <returns>a new DvMediaItem instance that refers to this instance</returns>
        /// <exception cref="Error_PendingDeleteException">
        /// Thrown if this item is marked for deletion. Cannot create a reference
        /// to an item that is pending removal from the content hierarchy.
        /// </exception>
        public IDvItem CreateReference(string id)
        {
            lock (this.m_LockReferences)
            {
                if (this.m_Deleting == false)
                {
                    DvMediaItem newItem = new DvMediaItem();
                    newItem.m_ID      = id;
                    newItem.m_RefItem = this;

                    if (m_ReferringItems == null)
                    {
                        this.m_ReferringItems = new ArrayList(1);
                    }

                    //this.m_ReferringItems.Add(new WeakReference(newItem));
                    this.m_ReferringItems.Add(newItem);

                    // Set the remaining the base metadata by using values
                    // from the underlying item.
                    //
                    newItem.m_Restricted = this.m_Restricted;
                    newItem.SetClass(this.Class.ToString(), this.Class.FriendlyName);
                    newItem.Title = this.Title;

                    return(newItem);
                }
                else
                {
                    throw new Error_PendingDeleteException(this);
                }
            }
        }
        /// <summary>
        /// Creates a
        /// <see cref="DvMediaItem"/>
        /// object, given a metadata instantiation
        /// block.
        /// </summary>
        /// <param name="info">
        /// The metadata to use when instantiating the media.
        /// </param>
        /// <returns>a new media item</returns>
        public static DvMediaItem CreateItem(MediaBuilder.item info)
        {
            DvMediaItem newObj = new DvMediaItem();

            MediaBuilder.SetObjectProperties(newObj, info);
            newObj.TrackMetadataChanges = true;
            return(newObj);
        }
        public DvMediaReference AddDvMediaReference(DvMediaItem underlyingItem)
        {
            underlyingItem.LockReferenceList();

            DvMediaReference newItem = underlyingItem.CreateDvMediaReference();

            this.AddObject(newItem, false);

            underlyingItem.UnlockReferenceList();

            return(newItem);
        }
        private static void EnableMetadataTracking(IDvMedia dvm)
        {
            DvMediaContainer dvc = dvm as DvMediaContainer;
            DvMediaItem      dvi = dvm as DvMediaItem;

            if (dvc != null)
            {
                dvc.TrackMetadataChanges = true;
                foreach (IDvMedia child in dvc.CompleteList)
                {
                    EnableMetadataTracking(child);
                }
            }
            else if (dvi != null)
            {
                dvi.TrackMetadataChanges = true;
            }
        }
        /// <summary>
        /// This method should only be used when deserializing a media items.
        ///
        /// <para>
        /// A media item can point to another media item. The item doing the pointing
        /// is called the reference item. The item that is being pointed to is the
        /// underlying item.
        /// </para>
        ///
        /// <para>
        /// The standard binary serialization process destroys the object reference
        /// between a reference item and its underlying item. This method provides a means
        /// to restore the reference relationship.
        /// </para>
        /// </summary>
        /// <param name="underlyingItem">
        /// The media item that is being referred.
        /// </param>
        /// <param name="refItem">
        /// The media item that is doing the referring.
        /// </param>
        public static void AttachRefItem(DvMediaItem underlyingItem, DvMediaItem refItem)
        {
            underlyingItem.LockReferenceList();
            if (underlyingItem.m_ReferringItems == null)
            {
                underlyingItem.m_ReferringItems = new ArrayList();
            }
            //underlyingItem.m_ReferringItems.Add(new WeakReference(refItem));
            underlyingItem.m_ReferringItems.Add(refItem);

            // Just to avoid confusion - a refitem is actually an underlying item.
            // The naming convention follows the AV spec - the spec opted to use
            // "refID" notation instead of using "underlyingItem"...

            refItem.m_RefItem = underlyingItem;
            refItem.m_RefID   = "";
            underlyingItem.UnlockReferenceList();
        }
        /// <summary>
        /// This creates a new DvMediaItem instance that refers
        /// to this instance, by simply specifying the intended ID.
        /// Generally, public programmers should use the 
        /// <see cref="DvMediaContainer.AddReference"/> method
        /// to prevent object ID collisions.
        /// </summary>
        /// <returns>a new DvMediaItem instance that refers to this instance</returns>
        /// <exception cref="Error_PendingDeleteException">
        /// Thrown if this item is marked for deletion. Cannot create a reference
        /// to an item that is pending removal from the content hierarchy.
        /// </exception>
        public IDvItem CreateReference(string id)
        {
            lock(this.m_LockReferences)
            {
                if (this.m_Deleting == false)
                {
                    DvMediaItem newItem = new DvMediaItem();
                    newItem.m_ID = id;
                    newItem.m_RefItem = this;

                    if (m_ReferringItems == null)
                    {
                        this.m_ReferringItems = new ArrayList(1);
                    }

                    //this.m_ReferringItems.Add(new WeakReference(newItem));
                    this.m_ReferringItems.Add(newItem);

                    // Set the remaining the base metadata by using values
                    // from the underlying item.
                    //
                    newItem.m_Restricted = this.m_Restricted;
                    newItem.SetClass(this.Class.ToString(), this.Class.FriendlyName);
                    newItem.Title = this.Title;

                    return newItem;
                }
                else
                {
                    throw new Error_PendingDeleteException(this);
                }
            }
        }
        /// <summary>
        /// This method should only be used when deserializing a media items.
        /// 
        /// <para>
        /// A media item can point to another media item. The item doing the pointing
        /// is called the reference item. The item that is being pointed to is the
        /// underlying item.
        /// </para>
        /// 
        /// <para>
        /// The standard binary serialization process destroys the object reference
        /// between a reference item and its underlying item. This method provides a means
        /// to restore the reference relationship.
        /// </para>
        /// </summary>
        /// <param name="underlyingItem">
        /// The media item that is being referred.
        /// </param>
        /// <param name="refItem">
        /// The media item that is doing the referring.
        /// </param>
        public static void AttachRefItem(DvMediaItem underlyingItem, DvMediaItem refItem)
        {
            underlyingItem.LockReferenceList();
            if (underlyingItem.m_ReferringItems == null)
            {
                underlyingItem.m_ReferringItems = new ArrayList();
            }
            //underlyingItem.m_ReferringItems.Add(new WeakReference(refItem));
            underlyingItem.m_ReferringItems.Add(refItem);

            // Just to avoid confusion - a refitem is actually an underlying item.
            // The naming convention follows the AV spec - the spec opted to use
            // "refID" notation instead of using "underlyingItem"...

            refItem.m_RefItem = underlyingItem;
            refItem.m_RefID = "";
            underlyingItem.UnlockReferenceList();
        }
 public DvMediaReference(DvMediaItem underlyingItem)
 {
     m_uniqueId        = MediaBuilder.GetUniqueId();
     this.m_Underlying = underlyingItem;
 }
        /// <summary>
        /// Method executes when a control point invokes the ContentDirectory.UpdateObject action.
        /// The caller must specify the object to be modified, the old XML values to replace,
        /// and the new XML values to use.
        /// </summary>
        /// <param name="objectID">update media object with this ID</param>
        /// <param name="currentTagValue">
        /// Comma separated value list of current XML elements in the object.
        /// Empty value or double comma indicates that a new XML element is to be created.
        /// CDS spec defines that the text of the XML elements must match
        /// exactly with the text provided by the server. This means
        /// XML attributes have to be in the correct order within the XML element.
        /// XML elements need not be in any particular order though.. just the
        /// text between the commas separating things need to be that way.
        /// The order of XML elements correspond with the order of replacement
        /// XML elements.
        /// </param>
        /// <param name="newTagValue">
        /// Comma separated value list of new/replace XML elements in the object.
        /// Empty value or double comma indicates that an existing XML element is to be deleted.
        /// The order of XML elements correspond with the order of current
        /// XML elements.
        /// </param>
        private void SinkCd_UpdateObject(System.String objectID, System.String currentTagValue, System.String newTagValue)
        {
            try
            {
                // Ensure that the specified object actually exists
                IDvMedia obj = (IDvMedia) this.GetCdsEntry(objectID);

                if (obj == null)
                {
                    throw new Error_NoSuchObject("("+objectID+")");
                }
                else if (obj.IsReference)
                {
                    throw new UPnPCustomException(830, "This server will not allow UpdateObject() on a reference.");
                }

                // Set up text parsers.
                DText currentTagParser = new DText();
                DText newTagParser = new DText();

                currentTagParser.ATTRMARK = ",";
                newTagParser.ATTRMARK = ",";

                currentTagParser[0] = currentTagValue;
                newTagParser[0] = newTagValue;

                // ensure that the number of old xml values is equal
                // to the number of new values

                // BUG - Need to fix a case where the XML values include commas.

                int nFrags = currentTagParser.DCOUNT();
                int nFrags2 = newTagParser.DCOUNT();

                if (nFrags != nFrags2)
                {
                    throw new Error_ParameterMismatch("The number of tag/value pairs is not the same between currentTagValue and newTagValue.");
                }

                // determine the base URL using the IP address and port
                // where the request came in from and then
                // build an DIDL-Lite representation of the
                // object that needs updating

                //TODO: get all interfaces
                string baseUrl = this.GetBaseUrlByInterface();
                ArrayList properties = new ArrayList();
                ArrayList entries = new ArrayList();
                entries.Add(obj);

                string oldXml;
                try
                {
                    oldXml = BuildXmlRepresentation(baseUrl, properties, entries);
                }
                catch (Exception e)
                {
                    throw (e);
                }

                //  At this point oldXml represents the object we're trying to
                //  update, in an xml string format. Now go and replace (or add) all
                //  of the text values appropriately.
                //

                string newXml = oldXml;
                for (int i=1; i <= nFrags; i++)
                {
                    // grab the old and new value XML fragments

                    string oldFrag = currentTagParser[i].Trim();
                    string newFrag = newTagParser[i].Trim();

                    if (
                        ((oldFrag.StartsWith("<") == false) || (oldFrag.EndsWith(">") == false)) &&
                        (oldFrag != "")
                        )
                    {
                        throw new Error_InvalidCurrentTagValue("Invalid args. (" + oldFrag + ") is not an xml element.");
                    }

                    int pos = newXml.IndexOf(oldFrag);
                    if ((oldFrag != "") && (pos < 0))
                    {
                        throw new Error_InvalidCurrentTagValue("Cannot find xml element (" +oldFrag+ ").");
                    }

                    if (oldFrag == "")
                    {
                        // The old value XML fragment indicates that a completely
                        // new XML element needs to be added.
                        if (obj.IsContainer)
                        {
                            StringBuilder newVal = new StringBuilder(newFrag.Length + 10);
                            newVal.AppendFormat("{0}</container>");
                            newXml = newXml.Replace("</container>", newVal.ToString());
                        }
                        else
                        {
                            StringBuilder newVal = new StringBuilder(newFrag.Length + 10);
                            newVal.AppendFormat("{0}</item>", newFrag);
                            newXml = newXml.Replace("</item>", newVal.ToString());
                        }
                    }
                    else
                    {
                        // The new value XML fragment indicates that a current
                        // XML fragment must be deleted.
                        if (newFrag == "")
                        {
                            newXml = newXml.Replace(oldFrag, "");
                        }
                        else
                        {
                            newXml = newXml.Replace(oldFrag, newFrag);
                        }
                    }
                }

                // At this point newXml represents what the proposed object should
                // look like with new changes. We'll continue by casting the
                // string into an xmldocument and instantiating a new IDvMedia
                // instance, making sure to keep the ID specified within the newXml.
                //

                // Cast the string into an XmlDocument so that we can instantiate
                // a media object using an XmlElement object.

                XmlDocument xmldoc = new XmlDocument();
                xmldoc.LoadXml(newXml);

                XmlNodeList didlheader = xmldoc.GetElementsByTagName(T[_DIDL.DIDL_Lite]);
                XmlNode didlRoot = didlheader[0];
                XmlElement xmlElement = (XmlElement) didlRoot.ChildNodes[0];

                IDvMedia newObj;
                if (obj.IsContainer)
                {
                    newObj = new DvMediaContainer(xmlElement);
                }
                else
                {
                    newObj = new DvMediaItem(xmlElement);
                }

                // Iterate through the resources of the new object
                // and ensure that resources that carried over
                // from the original object's metadata are
                // properly mapped in the new object's list.
                // This way, upper software layers cannot effectively
                // tell the difference if a comparison-by-value is done.

                foreach (IMediaResource newRes in newObj.Resources)
                {
                    string uri = newRes.ContentUri;
                    int pos = uri.IndexOf(this.m_VirtualDirName);

                    if (pos > 0)
                    {
                        // ensure that contentUri and importUri values don't violate automapping rule
                        string subUri = uri.Substring(pos + this.m_VirtualDirName.Length);
                        DText DirectiveParser = new DText();
                        DirectiveParser.ATTRMARK = "/";

                        DirectiveParser[0] = subUri;
                        string resourceID = DirectiveParser[2];
                        string objID = DirectiveParser[3];
                        System.Diagnostics.Debug.Assert(objID == objectID);
                        IDvResource res = this.GetResource(objectID, resourceID);

                        newRes[T[_RESATTRIB.importUri]] = null;
                        newRes.ContentUri = res.ContentUri;
                    }
                }

                // Request upper software components to accept/reject
                // request to change metadata. The delegate should
                // throw an exception (preferably UPnPCustomExeption)
                // to indicate a rejection.
                if (this.OnRequestChangeMetadata != null)
                {
                    this.OnRequestChangeMetadata (this, obj, newObj);
                }

                // If no rejection, then use the metadata of the new object
                // and apply it to the existing object.
                obj.UpdateObject(newObj);
            }
            catch (Exception e)
            {
                Exception ne = new Exception("MediaServer.SinkCd_UpdateObject()", e);
                throw ne;
            }

            this.m_Stats.UpdateObject++;
            this.FireStatsChange();
        }
 /// <summary>
 /// Creates a 
 /// <see cref="DvMediaItem"/>
 /// object, given a metadata instantiation
 /// block.
 /// </summary>
 /// <param name="info">
 /// The metadata to use when instantiating the media.
 /// </param>
 /// <returns>a new media item</returns>
 public static DvMediaItem CreateItem(MediaBuilder.item info)
 {
     DvMediaItem newObj = new DvMediaItem();
     MediaBuilder.SetObjectProperties(newObj, info);
     newObj.TrackMetadataChanges = true;
     return newObj;
 }
        public DvMediaReference AddDvMediaReference(DvMediaItem underlyingItem)
        {
            underlyingItem.LockReferenceList();

            DvMediaReference newItem = underlyingItem.CreateDvMediaReference();
            this.AddObject(newItem, false);

            underlyingItem.UnlockReferenceList();

            return newItem;
        }