// Copyright (c) Microsoft Corporation. All rights reserved. See License.txt in the project root for license information.
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.Serialization;
using System.Xml;
namespace System.Json
{
///
/// A JsonArray is an ordered sequence of zero or more objects.
///
[SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix",
Justification = "Array already conveys the meaning of collection")]
[DataContract]
public sealed class JsonArray : JsonValue, IList
{
[DataMember]
private List values = new List();
///
/// Creates an instance of the class initialized by
/// an enumeration of
/// objects of type .
///
/// The enumeration
/// of objects of type used to initialize the JavaScript Object Notation (JSON)
/// array.
/// If items is null.
/// If any of the items in the collection
/// is a with property of
/// value .
public JsonArray(IEnumerable items)
{
AddRange(items);
}
///
/// Creates an instance of the class, initialized by an array of type .
///
/// The array of type used to initialize the
/// JavaScript Object Notation (JSON) array.
/// If any of the items in the collection
/// is a with property of
/// value .
public JsonArray(params JsonValue[] items)
{
if (items != null)
{
AddRange(items);
}
}
///
/// Gets the JSON type of this . The return value
/// is always .
///
public override JsonType JsonType
{
get { return JsonType.Array; }
}
///
/// Gets a value indicating whether the is read-only.
///
public bool IsReadOnly
{
get { return ((IList)values).IsReadOnly; }
}
///
/// Returns the number of elements in the array.
///
public override int Count
{
get { return values.Count; }
}
///
/// Gets or sets the JSON value at a specified index.
///
/// The zero-based index of the element to get or set.
/// The element at the specified index.
/// If index is not a valid index for this array.
/// The property is set and the value is a
/// with
/// property of value .
public override JsonValue this[int index]
{
get { return values[index]; }
set
{
if (value != null && value.JsonType == JsonType.Default)
{
throw new ArgumentNullException("value", Properties.Resources.UseOfDefaultNotAllowed);
}
JsonValue oldValue = values[index];
RaiseItemChanging(value, JsonValueChange.Replace, index);
values[index] = value;
RaiseItemChanged(oldValue, JsonValueChange.Replace, index);
}
}
///
/// Adds the elements from a collection of type to this instance.
///
/// Collection of items to add.
/// If items is null.
/// If any of the items in the collection
/// is a with property of
/// value .
public void AddRange(IEnumerable items)
{
if (items == null)
{
throw new ArgumentNullException("items");
}
if (ChangingListenersCount > 0)
{
int index = Count;
foreach (JsonValue toBeAdded in items)
{
RaiseItemChanging(toBeAdded, JsonValueChange.Add, index++);
}
}
foreach (JsonValue item in items)
{
if (item != null && item.JsonType == JsonType.Default)
{
throw new ArgumentNullException("items", Properties.Resources.UseOfDefaultNotAllowed);
}
values.Add(item);
RaiseItemChanged(item, JsonValueChange.Add, values.Count - 1);
}
}
///
/// Adds the elements from an array of type to this instance.
///
/// The array of type JsonValue to be added to this instance.
/// If items is null.
/// If any of the items in the array
/// is a with property of
/// value .
public void AddRange(params JsonValue[] items)
{
AddRange(items as IEnumerable);
}
///
/// Searches for a specified object and returns the zero-based index of its first
/// occurrence within this .
///
/// The object to look up.
/// The zero-based index of the first occurrence of item within the
/// , if found; otherwise, -1.
public int IndexOf(JsonValue item)
{
return values.IndexOf(item);
}
///
/// Insert a JSON CLR type into the array at a specified index.
///
/// The zero-based index at which the item should be inserted.
/// The object to insert.
/// If index is less than zero or larger than
/// the size of the array.
/// If the object to insert has a
/// property of value
/// .
public void Insert(int index, JsonValue item)
{
if (item != null && item.JsonType == JsonType.Default)
{
throw new ArgumentNullException("item", Properties.Resources.UseOfDefaultNotAllowed);
}
RaiseItemChanging(item, JsonValueChange.Add, index);
values.Insert(index, item);
RaiseItemChanged(item, JsonValueChange.Add, index);
}
///
/// Remove the JSON value at a specified index of .
///
/// The zero-based index at which to remove the .
/// If index is less than zero or index
/// is equal or larger than the size of the array.
public void RemoveAt(int index)
{
JsonValue item = values[index];
RaiseItemChanging(item, JsonValueChange.Remove, index);
values.RemoveAt(index);
RaiseItemChanged(item, JsonValueChange.Remove, index);
}
///
/// Adds a object to the end of the array.
///
/// The object to add.
/// If the object to add has a
/// property of value
/// .
public void Add(JsonValue item)
{
if (item != null && item.JsonType == JsonType.Default)
{
throw new ArgumentNullException("item", Properties.Resources.UseOfDefaultNotAllowed);
}
int index = Count;
RaiseItemChanging(item, JsonValueChange.Add, index);
values.Add(item);
RaiseItemChanged(item, JsonValueChange.Add, index);
}
///
/// Removes all JSON CLR types from the .
///
public void Clear()
{
RaiseItemChanging(null, JsonValueChange.Clear, 0);
values.Clear();
RaiseItemChanged(null, JsonValueChange.Clear, 0);
}
///
/// Checks whether a specified JSON CLR type is in the .
///
/// The to check for in the array.
/// true if item is found in the ; otherwise, false.
public bool Contains(JsonValue item)
{
return values.Contains(item);
}
///
/// Copies the contents of the current JSON CLR array instance into a specified
/// destination array beginning at the specified index.
///
/// The destination array to which the elements of the current
/// object are copied.
/// The zero-based index in the destination array at which the
/// copying of the elements of the JSON CLR array begins.
public void CopyTo(JsonValue[] array, int arrayIndex)
{
values.CopyTo(array, arrayIndex);
}
///
/// Removes the first occurrence of the specified JSON value from the array.
///
/// The to remove from the .
/// true if item is successfully removed; otherwise, false. This method
/// also returns false if item was not found in the .
public bool Remove(JsonValue item)
{
int index = -1;
if (ChangingListenersCount > 0 || ChangedListenersCount > 0)
{
index = IndexOf(item);
}
if (index >= 0)
{
RaiseItemChanging(item, JsonValueChange.Remove, index);
}
bool result = values.Remove(item);
if (index >= 0)
{
RaiseItemChanged(item, JsonValueChange.Remove, index);
}
return result;
}
///
/// Returns an enumerator that iterates through the objects in the array.
///
/// Returns an object that
/// iterates through the elements in this .
IEnumerator IEnumerable.GetEnumerator()
{
return values.GetEnumerator();
}
///
/// Safe indexer for the type.
///
/// The zero-based index of the element to get.
/// If the index is within the array bounds and the value corresponding to the
/// index is not null, then it will return that value. Otherwise it will return a
/// instance with
/// equals to .
public override JsonValue ValueOrDefault(int index)
{
if (index >= 0 && index < Count && this[index] != null)
{
return this[index];
}
return base.ValueOrDefault(index);
}
///
/// Returns an enumerator that iterates through the objects in the array.
///
/// Returns an object that
/// iterates through the elements in this .
public new IEnumerator GetEnumerator()
{
return values.GetEnumerator();
}
///
/// Returns an enumerator which iterates through the values in this object.
///
/// An which iterates through the values in this object.
/// The enumerator returned by this class contains one pair for each element
/// in this array, whose key is the element index (as a string), and the value is the
/// element itself.
protected override IEnumerator> GetKeyValuePairEnumerator()
{
for (int i = 0; i < values.Count; i++)
{
yield return new KeyValuePair(i.ToString(CultureInfo.InvariantCulture), values[i]);
}
}
///
/// Callback method called to let an instance write the proper JXML attribute when saving this
/// instance.
///
/// The JXML writer used to write JSON.
internal override void WriteAttributeString(XmlDictionaryWriter jsonWriter)
{
if (jsonWriter == null)
{
throw new ArgumentNullException("jsonWriter");
}
jsonWriter.WriteAttributeString(JXmlToJsonValueConverter.TypeAttributeName, JXmlToJsonValueConverter.ArrayAttributeValue);
}
///
/// Callback method called during Save operations to let the instance write the start element
/// and return the next element in the collection.
///
/// The JXML writer used to write JSON.
/// The index within this collection.
/// The next item in the collection, or null of there are no more items.
internal override JsonValue WriteStartElementAndGetNext(XmlDictionaryWriter jsonWriter, int currentIndex)
{
if (jsonWriter == null)
{
throw new ArgumentNullException("jsonWriter");
}
jsonWriter.WriteStartElement(JXmlToJsonValueConverter.ItemElementName);
JsonValue nextValue = this[currentIndex];
return nextValue;
}
private void RaiseItemChanging(JsonValue child, JsonValueChange change, int index)
{
if (ChangingListenersCount > 0)
{
RaiseChangingEvent(this, new JsonValueChangeEventArgs(child, change, index));
}
}
private void RaiseItemChanged(JsonValue child, JsonValueChange change, int index)
{
if (ChangedListenersCount > 0)
{
RaiseChangedEvent(this, new JsonValueChangeEventArgs(child, change, index));
}
}
}
}