/// <summary>
        /// This method is called whenever an Item has been successfully
        /// reconstituted from the request's entity.
        /// It uses the information curren tin the XmlInventoryCollection
        /// to complete the item's specification, including any implied
        /// context and asset associations.
        /// It fails the request if any necessary item or asset information
        /// is missing.
        /// </summary>

        private void Validate(XmlInventoryCollection ic)
        {
            // There really should be an item present if we've
            // called validate. So fail if there is not.

            if (ic.Item == null)
            {
                Rest.Log.ErrorFormat("{0} Unable to parse request", MsgId);
                ic.Fail(Rest.HttpStatusCodeBadRequest, "request parse error");
            }

            // Every item is required to have a name (via REST anyway)

            if (ic.Item.Name == String.Empty)
            {
                Rest.Log.ErrorFormat("{0} An item name MUST be specified", MsgId);
                ic.Fail(Rest.HttpStatusCodeBadRequest, "item name required");
            }

            // An item MUST have an asset ID. AssetID should never be zero
            // here. It should always get set from the information stored
            // when the Asset element was processed.

            if (ic.Item.AssetID == UUID.Zero)
            {
                Rest.Log.ErrorFormat("{0} Unable to complete request", MsgId);
                Rest.Log.InfoFormat("{0} Asset information is missing", MsgId);
                ic.Fail(Rest.HttpStatusCodeBadRequest, "asset information required");
            }

            // If the item is new, then assign it an ID

            if (ic.Item.ID == UUID.Zero)
            {
                ic.Item.ID = UUID.Random();
            }

            // If the context is being implied, obtain the current
            // folder item's ID. If it was specified explicitly, make
            // sure that theparent folder exists.

            if (ic.Item.Folder == UUID.Zero)
            {
                ic.Item.Folder = ic.Parent();
            }
            else
            {
                bool found = false;

                foreach (InventoryFolderBase parent in ic.rdata.folders)
                {
                    if (parent.ID == ic.Item.Folder)
                    {
                        found = true;
                        break;
                    }
                }

                if (!found)
                {
                    Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in item {2}",
                                         MsgId, ic.Item.Folder, ic.Item.ID);
                    ic.Fail(Rest.HttpStatusCodeBadRequest, "parent information required");
                }
            }

            // If this is an inline asset being constructed in the context
            // of a new Item, then use the itm's name here too.

            if (ic.Asset != null)
            {
                if (ic.Asset.Name == String.Empty)
                    ic.Asset.Name = ic.Item.Name;
                if (ic.Asset.Description == String.Empty)
                    ic.Asset.Description = ic.Item.Description;
            }

            // Assign permissions

            ic.Item.CurrentPermissions  = ic.CurrentPermissions;
            ic.Item.EveryOnePermissions = ic.EveryOnePermissions;
            ic.Item.BasePermissions     = ic.BasePermissions;
            ic.Item.GroupPermissions    = ic.GroupPermissions;
            ic.Item.NextPermissions     = ic.NextPermissions;

            // If no type was specified for this item, we can attempt to
            // infer something from the file type maybe. This is NOT as
            // good as having type be specified in the XML.

            if (ic.Item.AssetType == (int) AssetType.Unknown ||
                ic.Item.InvType   == (int) InventoryType.Unknown)
            {
                Rest.Log.DebugFormat("{0} Attempting to infer item type", MsgId);

                string[] parts = ic.Item.Name.Split(Rest.CA_PERIOD);

                if (Rest.DEBUG)
                {
                    for (int i = 0; i < parts.Length; i++)
                    {
                        Rest.Log.DebugFormat("{0} Name part {1} : {2}",
                                             MsgId, i, parts[i]);
                    }
                }

                // If the associated item name is multi-part, then maybe
                // the last part will indicate the item type - if we're
                // lucky.

                if (parts.Length > 1)
                {
                    Rest.Log.DebugFormat("{0} File type is {1}",
                                         MsgId, parts[parts.Length - 1]);
                    switch (parts[parts.Length - 1])
                    {
                    case "jpeg2000" :
                    case "jpeg-2000" :
                    case "jpg2000" :
                    case "jpg-2000" :
                        Rest.Log.DebugFormat("{0} Type {1} inferred",
                                             MsgId, parts[parts.Length-1]);
                        if (ic.Item.AssetType == (int) AssetType.Unknown)
                            ic.Item.AssetType = (int) AssetType.ImageJPEG;
                        if (ic.Item.InvType == (int) InventoryType.Unknown)
                            ic.Item.InvType   = (int) InventoryType.Texture;
                        break;
                    case "jpg" :
                    case "jpeg" :
                        Rest.Log.DebugFormat("{0} Type {1} inferred",
                                             MsgId, parts[parts.Length - 1]);
                        if (ic.Item.AssetType == (int) AssetType.Unknown)
                            ic.Item.AssetType = (int) AssetType.ImageJPEG;
                        if (ic.Item.InvType == (int) InventoryType.Unknown)
                            ic.Item.InvType   = (int) InventoryType.Texture;
                        break;
                    case "tga" :
                        if (parts[parts.Length - 2].IndexOf("_texture") != -1)
                        {
                            if (ic.Item.AssetType == (int) AssetType.Unknown)
                                ic.Item.AssetType = (int) AssetType.TextureTGA;
                            if (ic.Item.InvType == (int) AssetType.Unknown)
                                ic.Item.InvType   = (int) InventoryType.Texture;
                        }
                        else
                        {
                            if (ic.Item.AssetType == (int) AssetType.Unknown)
                                ic.Item.AssetType = (int) AssetType.ImageTGA;
                            if (ic.Item.InvType == (int) InventoryType.Unknown)
                                ic.Item.InvType   = (int) InventoryType.Snapshot;
                        }
                        break;
                    default :
                        Rest.Log.DebugFormat("{0} Asset/Inventory type could not be inferred for {1}",
                               MsgId,ic.Item.Name);
                        break;
                    }
                }
            }

            /// If this is a TGA remember the fact

            if (ic.Item.AssetType == (int) AssetType.TextureTGA ||
                ic.Item.AssetType == (int) AssetType.ImageTGA)
            {
                Bitmap temp;
                Stream tgadata = new MemoryStream(ic.Asset.Data);

                temp = LoadTGAClass.LoadTGA(tgadata);
                try
                {
                    ic.Asset.Data = OpenJPEG.EncodeFromImage(temp, true);
                }
                catch (DllNotFoundException)
                {
                    Rest.Log.ErrorFormat("OpenJpeg is not installed correctly on this system.   Asset Data is emtpy for {0}", ic.Item.Name);
                    ic.Asset.Data = new Byte[0];
                }
                catch (IndexOutOfRangeException)
                {
                    Rest.Log.ErrorFormat("OpenJpeg was unable to encode this.   Asset Data is emtpy for {0}", ic.Item.Name);
                    ic.Asset.Data = new Byte[0];
                }
                catch (Exception)
                {
                    Rest.Log.ErrorFormat("OpenJpeg was unable to encode this.   Asset Data is emtpy for {0}", ic.Item.Name);
                    ic.Asset.Data = new Byte[0];
                }
            }

            ic.reset();
        }
        /// <summary>
        /// Store any permissions information provided by the request.
        /// This overrides the default permissions set when the
        /// XmlInventoryCollection object was created.
        /// </summary>
        private void CollectPermissions(XmlInventoryCollection ic)
        {
            if (ic.xml.HasAttributes)
            {
                for (int i = 0; i < ic.xml.AttributeCount; i++)
                {
                    ic.xml.MoveToAttribute(i);
                    switch (ic.xml.Name)
                    {
                        case "current":
                            ic.CurrentPermissions  = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber);
                            break;
                        case "next":
                            ic.NextPermissions     = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber);
                            break;
                        case "group":
                            ic.GroupPermissions    = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber);
                            break;
                        case "everyone":
                            ic.EveryOnePermissions = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber);
                            break;
                        case "base":
                            ic.BasePermissions     = UInt32.Parse(ic.xml.Value, NumberStyles.HexNumber);
                            break;
                        default:
                            Rest.Log.DebugFormat("{0} Permissions:  invalid attribute {1}:{2}",
                                                 MsgId,ic.xml.Name, ic.xml.Value);
                            ic.Fail(Rest.HttpStatusCodeBadRequest,
                                         String.Format("invalid attribute <{0}>", ic.xml.Name));
                            break;
                    }
                }
            }

            ic.xml.MoveToElement();
        }
        /// <summary>
        /// This method assembles an asset instance from the
        /// information supplied in the request's entity. It is
        /// called as a result of detecting a start tag for a
        /// type of Asset.
        /// The information is collected locally, and an asset
        /// instance is created only if the basic XML parsing
        /// completes successfully.
        /// Default values for all parts of the asset are
        /// established before overriding them from the supplied
        /// XML.
        /// If an asset has inline=true as an attribute, then
        /// the element contains the data representing the
        /// asset. This is saved as the data component.
        /// inline=false means that the element's payload is
        /// simply the UUID of the asset referenced by the
        /// item being constructed.
        /// An asset, if created is stored in the
        /// XmlInventoryCollection
        /// </summary>
        private void CollectAsset(XmlInventoryCollection ic)
        {
            Rest.Log.DebugFormat("{0} Interpret asset element", MsgId);

            string    name  = String.Empty;
            string    desc  = String.Empty;
            sbyte     type  = (sbyte) AssetType.Unknown;
            bool      temp  = false;
            bool     local  = false;

            // This is not a persistent attribute
            bool    inline  = false;

            UUID    uuid  = UUID.Zero;

            // Attribute is optional
            if (ic.xml.HasAttributes)
            {
                for (int i = 0; i < ic.xml.AttributeCount; i++)
                {
                    ic.xml.MoveToAttribute(i);
                    switch (ic.xml.Name)
                    {
                        case "name" :
                            name = ic.xml.Value;
                            break;

                        case "type" :
                            type = SByte.Parse(ic.xml.Value);
                            break;

                        case "description" :
                            desc = ic.xml.Value;
                            break;

                        case "temporary" :
                            temp = Boolean.Parse(ic.xml.Value);
                            break;

                        case "uuid" :
                            uuid = new UUID(ic.xml.Value);
                            break;

                        case "inline" :
                            inline = Boolean.Parse(ic.xml.Value);
                            break;

                        case "local" :
                            local = Boolean.Parse(ic.xml.Value);
                            break;

                        default :
                            Rest.Log.DebugFormat("{0} Asset: Unrecognized attribute: {1}:{2}",
                                                 MsgId, ic.xml.Name, ic.xml.Value);
                            ic.Fail(Rest.HttpStatusCodeBadRequest,
                                    String.Format("unrecognized attribute <{0}>", ic.xml.Name));
                            break;
                    }
                }
            }

            ic.xml.MoveToElement();

            // If this is a reference to an existing asset, just store the
            // asset ID into the item.

            if (!inline)
            {
                if (ic.Item != null)
                {
                    ic.Item.AssetID = new UUID(ic.xml.ReadElementContentAsString());
                    Rest.Log.DebugFormat("{0} Asset ID supplied: {1}", MsgId, ic.Item.AssetID);
                }
                else
                {
                    Rest.Log.DebugFormat("{0} LLUID unimbedded asset must be inline", MsgId);
                    ic.Fail(Rest.HttpStatusCodeBadRequest, "no context for asset");
                }
            }

            // Otherwise, generate an asset ID, store that into the item, and
            // create an entry in the asset list for the inlined asset. But
            // only if the size is non-zero.

            else
            {
                AssetBase asset = null;
                string b64string = null;

                // Generate a UUID if none were given, and generally none should
                // be. Ever.

                if (uuid == UUID.Zero)
                {
                    uuid = UUID.Random();
                }

                // Create AssetBase entity to hold the inlined asset

                asset = new AssetBase(uuid, name, type, UUID.Zero.ToString());

                asset.Description = desc;
                asset.Local       = local;
                asset.Temporary   = temp;

                b64string         = ic.xml.ReadElementContentAsString();

                Rest.Log.DebugFormat("{0} Data length is {1}", MsgId, b64string.Length);
                Rest.Log.DebugFormat("{0} Data content starts with: \n\t<{1}>", MsgId,
                                     b64string.Substring(0, b64string.Length > 132 ? 132 : b64string.Length));

                asset.Data        = Convert.FromBase64String(b64string);

                // Ensure the asset always has some kind of data component

                if (asset.Data == null)
                {
                    asset.Data = new byte[1];
                }

                // If this is in the context of an item, establish
                // a link with the item in context.

                if (ic.Item != null && ic.Item.AssetID == UUID.Zero)
                {
                    ic.Item.AssetID = uuid;
                }

                ic.Push(asset);
            }
        }
        /// <summary>
        /// This method is called to handle the construction of an Item
        /// instance from the supplied request entity. It is called
        /// whenever an Item start tag is detected.
        /// An instance of an Item is created and initialized to default
        /// values. These values are then overridden from values supplied
        /// as attributes to the Item element.
        /// This item is then stored in the XmlInventoryCollection and
        /// will be verified by Validate.
        /// All context is reset whenever the effective folder changes
        /// or an item is successfully validated.
        /// </summary>
        private void CollectItem(XmlInventoryCollection ic)
        {
            Rest.Log.DebugFormat("{0} Interpret item element", MsgId);

            InventoryItemBase result = new InventoryItemBase();

            result.Name        = String.Empty;
            result.Description = String.Empty;
            result.ID          = UUID.Zero;
            result.Folder      = UUID.Zero;
            result.Owner       = ic.UserID;
            result.CreatorId   = ic.UserID.ToString();
            result.AssetID     = UUID.Zero;
            result.GroupID     = UUID.Zero;
            result.GroupOwned  = false;
            result.InvType     = (int) InventoryType.Unknown;
            result.AssetType   = (int) AssetType.Unknown;

            if (ic.xml.HasAttributes)
            {
                for (int i = 0; i < ic.xml.AttributeCount; i++)
                {
                    ic.xml.MoveToAttribute(i);

                    switch (ic.xml.Name)
                    {
                        case "name":
                            result.Name         =     ic.xml.Value;
                            break;
                        case "desc":
                            result.Description  =     ic.xml.Value;
                            break;
                        case "uuid":
                            result.ID           = new UUID(ic.xml.Value);
                            break;
                        case "folder":
                            result.Folder       = new UUID(ic.xml.Value);
                            break;
                        case "owner":
                            result.Owner        = new UUID(ic.xml.Value);
                            break;
                        case "invtype":
                            result.InvType      =     Int32.Parse(ic.xml.Value);
                            break;
                        case "creator":
                            result.CreatorId    =     ic.xml.Value;
                            break;
                        case "assettype":
                            result.AssetType    =     Int32.Parse(ic.xml.Value);
                            break;
                        case "groupowned":
                            result.GroupOwned   =     Boolean.Parse(ic.xml.Value);
                            break;
                        case "groupid":
                            result.GroupID      = new UUID(ic.xml.Value);
                            break;
                        case "flags":
                            result.Flags        =     UInt32.Parse(ic.xml.Value);
                            break;
                        case "creationdate":
                            result.CreationDate =     Int32.Parse(ic.xml.Value);
                            break;
                        case "saletype":
                            result.SaleType     =     Byte.Parse(ic.xml.Value);
                            break;
                        case "saleprice":
                            result.SalePrice    =     Int32.Parse(ic.xml.Value);
                            break;

                        default:
                            Rest.Log.DebugFormat("{0} Item: Unrecognized attribute: {1}:{2}",
                                                 MsgId, ic.xml.Name, ic.xml.Value);
                            ic.Fail(Rest.HttpStatusCodeBadRequest, String.Format("unrecognized attribute",
                                ic.xml.Name));
                            break;
                    }
                }
            }

            ic.xml.MoveToElement();

            ic.Push(result);
        }
        /// <summary>
        /// This method creates an inventory Folder from the
        /// information supplied in the request's entity.
        /// A folder instance is created and initialized to reflect
        /// default values. These values are then overridden
        /// by information supplied in the entity.
        /// If context was not explicitly provided, then the
        /// appropriate ID values are determined.
        /// </summary>

        private void CollectFolder(XmlInventoryCollection ic)
        {
            Rest.Log.DebugFormat("{0} Interpret folder element", MsgId);

            InventoryFolderBase result = new InventoryFolderBase();

            // Default values

            result.Name     = String.Empty;
            result.ID       = UUID.Zero;
            result.Owner    = ic.UserID;
            result.ParentID = UUID.Zero; // Context
            result.Type     = (short) AssetType.Folder;
            result.Version  = 1;

            if (ic.xml.HasAttributes)
            {
                for (int i = 0; i < ic.xml.AttributeCount; i++)
                {
                    ic.xml.MoveToAttribute(i);
                    switch (ic.xml.Name)
                    {
                    case "name":
                        result.Name     =     ic.xml.Value;
                        break;
                    case "uuid":
                        result.ID       = new UUID(ic.xml.Value);
                        break;
                    case "parent":
                        result.ParentID = new UUID(ic.xml.Value);
                        break;
                    case "owner":
                        result.Owner    = new UUID(ic.xml.Value);
                        break;
                    case "type":
                        result.Type     =     Int16.Parse(ic.xml.Value);
                        break;
                    case "version":
                        result.Version  =     UInt16.Parse(ic.xml.Value);
                        break;
                    default:
                        Rest.Log.DebugFormat("{0} Folder: unrecognized attribute: {1}:{2}",
                                             MsgId, ic.xml.Name, ic.xml.Value);
                        ic.Fail(Rest.HttpStatusCodeBadRequest, String.Format("unrecognized attribute <{0}>",
                                 ic.xml.Name));
                        break;
                    }
                }
            }

            ic.xml.MoveToElement();

            // The client is relying upon the reconstitution process
            // to determine the parent's UUID based upon context. This
            // is necessary where a new folder may have been
            // introduced.

            if (result.ParentID == UUID.Zero)
            {
                result.ParentID = ic.Parent();
            }
            else
            {
                bool found = false;

                foreach (InventoryFolderBase parent in ic.rdata.folders)
                {
                    if (parent.ID == result.ParentID)
                    {
                        found = true;
                        break;
                    }
                }

                if (!found)
                {
                    Rest.Log.ErrorFormat("{0} Invalid parent ID ({1}) in folder {2}",
                                         MsgId, ic.Item.Folder, result.ID);
                    ic.Fail(Rest.HttpStatusCodeBadRequest, "invalid parent");
                }
            }

            // This is a new folder, so no existing UUID is available
            // or appropriate

            if (result.ID == UUID.Zero)
            {
                result.ID = UUID.Random();
            }

            // Treat this as a new context. Any other information is
            // obsolete as a consequence.

            ic.Push(result);
        }
        /// <summary>
        /// This method is called by PUT and POST to create an XmlInventoryCollection
        /// instance that reflects the content of the entity supplied on the request.
        /// Any elements in the completed collection whose UUID is zero, are
        /// considered to be located relative to the end-point identified int he
        /// URI. In this way, an entire sub-tree can be conveyed in a single REST
        /// PUT or POST request.
        ///
        /// A new instance of XmlInventoryCollection is created and, if the request
        /// has an entity, it is more completely initialized. thus, if no entity was
        /// provided the collection is valid, but empty.
        ///
        /// The entity is then scanned and each tag is processed to produce the
        /// appropriate inventory elements. At the end f the scan, teh XmlInventoryCollection
        /// will reflect the subtree described by the entity.
        ///
        /// This is a very flexible mechanism, the entity may contain arbitrary,
        /// discontiguous tree fragments, or may contain single element. The caller is
        /// responsible for integrating this collection (and ensuring that any
        /// missing parent IDs are resolved).
        /// </summary>
        /// <param name=rdata>HTTP service request work area</param>
        internal XmlInventoryCollection ReconstituteEntity(InventoryRequestData rdata)
        {
            Rest.Log.DebugFormat("{0} Reconstituting entity", MsgId);

            XmlInventoryCollection ic = new XmlInventoryCollection();

            if (rdata.request.HasEntityBody)
            {
                Rest.Log.DebugFormat("{0} Entity present", MsgId);

                ic.init(rdata);

                try
                {
                    while (ic.xml.Read())
                    {
                        switch (ic.xml.NodeType)
                        {
                            case XmlNodeType.Element:
                                Rest.Log.DebugFormat("{0} StartElement: <{1}>",
                                                     MsgId, ic.xml.Name);

                                switch (ic.xml.Name)
                                {
                                    case "Folder":
                                        Rest.Log.DebugFormat("{0} Processing {1} element",
                                                             MsgId, ic.xml.Name);
                                        CollectFolder(ic);
                                        break;
                                    case "Item":
                                        Rest.Log.DebugFormat("{0} Processing {1} element",
                                                             MsgId, ic.xml.Name);
                                        CollectItem(ic);
                                        break;
                                    case "Asset":
                                        Rest.Log.DebugFormat("{0} Processing {1} element",
                                                             MsgId, ic.xml.Name);
                                        CollectAsset(ic);
                                        break;
                                    case "Permissions":
                                        Rest.Log.DebugFormat("{0} Processing {1} element",
                                                             MsgId, ic.xml.Name);
                                        CollectPermissions(ic);
                                        break;
                                    default:
                                        Rest.Log.DebugFormat("{0} Ignoring {1} element",
                                                             MsgId, ic.xml.Name);
                                        break;
                                }

                                // This stinks, but the ReadElement call above not only reads
                                // the imbedded data, but also consumes the end tag for Asset
                                // and moves the element pointer on to the containing Item's
                                // element-end, however, if there was a permissions element
                                // following, it would get us to the start of that..
                                if (ic.xml.NodeType == XmlNodeType.EndElement &&
                                    ic.xml.Name     == "Item")
                                {
                                    Validate(ic);
                                }
                                break;

                            case XmlNodeType.EndElement :
                                switch (ic.xml.Name)
                                {
                                    case "Folder":
                                        Rest.Log.DebugFormat("{0} Completing {1} element",
                                                             MsgId, ic.xml.Name);
                                        ic.Pop();
                                        break;
                                    case "Item":
                                        Rest.Log.DebugFormat("{0} Completing {1} element",
                                                             MsgId, ic.xml.Name);
                                        Validate(ic);
                                        break;
                                    case "Asset":
                                        Rest.Log.DebugFormat("{0} Completing {1} element",
                                                             MsgId, ic.xml.Name);
                                        break;
                                    case "Permissions":
                                        Rest.Log.DebugFormat("{0} Completing {1} element",
                                                             MsgId, ic.xml.Name);
                                        break;
                                    default:
                                        Rest.Log.DebugFormat("{0} Ignoring {1} element",
                                                             MsgId, ic.xml.Name);
                                        break;
                                    }
                                break;

                            default:
                                Rest.Log.DebugFormat("{0} Ignoring: <{1}>:<{2}>",
                                                     MsgId, ic.xml.NodeType, ic.xml.Value);
                                break;
                        }
                    }
                }
                catch (XmlException e)
                {
                    Rest.Log.WarnFormat("{0} XML parsing error: {1}", MsgId, e.Message);
                    throw e;
                }
                catch (Exception e)
                {
                    Rest.Log.WarnFormat("{0} Unexpected XML parsing error: {1}", MsgId, e.Message);
                    throw e;
                }
            }
            else
            {
                Rest.Log.DebugFormat("{0} Entity absent", MsgId);
            }

            if (Rest.DEBUG)
            {
                Rest.Log.DebugFormat("{0} Reconstituted entity", MsgId);
                Rest.Log.DebugFormat("{0} {1} assets", MsgId, ic.Assets.Count);
                Rest.Log.DebugFormat("{0} {1} folder", MsgId, ic.Folders.Count);
                Rest.Log.DebugFormat("{0} {1} items", MsgId, ic.Items.Count);
            }

            return ic;
        }