using System; using System.Collections.Generic; using System.Linq; using System.Text; using Nuclex.Networking.Gallery3.Requests; using System.Diagnostics; using System.Net; using System.Web.Script.Serialization; namespace Nuclex.Networking.Gallery3 { /// Entity, common base for albums and items public class Entity : ConnectionUser { /// /// Initializes a new gallery entity (which is either an album or an item) /// /// /// Connection holding the resources needed to issue requests to /// the gallery installation /// /// /// The JSON data returned by the gallery REST API about this entity /// internal Entity(IGalleryConnection connection, ref Responses.JsonItem jsonEntity) : base(connection) { this.jsonItem = jsonEntity; } /// The unique ID of the entity /// /// Each album or item in gallery is assigned a unique ID by which it can be /// queried or /// public int Id { get { return this.jsonItem.Entity.Id; } } /// Date and time at which this entity was added to the gallery public DateTime CreationDate { get { return DateTimeHelper.DateTimeFromGalleryTime(this.jsonItem.Entity.Created); } } /// Description assigned to this entity public string Description { get { return this.jsonItem.Entity.Description; } } /// Human-readable title assigned to this entity public string Title { get { return this.jsonItem.Entity.Title; } } /// String used to refer to this entity in an URL public string Slug { get { return this.jsonItem.Entity.Slug; } } /// Name of the entity in the host's file system public string Name { get { return this.jsonItem.Entity.Name; } } /// IDs of this entity's children public int[] ChildIds { get { lock (this) { extractChildIdsIfNotCached(); return this.childIds; } } } /// IDs of the tags associated with this entity public int[] TagIDs { get { lock (this) { extractTagIdsIfNotCached(); return this.tagIds; } } } /// Retrieves the direct children of this entity /// The child entities /// /// /// Children can be albums (if the entity is an album itself), comments or /// items (in the form of photos and movies). /// /// public Entity GetChild(int index) { extractChildIdsIfNotCached(); if ((index < 0) || (index >= this.childIds.Length)) { throw new ArgumentOutOfRangeException("index", "Child index out of range"); } Responses.JsonItem jsonItem = RetrieveChildEntity(this.childIds[index]); return constructEntityForItem(ref jsonItem); } /// Retrieves the direct children of this entity /// The child entities /// /// /// Warning: This call will query *all* children. For larger albums and /// sensible use you should query the albums with the second overload of this /// method so you can display a progress bar and let the user cancel at /// various points in the query. /// /// /// Children can be albums (if the entity is an album itself), comments or /// items (in the form of photos and movies). /// /// public Entity[] GetChildren() { return getChildrenInternal(null, null); } /// Retrieves the direct children of this entity /// Index of the entity at which querying will resume /// Number of entities that will be queried /// The queried entities /// /// Children can be albums (if the entity is an album itself), comments or /// items (in the form of photos and movies). /// public Entity[] GetChildren(int start, int count) { return getChildrenInternal(start, count); } /// Refreshes this entity's data public virtual void Refresh() { lock (this) { this.jsonItem = RetrieveChildEntity(this.jsonItem.Entity.Id); this.childIds = null; this.tagIds = null; } } /// Retrieves the JSON data of a single child entity /// ID of the entity that will be queried /// The JSON data of the queried entity protected internal Responses.JsonItem RetrieveChildEntity(int id) { string resource = "rest/item/" + id.ToString(); IRequest request = Connection.RequestFactory.CreateRequest(resource); request.Method = "GET"; request.ContentType = "application/x-www-form-urlencoded; charset=UTF-8"; request.AddHeader("X-Gallery-Request-Method", "get"); if (Connection.ApiKey != null) { request.AddHeader("X-Gallery-Request-Key", Connection.ApiKey); } return request.GetJsonResponse(); } /// Retrieves informations about the direct children of the entity /// Index of the entity at which to begin /// Number of entities that will be returned /// The JSON data for the children of the entity /// /// Less than 100 entities can be returned if the entity had less than /// the specified number of children starting at the indicated index. /// The maximum number of children that can be queried at once is 100. /// protected internal Responses.JsonItem[] RetrieveChildren(int? start, int? count) { #if MULTI_ITEM_QUERY_WORKS StringBuilder urlBuilder = new StringBuilder(); urlBuilder.Append("rest/items?url="); // Docs say 'urls', code says 'url'... { JavaScriptSerializer serializer = new JavaScriptSerializer(); string json = serializer.Serialize(base.jsonItem.Members); urlBuilder.Append(json); //HttpUtility.UrlEncode(json)); } if(start.HasValue) { urlBuilder.Append("&start="); urlBuilder.Append(start.Value); } if(count.HasValue) { urlBuilder.Append("&num="); urlBuilder.Append(count.Value); } IRequest request = Connection.RequestFactory.CreateRequest(urlBuilder.ToString()); request.ContentType = "application/x-www-form-urlencoded"; request.AddHeader("X-Gallery-Request-Method", "get"); if (Connection.ApiKey != null) { request.AddHeader("X-Gallery-Request-Key", Connection.ApiKey); } return request.GetJsonResponse(); #else return retrieveChildEntitiesOneByOne(start, count); #endif } /// /// Stupid workaround because I cannot get the multi-item query to work /// /// Index of the entity at which to begin /// Number of entities that will be returned /// The JSON data for the children of the entity /// /// Less than 100 entities can be returned if the entity had less than /// the specified number of children starting at the indicated index. /// The maximum number of children that can be queried for at once is 100. /// private Responses.JsonItem[] retrieveChildEntitiesOneByOne(int? start, int? count) { extractChildIdsIfNotCached(); // Determine the safe starting and ending indices int begin = start ?? 0; int end = this.childIds.Length; if (count.HasValue) { end = Math.Min(end, begin + count.Value); } end = Math.Min(end, begin + 100); // Retrieve the children one by one... :-( var items = new List(Math.Max(0, end - begin)); for (int index = begin; index < end; ++index) { items.Add(RetrieveChildEntity(this.childIds[index])); } return items.ToArray(); } /// /// Internal method used to obtain the direct children of this entity /// /// Starting index if a subset is to be queried /// Number of items that will be queried. /// An array of children belonging to this entity private Entity[] getChildrenInternal(int? start, int? count) { Responses.JsonItem[] jsonEntities = RetrieveChildren(start, count); List entities = new List(jsonEntities.Length); for (int index = 0; index < jsonEntities.Length; ++index) { entities.Add(constructEntityForItem(ref jsonEntities[index])); } return entities.ToArray(); } /// Constructs an entity of the concrete type matching the JSON data /// Item for which an entity will be constructed /// The newly constructed entity private Entity constructEntityForItem(ref Responses.JsonItem jsonItem) { switch (jsonItem.Entity.Type) { case "album": { return new Album(Connection, ref jsonItem); } case "photo": { return new Photo(Connection, ref jsonItem); } case "movie": { return new Movie(Connection, ref jsonItem); } case "comment": { return new Comment(Connection, ref jsonItem); } default: { return new Entity(Connection, ref jsonItem); } } } /// /// Extracts the IDs of this entity's children if they're not cached yet /// private void extractChildIdsIfNotCached() { lock (this) { if (this.childIds != null) { return; // Already cached } this.childIds = UrlHelper.ExtractIdsFromUrls(this.jsonItem.Members); } } /// /// Extracts the IDs of this entity's tags if they're not cached yet /// private void extractTagIdsIfNotCached() { lock (this) { if (this.tagIds != null) { return; // Already cached } this.tagIds = UrlHelper.ExtractIdsFromUrls( this.jsonItem.Relationships.Tags.Members ); } } /// The entity data received in JSON format protected Responses.JsonItem jsonItem; /// Contains the IDs of the entity's children private int[] childIds; /// Contains the IDs of the tags applied to the entity private int[] tagIds; } } // namespace Nuclex.Networking.Gallery3