From: Panagiotis Kanavos Date: Wed, 1 Jun 2011 06:45:00 +0000 (+0300) Subject: All files X-Git-Url: https://code.grnet.gr/git/pithos-ms-client/commitdiff_plain/d78cbf094dc59fc605a766b8b2c1f45af67b135e?ds=sidebyside All files --- diff --git a/trunk/.gitignore b/trunk/.gitignore new file mode 100644 index 0000000..2b67b7b --- /dev/null +++ b/trunk/.gitignore @@ -0,0 +1,107 @@ +#OS junk files +[Tt]humbs.db +*.DS_Store + +#Visual Studio files +*.[Oo]bj +*.exe +*.pdb +*.user +*.aps +*.pch +*.vspscc +*.vssscc +*_i.c +*_p.c +*.ncb +*.suo +*.tlb +*.tlh +*.bak +*.[Cc]ache +*.ilk +*.log +*.lib +*.sbr +*.sdf +ipch/ +obj/ +[Bb]in +[Dd]ebug*/ +[Rr]elease*/ +Ankh.NoLoad + +#Tooling +_ReSharper*/ +*.resharper +[Tt]est[Rr]esult* + +#Project files +[Bb]uild/ + +#Subversion files +.svn + +# Office Temp Files +~$* + + + + + + +*.*scc +*.FileListAbsolute.txt +*.aps +*.bak +*.[Cc]ache +*.clw +*.eto +*.exe +*.fb6lck +*.fbl6 +*.fbpInf +*.ilk +*.lib +*.log +*.ncb +*.nlb +*.obj +*.patch +*.pch +*.pdb +*.plg +*.[Pp]ublish.xml +*.rdl.data +*.sbr +*.scc +*.sig +*.sqlsuo +*.suo +*.svclog +*.tlb +*.tlh +*.tli +*.tmp +*.user +*.vshost.* +*DXCore.Solution +*_i.c +*_p.c +Ankh.Load +Backup* +CVS/ +PrecompiledWeb/ +UpgradeLog*.* +[Bb]in/ +[Dd]ebug/ +[Oo]bj/ +[Rr]elease/ +[Tt]humbs.db +_UpgradeReport_Files +_[Rr]e[Ss]harper.*/ +hgignore[.-]* +ignore[.-]* +svnignore[.-]* +lint.db +*.vs10x diff --git a/trunk/Libraries/Json40r2/Documentation.chm b/trunk/Libraries/Json40r2/Documentation.chm new file mode 100644 index 0000000..289803b Binary files /dev/null and b/trunk/Libraries/Json40r2/Documentation.chm differ diff --git a/trunk/Libraries/Json40r2/Source/Doc/ContractResolver.html b/trunk/Libraries/Json40r2/Source/Doc/ContractResolver.html new file mode 100644 index 0000000..99ef6de --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/ContractResolver.html @@ -0,0 +1,92 @@ + + + + Contract Resolver + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Contract Resolvers
+ +
+ + + +

The IContractResolver interface provides a way to customize how the JsonSerializer serializes and deserializes .NET objects to JSON.

+

Implementing the IContractResolver interface and then assigning an instance to a JsonSerializer lets you control + whether the object is serialized as a JSON object or JSON array, what object members should be serialized, how they are serialized and what they are called.

+ +

DefaultContractResolver

+

The DefaultContractResolver is the default resolver used by the serializer. It provides many avenues of extensibility in the form of virtual methods that can be overriden.

+ +

CamelCasePropertyNamesContractResolver

+

CamelCasePropertyNamesContractResolver inherits from DefaultContractResolver and simply overrides the JSON property name to be written in camelcase.

+ +
Product product = new Product
+ +
                    {
+ +
                      ExpiryDate = new DateTime(2010, 12, 20, 18, 1, 0, DateTimeKind.Utc),
+ +
                      Name = "Widget",
+ +
                      Price = 9.99m,
+ +
                      Sizes = new[] {"Small", "Medium", "Large"}
+ +
                    };
+ +
 
+ +
string json = 
+ +
  JsonConvert.SerializeObject(
+ +
    product,
+ +
    Formatting.Indented,
+ +
    new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() }
+ +
  );
+ +
 
+ +
//{
+ +
//  "name": "Widget",
+ +
//  "expiryDate": "\/Date(1292868060000)\/",
+ +
//  "price": 9.99,
+ +
//  "sizes": [
+ +
//    "Small",
+ +
//    "Medium",
+ +
//    "Large"
+ +
//  ]
+ +
//}
+
+
+
+ + + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/ConvertingJSONandXML.html b/trunk/Libraries/Json40r2/Source/Doc/ConvertingJSONandXML.html new file mode 100644 index 0000000..38cedfa --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/ConvertingJSONandXML.html @@ -0,0 +1,146 @@ + + + + Converting between JSON and XML + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Converting between JSON and XML
+ +
+ +

Json.NET supports converting JSON to XML and vice versa using the + XmlNodeConverter.

+ +

+ Elements, attributes, text, comments, character data, processing instructions, + namespaces and the XML declaration are all preserved when converting between the + two. The only caveat is that it is possible to lose the order of differently + named nodes at the same level when they are grouped together into an array.

+

Conversion Rules

+ + +

SerializeXmlNode

+

The JsonConvert has two helper methods for converting between JSON and XML. The first is SerializeXmlNode. This method takes an +XmlNode and serializes it to JSON text.

+
+
+
+
string xml = @"<?xml version=""1.0"" standalone=""no""?>
+
<root>
+
  <person id=""1"">
+
  <name>Alan</name>
+
  <url>http://www.google.com</url>
+
  </person>
+
  <person id=""2"">
+
  <name>Louis</name>
+
  <url>http://www.yahoo.com</url>
+
  </person>
+
</root>";
+
 
+
XmlDocument doc = new XmlDocument();
+
doc.LoadXml(xml);
+
 
+
string jsonText = JsonConvert.SerializeXmlNode(doc);
+
//{
+
//  "?xml": {
+
//    "@version": "1.0",
+
//    "@standalone": "no"
+
//  },
+
//  "root": {
+
//    "person": [
+
//      {
+
//        "@id": "1",
+
//        "name": "Alan",
+
//        "url": "http://www.google.com"
+
//      },
+
//      {
+
//        "@id": "2",
+
//        "name": "Louis",
+
//        "url": "http://www.yahoo.com"
+
//      }
+
//    ]
+
//  }
+
//}
+
+ +
+
+ +

DeserializeXmlNode

+ +

The second helper method on JsonConvert is DeserializeXmlNode. This method takes + JSON text and deserializes it into a XmlNode.

+

Because valid XML must have one root element the JSON passed to + DeserializeXmlNode should have one property in the root JSON object. If the root + JSON object has multiple properties then the overload that also takes an element + name should be used. A root element with that name will be inserted into the + deserialized XmlNode.

+ +
+ +
+ +
+
string json = @"{
+
  ""?xml"": {
+
    ""@version"": ""1.0"",
+
    ""@standalone"": ""no""
+
  },
+
  ""root"": {
+
    ""person"": [
+
      {
+
        ""@id"": ""1"",
+
        ""name"": ""Alan"",
+
        ""url"": ""http://www.google.com""
+
      },
+
      {
+
        ""@id"": ""2"",
+
        ""name"": ""Louis"",
+
        ""url"": ""http://www.yahoo.com""
+
      }
+
    ]
+
  }
+
}";
+
 
+
XmlDocument doc = (XmlDocument)JsonConvert.DeserializeXmlNode(json);
+
// <?xml version="1.0" standalone="no"?>
+
// <root>
+
//   <person id="1">
+
//   <name>Alan</name>
+
//   <url>http://www.google.com</url>
+
//   </person>
+
//   <person id="2">
+
//   <name>Louis</name>
+
//   <url>http://www.yahoo.com</url>
+
//   </person>
+
// </root>
+
+ + +
+
+ + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/CustomCreationConverter.html b/trunk/Libraries/Json40r2/Source/Doc/CustomCreationConverter.html new file mode 100644 index 0000000..edc403b --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/CustomCreationConverter.html @@ -0,0 +1,99 @@ + + + + CustomCreationConverter + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ CustomCreationConverter
+ +
+ + +

+ The CustomCreationConverter is a JsonConverter that provides a way to customize how an object is created + during JSON deserialization. + Once the object has been created it will then have values populated onto it by the serializer. +

+ +
+
+
public interface IPerson
+
{
+
  string FirstName { get; set; }
+
  string LastName { get; set; }
+
  DateTime BirthDate { get; set; }
+
}
+
 
+
public class Employee : IPerson
+
{
+
  public string FirstName { get; set; }
+
  public string LastName { get; set; }
+
  public DateTime BirthDate { get; set; }
+
 
+
  public string Department { get; set; }
+
  public string JobTitle { get; set; }
+
}
+
 
+
public class PersonConverter : CustomCreationConverter<IPerson>
+
{
+
  public override IPerson Create(Type objectType)
+
  {
+
    return new Employee();
+
  }
+
}
+
+
+ +

+This is an extremely simple example. A more complicated scenario could involve an object factory or service locator + which resolves the object at runtime. +

+ +
+
+
//[
+
//  {
+
//    "FirstName": "Maurice",
+
//    "LastName": "Moss",
+
//    "BirthDate": "\/Date(252291661000)\/",
+
//    "Department": "IT",
+
//    "JobTitle": "Support"
+
//  },
+
//  {
+
//    "FirstName": "Jen",
+
//    "LastName": "Barber",
+
//    "BirthDate": "\/Date(258771661000)\/",
+
//    "Department": "IT",
+
//    "JobTitle": "Manager"
+
//  }
+
//]
+
 
+
List<IPerson> people = JsonConvert.DeserializeObject<List<IPerson>>(json, new PersonConverter());
+
 
+
IPerson person = people[0];
+
 
+
Console.WriteLine(person.GetType());
+
// Newtonsoft.Json.Tests.Employee
+
 
+
Console.WriteLine(person.FirstName);
+
// Maurice
+
 
+
Employee employee = (Employee)person;
+
 
+
Console.WriteLine(employee.JobTitle);
+
// Support
+
+
+ + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/DatesInJSON.html b/trunk/Libraries/Json40r2/Source/Doc/DatesInJSON.html new file mode 100644 index 0000000..e59af91 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/DatesInJSON.html @@ -0,0 +1,94 @@ + + + + Serializing Dates in JSON + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Serializing Dates in JSON
+ +
+ +

DateTimes in JSON are hard.

+

+ The problem comes from the JSON spec itself, there is no literal syntax for dates in JSON. The spec has objects, arrays, strings, integers and floats, but it defines no standard for what a date looks like.

+

The default format used by Json.NET for dates is the same one used by Microsoft: "\/Date(1198908717056)\/". You can read more about it here.

+ +

DateTime JsonConverters

+

With no standard for dates in JSON, the number of possible different formats when interoping with other systems is endless. Fortunately Json.NET has a solution to deal with reading and writing custom dates: JsonConverters. A JsonConverter is used to override how a type is serialized.

+ +
+
+
+
public class LogEntry
+
{
+
  public string Details { get; set; }
+ +
  public DateTime LogDate { get; set; }
+
}
+
 
+
[Test]
+ +
public void WriteJsonDates()
+
{
+
  LogEntry entry = new LogEntry
+
  {
+ +
    LogDate = new DateTime(2009, 2, 15, 0, 0, 0, DateTimeKind.Utc),
+
    Details = "Application started."
+
  };
+
 
+ +
  string defaultJson = JsonConvert.SerializeObject(entry);
+
  // {"Details":"Application started.","LogDate":"\/Date(1234656000000)\/"}
+

+
 
+
  string javascriptJson = JsonConvert.SerializeObject(entry, new JavaScriptDateTimeConverter());
+ +
  // {"Details":"Application started.","LogDate":new Date(1234656000000)}
+
 
+
  string isoJson = JsonConvert.SerializeObject(entry, new IsoDateTimeConverter());
+
  // {"Details":"Application started.","LogDate":"2009-02-15T00:00:00Z"}
+ +
}
+
+
+
+

Simply pass the JsonConverter you wish to use to the Json.NET serializer.

+

JavaScriptDateTimeConverter

+

The JavaScriptDateTimeConverter class is one of the two DateTime JsonConverters that come with Json.NET. This converter serializes a DateTime as a JavaScript Date object.

+
+
+
+
new Date(1234656000000)
+ +
+
+
+

Technically this is invalid JSON according to the spec but all browsers, and some JSON frameworks including Json.NET, support it.

+

IsoDateTimeConverter

+

IsoDateTimeConverter seralizes a DateTime to an ISO 8601 formatted string.

+
+
+
+
"2009-02-15T00:00:00Z"
+ +
+
+
+

The IsoDateTimeConverter class has a property, DateTimeFormat, to further customize the formatted string.

+

 

+

One final thing to note is all date values returned by Json.NET are in UTC time.

+ + + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/Introduction.html b/trunk/Libraries/Json40r2/Source/Doc/Introduction.html new file mode 100644 index 0000000..0282159 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/Introduction.html @@ -0,0 +1,68 @@ + + + Introduction + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Introduction
+
+ +

+ Json.NET makes working with JSON formatted data in .NET + simple. Quickly read and write JSON using LINQ to JSON or serialize + your .NET objects with a single method call using the JsonSerializer.

+

+ Features

+ +

The JSON serializer is a good choice when the JSON you are reading or writing maps closely + to a .NET class. The serializer automatically reads and writes JSON for the class.

+

+ For situations where you are only interested in getting values from JSON, don't + have a class to serialize or deserialize to, or the JSON is radically different + from your class and you need to manually read and write from your objects then + LINQ to JSON is what you should use. LINQ to JSON allows you to easily read, + create and modify JSON in .NET.

+

+ History

+

+ Json.NET grew out of projects I was working on in late 2005 involving JavaScript, + AJAX and .NET. At the time there were no libraries for working with JavaScript in + .NET so I began to grow my own.

+

+ Starting out as a couple of static methods for escaping JavaScript strings, Json.NET + evolved as features were added. To add support for reading JSON a major refactor + was required and Json.NET will split into the three major classes it still uses + today, JsonReader, JsonWriter and JsonSerializer. +

+

+ Json.NET was first released in June 2006. Since then Json.NET has been downloaded + thousands of times by developers and is used in a number of major projects open + source projects including MonoRail, + Castle Project's MVC web framework, and Mono, + an open source implementation + of the .NET framework.

+

+ ~ James Newton-King

+

+ Donate

+

+ Json.NET is a free open source project that I have developed in my personal time.

+

+ I really appreciate your feedback and support for Json.NET and its future development.

+

+ Donate +

+ +
+ + diff --git a/trunk/Libraries/Json40r2/Source/Doc/LINQtoJSON.html b/trunk/Libraries/Json40r2/Source/Doc/LINQtoJSON.html new file mode 100644 index 0000000..c80d3b7 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/LINQtoJSON.html @@ -0,0 +1,254 @@ + + + + LINQ to JSON + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ LINQ to JSON
+ +
+ + +

LINQ to JSON is a programming API for working with JSON objects. The API has been designed with LINQ in mind to enable to quick querying and creation of JSON objects. LINQ to JSON sits under the Newtonsoft.Json.Linq namespace.

+ +

Creating JSON

+

There are a number of different options when it comes to creating JSON using LINQ to JSON. The first to create objects imperatively. You have total control but it is more verbose than other options.

+ +
+ +
+
JArray array = new JArray();
+
JValue text = new JValue("Manual text");
+
JValue date = new JValue(new DateTime(2000, 5, 23));
+
 
+
array.Add(text);
+
array.Add(date);
+
 
+
string json = array.ToString();
+
// [
+
//   "Manual text",
+
//   "\/Date(958996800000+1200)\/"
+
// ]
+
+ +
+ +

Another option is to create JSON objects declaratively.

+ + +
+ +
+
List<Post> posts = GetPosts();
+
 
+
JObject rss =
+
  new JObject(
+
    new JProperty("channel",
+
      new JObject(
+
        new JProperty("title", "James Newton-King"),
+
        new JProperty("link", "http://james.newtonking.com"),
+
        new JProperty("description", "James Newton-King's blog."),
+
        new JProperty("item",
+
          new JArray(
+
            from p in posts
+
            orderby p.Title
+
            select new JObject(
+
              new JProperty("title", p.Title),
+
              new JProperty("description", p.Description),
+
              new JProperty("link", p.Link),
+
              new JProperty("category",
+
                new JArray(
+
                  from c in p.Categories
+
                  select new JValue(c)))))))));
+
 
+
Console.WriteLine(rss.ToString());
+
 
+
//{
+
//  "channel": {
+
//    "title": "James Newton-King",
+
//    "link": "http://james.newtonking.com",
+
//    "description": "James Newton-King's blog.",
+
//    "item": [
+
//      {
+
//        "title": "Json.NET 1.3 + New license + Now on CodePlex",
+
//        "description": "Annoucing the release of Json.NET 1.3, the MIT license and the source being available on CodePlex",
+
//        "link": "http://james.newtonking.com/projects/json-net.aspx",
+
//        "category": [
+
//          "Json.NET",
+
//          "CodePlex"
+
//        ]
+
//      },
+
//      {
+
//        "title": "LINQ to JSON beta",
+
//        "description": "Annoucing LINQ to JSON",
+
//        "link": "http://james.newtonking.com/projects/json-net.aspx",
+
//        "category": [
+
//          "Json.NET",
+
//          "LINQ"
+
//        ]
+
//      }
+
//    ]
+
//  }
+
//}
+
+ +
+ +

You can create a JSON object from a non-JSON type using the FromObject method.

+ +
+ +
+
JObject o = JObject.FromObject(new
+
{
+
  channel = new
+
  {
+
    title = "James Newton-King",
+
    link = "http://james.newtonking.com",
+
    description = "James Newton-King's blog.",
+
    item =
+
        from p in posts
+
        orderby p.Title
+
        select new
+
        {
+
          title = p.Title,
+
          description = p.Description,
+
          link = p.Link,
+
          category = p.Categories
+
        }
+
  }
+
});
+
+ +
+ +

Finally JSON objects can be created from a string use the Parse method.

+ +
+ +
+
string json = @"{
+
  CPU: 'Intel',
+
  Drives: [
+
    'DVD read/writer',
+
    ""500 gigabyte hard drive""
+
  ]
+
}";
+
 
+
JObject o = JObject.Parse(json);
+
+ +
+ +

Querying JSON

+ + +

The properties methods that are the most useful when querying JSON objects are + the Children + method and the property index.

+

Children returns all the children of that object. If it is a JObject it will + return a collection of properties to work with and if it is a JArray you will + get a collection of the array's values.

+

The property index is used to get a specific child, either by index position for JSON arrays or property name for JSON objects.

+ +
+ +
+
var postTitles =
+
  from p in rss["channel"]["item"].Children()
+
  select (string)p["title"];
+
 
+
foreach (var item in postTitles)
+
{
+
  Console.WriteLine(item);
+
}
+
 
+
//LINQ to JSON beta
+
//Json.NET 1.3 + New license + Now on CodePlex
+
 
+
var categories =
+
  from c in rss["channel"]["item"].Children()["category"].Values<string>()
+
  group c by c into g
+
  orderby g.Count() descending
+
  select new { Category = g.Key, Count = g.Count() };
+
 
+
foreach (var c in categories)
+
{
+
  Console.WriteLine(c.Category + " - Count: " + c.Count);
+
}
+
 
+
//Json.NET - Count: 2
+
//LINQ - Count: 1
+
//CodePlex - Count: 1
+
+ +
+ +

LINQ to JSON can also be used to manually convert from JSON to a .NET object.

+ +
+
+
public class Shortie
+
{
+
  public string Original { get; set; }
+
  public string Shortened { get; set; }
+
  public string Short { get; set; }
+
  public ShortieException Error { get; set; }
+
}
+
 
+
public class ShortieException
+
{
+
  public int Code { get; set; }
+
  public string ErrorMessage { get; set; }
+
}
+
+
+ +

Manually serializing and deserializing between .NET objects is most useful when working with JSON that doesn't closely match your .NET objects.

+ +
+ +
+
string jsonText = @"{
+
  ""short"":{
+
    ""original"":""http://www.foo.com/"",
+
    ""short"":""krehqk"",
+
    ""error"":{
+
      ""code"":0,
+
      ""msg"":""No action taken""}
+
}";
+
 
+
JObject json = JObject.Parse(jsonText);
+
 
+
Shortie shortie = new Shortie
+
                  {
+
                    Original = (string)json["short"]["original"],
+
                    Short = (string)json["short"]["short"],
+
                    Error = new ShortieException
+
                            {
+
                              Code = (int)json["short"]["error"]["code"],
+
                              ErrorMessage = (string)json["short"]["error"]["msg"]
+
                            }
+
                  };
+
 
+
Console.WriteLine(shortie.Original);
+
// http://www.foo.com/
+
 
+
Console.WriteLine(shortie.Error.ErrorMessage);
+
// No action taken
+
+ +
+ + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/PreserveObjectReferences.html b/trunk/Libraries/Json40r2/Source/Doc/PreserveObjectReferences.html new file mode 100644 index 0000000..471ac68 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/PreserveObjectReferences.html @@ -0,0 +1,138 @@ + + + + Serialization and Preserving Object References + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Serialization and Preserving Object References
+ +
+ + +

By default Json.NET will serialize all objects it encounters by value. If a list + contains two Person references, and both references point to the same object + then the JsonSerializer will write out all the names and values for each + reference.

+ +
+ +
+
Person p = new Person
+
  {
+
    BirthDate = new DateTime(1980, 12, 23, 0, 0, 0, DateTimeKind.Utc),
+
    LastModified = new DateTime(2009, 2, 20, 12, 59, 21, DateTimeKind.Utc),
+
    Name = "James"
+
  };
+
 
+
List<Person> people = new List<Person>();
+
people.Add(p);
+
people.Add(p);
+
 
+
string json = JsonConvert.SerializeObject(people, Formatting.Indented);
+
//[
+
//  {
+
//    "Name": "James",
+
//    "BirthDate": "\/Date(346377600000)\/",
+
//    "LastModified": "\/Date(1235134761000)\/"
+
//  },
+
//  {
+
//    "Name": "James",
+
//    "BirthDate": "\/Date(346377600000)\/",
+
//    "LastModified": "\/Date(1235134761000)\/"
+
//  }
+
//]
+
+
+

In most cases this is the desired result but in certain scenarios writing the second item in the list as a reference to the first is a better solution. + If the above JSON was deserialized now then the returned list would contain two + completely separate Person objects with the same values. Writing references by value will also cause problems on objects where a + circular reference occurs.

+ +

PreserveReferencesHandling

+

Settings PreserveReferencesHandling will track object references when serializing + and deserializing JSON.

+ +
+
+
string json = JsonConvert.SerializeObject(people, Formatting.Indented,
+
  new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
+
//[
+
//  {
+
//    "$id": "1",
+
//    "Name": "James",
+
//    "BirthDate": "\/Date(346377600000)\/",
+
//    "LastModified": "\/Date(1235134761000)\/"
+
//  },
+
//  {
+
//    "$ref": "1"
+
//  }
+
//]
+
 
+
List<Person> deserializedPeople = JsonConvert.DeserializeObject<List<Person>>(json,
+
  new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
+
 
+
Console.WriteLine(deserializedPeople.Count);
+
// 2
+
 
+
Person p1 = deserializedPeople[0];
+
Person p2 = deserializedPeople[1];
+
 
+
Console.WriteLine(p1.Name);
+
// James
+
Console.WriteLine(p2.Name);
+
// James
+
 
+
bool equal = Object.ReferenceEquals(p1, p2);
+
// true
+
+
+ +

The first Person in the list is serizlied with the addition of an object Id. The + second Person in JSON is now only a reference to the first.

+

With PreserveReferencesHandling on now only one Person object is created on + deserialization and the list contains two references to it, mirroring what we + started with.

+ +

IsReference on JsonObjectAttribute, JsonArrayAttribute and JsonPropertyAttribute

+ + +

The PreserveReferencesHandling setting on the JsonSerializer will change + how all objects are serialized and deserialized. For fine grain control over which + objects and members should be serialized as a reference there is the IsReference property on + the JsonObjectAttribute, JsonArrayAttribute and JsonPropertyAttribute.

+

Setting IsReference on JsonObjectAttribute or JsonArrayAttribute to true will + mean the JsonSerializer will always serialize the type the attribute is against + as a reference. Setting IsReference on the JsonPropertyAttribute to true will + serialize only that property as a reference.

+ +
+ +
+
[JsonObject(IsReference = true)]
+
public class EmployeeReference
+
{
+
  public string Name { get; set; }
+
  public EmployeeReference Manager { get; set; }
+
}
+
+ +
+

IReferenceResolver

+

To customize how references are generated and resolved the IReferenceResolver interface is available to inherit from and use with the JsonSerializer.

+ + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/ReadingWritingJSON.html b/trunk/Libraries/Json40r2/Source/Doc/ReadingWritingJSON.html new file mode 100644 index 0000000..207e146 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/ReadingWritingJSON.html @@ -0,0 +1,87 @@ + + + + Reading and Writing JSON + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Reading and Writing JSON
+ +
+ + +

To manually read and write JSON Json.NET provides the JsonReader and JsonWriter classes.

+ +

JsonTextReader and JsonTextWriter

+

JsonTextReader and JsonTextWriter are used to read and write JSON text. The JsonTextWriter has a number of settings on it to control how JSON is formatted when it is written. These options include formatting, indention character, indent count and quote character.

+ +
+ +
+
StringBuilder sb = new StringBuilder();
+
StringWriter sw = new StringWriter(sb);
+
 
+
using (JsonWriter jsonWriter = new JsonTextWriter(sw))
+
{
+
  jsonWriter.Formatting = Formatting.Indented;
+
 
+
  jsonWriter.WriteStartObject();
+
  jsonWriter.WritePropertyName("CPU");
+
  jsonWriter.WriteValue("Intel");
+
  jsonWriter.WritePropertyName("PSU");
+
  jsonWriter.WriteValue("500W");
+
  jsonWriter.WritePropertyName("Drives");
+
  jsonWriter.WriteStartArray();
+
  jsonWriter.WriteValue("DVD read/writer");
+
  jsonWriter.WriteComment("(broken)");
+
  jsonWriter.WriteValue("500 gigabyte hard drive");
+
  jsonWriter.WriteValue("200 gigabype hard drive");
+
  jsonWriter.WriteEnd();
+
  jsonWriter.WriteEndObject();
+
}
+
 
+
// {
+
//   "CPU": "Intel",
+
//   "PSU": "500W",
+
//   "Drives": [
+
//     "DVD read/writer"
+
//     /*(broken)*/,
+
//     "500 gigabyte hard drive",
+
//     "200 gigabype hard drive"
+
//   ]
+
// }
+
+ +
+ +

JTokenReader and JTokenWriter

+

JTokenReader and JTokenWriter read and write LINQ to JSON objects. They are located in the Newtonsoft.Json.Linq namespace. These objects allow you to use LINQ to JSON objects with objects that read and write JSON such as the JsonSerializer. For example you can deserialize from a LINQ to JSON object into a regular .NET object and vice versa.

+ +
+ +
+
JObject o = new JObject(
+
  new JProperty("Name", "John Smith"),
+
  new JProperty("BirthDate", new DateTime(1983, 3, 20))
+
  );
+
 
+
JsonSerializer serializer = new JsonSerializer();
+
Person p = (Person)serializer.Deserialize(new JTokenReader(o), typeof(Person));
+
 
+
Console.WriteLine(p.Name);
+
// John Smith
+
+ +
+ + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/ReducingSerializedJSONSize.html b/trunk/Libraries/Json40r2/Source/Doc/ReducingSerializedJSONSize.html new file mode 100644 index 0000000..64d002e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/ReducingSerializedJSONSize.html @@ -0,0 +1,280 @@ + + + + Reducing Serialized JSON Size + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Reducing Serialized JSON Size
+ +
+ +

One of the common problems encountered when serializing .NET objects to JSON is that the JSON ends up containing a lot of unwanted properties and values. This can be especially important when returning JSON to the client. More JSON means more bandwidth and a slower website.

+ +

To solve the issue of unwanted JSON Json.NET has a range of built in options to fine tune what gets written from a serialized object.

+ +

JsonIgnoreAttribute and DataMemberAttribute

+

By default Json.NET will include all of a classes public properties and fields in + the JSON it creates. Adding the JsonIgnoreAttribute to a property tells the serializer + to always skip writing it to the JSON result.

+ +
+
+
+
public class Car
+
{
+
  // included in JSON
+
  public string Model { get; set; }
+
  public DateTime Year { get; set; }
+
  public List<string> Features { get; set; }
+
 
+
  // ignored
+
  [JsonIgnore]
+
  public DateTime LastModified { get; set; }
+
}
+
+
+
+ +

If a class has many properties and you only want to serialize a small subset of + them then adding JsonIgnore to all the others will be tedious and error prone. + The way to tackle this scenario is to add the + DataContractAttribute to the class and + DataMemberAttributes to the properties to serialize. This is opt-in + serialization, only the properties you mark up with be serialized, compared to + opt-out serialization using JsonIgnoreAttribute.

+ +
+ +
+
+
[DataContract]
+
public class Computer
+
{
+
  // included in JSON
+
  [DataMember]
+
  public string Name { get; set; }
+
  [DataMember]
+
  public decimal SalePrice { get; set; }
+
 
+
  // ignored
+
  public string Manufacture { get; set; }
+
  public int StockCount { get; set; }
+
  public decimal WholeSalePrice { get; set; }
+
  public DateTime NextShipmentDate { get; set; }
+
}
+
+
+
+ +

Formatting

+ +

JSON written by the serializer with an option of Formatting.Indented produces nicely formatted, easy to read JSON – great when you are developing. Formatting.None on the other hand keeps the JSON result small, skipping all unnecessary spaces and line breaks to produce the most compact and efficient JSON possible.

+ +

NullValueHandling

+ +

NullValueHandling is an option on the JsonSerializer and controls how the serializer handles properties with a null value. By setting a value of NullValueHandling.Ignore the JsonSerializer skips writing any properties that have a value of null.

+ +
+
+
+
public class Movie
+
{
+
  public string Name { get; set; }
+
  public string Description { get; set; }
+
  public string Classification { get; set; }
+
  public string Studio { get; set; }
+
  public DateTime? ReleaseDate { get; set; }
+
  public List<string> ReleaseCountries { get; set; }
+
}
+
+
+
+ +
+
+
+
Movie movie = new Movie();
+
movie.Name = "Bad Boys III";
+
movie.Description = "It's no Bad Boys";
+
 
+
string included = JsonConvert.SerializeObject(movie,
+
  Formatting.Indented,
+
  new JsonSerializerSettings { });
+
 
+
// {
+
//   "Name": "Bad Boys III",
+
//   "Description": "It's no Bad Boys",
+
//   "Classification": null,
+
//   "Studio": null,
+
//   "ReleaseDate": null,
+
//   "ReleaseCountries": null
+
// }
+
 
+
string ignored = JsonConvert.SerializeObject(movie,
+
  Formatting.Indented,
+
  new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore });
+
 
+
// {
+
//   "Name": "Bad Boys III",
+
//   "Description": "It's no Bad Boys"
+
// }
+
+
+
+ +

NullValueHandling can also be customized on individual properties using the a JsonPropertyAttribute. The JsonPropertyAttribute value of NullValueHandling will override the setting on the JsonSerializer for that property.

+ +

DefaultValueHandling

+ +

DefaultValueHandling is an option on the JsonSerializer and controls how the serializer handles properties with a default value. Setting a value of DefaultValueHandling.Ignore will make the JsonSerializer skip writing any properties that have a default value to the JSON result. For object references this will be null. For value types like int and DateTime the serializer will skip the default unitialized value for that value type.

+ +

Json.NET also allows you to customize what the default value of an individual property is using the DefaultValueAttribute. For example if a string property called Department always returns an empty string in its default state and you didn't want that empty string in your JSON then placing the DefaultValueAttribute on Department with that value will mean Department is no longer written to JSON unless it has a value.

+ +
+
+
+
public class Invoice
+
{
+
  public string Company { get; set; }
+
  public decimal Amount { get; set; }
+
 
+
  // false is default value of bool
+
  public bool Paid { get; set; }
+
  // null is default value of nullable
+
  public DateTime? PaidDate { get; set; }
+
 
+
  // customize default values
+
  [DefaultValue(30)]
+
  public int FollowUpDays { get; set; }
+
  [DefaultValue("")]
+
  public string FollowUpEmailAddress { get; set; }
+
}
+
+
+
+ +
+
+
+
Invoice invoice = new Invoice
+
{
+
  Company = "Acme Ltd.",
+
  Amount = 50.0m,
+
  Paid = false,
+
  FollowUpDays = 30,
+
  FollowUpEmailAddress = string.Empty,
+
  PaidDate = null
+
};
+
 
+
string included = JsonConvert.SerializeObject(invoice,
+
  Formatting.Indented,
+
  new JsonSerializerSettings { });
+
 
+
// {
+
//   "Company": "Acme Ltd.",
+
//   "Amount": 50.0,
+
//   "Paid": false,
+
//   "PaidDate": null,
+
//   "FollowUpDays": 30,
+
//   "FollowUpEmailAddress": ""
+
// }
+
 
+
string ignored = JsonConvert.SerializeObject(invoice,
+
  Formatting.Indented,
+
  new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore });
+
 
+
// {
+
//   "Company": "Acme Ltd.",
+
//   "Amount": 50.0
+
// }
+
+
+
+ +

DefaultValueHandling can also be customized on individual properties using the a JsonPropertyAttribute. The JsonPropertyAttribute value of DefaultValueHandling will override the setting on the JsonSerializer for that property.

+ +

IContractResolver

+ +

For more flexibility the IContractResolver provides an interface to customize almost every aspect of how a .NET object gets serialized to JSON, including changing serialization behavior at runtime.

+ +
+
+
+
public class DynamicContractResolver : DefaultContractResolver
+
{
+
  private readonly char _startingWithChar;
+
  public DynamicContractResolver(char startingWithChar)
+
  {
+
    _startingWithChar = startingWithChar;
+
  }
+
 
+
  protected override IList<JsonProperty> CreateProperties(JsonObjectContract contract)
+
  {
+
    IList<JsonProperty> properties = base.CreateProperties(contract);
+
 
+
    // only serializer properties that start with the specified character
+
    properties = 
+
      properties.Where(p => p.PropertyName.StartsWith(_startingWithChar.ToString())).ToList();
+
 
+
    return properties;
+
  }
+
}
+
 
+
public class Book
+
{
+
  public string BookName { get; set; }
+
  public decimal BookPrice { get; set; }
+
  public string AuthorName { get; set; }
+
  public int AuthorAge { get; set; }
+
  public string AuthorCountry { get; set; }
+
}
+
+
+
+ +
+
+
+
Book book = new Book
+
              {
+
                BookName = "The Gathering Storm",
+
                BookPrice = 16.19m,
+
                AuthorName = "Brandon Sanderson",
+
                AuthorAge = 34,
+
                AuthorCountry = "United States of America"
+
              };
+
 
+
string startingWithA = JsonConvert.SerializeObject(book, Formatting.Indented,
+
  new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('A') });
+
 
+
// {
+
//   "AuthorName": "Brandon Sanderson",
+
//   "AuthorAge": 34,
+
//   "AuthorCountry": "United States of America"
+
// }
+
 
+
string startingWithB = JsonConvert.SerializeObject(book, Formatting.Indented,
+
  new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('B') });
+
 
+
// {
+
//   "BookName": "The Gathering Storm",
+
//   "BookPrice": 16.19
+
// }
+
+
+
+ + + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/SelectToken.html b/trunk/Libraries/Json40r2/Source/Doc/SelectToken.html new file mode 100644 index 0000000..14ecc5a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/SelectToken.html @@ -0,0 +1,116 @@ + + + + Querying LINQ to JSON with SelectToken + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Querying LINQ to JSON with SelectToken
+ +
+ +

SelectToken provides a method to query LINQ to JSON using a single string path to a + desired JToken. SelectToken makes dynamic queries + easy because the entire + query is defined in a string.

+ +
+
+
+
string name = (string)o.SelectToken("Manufacturers[0].Name");
+ +
+
+
+ +

SelectToken

+

SelectToken is a method on JToken and takes a string path to a child token. + SelectToken returns the child token or a null reference if a token couldn't be + found at the path's location.

+

The + path is made up of property names and array indexes separated by periods. Array indexes + can use either square or round brackets. Both + of the following are valid paths and are equivalent to each other: Manufacturers[0].Name + and Manufacturers(0).Name.

+ +
+
+
+
JObject o = JObject.Parse(@"{
+
  ""Stores"": [
+
    ""Lambton Quay"",
+
    ""Willis Street""
+
  ],
+
  ""Manufacturers"": [
+
    {
+
      ""Name"": ""Acme Co"",
+
      ""Products"": [
+
        {
+
          ""Name"": ""Anvil"",
+
          ""Price"": 50
+
        }
+
      ]
+
    },
+
    {
+
      ""Name"": ""Contoso"",
+
      ""Products"": [
+
        {
+
          ""Name"": ""Elbow Grease"",
+
          ""Price"": 99.95
+
        },
+
        {
+
          ""Name"": ""Headlight Fluid"",
+
          ""Price"": 4
+
        }
+
      ]
+
    }
+
  ]
+
}");
+
 
+
string name = (string)o.SelectToken("Manufacturers[0].Name");
+
// Acme Co
+
 
+
decimal productPrice = (decimal)o.SelectToken("Manufacturers[0].Products[0].Price");
+
// 50
+
 
+
string productName = (string)o.SelectToken("Manufacturers[1].Products[0].Name");
+
// Elbow Grease
+
+
+
+ +

SelectToken with LINQ

+ +

SelectToken can be used in combination with standard LINQ methods.

+ +
+ +
+ +
+
IList<string> storeNames = o.SelectToken("Stores").Select(s => (string)s).ToList();
+
// Lambton Quay
+
// Willis Street
+
 
+
IList<string> firstProductNames = o["Manufacturers"].Select(m => (string)m.SelectToken("Products[1].Name")).ToList();
+
// null
+
// Headlight Fluid
+
 
+
decimal totalPrice = o["Manufacturers"].Sum(m => (decimal)m.SelectToken("Products[0].Price"));
+
// 149.95
+
+ +
+
+ + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/SerializationAttributes.html b/trunk/Libraries/Json40r2/Source/Doc/SerializationAttributes.html new file mode 100644 index 0000000..eb3cce7 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/SerializationAttributes.html @@ -0,0 +1,123 @@ + + + + Customizing JSON serialization with attributes + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Customizing JSON serialization with attributes
+ +
+ + +

Attributes can be used to control how Json.NET serializes and deserializes .NET objects.

+ +

As well as using the built-in Json.NET attributes, Json.NET also looks for the + DataContract and + DataMember attributes when determining how JSON is to be serialized and deserialized. If both are present the Json.NET serialization attributes take precedence.

+ +
+ +
+
[JsonObject(MemberSerialization.OptIn)]
+
public class Person
+
{
+
  // "John Smith"
+
  [JsonProperty]
+
  public string Name { get; set; }
+
 
+
  // "2000-12-15T22:11:03"
+
  [JsonProperty]
+
  [JsonConverter(typeof(IsoDateTimeConverter))]
+
  public DateTime BirthDate { get; set; }
+
 
+
  // new Date(976918263055)
+
  [JsonProperty]
+
  [JsonConverter(typeof(JavaScriptDateTimeConverter))]
+
  public DateTime LastModified { get; set; }
+
 
+
  // not serialized
+
  public string Department { get; set; }
+
}
+
+ +
+ +
JsonObjectAttribute
+

The MemberSerialization flag on this attribute specifies whether member serialization is opt-in (a member must have the JsonProperty or DataMember attribute to be serialized) or opt-out (everything is serialized by default but can be ignored with the JsonIgnoreAttribute, + Json.NET's default behavor).

+

Json.NET serializes .NET classes that implement IEnumerable as an + JSON array populated with the IEnumerable values. Placing the JsonPropertyAttribute overrides this + behavor and forces the serializer to serialize the class's fields and properties.

+
JsonPropertyAttribute
+

JsonPropertyAttribute has a number of uses:

+ +
JsonIgnoreAttribute
+

Excludes a field or property from serialization.

+
JsonConverterAttribute
+

The JsonConverterAttribute specifies which JsonSerializer is used to convert an object.

+

The attribute can be placed on a class or a member. When placed on a class the JsonConverter + specified by the attribute will be the default way of serializing that class. When the attribute is on a field +or property then the specified JsonConverter will always be used to serialize that value.

+

The priority of which JsonConverter is used is member attribute then class attribute and +finally any converters passed to the JsonSerializer.

+ +
+
+
public class MemberConverterClass
+
{
+
  public DateTime DefaultConverter { get; set; }
+
  [JsonConverter(typeof(IsoDateTimeConverter))]
+
  public DateTime MemberConverter { get; set; }
+
}
+
+
+

This example shows the JsonConverterAttribute being applied to a property.

+ + +
+
+
DateTime date = Convert.ToDateTime("1970-01-01T00:00:00Z").ToUniversalTime();
+
 
+
MemberConverterClass c = new MemberConverterClass
+
  {
+
    DefaultConverter = date,
+
    MemberConverter = date
+
  };
+
 
+
string json = JsonConvert.SerializeObject(c, Formatting.Indented);
+
 
+
Console.WriteLine(json);
+
//{
+
//  "DefaultConverter": "\/Date(0)\/",
+
//  "MemberConverter": "1970-01-01T00:00:00Z"
+
//}
+
+ +
+ + + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/SerializationCallbacks.html b/trunk/Libraries/Json40r2/Source/Doc/SerializationCallbacks.html new file mode 100644 index 0000000..11fd37c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/SerializationCallbacks.html @@ -0,0 +1,143 @@ + + + + Serialization Callbacks + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Serialization Callbacks
+ +
+ + +

Json.NET supports serialization callback methods. A callback can be used to + manipulate an object before and after its serialization and deserialization by + the JsonSerializer.

+ +

To tell the serializer which methods should be called during the object's serialization + lifecycle, decorate a method with the appropraite attribute (OnSerializingAttribute, + OnSerializedAttribute, + OnDeserializingAttribute, + OnDeserializedAttribute).

+

Example object with serialization callback methods:

+ +
+
+
public class SerializationEventTestObject
+
{
+
  // This member is serialized and deserialized with no change.
+
  public int Member1 { get; set; }
+
 
+
  // The value of this field is set and reset during and 
+
  // after serialization.
+
  public string Member2 { get; set; }
+
 
+
  // This field is not serialized. The OnDeserializedAttribute 
+
  // is used to set the member value after serialization.
+
  [JsonIgnore]
+
  public string Member3 { get; set; }
+
 
+
  // This field is set to null, but populated after deserialization.
+
  public string Member4 { get; set; }
+
 
+
  public SerializationEventTestObject()
+
  {
+
    Member1 = 11;
+
    Member2 = "Hello World!";
+
    Member3 = "This is a nonserialized value";
+
    Member4 = null;
+
  }
+
 
+
  [OnSerializing]
+
  internal void OnSerializingMethod(StreamingContext context)
+
  {
+
    Member2 = "This value went into the data file during serialization.";
+
  }
+
 
+
  [OnSerialized]
+
  internal void OnSerializedMethod(StreamingContext context)
+
  {
+
    Member2 = "This value was reset after serialization.";
+
  }
+
 
+
  [OnDeserializing]
+
  internal void OnDeserializingMethod(StreamingContext context)
+
  {
+
    Member3 = "This value was set during deserialization";
+
  }
+
 
+
  [OnDeserialized]
+
  internal void OnDeserializedMethod(StreamingContext context)
+
  {
+
    Member4 = "This value was set after deserialization.";
+
  }
+
}
+
+
+ +

The example object being serialized and deserialized by Json.NET:

+ +
+
+
SerializationEventTestObject obj = new SerializationEventTestObject();
+
 
+
Console.WriteLine(obj.Member1);
+
// 11
+
Console.WriteLine(obj.Member2);
+
// Hello World!
+
Console.WriteLine(obj.Member3);
+
// This is a nonserialized value
+
Console.WriteLine(obj.Member4);
+
// null
+
 
+
string json = JsonConvert.SerializeObject(obj, Formatting.Indented);
+
// {
+
//   "Member1": 11,
+
//   "Member2": "This value went into the data file during serialization.",
+
//   "Member4": null
+
// }
+
 
+
Console.WriteLine(obj.Member1);
+
// 11
+
Console.WriteLine(obj.Member2);
+
// This value was reset after serialization.
+
Console.WriteLine(obj.Member3);
+
// This is a nonserialized value
+
Console.WriteLine(obj.Member4);
+
// null
+
 
+
obj = JsonConvert.DeserializeObject<SerializationEventTestObject>(json);
+
 
+
Console.WriteLine(obj.Member1);
+
// 11
+
Console.WriteLine(obj.Member2);
+
// This value went into the data file during serialization.
+
Console.WriteLine(obj.Member3);
+
// This value was set during deserialization
+
Console.WriteLine(obj.Member4);
+
// This value was set after deserialization.
+
+
+ + + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/SerializationErrorHandling.html b/trunk/Libraries/Json40r2/Source/Doc/SerializationErrorHandling.html new file mode 100644 index 0000000..5a50730 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/SerializationErrorHandling.html @@ -0,0 +1,172 @@ + + + + Serialization Error Handling + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Serialization Error Handling
+ +
+ + +

Json.NET supports error handling during serialization and deserialization. Error handling lets + you catch an error and choose whether to handle it and continue with serialization or let the error + bubble up and be thrown in your application.

+

Error handling is defined through two methods: + the Error event on JsonSerializer and the OnErrorAttribute.

+

Error Event

+ +

+ The Error event is an event handler found on JsonSerializer. The error event is raised whenever an + exception is thrown while serializing or deserialing JSON. Like all settings found on JsonSerializer + it can also be set on JsonSerializerSettings and passed to the serialization methods on JsonConvert.

+ +
+
+
List<string> errors = new List<string>();
+
 
+
List<DateTime> c = JsonConvert.DeserializeObject<List<DateTime>>(@"[
+
  ""2009-09-09T00:00:00Z"",
+
  ""I am not a date and will error!"",
+
  [
+
    1
+
  ],
+
  ""1977-02-20T00:00:00Z"",
+
  null,
+
  ""2000-12-01T00:00:00Z""
+
]",
+
  new JsonSerializerSettings
+
    {
+
      Error = delegate(object sender, ErrorEventArgs args)
+
        {
+
          errors.Add(args.ErrorContext.Error.Message);
+
          args.ErrorContext.Handled = true;
+
        },
+
      Converters = { new IsoDateTimeConverter() }
+
    });
+
 
+
// 2009-09-09T00:00:00Z
+
// 1977-02-20T00:00:00Z
+
// 2000-12-01T00:00:00Z
+
 
+
// The string was not recognized as a valid DateTime. There is a unknown word starting at index 0.
+
// Unexpected token parsing date. Expected String, got StartArray.
+
// Cannot convert null value to System.DateTime.
+
+
+
+

+ In this example we are deserializing a JSON array to a collection of DateTimes. On the JsonSerializerSettings + a handler has been assigned to the Error event which will log the error message and mark the error as handled. +

+

+ The result of deserializing the JSON is three successfully deserialized dates and three error messages: + one for the badly formatted string, "I am not a date and will error!", one for the nested JSON array and one + for the null value since the list doesn't allow nullable DateTimes. The event handler has logged these messages + and Json.NET has continued on deserializing the JSON because the errors were marked as handled. +

+

+ One thing to note with error handling in Json.NET is that an unhandled error will bubble up and raise the event + on each of its parents, e.g. an unhandled error when serializing a collection of objects will be raised twice, + once against the object and then again on the collection. This will let you handle an error either where it + occurred or on one of its parents. +

+ + +
+
+
JsonSerializer serializer = new JsonSerializer();
+
serializer.Error += delegate(object sender, ErrorEventArgs args)
+
  {
+
    // only log an error once
+
    if (args.CurrentObject == args.ErrorContext.OriginalObject)
+
      errors.Add(args.ErrorContext.Error.Message);
+
  };
+
+
+ +

If you aren't immediately handling an error and only want to perform an action against it once then +you can check to see whether the ErrorEventArg's CurrentObject is equal to the OriginalObject. +OriginalObject is the object that threw the error and CurrentObject is the object that the event is being raised +against. They will only equal the first time the event is raised against the OriginalObject.

+ +

OnErrorAttribute

+ +

+ The OnErrorAttribute works much like the other .NET serialization attributes that Json.NET supports. + To use it you simply place the attribute on a method which takes the correct parameters: a StreamingContext and a ErrorContext. + The name of the method doesn't matter.

+
+ +
+
public class PersonError
+
{
+
  private List<string> _roles;
+
 
+
  public string Name { get; set; }
+
  public int Age { get; set; }
+
  public List<string> Roles
+
  {
+
    get
+
    {
+
      if (_roles == null)
+
        throw new Exception("Roles not loaded!");
+
 
+
      return _roles;
+
    }
+
    set { _roles = value; }
+
  }
+
  public string Title { get; set; }
+
 
+
  [OnError]
+
  internal void OnError(StreamingContext context, ErrorContext errorContext)
+
  {
+
    errorContext.Handled = true;
+
  }
+
}
+
+
+

+ In this example accessing the the Roles property will throw an exception when no roles have + been set. The HandleError method will set the error when serializing Roles as handled and allow Json.NET to continue + serializing the class. +

+ +
+
+
PersonError person = new PersonError
+
  {
+
    Name = "George Michael Bluth",
+
    Age = 16,
+
    Roles = null,
+
    Title = "Mister Manager"
+
  };
+
 
+
string json = JsonConvert.SerializeObject(person, Formatting.Indented);
+
 
+
Console.WriteLine(json);
+
//{
+
//  "Name": "George Michael Bluth",
+
//  "Age": 16,
+
//  "Title": "Mister Manager"
+
//}
+
+
+ + + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/SerializingCollections.html b/trunk/Libraries/Json40r2/Source/Doc/SerializingCollections.html new file mode 100644 index 0000000..53e59d3 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/SerializingCollections.html @@ -0,0 +1,128 @@ + + + + Serializing Collections + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Serializing Collections
+ +
+ + +

The JsonSerializer has + great support for serializing and deserializing collections of objects.

+ +

Serializing

+

To serialize a collection - a generic list, array, dictionary, or your own custom collection - simply call the serializer with the object you want to get JSON for. + Json.NET will serialize the collection and all of the values it contains.

+ + +
+ +
+
Product p1 = new Product
+
  {
+
    Name = "Product 1",
+
    Price = 99.95m,
+
    ExpiryDate = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc),
+
  };
+
Product p2 = new Product
+
{
+
  Name = "Product 2",
+
  Price = 12.50m,
+
  ExpiryDate = new DateTime(2009, 7, 31, 0, 0, 0, DateTimeKind.Utc),
+
};
+
 
+
List<Product> products = new List<Product>();
+
products.Add(p1);
+
products.Add(p2);
+
 
+
string json = JsonConvert.SerializeObject(products, Formatting.Indented);
+
//[
+
//  {
+
//    "Name": "Product 1",
+
//    "ExpiryDate": "\/Date(978048000000)\/",
+
//    "Price": 99.95,
+
//    "Sizes": null
+
//  },
+
//  {
+
//    "Name": "Product 2",
+
//    "ExpiryDate": "\/Date(1248998400000)\/",
+
//    "Price": 12.50,
+
//    "Sizes": null
+
//  }
+
//]
+
+ +
+ +

Deserializing

+

To deserialize JSON into a .NET collection just specify the collection type you want to deserialize to. Json.NET supports a wide range of collection types.

+ +
+ +
+
string json = @"[
+
  {
+
    ""Name"": ""Product 1"",
+
    ""ExpiryDate"": ""\/Date(978048000000)\/"",
+
    ""Price"": 99.95,
+
    ""Sizes"": null
+
  },
+
  {
+
    ""Name"": ""Product 2"",
+
    ""ExpiryDate"": ""\/Date(1248998400000)\/"",
+
    ""Price"": 12.50,
+
    ""Sizes"": null
+
  }
+
]";
+
 
+
List<Product> products = JsonConvert.DeserializeObject<List<Product>>(json);
+
 
+
Console.WriteLine(products.Count);
+
// 2
+
 
+
Product p1 = products[0];
+
 
+
Console.WriteLine(p1.Name);
+
// Product 1
+
+ +
+ +

Deserializing Dictionaries

+ +

Using Json.NET you can also deserialize a JSON object into a .NET generic dictionary. The JSON object's property names and values will be added to the dictionary.

+ +
+ +
+
string json = @"{""key1"":""value1"",""key2"":""value2""}";
+
 
+
Dictionary<string, string> values = JsonConvert.DeserializeObject<Dictionary<string, string>>(json);
+
 
+
Console.WriteLine(values.Count);
+
// 2
+
 
+
Console.WriteLine(values["key1"]);
+
// value1
+
+ +
+ + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/SerializingJSON.html b/trunk/Libraries/Json40r2/Source/Doc/SerializingJSON.html new file mode 100644 index 0000000..b08e870 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/SerializingJSON.html @@ -0,0 +1,146 @@ + + + + Serializing and deserializing JSON + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Serializing and deserializing JSON
+ +
+ + +

The quickest method of converting between JSON text and a .NET object is using + the JsonSerializer. The JsonSerializer converts .NET objects into their JSON + equivalent and back again.

+ + +

For simple scenarios where you want to convert to and from a JSON string the SerializeObject and DeserializeObject methods on JsonConvert provide an easy to use wrapper over + JsonSerializer.

+ + +
+ +
+
Product product = new Product();
+
 
+
product.Name = "Apple";
+
product.Expiry = new DateTime(2008, 12, 28);
+
product.Price = 3.99M;
+
product.Sizes = new string[] { "Small", "Medium", "Large" };
+
 
+
string output = JsonConvert.SerializeObject(product);
+
//{
+
//  "Name": "Apple",
+
//  "Expiry": "\/Date(1230375600000+1300)\/",
+
//  "Price": 3.99,
+
//  "Sizes": [
+
//    "Small",
+
//    "Medium",
+
//    "Large"
+
//  ]
+
//}
+
 
+
Product deserializedProduct = JsonConvert.DeserializeObject<Product>(output);
+
+ +
+ +

JsonSerializer

+ + +

For more control over how an object is serialized the JsonSerializer can be used + directly. The JsonSerializer is able to read and write JSON text directly to + a stream via JsonTextReader and JsonTextWriter. Other kinds of JsonWriters can + also be used such as JTokenReader/JTokenWriter to + convert your object to and + from LINQ to JSON objects or BsonReader/BsonWriter to convert to and from BSON.

+ +
+ +
+
Product product = new Product();
+
product.Expiry = new DateTime(2008, 12, 28);
+
 
+
JsonSerializer serializer = new JsonSerializer();
+
serializer.Converters.Add(new JavaScriptDateTimeConverter());
+
serializer.NullValueHandling = NullValueHandling.Ignore;
+
 
+
using (StreamWriter sw = new StreamWriter(@"c:\json.txt"))
+
using (JsonWriter writer = new JsonTextWriter(sw))
+
{
+
  serializer.Serialize(writer, product);
+
  // {"Expiry":new Date(1230375600000),"Price":0}
+
}
+
+ +
+

JsonSerializer has a number of properties on it to customize how it serializes JSON. + These can also be used with the methods on JsonConvert via the + JsonSerializerSettings overloads.

+ +
ReferenceLoopHandling
+

Controls how circular referencing objects are serialized. Error, ignore or + serialize.

+
MissingMemberHandling
+

Controls how missing members (e.g. JSON contains a property that isn't a member + on the object) are handled during deserialization. Ignore or error.

+
NullValueHandling
+

Controls how null values are handled during serialization and deserialization. + Include or ignore.

+
DefaultValueHandling
+

Controls whether a value will be written to JSON or not if it matches the value specified in +the member's DefaultValueAttribute. Include or ignore.

+
ObjectCreationHandling
+

Controls how objects are created during deserialization. Auto, reuse, replace.

+
TypeNameHandling
+

Controls whether .NET type names are included in serialized JSON and read during deserialization when creating objects. None, Objects, Arrays or All.

+
ConstructorHandling
+

Controls how constructors are used when initializing objects during deserialization. Default or AllowNonPublicDefaultConstructor.

+
Converters
+

A collection of JsonConverters that will be used during serialization and + deserialization.

+ + +

JsonConverters

+

JsonConverters allows JSON to be manually written during serialization and read during deserialization. This is useful for particularly complex JSON structures or for when you want to change how a type is serialized.

+

To create your own custom converter inherit from the JsonConverter class. Json.NET also comes with a number of JsonConverters:

+ + + +
DateTime JSON Converters
+

Json.NET comes with a number of JsonConverters for serializing and deserializing DateTimes. Read + more about dates and Json.NET here.

+ +
XmlNodeConverter
+

Converts an XmlNode to and from JSON. Note that to convert a JSON object it must have only a single property + or you must define a root node name to be inserted when using this converter. This is required because properties are converted into nodes and + well formed XML can only have one root node. XmlNodeConverter has an option to + insert a root node for you.

+ + +
BinaryConverter
+

Converts binary data like the SqlBinary object to JSON. The binary + data is written as a string in JSON and is encoded in Base64.

+ +
CustomCreationConverter
+

An abstract JsonConverter for customizing how an object is create during deserialization. + Inherit from this class and implement the Create method with your own code to create and return an object. + The object will then be populated with JSON values by the serializer.

+

A possible example of using this converter would be to call out to a dependency injection framework to resolve what object should be created.

+ + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/SerializingJSONFragments.html b/trunk/Libraries/Json40r2/Source/Doc/SerializingJSONFragments.html new file mode 100644 index 0000000..dca923f --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/SerializingJSONFragments.html @@ -0,0 +1,125 @@ + + + + Serializing Partial JSON Fragments + + + + + + +
+ Json.NET - Quick Starts & API Documentation
+ Serializing Partial JSON Fragments
+ +
+ +

Often when working with large JSON documents you're only interested in a small fragment of information. + This scenario can be annoying when you want to serialize that Json.NET into .NET + objects because you have to define .NET classes for the entire JSON result.

+

With Json.NET it is easy to get around this problem. Using LINQ to JSON you can + extract the pieces of JSON you want to serialize before passing them to the Json.NET serializer.

+ +
+
+ +
+
public class SearchResult
+
{
+
  public string Title { get; set; }
+
  public string Content { get; set; }
+
  public string Url { get; set; }
+
}
+
+ +
+
+ +
+
+ +
+
string googleSearchText = @"{
+
  ""responseData"": {
+
    ""results"": [
+
      {
+
        ""GsearchResultClass"": ""GwebSearch"",
+
        ""unescapedUrl"": ""http://en.wikipedia.org/wiki/Paris_Hilton"",
+
        ""url"": ""http://en.wikipedia.org/wiki/Paris_Hilton"",
+
        ""visibleUrl"": ""en.wikipedia.org"",
+
        ""cacheUrl"": ""http://www.google.com/search?q=cache:TwrPfhd22hYJ:en.wikipedia.org"",
+
        ""title"": ""<b>Paris Hilton</b> - Wikipedia, the free encyclopedia"",
+
        ""titleNoFormatting"": ""Paris Hilton - Wikipedia, the free encyclopedia"",
+
        ""content"": ""[1] In 2006, she released her debut album...""
+
      },
+
      {
+
        ""GsearchResultClass"": ""GwebSearch"",
+
        ""unescapedUrl"": ""http://www.imdb.com/name/nm0385296/"",
+
        ""url"": ""http://www.imdb.com/name/nm0385296/"",
+
        ""visibleUrl"": ""www.imdb.com"",
+
        ""cacheUrl"": ""http://www.google.com/search?q=cache:1i34KkqnsooJ:www.imdb.com"",
+
        ""title"": ""<b>Paris Hilton</b>"",
+
        ""titleNoFormatting"": ""Paris Hilton"",
+
        ""content"": ""Self: Zoolander. Socialite <b>Paris Hilton</b>...""
+
      }
+
    ],
+
    ""cursor"": {
+
      ""pages"": [
+
        {
+
          ""start"": ""0"",
+
          ""label"": 1
+
        },
+
        {
+
          ""start"": ""4"",
+
          ""label"": 2
+
        },
+
        {
+
          ""start"": ""8"",
+
          ""label"": 3
+
        },
+
        {
+
          ""start"": ""12"",
+
          ""label"": 4
+
        }
+
      ],
+
      ""estimatedResultCount"": ""59600000"",
+
      ""currentPageIndex"": 0,
+
      ""moreResultsUrl"": ""http://www.google.com/search?oe=utf8&ie=utf8...""
+
    }
+
  },
+
  ""responseDetails"": null,
+
  ""responseStatus"": 200
+
}";
+
 
+
JObject googleSearch = JObject.Parse(googleSearchText);
+
 
+
// get JSON result objects into a list
+
IList<JToken> results = googleSearch["responseData"]["results"].Children().ToList();
+
 
+
// serialize JSON results into .NET objects
+
IList<SearchResult> searchResults = new List<SearchResult>();
+
foreach (JToken result in results)
+
{
+
  SearchResult searchResult = JsonConvert.DeserializeObject<SearchResult>(result.ToString());
+
  searchResults.Add(searchResult);
+
}
+
 
+
// Title = <b>Paris Hilton</b> - Wikipedia, the free encyclopedia
+
// Content = [1] In 2006, she released her debut album...
+
// Url = http://en.wikipedia.org/wiki/Paris_Hilton
+
 
+
// Title = <b>Paris Hilton</b>
+
// Content = Self: Zoolander. Socialite <b>Paris Hilton</b>...
+
// Url = http://www.imdb.com/name/nm0385296/
+
+ + +
+
+ + +
+ + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/custom.css b/trunk/Libraries/Json40r2/Source/Doc/custom.css new file mode 100644 index 0000000..3bcd480 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/custom.css @@ -0,0 +1,19 @@ +div#content { + padding: 0 10px 10px 10px; +} + +.overflowpanel +{ + width: 98%; + border: solid 1px #ccc; + overflow: auto; + overflow-y: hidden; + background-color: #fcfcfc; + margin-bottom: 1em; +} + +.overflowpanel .code +{ + padding: 10px 4px 20px 10px; + display: block; +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/doc.shfbproj b/trunk/Libraries/Json40r2/Source/Doc/doc.shfbproj new file mode 100644 index 0000000..84f6d68 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/doc.shfbproj @@ -0,0 +1,87 @@ + + + + Debug + AnyCPU + 2.0 + {fd19f68e-72c8-4576-9775-b4480b0db686} + 1.9.0.0 + + Documentation + Documentation + Documentation + + ..\Working\Documentation\ + Documentation + + + Summary, Parameter, Returns, AutoDocumentCtors, Namespace, TypeParameter + InheritedMembers, InheritedFrameworkMembers, Protected, SealedProtected + + + + + + + + + + + HtmlHelp1, MSHelpViewer, Website + 3.5 + Json.NET - Quick Starts &amp%3b API Documentation + Prototype + MemberName + ..\Src\Newtonsoft.Json\bin\Release + + + + + + + The <b>Newtonsoft.Json</b> namespace provides classes that are used to implement the core services of the framework. + The <b>Newtonsoft.Json.Converters</b> namespace provides classes that inherit from <a href="T_Newtonsoft_Json_JsonConverter.htm">JsonConverter</a>. + The <b>Newtonsoft.Json.Linq</b> namespace provides classes that are used to implement LINQ to JSON. + The <b>Newtonsoft.Json.Linq.ComponentModel</b> namespace provides classes for LINQ to JSON databinding. + The <b>Newtonsoft.Json.Schema</b> namespace provides classes that are used to implement JSON schema. + The <b>Newtonsoft.Json.Linq</b> namespace provides classes that are used when serializing and deserializing JSON. + + The <b>Newtonsoft.Json.Linq</b> namespace provides classes that are used to implement BSON. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/doc.sitemap b/trunk/Libraries/Json40r2/Source/Doc/doc.sitemap new file mode 100644 index 0000000..996625e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/doc.sitemap @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/donate.gif b/trunk/Libraries/Json40r2/Source/Doc/donate.gif new file mode 100644 index 0000000..d017250 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Doc/donate.gif differ diff --git a/trunk/Libraries/Json40r2/Source/Doc/readme.txt b/trunk/Libraries/Json40r2/Source/Doc/readme.txt new file mode 100644 index 0000000..ca85148 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/readme.txt @@ -0,0 +1,59 @@ +Json.NET + +http://james.newtonking.com/projects/json-net.aspx +http://www.codeplex.com/json/ + + +Description: + +Json.NET makes working with JSON formatted data in .NET simple. Quickly read and write JSON using LINQ to JSON or serialize your .NET objects with a single method call using the JsonSerializer. + +-Flexible JSON serializer to convert .NET objects to JSON and back again +-LINQ to JSON for reading and writing JSON +-Writes indented, easy to read JSON +-Convert JSON to and from XML +-Supports Silverlight and Windows Phone + + + +Versions: + +Json.NET comes in different versions for the various .NET frameworks. + +-DotNet: + .NET latest (4.0) + +-DotNet35: + .NET 3.5 SP1, Mono + +-DotNet20: + .NET 2.0 + +-Silverlight: + Silverlight 4.0 + +-WindowsPhone: + Windows Phone 7 + +Microsoft stopped support for the Compact Framework in Visual Studio 2010. +For a Compact Framework 3.5 build download Json.NET 3.5. + +For a Silverlight 3.0 build down download Json.NET 3.5. + + +Instructions: + + 1. Extract Newtonsoft.Json.dll and Newtonsoft.Json.xml from the archive's /bin directory into your own applications. + 2. Add a reference to Newtonsoft.Json.dll within Visual Studio.NET to your project. + + + +License: + +Copyright (c) 2007 James Newton-King + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Doc/styles.css b/trunk/Libraries/Json40r2/Source/Doc/styles.css new file mode 100644 index 0000000..47278fb --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/styles.css @@ -0,0 +1,439 @@ +/* page style */ + +body { + margin: 0; + background-color: #FFFFFF; + padding: 0; + font-size: 8.5pt; + font-family: verdana, arial, sans-serif; + color: #000000; +} + +html>body { + margin: 0; + background-color: #FFFFFF; + padding: 0; + font-size: 8.5pt; + font-family: verdana, arial, sans-serif; + color: #000000; + overflow: auto; +} + +table { + /* this is a trick to force tables to inherit the body font size */ + font-size: 100%; +} + +/* non-scrolling (control) region style */ + +div#control { + margin: 0; + background-color: #D4DFFF; + padding: 4px; + width: 100%; + border-bottom-color: #C8CDDE; + border-bottom-style: solid; + border-bottom-width: 1px; + z-index: 2; +} + +span.productTitle { + font-size: 80%; +} + +span.topicTitle { + font-size: 140%; + font-weight: bold; + color: #003399; +} + +span#chickenFeet { + float: left; +} + +span#languageFilter { + float: right; + height: 1px; + max-height: 1px; + vertical-align: bottom; + overflow: visible; +} + +/* scrolling (content) region style */ + +div#main +{ + clear: both; + margin: 0; + padding: 1em; + width: 100%; + z-index: 1; + overflow: auto; +} + +/* sections */ + +div#header { + font-size: 80%; + color: #666666; + margin-bottom: 0.5em; +} + +div.summary { + margin-top: 0em; + margin-bottom: 1em; +} + +div.section { + margin-bottom: 1em; +} + +div.sectionTitle { + display: inline; + font-size: 120%; + font-weight: bold; + color: #003399; +} + +div.sectionContent { + margin-top: 0.2em; +} + +span.subsectionTitle { + font-weight: bold; +} + +div#footer { + margin-top: 1em; + border-top: thin solid #003399; + padding-top: 0.5em; +} + +div#footer p { + margin-top: 0.2em; + margin-bottom: 0.2em; +} + +/* authored content (block) */ + +p { + margin-top: 1em; + margin-bottom: 1em; +} + +dl, ul, ol { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + +pre { + margin: 0; + padding: 0; + font-family: "Andale Mono", "Courier New", Courier, monospace; +} + +table.authoredTable { + table-layout: fixed; + width: 100%; + margin-bottom: 1em; +} + +table.authoredTable th { + border-bottom-color: #C8CDDE; + border-bottom-style: solid; + border-bottom-width: 1px; + background: #EFEFF7; + padding: 0.2em; + text-align: left; + color: #000066; + font-weight: bold; +} + +table.authoredTable td { + border-bottom-style: solid; + border-bottom-color: #C8CDDE; + border-bottom-width: 1px; + background: #F7F7FF; + padding: 0.2em; + vertical-align: top; +} + +div.alert { + border: 1px solid #C8CDDE; + background: #F7F7FF; +} + +div.media { + text-align: center; + margin-bottom: 1em; +} + + +/* authored content (inline) */ + +span.keyword { + font-weight: bold; +} + +span.code { + font-family: "Andale Mono", "Courier New", Courier, monospace; + font-size: 110%; + color: #000066; +} + +/* auto-generated controls */ + +div.langTabs { + /*width: 100%;*/ +} + +div.langTab { + float: left; + width: 15%; + border-top: 1px solid #C8CDDE; + border-left: 1px solid #C8CDDE; + border-right: 1px solid #C8CDDE; + background: #F7F7FF; + padding: 0.2em; + text-align: left; + color: #000066; + font-weight: normal; +} + +div.activeLangTab { + float: left; + width: 15%; + border-top: 1px solid #C8CDDE; + border-left: 1px solid #C8CDDE; + border-right: 1px solid #C8CDDE; + background: #EFEFF7; + padding: 0.2em; + text-align: left; + color: #000066; + font-weight: bold; +} + +table.members { + /* table-layout: fixed; */ + width: 100%; +} + +table.members th.iconColumn { + width: 60px; +} + +table.members th.nameColumn { + width: 40%; +} + +table.members th.descriptionColumn { + width: 60%; +} + +table.members th { + border-bottom-color: #C8CDDE; + border-bottom-style: solid; + border-bottom-width: 1px; + background: #EFEFF7; + padding: 0.2em; + text-align: left; + color: #000066; + font-weight: bold; +} + +table.members td { + border-bottom-style: solid; + border-bottom-color: #C8CDDE; + border-bottom-width: 1px; + background: #F7F7FF; + padding: 0.2em; + vertical-align: top; + overflow: hidden; +} + +table.exceptions { + table-layout: fixed; + width: 100%; +} + + +table.exceptions th.exceptionNameColumn { + width: 40%; +} + +table.exceptions th.exceptionConditionColumn { + width: 60%; +} + +table.exceptions th { + border-bottom-color: #C8CDDE; + border-bottom-style: solid; + border-bottom-width: 1px; + background: #EFEFF7; + padding: 0.2em; + text-align: left; + color: #000066; + font-weight: bold; +} + +table.exceptions td { + border-bottom-style: solid; + border-bottom-color: #C8CDDE; + border-bottom-width: 1px; + background: #F7F7FF; + padding: 0.2em; + vertical-align: top; +} + +table.permissions { + table-layout: fixed; + width: 100%; +} + + +table.permissions th.permissionNameColumn { + width: 40%; +} + +table.permissions th.permissionConditionColumn { + width: 60%; +} + +table.permissions th { + border-bottom-color: #C8CDDE; + border-bottom-style: solid; + border-bottom-width: 1px; + background: #EFEFF7; + padding: 0.2em; + text-align: left; + color: #000066; + font-weight: bold; +} + +table.permissions td { + border-bottom-style: solid; + border-bottom-color: #C8CDDE; + border-bottom-width: 1px; + background: #F7F7FF; + padding: 0.2em; + vertical-align: top; +} + +span.obsolete { + color: red; +} + +span.cs { + display: inline; +} + +span.vb { + display: none; +} + +span.cpp { + display: none; +} + +span.nu +{ + display: none; +} + +/* syntax styling */ + +div.code span.identifier { + font-weight: bold; +} + +div.code span.keyword { + color: green; +} + +div.code span.parameter { + font-style: italic; + color: purple; +} + +div.code span.literal { + color: purple; +} + +div.code span.comment { + color: red; +} + +span.foreignPhrase { + font-style: italic; +} + +span.placeholder { + font-style: italic; +} + +span.parameter +{ + font-style: italic; +} + +span.typeparameter +{ + font-style: italic; +} + +a { + color: blue; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +MSHelp\:link { + color: blue; + hoverColor: #3366ff; +} + +span.nolink { + font-weight: bold; +} + +span.selflink { + font-weight: bold; +} + +table.filter { + table-layout: fixed; +} + +tr.tabs td.tab { + width: 10em; + background: #F7F7FF; + padding: 0.2em; + text-align: left; + color: #000066; + font-weight: normal; + overflow: hidden; + cursor: pointer; +} + +tr.tabs td.activeTab { + width: 10em; + background: #EFEFF7; + padding: 0.2em; + text-align: left; + color: #000066; + font-weight: bold; + overflow: hidden; +} + +td.line { + background: #EFEFF7; +} + +dt { + font-weight: bold; + margin-top: 10px; + margin-left: 10px; +} diff --git a/trunk/Libraries/Json40r2/Source/Doc/versions.txt b/trunk/Libraries/Json40r2/Source/Doc/versions.txt new file mode 100644 index 0000000..db86a13 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Doc/versions.txt @@ -0,0 +1,23 @@ +Versions: + +Json.NET comes in different versions for the various .NET frameworks. + +-DotNet: + .NET latest (4.0) + +-DotNet35: + .NET 3.5 SP1, Mono + +-DotNet20: + .NET 2.0 + +-Silverlight: + Silverlight 4.0 + +-WindowsPhone: + Windows Phone 7 + +Microsoft stopped support for the Compact Framework in Visual Studio 2010. +For a Compact Framework 3.5 build download Json.NET 3.5. + +For a Silverlight 3.0 build down download Json.NET 3.5. \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Lib/LinqBridge.dll b/trunk/Libraries/Json40r2/Source/Src/Lib/LinqBridge.dll new file mode 100644 index 0000000..305c9bb Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Src/Lib/LinqBridge.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Src/Lib/NUnit/Compact/NUnitLite.dll b/trunk/Libraries/Json40r2/Source/Src/Lib/NUnit/Compact/NUnitLite.dll new file mode 100644 index 0000000..2bf1533 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Src/Lib/NUnit/Compact/NUnitLite.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Src/Lib/NUnit/DotNet/nunit.framework.dll b/trunk/Libraries/Json40r2/Source/Src/Lib/NUnit/DotNet/nunit.framework.dll new file mode 100644 index 0000000..de687d9 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Src/Lib/NUnit/DotNet/nunit.framework.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Src/Lib/NUnit/DotNet/nunit.framework.xml b/trunk/Libraries/Json40r2/Source/Src/Lib/NUnit/DotNet/nunit.framework.xml new file mode 100644 index 0000000..bf4c30c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Lib/NUnit/DotNet/nunit.framework.xml @@ -0,0 +1,5521 @@ + + + + nunit.framework + + + + + SubstringConstraint can test whether a string contains + the expected substring. + + + + + The Constraint class is the base of all built-in or + user-defined constraints in NUnit. It provides the operator + overloads used to combine constraints. + + + + + Static UnsetObject used to detect derived constraints + failing to set the actual value. + + + + + If true, all string comparisons will ignore case + + + + + If true, arrays will be treated as collections, allowing + those of different dimensions to be compared + + + + + If non-zero, equality comparisons within the specified + tolerance will succeed. + + + + + IComparer object used in comparisons for some constraints. + + + + + The actual value being tested against a constraint + + + + + Flag the constraint to use a tolerance when determining equality. + Currently only used for doubles and floats. + + Tolerance to be used + Self. + + + + Flag the constraint to use the supplied IComparer object. + + The IComparer object to use. + Self. + + + + Write the failure message to the MessageWriter provided + as an argument. The default implementation simply passes + the constraint and the actual value to the writer, which + then displays the constraint description and the value. + + Constraints that need to provide additional details, + such as where the error occured can override this. + + The MessageWriter on which to display the message + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + This operator creates a constraint that is satisfied only if both + argument constraints are satisfied. + + + + + This operator creates a constraint that is satisfied if either + of the argument constraints is satisfied. + + + + + This operator creates a constraint that is satisfied if the + argument constraint is not satisfied. + + + + + Flag the constraint to ignore case and return self. + + + + + Flag the constraint to compare arrays as collections + and return self. + + + + + Class used to detect any derived constraints + that fail to set the actual value in their + Matches override. + + + + + Initializes a new instance of the class. + + The expected. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + StartsWithConstraint can test whether a string starts + with an expected substring. + + + + + Initializes a new instance of the class. + + The expected string + + + + Test whether the constraint is matched by the actual value. + This is a template method, which calls the IsMatch method + of the derived class. + + + + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + EndsWithConstraint can test whether a string ends + with an expected substring. + + + + + Initializes a new instance of the class. + + The expected string + + + + Test whether the constraint is matched by the actual value. + This is a template method, which calls the IsMatch method + of the derived class. + + + + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + RegexConstraint can test whether a string matches + the pattern provided. + + + + + Initializes a new instance of the class. + + The pattern. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + EmptyConstraint tests a whether a string or collection is empty, + postponing the decision about which test is applied until the + type of the actual argument is known. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + ConstraintBuilder is used to resolve the Not and All properties, + which serve as prefix operators for constraints. With the addition + of an operand stack, And and Or could be supported, but we have + left them out in favor of a simpler, more type-safe implementation. + Use the & and | operator overloads to combine constraints. + + + + + Implicitly convert ConstraintBuilder to an actual Constraint + at the point where the syntax demands it. + + + + + + + Resolves the chain of constraints using an + EqualConstraint as base. + + + + + Resolves the chain of constraints using a + SameAsConstraint as base. + + + + + Resolves the chain of constraints using a + LessThanConstraint as base. + + + + + Resolves the chain of constraints using a + GreaterThanConstraint as base. + + + + + Resolves the chain of constraints using a + LessThanOrEqualConstraint as base. + + + + + Resolves the chain of constraints using a + LessThanOrEqualConstraint as base. + + + + + Resolves the chain of constraints using a + GreaterThanOrEqualConstraint as base. + + + + + Resolves the chain of constraints using a + GreaterThanOrEqualConstraint as base. + + + + + Resolves the chain of constraints using an + ExactTypeConstraint as base. + + + + + Resolves the chain of constraints using an + InstanceOfTypeConstraint as base. + + + + + Resolves the chain of constraints using an + AssignableFromConstraint as base. + + + + + Resolves the chain of constraints using a + ContainsConstraint as base. This constraint + will, in turn, make use of the appropriate + second-level constraint, depending on the + type of the actual argument. + + + + + Resolves the chain of constraints using a + CollectionContainsConstraint as base. + + The expected object + + + + Resolves the chain of constraints using a + StartsWithConstraint as base. + + + + + Resolves the chain of constraints using a + StringEndingConstraint as base. + + + + + Resolves the chain of constraints using a + StringMatchingConstraint as base. + + + + + Resolves the chain of constraints using a + CollectionEquivalentConstraint as base. + + + + + Resolves the chain of constraints using a + CollectionContainingConstraint as base. + + + + + Resolves the chain of constraints using a + CollectionSubsetConstraint as base. + + + + + Resolves the chain of constraints using a + PropertyConstraint as base + + + + + Resolves the chain of constraints using a + PropertyCOnstraint on Length as base + + + + + + + Resolves the chain of constraints using a + PropertyCOnstraint on Length as base + + + + + + + Modifies the ConstraintBuilder by pushing a Prop operator on the + ops stack and the name of the property on the opnds stack. + + + + + + + Resolve a constraint that has been recognized by applying + any pending operators and returning the resulting Constraint. + + A constraint that incorporates all pending operators + + + + Resolves the chain of constraints using + EqualConstraint(null) as base. + + + + + Resolves the chain of constraints using + EqualConstraint(true) as base. + + + + + Resolves the chain of constraints using + EqualConstraint(false) as base. + + + + + Resolves the chain of constraints using + Is.NaN as base. + + + + + Resolves the chain of constraints using + Is.Empty as base. + + + + + Resolves the chain of constraints using + Is.Unique as base. + + + + + Modifies the ConstraintBuilder by pushing a Not operator on the stack. + + + + + Modifies the ConstraintBuilder by pushing a Not operator on the stack. + + + + + Modifies the ConstraintBuilder by pushing an All operator on the stack. + + + + + Modifies the ConstraintBuilder by pushing a Some operator on the stack. + + + + + Modifies the constraint builder by pushing All and Not operators on the stack + + + + + CollectionConstraint is the abstract base class for + constraints that operate on collections. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Protected method to be implemented by derived classes + + + + + + + CollectionTally counts (tallies) the number of + occurences of each object in one or more enuerations. + + + + + Construct a CollectionTally object from a collection + + + + + + Remove the counts for a collection from the tally, + so long as their are sufficient items to remove. + The tallies are not permitted to become negative. + + The collection to remove + True if there were enough items to remove, otherwise false + + + + Test whether all the counts are equal to a given value + + The value to be looked for + True if all counts are equal to the value, otherwise false + + + + Get the count of the number of times an object is present in the tally + + + + + UniqueItemsConstraint tests whether all the items in a + collection are unique. + + + + + Apply the item constraint to each item in the collection, + failing if any item fails. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + CollectionContainsConstraint is used to test whether a collection + contains an expected object as a member. + + + + + Construct a CollectionContainsConstraint + + + + + + Test whether the expected item is contained in the collection + + + + + + + Write a descripton of the constraint to a MessageWriter + + + + + + CollectionEquivalentCOnstraint is used to determine whether two + collections are equivalent. + + + + + Construct a CollectionEquivalentConstraint + + + + + + Test whether two collections are equivalent + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + CollectionSubsetConstraint is used to determine whether + one collection is a subset of another + + + + + Construct a CollectionSubsetConstraint + + The collection that the actual value is expected to be a subset of + + + + Test whether the actual collection is a subset of + the expected collection provided. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + EqualConstraint is able to compare an actual value with the + expected value provided in its constructor. + + + + + Initializes a new instance of the class. + + The expected value. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write a failure message. Overridden to provide custom + failure messages for EqualConstraint. + + The MessageWriter to write to + + + + Write description of this constraint + + The MessageWriter to write to + + + + Helper method to compare two arrays + + + + + Display the failure information for two collections that did not match. + + The MessageWriter on which to display + The expected collection. + The actual collection + The depth of this failure in a set of nested collections + + + + Displays a single line showing the types and sizes of the expected + and actual collections or arrays. If both are identical, the value is + only shown once. + + The MessageWriter on which to display + The expected collection or array + The actual collection or array + The indentation level for the message line + + + + Displays a single line showing the point in the expected and actual + arrays at which the comparison failed. If the arrays have different + structures or dimensions, both values are shown. + + The MessageWriter on which to display + The expected array + The actual array + Index of the failure point in the underlying collections + The indentation level for the message line + + + + Abstract base class used for prefixes + + + + + The base constraint + + + + + Construct given a base constraint + + + + + + Set all modifiers applied to the prefix into + the base constraint before matching + + + + + NotConstraint negates the effect of some other constraint + + + + + Initializes a new instance of the class. + + The base constraint to be negated. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for if the base constraint fails, false if it succeeds + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a MessageWriter. + + The writer on which the actual value is displayed + + + + AllItemsConstraint applies another constraint to each + item in a collection, succeeding if they all succeed. + + + + + Construct an AllItemsConstraint on top of an existing constraint + + + + + + Apply the item constraint to each item in the collection, + failing if any item fails. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + SomeItemsConstraint applies another constraint to each + item in a collection, succeeding if any of them succeeds. + + + + + Construct a SomeItemsConstraint on top of an existing constraint + + + + + + Apply the item constraint to each item in the collection, + failing if any item fails. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + SomeItemsConstraint applies another constraint to each + item in a collection, succeeding if any of them succeeds. + + + + + Construct a SomeItemsConstraint on top of an existing constraint + + + + + + Apply the item constraint to each item in the collection, + failing if any item fails. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + SameAsConstraint tests whether an object is identical to + the object passed to its constructor + + + + + Initializes a new instance of the class. + + The expected object. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + TypeConstraint is the abstract base for constraints + that take a Type as their expected value. + + + + + The expected Type used by the constraint + + + + + Construct a TypeConstraint for a given Type + + + + + + Write the actual value for a failing constraint test to a + MessageWriter. TypeCOnstraints override this method to write + the name of the type. + + The writer on which the actual value is displayed + + + + ExactTypeConstraint is used to test that an object + is of the exact type provided in the constructor + + + + + Construct an ExactTypeConstraint for a given Type + + + + + + Test that an object is of the exact type specified + + + + + + + Write the description of this constraint to a MessageWriter + + + + + + InstanceOfTypeConstraint is used to test that an object + is of the same type provided or derived from it. + + + + + Construct an InstanceOfTypeConstraint for the type provided + + + + + + Test whether an object is of the specified type or a derived type + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + AssignableFromConstraint is used to test that an object + can be assigned from a given Type. + + + + + Construct an AssignableFromConstraint for the type provided + + + + + + Test whether an object can be assigned from the specified type + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + Abstract base class for constraints that compare values to + determine if one is greater than, equal to or less than + the other. + + + + + The value against which a comparison is to be made + + + + + If true, less than returns success + + + + + if true, equal returns success + + + + + if true, greater than returns success + + + + + The predicate used as a part of the description + + + + + Initializes a new instance of the class. + + The value against which to make a comparison. + if set to true less succeeds. + if set to true equal succeeds. + if set to true greater succeeds. + String used in describing the constraint. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Tests whether a value is greater than the value supplied to its constructor + + + + + Initializes a new instance of the class. + + The expected value. + + + + Tests whether a value is greater than or equal to the value supplied to its constructor + + + + + Initializes a new instance of the class. + + The expected value. + + + + Tests whether a value is less than the value supplied to its constructor + + + + + Initializes a new instance of the class. + + The expected value. + + + + Tests whether a value is less than or equal to the value supplied to its constructor + + + + + Initializes a new instance of the class. + + The expected value. + + + + ContainsConstraint tests a whether a string contains a substring + or a collection contains an object. It postpones the decision of + which test to use until the type of the actual argument is known. + This allows testing whether a string is contained in a collection + or as a substring of another string using the same syntax. + + + + + Initializes a new instance of the class. + + The expected. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Summary description for PropertyConstraint. + + + + + Initializes a new instance of the class. + + The name. + The constraint to apply to the property. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + BinaryOperation is the abstract base of all constraints + that combine two other constraints in some fashion. + + + + + The first constraint being combined + + + + + The second constraint being combined + + + + + Construct a BinaryOperation from two other constraints + + The first constraint + The second constraint + + + + AndConstraint succeeds only if both members succeed. + + + + + Create an AndConstraint from two other constraints + + The first constraint + The second constraint + + + + Apply both member constraints to an actual value, succeeding + succeeding only if both of them succeed. + + The actual value + True if the constraints both succeeded + + + + Write a description for this contraint to a MessageWriter + + The MessageWriter to receive the description + + + + OrConstraint succeeds if either member succeeds + + + + + Create an OrConstraint from two other constraints + + The first constraint + The second constraint + + + + Apply the member constraints to an actual value, succeeding + succeeding as soon as one of them succeeds. + + The actual value + True if either constraint succeeded + + + + Write a description for this contraint to a MessageWriter + + The MessageWriter to receive the description + + + + The Numerics class contains common operations on numeric values. + + + + + Checks the type of the object, returning true if + the object is a numeric type. + + The object to check + true if the object is a numeric type + + + + Checks the type of the object, returning true if + the object is a floating point numeric type. + + The object to check + true if the object is a floating point numeric type + + + + Checks the type of the object, returning true if + the object is a fixed point numeric type. + + The object to check + true if the object is a fixed point numeric type + + + + The Is class is a helper class with properties and methods + that supply a number of constraints used in Asserts. + + + + + Is.Null returns a static constraint that tests for null + + + + + Is.True returns a static constraint that tests whether a value is true + + + + + Is.False returns a static constraint that tests whether a value is false + + + + + Is.NaN returns a static constraint that tests whether a value is an NaN + + + + + Is.Empty returns a static constraint that tests whether a string or collection is empty + + + + + Is.Unique returns a static constraint that tests whether a collection contains all unque items. + + + + + Is.EqualTo returns a constraint that tests whether the + actual value equals the supplied argument + + + + + + + Is.SameAs returns a constraint that tests whether the + actual value is the same object as the supplied argument. + + + + + + + Is.GreaterThan returns a constraint that tests whether the + actual value is greater than the suppled argument + + + + + Is.GreaterThanOrEqualTo returns a constraint that tests whether the + actual value is greater than or equal to the suppled argument + + + + + Is.AtLeast is a synonym for Is.GreaterThanOrEqualTo + + + + + Is.LessThan returns a constraint that tests whether the + actual value is less than the suppled argument + + + + + Is.LessThanOrEqualTo returns a constraint that tests whether the + actual value is less than or equal to the suppled argument + + + + + Is.AtMost is a synonym for Is.LessThanOrEqualTo + + + + + Is.TypeOf returns a constraint that tests whether the actual + value is of the exact type supplied as an argument. + + + + + Is.InstanceOfType returns a constraint that tests whether + the actual value is of the type supplied as an argument + or a derived type. + + + + + Is.AssignableFrom returns a constraint that tests whether + the actual value is assignable from the type supplied as + an argument. + + + + + + + Is.EquivalentTo returns a constraint that tests whether + the actual value is a collection containing the same + elements as the collection supplied as an arument + + + + + Is.SubsetOf returns a constraint that tests whether + the actual value is a subset of the collection + supplied as an arument + + + + + Is.Not returns a ConstraintBuilder that negates + the constraint that follows it. + + + + + Is.All returns a ConstraintBuilder, which will apply + the following constraint to all members of a collection, + succeeding if all of them succeed. This property is + a synonym for Has.AllItems. + + + + + The Iz class is a synonym for Is intended for use in VB, + which regards Is as a keyword. + + + + + The Text class is a helper class with properties and methods + that supply a number of constraints used with strings. + + + + + Contains returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + DoesNotContain returns a constraint that fails if the actual + value contains the substring supplied as an argument. + + + + + StartsWith returns a constraint that succeeds if the actual + value starts with the substring supplied as an argument. + + + + + DoesNotStartWith returns a constraint that fails if the actual + value starts with the substring supplied as an argument. + + + + + EndsWith returns a constraint that succeeds if the actual + value ends with the substring supplied as an argument. + + + + + DoesNotEndWith returns a constraint that fails if the actual + value ends with the substring supplied as an argument. + + + + + Matches returns a constraint that succeeds if the actual + value matches the pattern supplied as an argument. + + + + + + + DoesNotMatch returns a constraint that failss if the actual + value matches the pattern supplied as an argument. + + + + + + + Text.All returns a ConstraintBuilder, which will apply + the following constraint to all members of a collection, + succeeding if all of them succeed. + + + + + The List class is a helper class with properties and methods + that supply a number of constraints used with lists and collections. + + + + + List.Map returns a ListMapper, which can be used to map + the original collection to another collection. + + + + + + + ListMapper is used to transform a collection used as an actual argument + producing another collection to be used in the assertion. + + + + + Construct a ListMapper based on a collection + + The collection to be transformed + + + + Produces a collection containing all the values of a property + + The collection of property values + + + + + Summary description for HasNoPrefixB. + + + + + Returns a new ConstraintBuilder, which will apply the + following constraint to a named property of the object + being tested. + + The name of the property + + + + Returns a new PropertyConstraint checking for the + existence of a particular property value. + + The name of the property to look for + The expected value of the property + + + + Returns a new PropertyConstraint for the Length property + + + + + + + Returns a new PropertyConstraint or the Count property + + + + + + + Returns a new CollectionContainsConstraint checking for the + presence of a particular object in the collection. + + The expected object + + + + Has.No returns a ConstraintBuilder that negates + the constraint that follows it. + + + + + Has.AllItems returns a ConstraintBuilder, which will apply + the following constraint to all members of a collection, + succeeding if all of them succeed. + + + + + Has.Some returns a ConstraintBuilder, which will apply + the following constraint to all members of a collection, + succeeding if any of them succeed. It is a synonym + for Has.Item. + + + + + Has.None returns a ConstraintBuilder, which will apply + the following constraint to all members of a collection, + succeeding only if none of them succeed. + + + + + Nested class that allows us to restrict the number + of key words that may appear after Has.No. + + + + + Return a ConstraintBuilder conditioned to apply + the following constraint to a property. + + The property name + A ConstraintBuilder + + + + Return a Constraint that succeeds if the expected object is + not contained in a collection. + + The expected object + A Constraint + + + + The Assert class contains a collection of static methods that + implement the most common assertions used in NUnit. + + + + + We don't actually want any instances of this object, but some people + like to inherit from it to add other static methods. Hence, the + protected constructor disallows any instances of this object. + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display if the condition is false + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display if the condition is false + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + The message to display if the condition is true + Arguments to be used in formatting the message + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + The message to display if the condition is true + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + The message to be displayed when the object is null + Arguments to be used in formatting the message + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + The message to be displayed when the object is null + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + The message to be displayed when the object is not null + Arguments to be used in formatting the message + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + The message to be displayed when the object is not null + + + + Verifies that the object that is passed in is equal to null + If the object is not null null then an + is thrown. + + The object that is to be tested + + + + Verifies that the double is passed is an NaN value. + If the object is not NaN then an + is thrown. + + The value that is to be tested + The message to be displayed when the object is not null + Arguments to be used in formatting the message + + + + Verifies that the double is passed is an NaN value. + If the object is not NaN then an + is thrown. + + The object that is to be tested + The message to be displayed when the object is not null + + + + Verifies that the double is passed is an NaN value. + If the object is not NaN then an + is thrown. + + The object that is to be tested + + + + Assert that a string is empty - that is equal to string.Empty + + The string to be tested + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that a string is empty - that is equal to string.Emtpy + + The string to be tested + The message to be displayed on failure + + + + Assert that a string is empty - that is equal to string.Emtpy + + The string to be tested + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + The message to be displayed on failure + + + + Assert that an array,list or other collection is empty + + An array, list or other collection implementing ICollection + + + + Assert that a string is not empty - that is not equal to string.Empty + + The string to be tested + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that a string is empty - that is equal to string.Emtpy + + The string to be tested + The message to be displayed on failure + + + + Assert that a string is empty - that is equal to string.Emtpy + + The string to be tested + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + The message to be displayed on failure + + + + Assert that an array,list or other collection is empty + + An array, list or other collection implementing ICollection + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + The messge to display in case of failure + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + The messge to display in case of failure + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + A message to display in case of failure + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + A message to display in case of failure + An array of objects to be used in formatting the message + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + A message to display in case of failure + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + A message to display in case of failure + An array of objects to be used in formatting the message + + + + Verifies that two ints are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that two ints are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message that will be displayed on failure + + + + Verifies that two ints are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two longs are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that two longs are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message that will be displayed on failure + + + + Verifies that two longs are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two uints are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that two uints are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message that will be displayed on failure + + + + Verifies that two uints are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two ulongs are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that two ulongs are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message that will be displayed on failure + + + + Verifies that two ulongs are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two decimals are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that two decimal are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message that will be displayed on failure + + + + Verifies that two decimals are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equals then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equals then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message that will be displayed on failure + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equals then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + + + + Verifies that two floats are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equals then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message displayed upon failure + Arguments to be used in formatting the message + + + + Verifies that two floats are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equals then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message displayed upon failure + + + + Verifies that two floats are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equals then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + + + + Verifies that two objects are equal. Two objects are considered + equal if both are null, or if both have the same value. All + non-numeric types are compared by using the Equals method. + Arrays are compared by comparing each element using the same rules. + If they are not equal an is thrown. + + The value that is expected + The actual value + The message to display if objects are not equal + Arguments to be used in formatting the message + + + + Verifies that two objects are equal. Two objects are considered + equal if both are null, or if both have the same value. All + non-numeric types are compared by using the Equals method. + If they are not equal an is thrown. + + The value that is expected + The actual value + The message to display if objects are not equal + + + + Verifies that two objects are equal. Two objects are considered + equal if both are null, or if both have the same value. All + non-numeric types are compared by using the Equals method. + If they are not equal an is thrown. + + The value that is expected + The actual value + + + + Asserts that two objects are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the two objects are the same object. + Arguments to be used in formatting the message + + + + Asserts that two objects are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the objects are the same + + + + Asserts that two objects are not equal. If they are equal + an is thrown. + + The expected object + The actual object + + + + Asserts that two ints are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the two objects are the same object. + Arguments to be used in formatting the message + + + + Asserts that two ints are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the objects are the same + + + + Asserts that two ints are not equal. If they are equal + an is thrown. + + The expected object + The actual object + + + + Asserts that two longss are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the two objects are the same object. + Arguments to be used in formatting the message + + + + Asserts that two longs are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the objects are the same + + + + Asserts that two longs are not equal. If they are equal + an is thrown. + + The expected object + The actual object + + + + Asserts that two uints are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the two objects are the same object. + Arguments to be used in formatting the message + + + + Asserts that two uints are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the objects are the same + + + + Asserts that two uints are not equal. If they are equal + an is thrown. + + The expected object + The actual object + + + + Asserts that two ulongs are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the two objects are the same object. + Arguments to be used in formatting the message + + + + Asserts that two ulongs are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the objects are the same + + + + Asserts that two ulong are not equal. If they are equal + an is thrown. + + The expected object + The actual object + + + + Asserts that two decimals are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the two objects are the same object. + Arguments to be used in formatting the message + + + + Asserts that two decimals are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the objects are the same + + + + Asserts that two decimals are not equal. If they are equal + an is thrown. + + The expected object + The actual object + + + + Asserts that two floats are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the two objects are the same object. + Arguments to be used in formatting the message + + + + Asserts that two floats are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the objects are the same + + + + Asserts that two floats are not equal. If they are equal + an is thrown. + + The expected object + The actual object + + + + Asserts that two doubles are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the two objects are the same object. + Arguments to be used in formatting the message + + + + Asserts that two doubles are not equal. If they are equal + an is thrown. + + The expected object + The actual object + The message to be displayed when the objects are the same + + + + Asserts that two doubles are not equal. If they are equal + an is thrown. + + The expected object + The actual object + + + + Asserts that two objects refer to the same object. If they + are not the same an is thrown. + + The expected object + The actual object + The message to be displayed when the two objects are not the same object. + Arguments to be used in formatting the message + + + + Asserts that two objects refer to the same object. If they + are not the same an is thrown. + + The expected object + The actual object + The message to be displayed when the object is null + + + + Asserts that two objects refer to the same object. If they + are not the same an is thrown. + + The expected object + The actual object + + + + Asserts that two objects do not refer to the same object. If they + are the same an is thrown. + + The expected object + The actual object + The message to be displayed when the two objects are the same object. + Arguments to be used in formatting the message + + + + Asserts that two objects do not refer to the same object. If they + are the same an is thrown. + + The expected object + The actual object + The message to be displayed when the objects are the same + + + + Asserts that two objects do not refer to the same object. If they + are the same an is thrown. + + The expected object + The actual object + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Asserts that an object is contained in a list. + + The expected object + The list to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that an object is contained in a list. + + The expected object + The list to be examined + The message to display in case of failure + + + + Asserts that an object is contained in a list. + + The expected object + The list to be examined + + + + Throws an with the message and arguments + that are passed in. This is used by the other Assert functions. + + The message to initialize the with. + Arguments to be used in formatting the message + + + + Throws an with the message that is + passed in. This is used by the other Assert functions. + + The message to initialize the with. + + + + Throws an . + This is used by the other Assert functions. + + + + + Throws an with the message and arguments + that are passed in. This causes the test to be reported as ignored. + + The message to initialize the with. + Arguments to be used in formatting the message + + + + Throws an with the message that is + passed in. This causes the test to be reported as ignored. + + The message to initialize the with. + + + + Throws an . + This causes the test to be reported as ignored. + + + + + NOTE: The use of asserters for extending NUnit has + now been replaced by the use of constraints. This + method is marked obsolete. + + Test the condition asserted by an asserter and throw + an assertion exception using provided message on failure. + + An object that implements IAsserter + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint to be applied + The actual value to test + + + + Apply a constraint to an actual value, succeedingt if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint to be applied + The actual value to test + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeedingt if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint to be applied + The actual value to test + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display if the condition is false + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display if the condition is false + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater or equal to than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater or equal to than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater or equal to than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater than or equal to the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message that will be displayed on failure + + + + Verifies that the first value is greater than the second + value. If they are not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message that will be displayed on failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Gets the number of assertions executed so far and + resets the counter to zero. + + + + + Enumeration indicating how the expected message parameter is to be used + + + + Expect an exact match + + + Expect a message containing the parameter string + + + Match the regular expression provided as a parameter + + + + ExpectedExceptionAttribute + + + + + + Constructor for a non-specific exception + + + + + Constructor for a given type of exception + + The type of the expected exception + + + + Constructor for a given exception name + + The full name of the expected exception + + + + Constructor for a given type of exception and expected message text + + The type of the expected exception + The expected message text + + + + Constructor for a given exception name and expected message text + + The full name of the expected exception + The expected messge text + + + + Gets or sets the expected exception type + + + + + Gets or sets the full Type name of the expected exception + + + + + Gets or sets the expected message text + + + + + Gets or sets the user message displayed in case of failure + + + + + Gets or sets the type of match to be performed on the expected message + + + + + Gets the name of a method to be used as an exception handler + + + + + A set of Assert methods operationg on one or more collections + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + Asserts that all items contained in collection are of the type specified by expectedType. + + ICollection of objects to be considered + System.Type that all objects in collection must be instances of + + + + Asserts that all items contained in collection are of the type specified by expectedType. + + ICollection of objects to be considered + System.Type that all objects in collection must be instances of + The message that will be displayed on failure + + + + Asserts that all items contained in collection are of the type specified by expectedType. + + ICollection of objects to be considered + System.Type that all objects in collection must be instances of + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that all items contained in collection are not equal to null. + + ICollection of objects to be considered + + + + Asserts that all items contained in collection are not equal to null. + + ICollection of objects to be considered + The message that will be displayed on failure + + + + Asserts that all items contained in collection are not equal to null. + + ICollection of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Ensures that every object contained in collection exists within the collection + once and only once. + + ICollection of objects to be considered + + + + Ensures that every object contained in collection exists within the collection + once and only once. + + ICollection of objects to be considered + The message that will be displayed on failure + + + + Ensures that every object contained in collection exists within the collection + once and only once. + + ICollection of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + If comparer is not null then it will be used to compare the objects. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The IComparer to use in comparing objects from each ICollection + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The message that will be displayed on failure + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + If comparer is not null then it will be used to compare the objects. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The IComparer to use in comparing objects from each ICollection + The message that will be displayed on failure + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + If comparer is not null then it will be used to compare the objects. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The IComparer to use in comparing objects from each ICollection + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are equivalent, containing the same objects but the match may be in any order. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + + + + Asserts that expected and actual are equivalent, containing the same objects but the match may be in any order. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The message that will be displayed on failure + + + + Asserts that expected and actual are equivalent, containing the same objects but the match may be in any order. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are not exactly equal. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + + + + Asserts that expected and actual are not exactly equal. + If comparer is not null then it will be used to compare the objects. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The IComparer to use in comparing objects from each ICollection + + + + Asserts that expected and actual are not exactly equal. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The message that will be displayed on failure + + + + Asserts that expected and actual are not exactly equal. + If comparer is not null then it will be used to compare the objects. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The IComparer to use in comparing objects from each ICollection + The message that will be displayed on failure + + + + Asserts that expected and actual are not exactly equal. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are not exactly equal. + If comparer is not null then it will be used to compare the objects. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The IComparer to use in comparing objects from each ICollection + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are not equivalent. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + + + + Asserts that expected and actual are not equivalent. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The message that will be displayed on failure + + + + Asserts that expected and actual are not equivalent. + + The first ICollection of objects to be considered + The second ICollection of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that collection contains actual as an item. + + ICollection of objects to be considered + Object to be found within collection + + + + Asserts that collection contains actual as an item. + + ICollection of objects to be considered + Object to be found within collection + The message that will be displayed on failure + + + + Asserts that collection contains actual as an item. + + ICollection of objects to be considered + Object to be found within collection + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that collection does not contain actual as an item. + + ICollection of objects to be considered + Object that cannot exist within collection + + + + Asserts that collection does not contain actual as an item. + + ICollection of objects to be considered + Object that cannot exist within collection + The message that will be displayed on failure + + + + Asserts that collection does not contain actual as an item. + + ICollection of objects to be considered + Object that cannot exist within collection + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that superset is not a subject of subset. + + The ICollection superset to be considered + The ICollection subset to be considered + + + + Asserts that superset is not a subject of subset. + + The ICollection superset to be considered + The ICollection subset to be considered + The message that will be displayed on failure + + + + Asserts that superset is not a subject of subset. + + The ICollection superset to be considered + The ICollection subset to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that superset is a subset of subset. + + The ICollection superset to be considered + The ICollection subset to be considered + + + + Asserts that superset is a subset of subset. + + The ICollection superset to be considered + The ICollection subset to be considered + The message that will be displayed on failure + + + + Asserts that superset is a subset of subset. + + The ICollection superset to be considered + The ICollection subset to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + The message to be displayed on failure + + + + Assert that an array,list or other collection is empty + + An array, list or other collection implementing ICollection + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + The message to be displayed on failure + + + + Assert that an array,list or other collection is empty + + An array, list or other collection implementing ICollection + + + + NOTE: The use of asserters for extending NUnit has + now been replaced by the use of constraints. This + class is marked obsolete. + + AbstractAsserter is the base class for all asserters. + Asserters encapsulate a condition test and generation + of an AssertionException with a tailored message. They + are used by the Assert class as helper objects. + + User-defined asserters may be passed to the + Assert.DoAssert method in order to implement + extended asserts. + + + + + NOTE: The use of asserters for extending NUnit has + now been replaced by the use of constraints. This + interface is marked obsolete. + + The interface implemented by an asserter. Asserters + encapsulate a condition test and generation of an + AssertionException with a tailored message. They + are used by the Assert class as helper objects. + + User-defined asserters may be passed to the + Assert.DoAssert method in order to implement + extended asserts. + + + + + Test the condition for the assertion. + + True if the test succeeds + + + + Return the message giving the failure reason. + The return value is unspecified if no failure + has occured. + + + + + The user-defined message for this asserter. + + + + + Arguments to use in formatting the user-defined message. + + + + + Our failure message object, initialized as needed + + + + + Constructs an AbstractAsserter + + The message issued upon failure + Arguments to be used in formatting the message + + + + Test method to be implemented by derived types. + Default always succeeds. + + True if the test succeeds + + + + AssertionFailureMessage object used internally + + + + + Message related to a failure. If no failure has + occured, the result is unspecified. + + + + + The Assertion class is obsolete and has been + replaced by the Assert class. + + + + + Asserts that a condition is true. If it isn't it throws + an . + + The message to display is the condition + is false + The evaluated condition + + + + Asserts that a condition is true. If it isn't it throws + an . + + The evaluated condition + + + + /// Asserts that two doubles are equal concerning a delta. If the + expected value is infinity then the delta value is ignored. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + + + + /// Asserts that two singles are equal concerning a delta. If the + expected value is infinity then the delta value is ignored. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + + + Asserts that two objects are equal. If they are not + an is thrown. + + + Asserts that two ints are equal. If they are not + an is thrown. + + + Asserts that two ints are equal. If they are not + an is thrown. + + + Asserts that two doubles are equal concerning a delta. + If the expected value is infinity then the delta value is ignored. + + + + Asserts that two floats are equal concerning a delta. + If the expected value is infinity then the delta value is ignored. + + + + + Asserts that two objects are equal. Two objects are considered + equal if both are null, or if both have the same value. Numeric + types are compared via string comparision on their contents to + avoid problems comparing values between different types. All + non-numeric types are compared by using the Equals method. + If they are not equal an is thrown. + + + + Asserts that an object isn't null. + + + Asserts that an object isn't null. + + + Asserts that an object is null. + + + Asserts that an object is null. + + + Asserts that two objects refer to the same object. If they + are not the same an is thrown. + + + + Asserts that two objects refer to the same object. + If they are not an is thrown. + + + + Fails a test with no message. + + + Fails a test with the given message. + + + + Thrown when an assertion failed. + + + + + The error message that explains + the reason for the exception + + + The error message that explains + the reason for the exception + The exception that caused the + current exception + + + + Serialization Constructor + + + + + AssertionFailureMessage encapsulates a failure message + issued as a result of an Assert failure. + + + + + Number of characters before a highlighted position before + clipping will occur. Clipped text is replaced with an + elipsis "..." + + + + + Number of characters after a highlighted position before + clipping will occur. Clipped text is replaced with an + elipsis "..." + + + + + Prefix used to start an expected value line. + Must be same length as actualPrefix. + + + + + Prefix used to start an actual value line. + Must be same length as expectedPrefix. + + + + + Construct an AssertionFailureMessage with a message + and optional arguments. + + + + + + + Construct an empty AssertionFailureMessage + + + + + Add an expected value line to the message containing + the text provided as an argument. + + Text describing what was expected. + + + + Add an actual value line to the message containing + the text provided as an argument. + + Text describing the actual value. + + + + Add an expected value line to the message containing + a string representation of the object provided. + + An object representing the expected value + + + + Add an expected value line to the message containing a double + and the tolerance used in making the comparison. + + The expected value + The tolerance specified in the Assert + + + + Add an actual value line to the message containing + a string representation of the object provided. + + An object representing what was actually found + + + + Display two lines that communicate the expected value, and the actual value + + The expected value + The actual value found + + + + Display two lines that communicate the expected value, the actual value and + the tolerance used in comparing two doubles. + + The expected value + The actual value found + The tolerance specified in the Assert + + + + Draws a marker under the expected/actual strings that highlights + where in the string a mismatch occurred. + + The position of the mismatch + + + + Reports whether the string lengths are the same or different, and + what the string lengths are. + + The expected string + The actual string value + + + + Called to create additional message lines when two objects have been + found to be unequal. If the inputs are strings, a special message is + rendered that can help track down where the strings are different, + based on differences in length, or differences in content. + + If the inputs are not strings, the ToString method of the objects + is used to show what is different about them. + + The expected value + The actual value + True if a case-insensitive comparison is being performed + + + + Called to create additional message lines when two doubles have been + found to be unequal, within the specified tolerance. + + + + + Constructs a message that can be displayed when the content of two + strings are different, but the string lengths are the same. The + message will clip the strings to a reasonable length, centered + around the first position where they are mismatched, and draw + a line marking the position of the difference to make comparison + quicker. + + The expected string value + The actual string value + True if a case-insensitive comparison is being performed + + + + Display a standard message showing the differences found between + two arrays that were expected to be equal. + + The expected array value + The actual array value + The index at which a difference was found + + + + Display a standard message showing the differences found between + two collections that were expected to be equal. + + The expected collection value + The actual collection value + The index at which a difference was found + + + + Get an array of indices representing the point in a collection or + array corresponding to a single int index into the collection. + + The collection to which the indices apply + Index in the collection + Array of indices + + + + Displays elements from a list on a line + + Text to prefix the line with + The list of items to display + The index in the list of the first element to display + The maximum number of elements to display + + + + Formats an object for display in a message line + + The object to be displayed + + + + + Tests two objects to determine if they are strings. + + + + + + + + Renders up to M characters before, and up to N characters after + the specified index position. If leading or trailing text is + clipped, and elipses "..." is added where the missing text would + be. + + Clips strings to limit previous or post newline characters, + since these mess up the comparison + + + + + + + + Shows the position two strings start to differ. Comparison + starts at the start index. + + + + + -1 if no mismatch found, or the index where mismatch found + + + + Turns CR, LF, or TAB into visual indicator to preserve visual marker + position. This is done by replacing the '\r' into '\\' and 'r' + characters, and the '\n' into '\\' and 'n' characters, and '\t' into + '\\' and 't' characters. + + Thus the single character becomes two characters for display. + + + + + + + Attribute used to apply a category to a test + + + + + The name of the category + + + + + Construct attribute for a given category + + The name of the category + + + + Protected constructor uses the Type name as the name + of the category. + + + + + The name of the category + + + + + Abstract base for Attributes that are used to include tests + in the test run based on environmental settings. + + + + + Constructor with no included items specified, for use + with named property syntax. + + + + + Constructor taking one or more included items + + Comma-delimited list of included items + + + + Name of the item that is needed in order for + a test to run. Multiple itemss may be given, + separated by a comma. + + + + + Name of the item to be excluded. Multiple items + may be given, separated by a comma. + + + + + The reason for including or excluding the test + + + + + PlatformAttribute is used to mark a test fixture or an + individual method as applying to a particular platform only. + + + + + Constructor with no platforms specified, for use + with named property syntax. + + + + + Constructor taking one or more platforms + + Comma-deliminted list of platforms + + + + CultureAttribute is used to mark a test fixture or an + individual method as applying to a particular Culture only. + + + + + Constructor with no cultures specified, for use + with named property syntax. + + + + + Constructor taking one or more cultures + + Comma-deliminted list of cultures + + + + MessageWriter is the abstract base for classes that write + constraint descriptions and messages in some form. The + class has separate methods for writing various components + of a message, allowing implementations to tailor the + presentation as needed. + + + + + Construct a MessageWriter given a culture + + + + + Method to write single line message with optional args, usually + written to precede the general failure message. + + The message to be written + Any arguments used in formatting the message + + + + Method to write single line message with optional args, usually + written to precede the general failure message, at a givel + indentation level. + + The indentation level of the message + The message to be written + Any arguments used in formatting the message + + + + Display Expected and Actual lines for a constraint. This + is called by MessageWriter's default implementation of + WriteMessageTo and provides the generic two-line display. + + The constraint that failed + + + + Display Expected and Actual lines for given values. This + method may be called by constraints that need more control over + the display of actual and expected values than is provided + by the default implementation. + + The expected value + The actual value causing the failure + + + + Display Expected and Actual lines for given values, including + a tolerance value on the Expected line. + + The expected value + The actual value causing the failure + The tolerance within which the test was made + + + + Display the expected and actual string values on separate lines. + If the mismatch parameter is >=0, an additional line is displayed + line containing a caret that points to the mismatch point. + + The expected string value + The actual string value + The point at which the strings don't match or -1 + If true, case is ignored in locating the point where the strings differ + + + + Writes the text for a connector. + + The connector. + + + + Writes the text for a predicate. + + The predicate. + + + + Writes the text for an expected value. + + The expected value. + + + + Writes the text for a modifier + + The modifier. + + + + Writes the text for an actual value. + + The actual value. + + + + Writes the text for a generalized value. + + The value. + + + + Writes the text for a collection value, + starting at a particular point, to a max length + + The collection containing elements to write. + The starting point of the elements to write + The maximum number of elements to write + + + + Abstract method to get the max line length + + + + + Summary description for SetCultureAttribute. + + + + + PropertyAttribute is used to attach information to a test as a name/value pair.. + + + + + The property name + + + + + The property value + + + + + Construct a PropertyAttribute with a name and value + + The name of the property + The property value + + + + Constructor for use by inherited classes that use the + name of the type as the property name. + + + + + Gets the property name + + + + + Gets the property value + + + + + Construct given the name of a culture + + + + + + TextMessageWriter writes constraint descriptions and messages + in displayable form as a text stream. It tailors the display + of individual message components to form the standard message + format of NUnit assertion failure messages. + + + + + Prefix used for the expected value line of a message + + + + + Prefix used for the actual value line of a message + + + + + Length of a message prefix + + + + + Construct a TextMessageWriter + + + + + Construct a TextMessageWriter, specifying a user message + and optional formatting arguments. + + + + + + + Method to write single line message with optional args, usually + written to precede the general failure message, at a givel + indentation level. + + The indentation level of the message + The message to be written + Any arguments used in formatting the message + + + + Display Expected and Actual lines for a constraint. This + is called by MessageWriter's default implementation of + WriteMessageTo and provides the generic two-line display. + + The constraint that failed + + + + Display Expected and Actual lines for given values. This + method may be called by constraints that need more control over + the display of actual and expected values than is provided + by the default implementation. + + The expected value + The actual value causing the failure + + + + Display Expected and Actual lines for given values, including + a tolerance value on the expected line. + + The expected value + The actual value causing the failure + The tolerance within which the test was made + + + + Display the expected and actual string values on separate lines. + If the mismatch parameter is >=0, an additional line is displayed + line containing a caret that points to the mismatch point. + + The expected string value + The actual string value + The point at which the strings don't match or -1 + If true, case is ignored in string comparisons + + + + Writes the text for a connector. + + The connector. + + + + Writes the text for a predicate. + + The predicate. + + + + Write the text for a modifier. + + The modifier. + + + + Writes the text for an expected value. + + The expected value. + + + + Writes the text for an actual value. + + The actual value. + + + + Writes the text for a generalized value. + + The value. + + + + Writes the text for a collection value, + starting at a particular point, to a max length + + The collection containing elements to write. + The starting point of the elements to write + The maximum number of elements to write + + + + Write the generic 'Expected' line for a constraint + + The constraint that failed + + + + Write the generic 'Expected' line for a given value + + The expected value + + + + Write the generic 'Expected' line for a given value + and tolerance. + + The expected value + The tolerance within which the test was made + + + + Write the generic 'Actual' line for a constraint + + The constraint for which the actual value is to be written + + + + Write the generic 'Actual' line for a given value + + The actual value causing a failure + + + + Gets the maximum line length for this writer + + + + + Basic Asserts on strings. + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + Asserts that a string is found within another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string is found within another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string is found within another string. + + The expected string + The string to be examined + + + + Asserts that a string starts with another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string starts with another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string starts with another string. + + The expected string + The string to be examined + + + + Asserts that a string ends with another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string ends with another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string ends with another string. + + The expected string + The string to be examined + + + + Asserts that two strings are equal, without regard to case. + + The expected string + The actual string + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that two strings are equal, without regard to case. + + The expected string + The actual string + The message to display in case of failure + + + + Asserts that two strings are equal, without regard to case. + + The expected string + The actual string + + + + Asserts that a string matches an expected regular expression pattern. + + The expected expression + The actual string + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string matches an expected regular expression pattern. + + The expected expression + The actual string + The message to display in case of failure + + + + Asserts that a string matches an expected regular expression pattern. + + The expected expression + The actual string + + + + Static methods used in creating messages + + + + + Returns the representation of a type as used in NUnitLite. + This is the same as Type.ToString() except for arrays, + which are displayed with their declared sizes. + + + + + + + Converts any control characters in a string + to their escaped representation. + + The string to be converted + The converted string + + + + Return the a string representation for a set of indices into an array + + Array of indices for which a string is needed + + + + Get an array of indices representing the point in a collection or + array corresponding to a single int index into the collection. + + The collection to which the indices apply + Index in the collection + Array of indices + + + + Clip a string around a particular point, returning the clipped + string with ellipses representing the removed parts + + The string to be clipped + The maximum permitted length of the result string + The point around which clipping is to occur + The clipped string + + + + Shows the position two strings start to differ. Comparison + starts at the start index. + + The expected string + The actual string + The index in the strings at which comparison should start + Boolean indicating whether case should be ignored + -1 if no mismatch found, or the index where mismatch found + + + + AssertionHelper is an optional base class for user tests, + allowing the use of shorter names for constraints and + asserts and avoiding conflict with the definition of + , from which it inherits much of its + behavior, in certain mock object frameworks. + + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. Works + identically to + + A Constraint to be applied + The actual value to test + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. Works + identically to + + A Constraint to be applied + The actual value to test + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. Works + identically to + + A Constraint to be applied + The actual value to test + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . Works Identically to + . + + The evaluated condition + The message to display if the condition is false + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . Works Identically to + . + + The evaluated condition + The message to display if the condition is false + + + + Asserts that a condition is true. If the condition is false the method throws + an . Works Identically to . + + The evaluated condition + + + + Returns a ListMapper based on a collection. + + The original collection + + + + + Summary description for FileAssert. + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + We don't actually want any instances of this object, but some people + like to inherit from it to add other static methods. Hence, the + protected constructor disallows any instances of this object. + + + + + Verifies that two Streams are equal. Two Streams are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The expected Stream + The actual Stream + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Verifies that two Streams are equal. Two Streams are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The expected Stream + The actual Stream + The message to display if objects are not equal + + + + Verifies that two Streams are equal. Two Streams are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The expected Stream + The actual Stream + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A file containing the value that is expected + A file containing the actual value + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A file containing the value that is expected + A file containing the actual value + The message to display if objects are not equal + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A file containing the value that is expected + A file containing the actual value + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + The message to display if objects are not equal + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + + + + Asserts that two Streams are not equal. If they are equal + an is thrown. + + The expected Stream + The actual Stream + The message to be displayed when the two Stream are the same. + Arguments to be used in formatting the message + + + + Asserts that two Streams are not equal. If they are equal + an is thrown. + + The expected Stream + The actual Stream + The message to be displayed when the Streams are the same. + + + + Asserts that two Streams are not equal. If they are equal + an is thrown. + + The expected Stream + The actual Stream + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + A file containing the value that is expected + A file containing the actual value + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + A file containing the value that is expected + A file containing the actual value + The message to display if objects are not equal + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + A file containing the value that is expected + A file containing the actual value + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + The message to display if objects are not equal + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + + + + Thrown when an assertion failed. + + + + + + + The error message that explains + the reason for the exception + The exception that caused the + current exception + + + + Serialization Constructor + + + + + Obsolete class, formerly used to identify tests through + inheritance. Avoid using this class for new tests. + + + + + Method called immediately before running the test. + + + + + Method Called immediately after running the test. It is + guaranteed to be called, even if an exception is thrown. + + + + + Attribute used to mark a class that contains one-time SetUp + and/or TearDown methods that apply to all the tests in a + namespace or an assembly. + + + + + SetUpFixtureAttribute is used to identify a SetUpFixture + + + + + Attribute used to mark a static (shared in VB) property + that returns a list of tests. + + + + + Attribute used to identify a method that is called + immediately after each test is run. The method is + guaranteed to be called, even if an exception is thrown. + + + + + Adding this attribute to a method within a + class makes the method callable from the NUnit test runner. There is a property + called Description which is optional which you can provide a more detailed test + description. This class cannot be inherited. + + + + [TestFixture] + public class Fixture + { + [Test] + public void MethodToTest() + {} + + [Test(Description = "more detailed description")] + publc void TestDescriptionMethod() + {} + } + + + + + + Descriptive text for this test + + + + + [TestFixture] + public class ExampleClass + {} + + + + + Descriptive text for this fixture + + + + + Attribute used to identify a method that is + called before any tests in a fixture are run. + + + + + Attribute used to identify a method that is called after + all the tests in a fixture have run. The method is + guaranteed to be called, even if an exception is thrown. + + + + + Attribute used to mark a test that is to be ignored. + Ignored tests result in a warning message when the + tests are run. + + + + + Constructs the attribute without giving a reason + for ignoring the test. + + + + + Constructs the attribute giving a reason for ignoring the test + + The reason for ignoring the test + + + + The reason for ignoring a test + + + + + ExplicitAttribute marks a test or test fixture so that it will + only be run if explicitly executed from the gui or command line + or if it is included by use of a filter. The test will not be + run simply because an enclosing suite is run. + + + + + Default constructor + + + + + Constructor with a reason + + The reason test is marked explicit + + + + The reason test is marked explicit + + + + + Attribute used to provide descriptive text about a + test case or fixture. + + + + + Construct the attribute + + Text describing the test + + + + Gets the test description + + + + + Interface implemented by a user fixture in order to + validate any expected exceptions. It is only called + for test methods marked with the ExpectedException + attribute. + + + + + Method to handle an expected exception + + The exception to be handled + + + diff --git a/trunk/Libraries/Json40r2/Source/Src/Lib/NUnit/Silverlight/nunit.framework.dll b/trunk/Libraries/Json40r2/Source/Src/Lib/NUnit/Silverlight/nunit.framework.dll new file mode 100644 index 0000000..d4e0b72 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Src/Lib/NUnit/Silverlight/nunit.framework.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Net20.sln b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Net20.sln new file mode 100644 index 0000000..eaaa295 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Net20.sln @@ -0,0 +1,33 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{620042D9-2753-48F5-BEDE-3905248781D2}" + ProjectSection(SolutionItems) = preProject + Lib\LinqBridge.dll = Lib\LinqBridge.dll + Lib\NUnit\DotNet\nunit.framework.dll = Lib\NUnit\DotNet\nunit.framework.dll + Lib\NUnit\DotNet\nunit.framework.xml = Lib\NUnit\DotNet\nunit.framework.xml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json.Net20", "Newtonsoft.Json\Newtonsoft.Json.Net20.csproj", "{A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json.Tests.Net20", "Newtonsoft.Json.Tests\Newtonsoft.Json.Tests.Net20.csproj", "{3E6E2335-B079-4B5B-A65A-9D586914BCBB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Release|Any CPU.Build.0 = Release|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Net35.sln b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Net35.sln new file mode 100644 index 0000000..2f1bc6e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Net35.sln @@ -0,0 +1,26 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json.Net35", "Newtonsoft.Json\Newtonsoft.Json.Net35.csproj", "{A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json.Tests.Net35", "Newtonsoft.Json.Tests\Newtonsoft.Json.Tests.Net35.csproj", "{3E6E2335-B079-4B5B-A65A-9D586914BCBB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Release|Any CPU.Build.0 = Release|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Silverlight.sln b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Silverlight.sln new file mode 100644 index 0000000..75720f1 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Silverlight.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{F69285DD-24FB-4A60-AE8B-4C2744285D0F}" + ProjectSection(SolutionItems) = preProject + Lib\NUnit\Silverlight\nunit.framework.dll = Lib\NUnit\Silverlight\nunit.framework.dll + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json.Silverlight", "Newtonsoft.Json\Newtonsoft.Json.Silverlight.csproj", "{DC3C6F3D-2CA1-4278-9B79-63770FB3EA2D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json.Tests.Silverlight", "Newtonsoft.Json.Tests\Newtonsoft.Json.Tests.Silverlight.csproj", "{0D8C3C2E-62C6-4C93-9377-6F74DD6BFD93}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {DC3C6F3D-2CA1-4278-9B79-63770FB3EA2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DC3C6F3D-2CA1-4278-9B79-63770FB3EA2D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DC3C6F3D-2CA1-4278-9B79-63770FB3EA2D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DC3C6F3D-2CA1-4278-9B79-63770FB3EA2D}.Release|Any CPU.Build.0 = Release|Any CPU + {0D8C3C2E-62C6-4C93-9377-6F74DD6BFD93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0D8C3C2E-62C6-4C93-9377-6F74DD6BFD93}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0D8C3C2E-62C6-4C93-9377-6F74DD6BFD93}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0D8C3C2E-62C6-4C93-9377-6F74DD6BFD93}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs new file mode 100644 index 0000000..8ba4351 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Bson/BsonReaderTests.cs @@ -0,0 +1,1121 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Bson; +using System.IO; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Bson +{ + public class BsonReaderTests : TestFixtureBase + { + private const char Euro = '\u20ac'; + + [Test] + public void CloseInput() + { + MemoryStream ms = new MemoryStream(); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(ms.CanRead); + reader.Close(); + Assert.IsFalse(ms.CanRead); + + ms = new MemoryStream(); + reader = new BsonReader(ms) { CloseInput = false }; + + Assert.IsTrue(ms.CanRead); + reader.Close(); + Assert.IsTrue(ms.CanRead); + } + + [Test] + public void ReadSingleObject() + { + byte[] data = MiscellaneousUtils.HexToBytes("0F-00-00-00-10-42-6C-61-68-00-01-00-00-00-00"); + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("Blah", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual(1, reader.Value); + Assert.AreEqual(typeof(long), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void WriteValues() + { + byte[] data = MiscellaneousUtils.HexToBytes("8C-00-00-00-12-30-00-FF-FF-FF-FF-FF-FF-FF-7F-12-31-00-FF-FF-FF-FF-FF-FF-FF-7F-10-32-00-FF-FF-FF-7F-10-33-00-FF-FF-FF-7F-10-34-00-FF-00-00-00-10-35-00-7F-00-00-00-02-36-00-02-00-00-00-61-00-01-37-00-00-00-00-00-00-00-F0-45-01-38-00-FF-FF-FF-FF-FF-FF-EF-7F-01-39-00-00-00-00-E0-FF-FF-EF-47-08-31-30-00-01-05-31-31-00-05-00-00-00-02-00-01-02-03-04-09-31-32-00-40-C5-E2-BA-E3-00-00-00-09-31-33-00-40-C5-E2-BA-E3-00-00-00-00"); + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + reader.JsonNet35BinaryCompatibility = true; + reader.ReadRootValueAsArray = true; + reader.DateTimeKindHandling = DateTimeKind.Utc; + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual(long.MaxValue, reader.Value); + Assert.AreEqual(typeof(long), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual(long.MaxValue, reader.Value); + Assert.AreEqual(typeof(long), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual(int.MaxValue, reader.Value); + Assert.AreEqual(typeof(long), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual(int.MaxValue, reader.Value); + Assert.AreEqual(typeof(long), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual(byte.MaxValue, reader.Value); + Assert.AreEqual(typeof(long), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual(sbyte.MaxValue, reader.Value); + Assert.AreEqual(typeof(long), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("a", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(decimal.MaxValue, reader.Value); + Assert.AreEqual(typeof(double), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(double.MaxValue, reader.Value); + Assert.AreEqual(typeof(double), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(float.MaxValue, reader.Value); + Assert.AreEqual(typeof(double), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Boolean, reader.TokenType); + Assert.AreEqual(true, reader.Value); + Assert.AreEqual(typeof(bool), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Bytes, reader.TokenType); + Assert.AreEqual(new byte[] { 0, 1, 2, 3, 4 }, reader.Value); + Assert.AreEqual(typeof(byte[]), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Date, reader.TokenType); + Assert.AreEqual(new DateTime(2000, 12, 29, 12, 30, 0, DateTimeKind.Utc), reader.Value); + Assert.AreEqual(typeof(DateTime), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Date, reader.TokenType); + Assert.AreEqual(new DateTime(2000, 12, 29, 12, 30, 0, DateTimeKind.Utc), reader.Value); + Assert.AreEqual(typeof(DateTime), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + + [Test] + public void ReadObjectBsonFromSite() + { + byte[] data = MiscellaneousUtils.HexToBytes("20-00-00-00-02-30-00-02-00-00-00-61-00-02-31-00-02-00-00-00-62-00-02-32-00-02-00-00-00-63-00-00"); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("0", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("a", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("1", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("b", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("2", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("c", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadArrayBsonFromSite() + { + byte[] data = MiscellaneousUtils.HexToBytes("20-00-00-00-02-30-00-02-00-00-00-61-00-02-31-00-02-00-00-00-62-00-02-32-00-02-00-00-00-63-00-00"); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.AreEqual(false, reader.ReadRootValueAsArray); + Assert.AreEqual(DateTimeKind.Local, reader.DateTimeKindHandling); + + reader.ReadRootValueAsArray = true; + reader.DateTimeKindHandling = DateTimeKind.Utc; + + Assert.AreEqual(true, reader.ReadRootValueAsArray); + Assert.AreEqual(DateTimeKind.Utc, reader.DateTimeKindHandling); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("a", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("b", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("c", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadBytes() + { + byte[] data = MiscellaneousUtils.HexToBytes("2B-00-00-00-02-30-00-02-00-00-00-61-00-02-31-00-02-00-00-00-62-00-05-32-00-0C-00-00-00-02-48-65-6C-6C-6F-20-77-6F-72-6C-64-21-00"); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms, true, DateTimeKind.Utc); + reader.JsonNet35BinaryCompatibility = true; + + Assert.AreEqual(true, reader.ReadRootValueAsArray); + Assert.AreEqual(DateTimeKind.Utc, reader.DateTimeKindHandling); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("a", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("b", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + byte[] encodedStringData = reader.ReadAsBytes(); + Assert.IsNotNull(encodedStringData); + Assert.AreEqual(JsonToken.Bytes, reader.TokenType); + Assert.AreEqual(encodedStringData, reader.Value); + Assert.AreEqual(typeof(byte[]), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsFalse(reader.Read()); + + string decodedString = Encoding.UTF8.GetString(encodedStringData, 0, encodedStringData.Length); + Assert.AreEqual("Hello world!", decodedString); + } + + [Test] + public void ReadOid() + { + byte[] data = MiscellaneousUtils.HexToBytes("29000000075F6964004ABBED9D1D8B0F02180000010274657374000900000031323334C2A335360000"); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("_id", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Bytes, reader.TokenType); + Assert.AreEqual(MiscellaneousUtils.HexToBytes("4ABBED9D1D8B0F0218000001"), reader.Value); + Assert.AreEqual(typeof(byte[]), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("test", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("1234£56", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadNestedArray() + { + string hexdoc = "82-00-00-00-07-5F-69-64-00-4A-78-93-79-17-22-00-00-00-00-61-CF-04-61-00-5D-00-00-00-01-30-00-00-00-00-00-00-00-F0-3F-01-31-00-00-00-00-00-00-00-00-40-01-32-00-00-00-00-00-00-00-08-40-01-33-00-00-00-00-00-00-00-10-40-01-34-00-00-00-00-00-00-00-14-50-01-35-00-00-00-00-00-00-00-18-40-01-36-00-00-00-00-00-00-00-1C-40-01-37-00-00-00-00-00-00-00-20-40-00-02-62-00-05-00-00-00-74-65-73-74-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("_id", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Bytes, reader.TokenType); + Assert.AreEqual(MiscellaneousUtils.HexToBytes("4A-78-93-79-17-22-00-00-00-00-61-CF"), reader.Value); + Assert.AreEqual(typeof(byte[]), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("a", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + for (int i = 1; i <= 8; i++) + { + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + + double value = (i != 5) + ? Convert.ToDouble(i) + : 5.78960446186581E+77d; + + Assert.AreEqual(value, reader.Value); + } + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("b", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("test", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadNestedArrayIntoLinq() + { + string hexdoc = "87-00-00-00-05-5F-69-64-00-0C-00-00-00-00-4A-78-93-79-17-22-00-00-00-00-61-CF-04-61-00-5D-00-00-00-01-30-00-00-00-00-00-00-00-F0-3F-01-31-00-00-00-00-00-00-00-00-40-01-32-00-00-00-00-00-00-00-08-40-01-33-00-00-00-00-00-00-00-10-40-01-34-00-00-00-00-00-00-00-14-50-01-35-00-00-00-00-00-00-00-18-40-01-36-00-00-00-00-00-00-00-1C-40-01-37-00-00-00-00-00-00-00-20-40-00-02-62-00-05-00-00-00-74-65-73-74-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + BsonReader reader = new BsonReader(new MemoryStream(data)); + reader.JsonNet35BinaryCompatibility = true; + + JObject o = (JObject)JToken.ReadFrom(reader); + Assert.AreEqual(3, o.Count); + + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + o.WriteTo(writer); + writer.Flush(); + + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + Assert.AreEqual(hexdoc, bson); + } + + [Test] + public void OidAndBytesAreEqual() + { + byte[] data1 = MiscellaneousUtils.HexToBytes( + "82-00-00-00-07-5F-69-64-00-4A-78-93-79-17-22-00-00-00-00-61-CF-04-61-00-5D-00-00-00-01-30-00-00-00-00-00-00-00-F0-3F-01-31-00-00-00-00-00-00-00-00-40-01-32-00-00-00-00-00-00-00-08-40-01-33-00-00-00-00-00-00-00-10-40-01-34-00-00-00-00-00-00-00-14-50-01-35-00-00-00-00-00-00-00-18-40-01-36-00-00-00-00-00-00-00-1C-40-01-37-00-00-00-00-00-00-00-20-40-00-02-62-00-05-00-00-00-74-65-73-74-00-00"); + + BsonReader reader1 = new BsonReader(new MemoryStream(data1)); + reader1.JsonNet35BinaryCompatibility = true; + + // oid + JObject o1 = (JObject)JToken.ReadFrom(reader1); + + byte[] data2 = MiscellaneousUtils.HexToBytes( + "87-00-00-00-05-5F-69-64-00-0C-00-00-00-02-4A-78-93-79-17-22-00-00-00-00-61-CF-04-61-00-5D-00-00-00-01-30-00-00-00-00-00-00-00-F0-3F-01-31-00-00-00-00-00-00-00-00-40-01-32-00-00-00-00-00-00-00-08-40-01-33-00-00-00-00-00-00-00-10-40-01-34-00-00-00-00-00-00-00-14-50-01-35-00-00-00-00-00-00-00-18-40-01-36-00-00-00-00-00-00-00-1C-40-01-37-00-00-00-00-00-00-00-20-40-00-02-62-00-05-00-00-00-74-65-73-74-00-00"); + + BsonReader reader2 = new BsonReader(new MemoryStream(data2)); + reader2.JsonNet35BinaryCompatibility = true; + + // bytes + JObject o2 = (JObject)JToken.ReadFrom(reader2); + + Assert.IsTrue(o1.DeepEquals(o2)); + } + + [Test] + public void ReadRegex() + { + string hexdoc = "15-00-00-00-0B-72-65-67-65-78-00-74-65-73-74-00-67-69-6D-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("regex", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(@"/test/gim", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadCode() + { + string hexdoc = "1A-00-00-00-0D-63-6F-64-65-00-0B-00-00-00-49-20-61-6D-20-63-6F-64-65-21-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("code", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(@"I am code!", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadUndefined() + { + string hexdoc = "10-00-00-00-06-75-6E-64-65-66-69-6E-65-64-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("undefined", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Undefined, reader.TokenType); + Assert.AreEqual(null, reader.Value); + Assert.AreEqual(null, reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadLong() + { + string hexdoc = "13-00-00-00-12-6C-6F-6E-67-00-FF-FF-FF-FF-FF-FF-FF-7F-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("long", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual(long.MaxValue, reader.Value); + Assert.AreEqual(typeof(long), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadReference() + { + string hexdoc = "1E-00-00-00-0C-6F-69-64-00-04-00-00-00-6F-69-64-00-01-02-03-04-05-06-07-08-09-0A-0B-0C-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("oid", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("$ref", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("oid", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("$id", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Bytes, reader.TokenType); + Assert.AreEqual(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, reader.Value); + Assert.AreEqual(typeof(byte[]), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadCodeWScope() + { + string hexdoc = "75-00-00-00-0F-63-6F-64-65-57-69-74-68-53-63-6F-70-65-00-61-00-00-00-35-00-00-00-66-6F-72-20-28-69-6E-74-20-69-20-3D-20-30-3B-20-69-20-3C-20-31-30-30-30-3B-20-69-2B-2B-29-0D-0A-7B-0D-0A-20-20-61-6C-65-72-74-28-61-72-67-31-29-3B-0D-0A-7D-00-24-00-00-00-02-61-72-67-31-00-15-00-00-00-4A-73-6F-6E-2E-4E-45-54-20-69-73-20-61-77-65-73-6F-6D-65-2E-00-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("codeWithScope", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("$code", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(@"for (int i = 0; i < 1000; i++) +{ + alert(arg1); +}", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("$scope", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("arg1", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("Json.NET is awesome.", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadEndOfStream() + { + BsonReader reader = new BsonReader(new MemoryStream()); + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadLargeStrings() + { + string bson = + "4E-02-00-00-02-30-2D-31-2D-32-2D-33-2D-34-2D-35-2D-36-2D-37-2D-38-2D-39-2D-31-30-2D-31-31-2D-31-32-2D-31-33-2D-31-34-2D-31-35-2D-31-36-2D-31-37-2D-31-38-2D-31-39-2D-32-30-2D-32-31-2D-32-32-2D-32-33-2D-32-34-2D-32-35-2D-32-36-2D-32-37-2D-32-38-2D-32-39-2D-33-30-2D-33-31-2D-33-32-2D-33-33-2D-33-34-2D-33-35-2D-33-36-2D-33-37-2D-33-38-2D-33-39-2D-34-30-2D-34-31-2D-34-32-2D-34-33-2D-34-34-2D-34-35-2D-34-36-2D-34-37-2D-34-38-2D-34-39-2D-35-30-2D-35-31-2D-35-32-2D-35-33-2D-35-34-2D-35-35-2D-35-36-2D-35-37-2D-35-38-2D-35-39-2D-36-30-2D-36-31-2D-36-32-2D-36-33-2D-36-34-2D-36-35-2D-36-36-2D-36-37-2D-36-38-2D-36-39-2D-37-30-2D-37-31-2D-37-32-2D-37-33-2D-37-34-2D-37-35-2D-37-36-2D-37-37-2D-37-38-2D-37-39-2D-38-30-2D-38-31-2D-38-32-2D-38-33-2D-38-34-2D-38-35-2D-38-36-2D-38-37-2D-38-38-2D-38-39-2D-39-30-2D-39-31-2D-39-32-2D-39-33-2D-39-34-2D-39-35-2D-39-36-2D-39-37-2D-39-38-2D-39-39-00-22-01-00-00-30-2D-31-2D-32-2D-33-2D-34-2D-35-2D-36-2D-37-2D-38-2D-39-2D-31-30-2D-31-31-2D-31-32-2D-31-33-2D-31-34-2D-31-35-2D-31-36-2D-31-37-2D-31-38-2D-31-39-2D-32-30-2D-32-31-2D-32-32-2D-32-33-2D-32-34-2D-32-35-2D-32-36-2D-32-37-2D-32-38-2D-32-39-2D-33-30-2D-33-31-2D-33-32-2D-33-33-2D-33-34-2D-33-35-2D-33-36-2D-33-37-2D-33-38-2D-33-39-2D-34-30-2D-34-31-2D-34-32-2D-34-33-2D-34-34-2D-34-35-2D-34-36-2D-34-37-2D-34-38-2D-34-39-2D-35-30-2D-35-31-2D-35-32-2D-35-33-2D-35-34-2D-35-35-2D-35-36-2D-35-37-2D-35-38-2D-35-39-2D-36-30-2D-36-31-2D-36-32-2D-36-33-2D-36-34-2D-36-35-2D-36-36-2D-36-37-2D-36-38-2D-36-39-2D-37-30-2D-37-31-2D-37-32-2D-37-33-2D-37-34-2D-37-35-2D-37-36-2D-37-37-2D-37-38-2D-37-39-2D-38-30-2D-38-31-2D-38-32-2D-38-33-2D-38-34-2D-38-35-2D-38-36-2D-38-37-2D-38-38-2D-38-39-2D-39-30-2D-39-31-2D-39-32-2D-39-33-2D-39-34-2D-39-35-2D-39-36-2D-39-37-2D-39-38-2D-39-39-00-00"; + + BsonReader reader = new BsonReader(new MemoryStream(MiscellaneousUtils.HexToBytes(bson))); + + StringBuilder largeStringBuilder = new StringBuilder(); + for (int i = 0; i < 100; i++) + { + if (i > 0) + largeStringBuilder.Append("-"); + + largeStringBuilder.Append(i.ToString(CultureInfo.InvariantCulture)); + } + string largeString = largeStringBuilder.ToString(); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual(largeString, reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(largeString, reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadEmptyStrings() + { + string bson = "0C-00-00-00-02-00-01-00-00-00-00-00"; + + BsonReader reader = new BsonReader(new MemoryStream(MiscellaneousUtils.HexToBytes(bson))); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void WriteAndReadEmptyListsAndDictionaries() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartObject(); + writer.WritePropertyName("Arguments"); + writer.WriteStartObject(); + writer.WriteEndObject(); + writer.WritePropertyName("List"); + writer.WriteStartArray(); + writer.WriteEndArray(); + writer.WriteEndObject(); + + string bson = BitConverter.ToString(ms.ToArray()); + + Assert.AreEqual("20-00-00-00-03-41-72-67-75-6D-65-6E-74-73-00-05-00-00-00-00-04-4C-69-73-74-00-05-00-00-00-00-00", bson); + + BsonReader reader = new BsonReader(new MemoryStream(MiscellaneousUtils.HexToBytes(bson))); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("Arguments", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("List", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void DateTimeKindHandling() + { + DateTime value = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc); + + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartObject(); + writer.WritePropertyName("DateTime"); + writer.WriteValue(value); + writer.WriteEndObject(); + + byte[] bson = ms.ToArray(); + + JObject o; + BsonReader reader; + + reader = new BsonReader(new MemoryStream(bson), false, DateTimeKind.Utc); + o = (JObject)JToken.ReadFrom(reader); + Assert.AreEqual(value, (DateTime)o["DateTime"]); + + reader = new BsonReader(new MemoryStream(bson), false, DateTimeKind.Local); + o = (JObject)JToken.ReadFrom(reader); + Assert.AreEqual(value.ToLocalTime(), (DateTime)o["DateTime"]); + + reader = new BsonReader(new MemoryStream(bson), false, DateTimeKind.Unspecified); + o = (JObject)JToken.ReadFrom(reader); + Assert.AreEqual(DateTime.SpecifyKind(value.ToLocalTime(), DateTimeKind.Unspecified), (DateTime)o["DateTime"]); + } + + private string WriteAndReadStringValue(string val) + { + MemoryStream ms = new MemoryStream(); + BsonWriter bs = new BsonWriter(ms); + bs.WriteStartObject(); + bs.WritePropertyName("StringValue"); + bs.WriteValue(val); + bs.WriteEnd(); + + ms.Seek(0, SeekOrigin.Begin); + + BsonReader reader = new BsonReader(ms); + // object + reader.Read(); + // property name + reader.Read(); + // string + reader.Read(); + return (string)reader.Value; + } + + private string WriteAndReadStringPropertyName(string val) + { + MemoryStream ms = new MemoryStream(); + BsonWriter bs = new BsonWriter(ms); + bs.WriteStartObject(); + bs.WritePropertyName(val); + bs.WriteValue("Dummy"); + bs.WriteEnd(); + + ms.Seek(0, SeekOrigin.Begin); + + BsonReader reader = new BsonReader(ms); + // object + reader.Read(); + // property name + reader.Read(); + return (string)reader.Value; + } + + [Test] + public void TestReadLenStringValueShortTripleByte() + { + StringBuilder sb = new StringBuilder(); + //sb.Append('1',127); //first char of euro at the end of the boundry. + //sb.Append(euro, 5); + //sb.Append('1',128); + sb.Append(Euro); + + string expected = sb.ToString(); + Assert.AreEqual(expected, WriteAndReadStringValue(expected)); + } + + [Test] + public void TestReadLenStringValueTripleByteCharBufferBoundry0() + { + StringBuilder sb = new StringBuilder(); + sb.Append('1', 127); //first char of euro at the end of the boundry. + sb.Append(Euro, 5); + sb.Append('1', 128); + sb.Append(Euro); + + string expected = sb.ToString(); + Assert.AreEqual(expected, WriteAndReadStringValue(expected)); + } + + [Test] + public void TestReadLenStringValueTripleByteCharBufferBoundry1() + { + StringBuilder sb = new StringBuilder(); + sb.Append('1', 126); + sb.Append(Euro, 5); //middle char of euro at the end of the boundry. + sb.Append('1', 128); + sb.Append(Euro); + + string expected = sb.ToString(); + string result = WriteAndReadStringValue(expected); + Assert.AreEqual(expected, result); + } + + [Test] + public void TestReadLenStringValueTripleByteCharOne() + { + StringBuilder sb = new StringBuilder(); + sb.Append(Euro, 1); //Just one triple byte char in the string. + + string expected = sb.ToString(); + Assert.AreEqual(expected, WriteAndReadStringValue(expected)); + } + + [Test] + public void TestReadLenStringValueTripleByteCharBufferBoundry2() + { + StringBuilder sb = new StringBuilder(); + sb.Append('1', 125); + sb.Append(Euro, 5); //last char of the eruo at the end of the boundry. + sb.Append('1', 128); + sb.Append(Euro); + + string expected = sb.ToString(); + Assert.AreEqual(expected, WriteAndReadStringValue(expected)); + } + + [Test] + public void TestReadStringValue() + { + string expected = "test"; + Assert.AreEqual(expected, WriteAndReadStringValue(expected)); + } + + [Test] + public void TestReadStringValueLong() + { + StringBuilder sb = new StringBuilder(); + sb.Append('t', 150); + string expected = sb.ToString(); + Assert.AreEqual(expected, WriteAndReadStringValue(expected)); + } + + [Test] + public void TestReadStringPropertyNameShortTripleByte() + { + StringBuilder sb = new StringBuilder(); + //sb.Append('1',127); //first char of euro at the end of the boundry. + //sb.Append(euro, 5); + //sb.Append('1',128); + sb.Append(Euro); + + string expected = sb.ToString(); + Assert.AreEqual(expected, WriteAndReadStringPropertyName(expected)); + } + + [Test] + public void TestReadStringPropertyNameTripleByteCharBufferBoundry0() + { + StringBuilder sb = new StringBuilder(); + sb.Append('1', 127); //first char of euro at the end of the boundry. + sb.Append(Euro, 5); + sb.Append('1', 128); + sb.Append(Euro); + + string expected = sb.ToString(); + string result = WriteAndReadStringPropertyName(expected); + Assert.AreEqual(expected, result); + } + + [Test] + public void TestReadStringPropertyNameTripleByteCharBufferBoundry1() + { + StringBuilder sb = new StringBuilder(); + sb.Append('1', 126); + sb.Append(Euro, 5); //middle char of euro at the end of the boundry. + sb.Append('1', 128); + sb.Append(Euro); + + string expected = sb.ToString(); + Assert.AreEqual(expected, WriteAndReadStringPropertyName(expected)); + } + + [Test] + public void TestReadStringPropertyNameTripleByteCharOne() + { + StringBuilder sb = new StringBuilder(); + sb.Append(Euro, 1); //Just one triple byte char in the string. + + string expected = sb.ToString(); + Assert.AreEqual(expected, WriteAndReadStringPropertyName(expected)); + } + + [Test] + public void TestReadStringPropertyNameTripleByteCharBufferBoundry2() + { + StringBuilder sb = new StringBuilder(); + sb.Append('1', 125); + sb.Append(Euro, 5); //last char of the eruo at the end of the boundry. + sb.Append('1', 128); + sb.Append(Euro); + + string expected = sb.ToString(); + Assert.AreEqual(expected, WriteAndReadStringPropertyName(expected)); + } + + [Test] + public void TestReadStringPropertyName() + { + string expected = "test"; + Assert.AreEqual(expected, WriteAndReadStringPropertyName(expected)); + } + + [Test] + public void TestReadStringPropertyNameLong() + { + StringBuilder sb = new StringBuilder(); + sb.Append('t', 150); + string expected = sb.ToString(); + Assert.AreEqual(expected, WriteAndReadStringPropertyName(expected)); + } + + [Test] + public void ReadRegexWithOptions() + { + string hexdoc = "1A-00-00-00-0B-72-65-67-65-78-00-61-62-63-00-69-00-0B-74-65-73-74-00-00-00-00"; + + byte[] data = MiscellaneousUtils.HexToBytes(hexdoc); + + MemoryStream ms = new MemoryStream(data); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("/abc/i", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("//", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void CanRoundTripStackOverflowData() + { + var doc = + @"{ +""AboutMe"": ""

I'm the Director for Research and Development for ProPhoenix, a public safety software company. This position allows me to investigate new and existing technologies and incorporate them into our product line, with the end goal being to help public safety agencies to do their jobs more effeciently and safely.

\r\n\r\n

I'm an advocate for PowerShell, as I believe it encourages administrative best practices and allows developers to provide additional access to their applications, without needing to explicity write code for each administrative feature. Part of my advocacy for PowerShell includes my blog, appearances on various podcasts, and acting as a Community Director for PowerShellCommunity.Org

\r\n\r\n

I’m also a co-host of Mind of Root (a weekly audio podcast about systems administration, tech news, and topics).

\r\n"", +""WebsiteUrl"": ""http://blog.usepowershell.com"" +}"; + JObject parsed = JObject.Parse(doc); + var memoryStream = new MemoryStream(); + var bsonWriter = new BsonWriter(memoryStream); + parsed.WriteTo(bsonWriter); + bsonWriter.Flush(); + memoryStream.Position = 0; + + BsonReader reader = new BsonReader(memoryStream); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("AboutMe", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(@"

I'm the Director for Research and Development for ProPhoenix, a public safety software company. This position allows me to investigate new and existing technologies and incorporate them into our product line, with the end goal being to help public safety agencies to do their jobs more effeciently and safely.

+ +

I'm an advocate for PowerShell, as I believe it encourages administrative best practices and allows developers to provide additional access to their applications, without needing to explicity write code for each administrative feature. Part of my advocacy for PowerShell includes my blog, appearances on various podcasts, and acting as a Community Director for PowerShellCommunity.Org

+ +

I’m also a co-host of Mind of Root (a weekly audio podcast about systems administration, tech news, and topics).

+", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("WebsiteUrl", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("http://blog.usepowershell.com", reader.Value); + Assert.AreEqual(typeof(string), reader.ValueType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void MultibyteCharacterPropertyNamesAndStrings() + { + string json = @"{ + ""ΕΝΤΟΛΗ ΧΧΧ ΧΧΧΧΧΧΧΧΧ ΤΑ ΠΡΩΤΑΣΦΑΛΙΣΤΗΡΙΑ ΠΟΥ ΔΕΝ ΕΧΟΥΝ ΥΠΟΛΟΙΠΟ ΝΑ ΤΑ ΣΤΕΛΝΟΥΜΕ ΑΠΕΥΘΕΙΑΣ ΣΤΟΥΣ ΠΕΛΑΤΕΣ"": ""ΕΝΤΟΛΗ ΧΧΧ ΧΧΧΧΧΧΧΧΧ ΤΑ ΠΡΩΤΑΣΦΑΛΙΣΤΗΡΙΑ ΠΟΥ ΔΕΝ ΕΧΟΥΝ ΥΠΟΛΟΙΠΟ ΝΑ ΤΑ ΣΤΕΛΝΟΥΜΕ ΑΠΕΥΘΕΙΑΣ ΣΤΟΥΣ ΠΕΛΑΤΕΣ"" +}"; + JObject parsed = JObject.Parse(json); + var memoryStream = new MemoryStream(); + var bsonWriter = new BsonWriter(memoryStream); + parsed.WriteTo(bsonWriter); + bsonWriter.Flush(); + memoryStream.Position = 0; + + BsonReader reader = new BsonReader(memoryStream); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("ΕΝΤΟΛΗ ΧΧΧ ΧΧΧΧΧΧΧΧΧ ΤΑ ΠΡΩΤΑΣΦΑΛΙΣΤΗΡΙΑ ΠΟΥ ΔΕΝ ΕΧΟΥΝ ΥΠΟΛΟΙΠΟ ΝΑ ΤΑ ΣΤΕΛΝΟΥΜΕ ΑΠΕΥΘΕΙΑΣ ΣΤΟΥΣ ΠΕΛΑΤΕΣ", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("ΕΝΤΟΛΗ ΧΧΧ ΧΧΧΧΧΧΧΧΧ ΤΑ ΠΡΩΤΑΣΦΑΛΙΣΤΗΡΙΑ ΠΟΥ ΔΕΝ ΕΧΟΥΝ ΥΠΟΛΟΙΠΟ ΝΑ ΤΑ ΣΤΕΛΝΟΥΜΕ ΑΠΕΥΘΕΙΑΣ ΣΤΟΥΣ ΠΕΛΑΤΕΣ", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs new file mode 100644 index 0000000..6b7d83c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Bson/BsonWriterTests.cs @@ -0,0 +1,629 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Bson; +using System.IO; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Tests.TestObjects; +using System.Globalization; + +namespace Newtonsoft.Json.Tests.Bson +{ + public class BsonWriterTests : TestFixtureBase + { + [Test] + public void CloseOutput() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + Assert.IsTrue(ms.CanRead); + writer.Close(); + Assert.IsFalse(ms.CanRead); + + ms = new MemoryStream(); + writer = new BsonWriter(ms) { CloseOutput = false }; + + Assert.IsTrue(ms.CanRead); + writer.Close(); + Assert.IsTrue(ms.CanRead); + } + + [Test] + public void WriteSingleObject() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartObject(); + writer.WritePropertyName("Blah"); + writer.WriteValue(1); + writer.WriteEndObject(); + + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + Assert.AreEqual("0F-00-00-00-10-42-6C-61-68-00-01-00-00-00-00", bson); + } + +#if !PocketPC && !NET20 + [Test] + public void WriteValues() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartArray(); + writer.WriteValue(long.MaxValue); + writer.WriteValue((ulong)long.MaxValue); + writer.WriteValue(int.MaxValue); + writer.WriteValue((uint)int.MaxValue); + writer.WriteValue(byte.MaxValue); + writer.WriteValue(sbyte.MaxValue); + writer.WriteValue('a'); + writer.WriteValue(decimal.MaxValue); + writer.WriteValue(double.MaxValue); + writer.WriteValue(float.MaxValue); + writer.WriteValue(true); + writer.WriteValue(new byte[] { 0, 1, 2, 3, 4 }); + writer.WriteValue(new DateTimeOffset(2000, 12, 29, 12, 30, 0, TimeSpan.Zero)); + writer.WriteValue(new DateTime(2000, 12, 29, 12, 30, 0, DateTimeKind.Utc)); + writer.WriteEnd(); + + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + Assert.AreEqual("8C-00-00-00-12-30-00-FF-FF-FF-FF-FF-FF-FF-7F-12-31-00-FF-FF-FF-FF-FF-FF-FF-7F-10-32-00-FF-FF-FF-7F-10-33-00-FF-FF-FF-7F-10-34-00-FF-00-00-00-10-35-00-7F-00-00-00-02-36-00-02-00-00-00-61-00-01-37-00-00-00-00-00-00-00-F0-45-01-38-00-FF-FF-FF-FF-FF-FF-EF-7F-01-39-00-00-00-00-E0-FF-FF-EF-47-08-31-30-00-01-05-31-31-00-05-00-00-00-00-00-01-02-03-04-09-31-32-00-40-C5-E2-BA-E3-00-00-00-09-31-33-00-40-C5-E2-BA-E3-00-00-00-00", bson); + } +#endif + + [Test] + public void WriteArrayBsonFromSite() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + writer.WriteStartArray(); + writer.WriteValue("a"); + writer.WriteValue("b"); + writer.WriteValue("c"); + writer.WriteEndArray(); + + writer.Flush(); + + ms.Seek(0, SeekOrigin.Begin); + + string expected = "20-00-00-00-02-30-00-02-00-00-00-61-00-02-31-00-02-00-00-00-62-00-02-32-00-02-00-00-00-63-00-00"; + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + + Assert.AreEqual(expected, bson); + } + + [Test] + public void WriteBytes() + { + byte[] data = Encoding.UTF8.GetBytes("Hello world!"); + + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + writer.WriteStartArray(); + writer.WriteValue("a"); + writer.WriteValue("b"); + writer.WriteValue(data); + writer.WriteEndArray(); + + writer.Flush(); + + ms.Seek(0, SeekOrigin.Begin); + + string expected = "2B-00-00-00-02-30-00-02-00-00-00-61-00-02-31-00-02-00-00-00-62-00-05-32-00-0C-00-00-00-00-48-65-6C-6C-6F-20-77-6F-72-6C-64-21-00"; + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + + Assert.AreEqual(expected, bson); + + BsonReader reader = new BsonReader(new MemoryStream(ms.ToArray())); + reader.ReadRootValueAsArray = true; + reader.Read(); + reader.Read(); + reader.Read(); + reader.Read(); + Assert.AreEqual(JsonToken.Bytes, reader.TokenType); + Assert.AreEqual(data, reader.Value); + } + + [Test] + public void WriteNestedArray() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + writer.WriteStartObject(); + + writer.WritePropertyName("_id"); + writer.WriteValue(MiscellaneousUtils.HexToBytes("4A-78-93-79-17-22-00-00-00-00-61-CF")); + + writer.WritePropertyName("a"); + writer.WriteStartArray(); + for (int i = 1; i <= 8; i++) + { + double value = (i != 5) + ? Convert.ToDouble(i) + : 5.78960446186581E+77d; + + writer.WriteValue(value); + } + writer.WriteEndArray(); + + writer.WritePropertyName("b"); + writer.WriteValue("test"); + + writer.WriteEndObject(); + + writer.Flush(); + + ms.Seek(0, SeekOrigin.Begin); + + string expected = "87-00-00-00-05-5F-69-64-00-0C-00-00-00-00-4A-78-93-79-17-22-00-00-00-00-61-CF-04-61-00-5D-00-00-00-01-30-00-00-00-00-00-00-00-F0-3F-01-31-00-00-00-00-00-00-00-00-40-01-32-00-00-00-00-00-00-00-08-40-01-33-00-00-00-00-00-00-00-10-40-01-34-00-00-00-00-00-00-00-14-50-01-35-00-00-00-00-00-00-00-18-40-01-36-00-00-00-00-00-00-00-1C-40-01-37-00-00-00-00-00-00-00-20-40-00-02-62-00-05-00-00-00-74-65-73-74-00-00"; + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + + Assert.AreEqual(expected, bson); + } + + [Test] + public void WriteSerializedStore() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + Store s1 = new Store(); + s1.Color = StoreColor.White; + s1.Cost = 999.59m; + s1.Employees = int.MaxValue - 1; + s1.Open = true; + s1.product.Add(new Product + { + ExpiryDate = new DateTime(2000, 9, 28, 3, 59, 58, DateTimeKind.Local), + Name = "BSON!", + Price = -0.1m, + Sizes = new [] { "First", "Second" } + }); + s1.Establised = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Local); + + JsonSerializer serializer = new JsonSerializer(); + serializer.Serialize(writer, s1); + + ms.Seek(0, SeekOrigin.Begin); + BsonReader reader = new BsonReader(ms); + Store s2 = (Store)serializer.Deserialize(reader, typeof (Store)); + + Assert.AreNotEqual(s1, s2); + Assert.AreEqual(s1.Color, s2.Color); + Assert.AreEqual(s1.Cost, s2.Cost); + Assert.AreEqual(s1.Employees, s2.Employees); + Assert.AreEqual(s1.Escape, s2.Escape); + Assert.AreEqual(s1.Establised, s2.Establised); + Assert.AreEqual(s1.Mottos.Count, s2.Mottos.Count); + Assert.AreEqual(s1.Mottos.First(), s2.Mottos.First()); + Assert.AreEqual(s1.Mottos.Last(), s2.Mottos.Last()); + Assert.AreEqual(s1.Open, s2.Open); + Assert.AreEqual(s1.product.Count, s2.product.Count); + Assert.AreEqual(s1.RoomsPerFloor.Length, s2.RoomsPerFloor.Length); + Assert.AreEqual(s1.Symbol, s2.Symbol); + Assert.AreEqual(s1.Width, s2.Width); + + MemoryStream ms1 = new MemoryStream(); + BsonWriter writer1 = new BsonWriter(ms1); + + serializer.Serialize(writer1, s1); + + Assert.AreEqual(ms.ToArray(), ms1.ToArray()); + + string s = JsonConvert.SerializeObject(s1); + byte[] textData = Encoding.UTF8.GetBytes(s); + + int l1 = textData.Length; + int l2 = ms.ToArray().Length; + + Console.WriteLine(l1); + Console.WriteLine(l2); + } + + [Test] + public void WriteLargeStrings() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + StringBuilder largeStringBuilder = new StringBuilder(); + for (int i = 0; i < 100; i++) + { + if (i > 0) + largeStringBuilder.Append("-"); + + largeStringBuilder.Append(i.ToString(CultureInfo.InvariantCulture)); + } + string largeString = largeStringBuilder.ToString(); + + writer.WriteStartObject(); + writer.WritePropertyName(largeString); + writer.WriteValue(largeString); + writer.WriteEndObject(); + + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + Assert.AreEqual("4E-02-00-00-02-30-2D-31-2D-32-2D-33-2D-34-2D-35-2D-36-2D-37-2D-38-2D-39-2D-31-30-2D-31-31-2D-31-32-2D-31-33-2D-31-34-2D-31-35-2D-31-36-2D-31-37-2D-31-38-2D-31-39-2D-32-30-2D-32-31-2D-32-32-2D-32-33-2D-32-34-2D-32-35-2D-32-36-2D-32-37-2D-32-38-2D-32-39-2D-33-30-2D-33-31-2D-33-32-2D-33-33-2D-33-34-2D-33-35-2D-33-36-2D-33-37-2D-33-38-2D-33-39-2D-34-30-2D-34-31-2D-34-32-2D-34-33-2D-34-34-2D-34-35-2D-34-36-2D-34-37-2D-34-38-2D-34-39-2D-35-30-2D-35-31-2D-35-32-2D-35-33-2D-35-34-2D-35-35-2D-35-36-2D-35-37-2D-35-38-2D-35-39-2D-36-30-2D-36-31-2D-36-32-2D-36-33-2D-36-34-2D-36-35-2D-36-36-2D-36-37-2D-36-38-2D-36-39-2D-37-30-2D-37-31-2D-37-32-2D-37-33-2D-37-34-2D-37-35-2D-37-36-2D-37-37-2D-37-38-2D-37-39-2D-38-30-2D-38-31-2D-38-32-2D-38-33-2D-38-34-2D-38-35-2D-38-36-2D-38-37-2D-38-38-2D-38-39-2D-39-30-2D-39-31-2D-39-32-2D-39-33-2D-39-34-2D-39-35-2D-39-36-2D-39-37-2D-39-38-2D-39-39-00-22-01-00-00-30-2D-31-2D-32-2D-33-2D-34-2D-35-2D-36-2D-37-2D-38-2D-39-2D-31-30-2D-31-31-2D-31-32-2D-31-33-2D-31-34-2D-31-35-2D-31-36-2D-31-37-2D-31-38-2D-31-39-2D-32-30-2D-32-31-2D-32-32-2D-32-33-2D-32-34-2D-32-35-2D-32-36-2D-32-37-2D-32-38-2D-32-39-2D-33-30-2D-33-31-2D-33-32-2D-33-33-2D-33-34-2D-33-35-2D-33-36-2D-33-37-2D-33-38-2D-33-39-2D-34-30-2D-34-31-2D-34-32-2D-34-33-2D-34-34-2D-34-35-2D-34-36-2D-34-37-2D-34-38-2D-34-39-2D-35-30-2D-35-31-2D-35-32-2D-35-33-2D-35-34-2D-35-35-2D-35-36-2D-35-37-2D-35-38-2D-35-39-2D-36-30-2D-36-31-2D-36-32-2D-36-33-2D-36-34-2D-36-35-2D-36-36-2D-36-37-2D-36-38-2D-36-39-2D-37-30-2D-37-31-2D-37-32-2D-37-33-2D-37-34-2D-37-35-2D-37-36-2D-37-37-2D-37-38-2D-37-39-2D-38-30-2D-38-31-2D-38-32-2D-38-33-2D-38-34-2D-38-35-2D-38-36-2D-38-37-2D-38-38-2D-38-39-2D-39-30-2D-39-31-2D-39-32-2D-39-33-2D-39-34-2D-39-35-2D-39-36-2D-39-37-2D-39-38-2D-39-39-00-00", bson); + } + + [Test] + public void SerializeGoogleGeoCode() + { + string json = @"{ + ""name"": ""1600 Amphitheatre Parkway, Mountain View, CA, USA"", + ""Status"": { + ""code"": 200, + ""request"": ""geocode"" + }, + ""Placemark"": [ + { + ""address"": ""1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA"", + ""AddressDetails"": { + ""Country"": { + ""CountryNameCode"": ""US"", + ""AdministrativeArea"": { + ""AdministrativeAreaName"": ""CA"", + ""SubAdministrativeArea"": { + ""SubAdministrativeAreaName"": ""Santa Clara"", + ""Locality"": { + ""LocalityName"": ""Mountain View"", + ""Thoroughfare"": { + ""ThoroughfareName"": ""1600 Amphitheatre Pkwy"" + }, + ""PostalCode"": { + ""PostalCodeNumber"": ""94043"" + } + } + } + } + }, + ""Accuracy"": 8 + }, + ""Point"": { + ""coordinates"": [-122.083739, 37.423021, 0] + } + } + ] +}"; + + GoogleMapGeocoderStructure jsonGoogleMapGeocoder = JsonConvert.DeserializeObject(json); + + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + JsonSerializer serializer = new JsonSerializer(); + serializer.Serialize(writer, jsonGoogleMapGeocoder); + + ms.Seek(0, SeekOrigin.Begin); + BsonReader reader = new BsonReader(ms); + GoogleMapGeocoderStructure bsonGoogleMapGeocoder = (GoogleMapGeocoderStructure)serializer.Deserialize(reader, typeof (GoogleMapGeocoderStructure)); + + Assert.IsNotNull(bsonGoogleMapGeocoder); + Assert.AreEqual("1600 Amphitheatre Parkway, Mountain View, CA, USA", bsonGoogleMapGeocoder.Name); + Assert.AreEqual("200", bsonGoogleMapGeocoder.Status.Code); + Assert.AreEqual("geocode", bsonGoogleMapGeocoder.Status.Request); + + IList placemarks = bsonGoogleMapGeocoder.Placemark; + Assert.IsNotNull(placemarks); + Assert.AreEqual(1, placemarks.Count); + + Placemark placemark = placemarks[0]; + Assert.AreEqual("1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA", placemark.Address); + Assert.AreEqual(8, placemark.AddressDetails.Accuracy); + Assert.AreEqual("US", placemark.AddressDetails.Country.CountryNameCode); + Assert.AreEqual("CA", placemark.AddressDetails.Country.AdministrativeArea.AdministrativeAreaName); + Assert.AreEqual("Santa Clara", placemark.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.SubAdministrativeAreaName); + Assert.AreEqual("Mountain View", placemark.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName); + Assert.AreEqual("1600 Amphitheatre Pkwy", placemark.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.Thoroughfare.ThoroughfareName); + Assert.AreEqual("94043", placemark.AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.PostalCode.PostalCodeNumber); + Assert.AreEqual(-122.083739m, placemark.Point.Coordinates[0]); + Assert.AreEqual(37.423021m, placemark.Point.Coordinates[1]); + Assert.AreEqual(0m, placemark.Point.Coordinates[2]); + } + + [Test] + public void WriteEmptyStrings() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartObject(); + writer.WritePropertyName(""); + writer.WriteValue(""); + writer.WriteEndObject(); + + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + Assert.AreEqual("0C-00-00-00-02-00-01-00-00-00-00-00", bson); + } + + [Test] + [ExpectedException(typeof(JsonWriterException), ExpectedMessage = "Cannot write JSON comment as BSON.")] + public void WriteComment() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartArray(); + writer.WriteComment("fail"); + } + + [Test] + [ExpectedException(typeof(JsonWriterException), ExpectedMessage = "Cannot write JSON constructor as BSON.")] + public void WriteConstructor() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartArray(); + writer.WriteStartConstructor("fail"); + } + + [Test] + [ExpectedException(typeof(JsonWriterException), ExpectedMessage = "Cannot write raw JSON as BSON.")] + public void WriteRaw() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartArray(); + writer.WriteRaw("fail"); + } + + [Test] + [ExpectedException(typeof(JsonWriterException), ExpectedMessage = "Cannot write raw JSON as BSON.")] + public void WriteRawValue() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartArray(); + writer.WriteRawValue("fail"); + } + + [Test] + public void Example() + { + Product p = new Product(); + p.ExpiryDate = DateTime.Parse("2009-04-05T14:45:00Z"); + p.Name = "Carlos' Spicy Wieners"; + p.Price = 9.95m; + p.Sizes = new[] { "Small", "Medium", "Large" }; + + MemoryStream ms = new MemoryStream(); + JsonSerializer serializer = new JsonSerializer(); + + // serialize product to BSON + BsonWriter writer = new BsonWriter(ms); + serializer.Serialize(writer, p); + + Console.WriteLine(BitConverter.ToString(ms.ToArray())); + // 7C-00-00-00-02-4E-61-6D-65-00-16-00-00-00-43-61-72-6C- + // 6F-73-27-20-53-70-69-63-79-20-57-69-65-6E-65-72-73-00- + // 09-45-78-70-69-72-79-44-61-74-65-00-E0-51-BD-76-20-01- + // 00-00-01-50-72-69-63-65-00-66-66-66-66-66-E6-23-40-04- + // 53-69-7A-65-73-00-2D-00-00-00-02-30-00-06-00-00-00-53- + // 6D-61-6C-6C-00-02-31-00-07-00-00-00-4D-65-64-69-75-6D- + // 00-02-32-00-06-00-00-00-4C-61-72-67-65-00-00-00 + + + ms.Seek(0, SeekOrigin.Begin); + + // deserialize product from BSON + BsonReader reader = new BsonReader(ms); + Product deserializedProduct = serializer.Deserialize(reader); + + Console.WriteLine(deserializedProduct.Name); + // Carlos' Spicy Wieners + + + Assert.AreEqual("Carlos' Spicy Wieners", deserializedProduct.Name); + Assert.AreEqual(9.95m, deserializedProduct.Price); + Assert.AreEqual(3, deserializedProduct.Sizes.Length); + } + + [Test] + public void WriteOid() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + byte[] oid = new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + + writer.WriteStartObject(); + writer.WritePropertyName("_oid"); + writer.WriteObjectId(oid); + writer.WriteEndObject(); + + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + Assert.AreEqual("17-00-00-00-07-5F-6F-69-64-00-01-02-03-04-05-06-07-08-09-0A-0B-0C-00", bson); + + ms.Seek(0, SeekOrigin.Begin); + BsonReader reader = new BsonReader(ms); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Bytes, reader.TokenType); + Assert.AreEqual(oid, reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + } + + [Test] + public void WriteOidPlusContent() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartObject(); + writer.WritePropertyName("_id"); + writer.WriteObjectId(MiscellaneousUtils.HexToBytes("4ABBED9D1D8B0F0218000001")); + writer.WritePropertyName("test"); + writer.WriteValue("1234£56"); + writer.WriteEndObject(); + + byte[] expected = MiscellaneousUtils.HexToBytes("29000000075F6964004ABBED9D1D8B0F02180000010274657374000900000031323334C2A335360000"); + + Assert.AreEqual(expected, ms.ToArray()); + } + + [Test] + public void WriteRegexPlusContent() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartObject(); + writer.WritePropertyName("regex"); + writer.WriteRegex("abc", "i"); + writer.WritePropertyName("test"); + writer.WriteRegex(string.Empty, null); + writer.WriteEndObject(); + + byte[] expected = MiscellaneousUtils.HexToBytes("1A-00-00-00-0B-72-65-67-65-78-00-61-62-63-00-69-00-0B-74-65-73-74-00-00-00-00"); + + Assert.AreEqual(expected, ms.ToArray()); + } + + [Test] + public void SerializeEmptyAndNullStrings() + { + Product p = new Product(); + p.ExpiryDate = DateTime.Parse("2009-04-05T14:45:00Z"); + p.Name = null; + p.Price = 9.95m; + p.Sizes = new[] { "Small", "", null }; + + MemoryStream ms = new MemoryStream(); + JsonSerializer serializer = new JsonSerializer(); + + BsonWriter writer = new BsonWriter(ms); + serializer.Serialize(writer, p); + + ms.Seek(0, SeekOrigin.Begin); + + BsonReader reader = new BsonReader(ms); + Product deserializedProduct = serializer.Deserialize(reader); + + Console.WriteLine(deserializedProduct.Name); + + Assert.AreEqual(null, deserializedProduct.Name); + Assert.AreEqual(9.95m, deserializedProduct.Price); + Assert.AreEqual(3, deserializedProduct.Sizes.Length); + Assert.AreEqual("Small", deserializedProduct.Sizes[0]); + Assert.AreEqual("", deserializedProduct.Sizes[1]); + Assert.AreEqual(null, deserializedProduct.Sizes[2]); + } + + [Test] + public void WriteReadEmptyAndNullStrings() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + + writer.WriteStartArray(); + writer.WriteValue("Content!"); + writer.WriteValue(""); + writer.WriteValue((string)null); + writer.WriteEndArray(); + + ms.Seek(0, SeekOrigin.Begin); + + BsonReader reader = new BsonReader(ms); + reader.ReadRootValueAsArray = true; + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("Content!", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Null, reader.TokenType); + Assert.AreEqual(null, reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void WriteDateTimes() + { + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + writer.DateTimeKindHandling = DateTimeKind.Unspecified; + + writer.WriteStartArray(); + writer.WriteValue(new DateTime(2000, 10, 12, 20, 55, 0, DateTimeKind.Utc)); + writer.WriteValue(new DateTime(2000, 10, 12, 20, 55, 0, DateTimeKind.Local)); + writer.WriteValue(new DateTime(2000, 10, 12, 20, 55, 0, DateTimeKind.Unspecified)); + writer.WriteEndArray(); + + ms.Seek(0, SeekOrigin.Begin); + + BsonReader reader = new BsonReader(ms); + reader.ReadRootValueAsArray = true; + reader.DateTimeKindHandling = DateTimeKind.Utc; + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Date, reader.TokenType); + Assert.AreEqual(new DateTime(2000, 10, 12, 20, 55, 0, DateTimeKind.Utc), reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Date, reader.TokenType); + Assert.AreEqual(new DateTime(2000, 10, 12, 20, 55, 0, DateTimeKind.Utc), reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Date, reader.TokenType); + Assert.AreEqual(new DateTime(2000, 10, 12, 20, 55, 0, DateTimeKind.Utc), reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/BinaryConverterTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/BinaryConverterTests.cs new file mode 100644 index 0000000..0643c18 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/BinaryConverterTests.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +#if !SILVERLIGHT && !PocketPC && !NET20 +using System.Data.Linq; +#endif +#if !SILVERLIGHT +using System.Data.SqlTypes; +#endif +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class BinaryConverterTests : TestFixtureBase + { + private static readonly byte[] TestData = Encoding.UTF8.GetBytes("This is some test data!!!"); + + public class ByteArrayClass + { + public byte[] ByteArray { get; set; } + public byte[] NullByteArray { get; set; } + } + +#if !SILVERLIGHT && !PocketPC && !NET20 + [Test] + public void DeserializeBinaryClass() + { + string json = @"{ + ""Binary"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullBinary"": null +}"; + + BinaryClass binaryClass = JsonConvert.DeserializeObject(json, new BinaryConverter()); + + Assert.AreEqual(new Binary(TestData), binaryClass.Binary); + Assert.AreEqual(null, binaryClass.NullBinary); + } + + public class BinaryClass + { + public Binary Binary { get; set; } + public Binary NullBinary { get; set; } + } + + [Test] + public void SerializeBinaryClass() + { + BinaryClass binaryClass = new BinaryClass(); + binaryClass.Binary = new Binary(TestData); + binaryClass.NullBinary = null; + + string json = JsonConvert.SerializeObject(binaryClass, Formatting.Indented, new BinaryConverter()); + + Assert.AreEqual(@"{ + ""Binary"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullBinary"": null +}", json); + } +#endif + + [Test] + public void SerializeByteArrayClass() + { + ByteArrayClass byteArrayClass = new ByteArrayClass(); + byteArrayClass.ByteArray = TestData; + byteArrayClass.NullByteArray = null; + + string json = JsonConvert.SerializeObject(byteArrayClass, Formatting.Indented, new BinaryConverter()); + + Assert.AreEqual(@"{ + ""ByteArray"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullByteArray"": null +}", json); + } + +#if !SILVERLIGHT + public class SqlBinaryClass + { + public SqlBinary SqlBinary { get; set; } + public SqlBinary? NullableSqlBinary1 { get; set; } + public SqlBinary? NullableSqlBinary2 { get; set; } + } + + [Test] + public void SerializeSqlBinaryClass() + { + SqlBinaryClass sqlBinaryClass = new SqlBinaryClass(); + sqlBinaryClass.SqlBinary = new SqlBinary(TestData); + sqlBinaryClass.NullableSqlBinary1 = new SqlBinary(TestData); + sqlBinaryClass.NullableSqlBinary2 = null; + + string json = JsonConvert.SerializeObject(sqlBinaryClass, Formatting.Indented, new BinaryConverter()); + + Assert.AreEqual(@"{ + ""SqlBinary"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullableSqlBinary1"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullableSqlBinary2"": null +}", json); + } + + [Test] + public void DeserializeSqlBinaryClass() + { + string json = @"{ + ""SqlBinary"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullableSqlBinary1"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullableSqlBinary2"": null +}"; + + SqlBinaryClass sqlBinaryClass = JsonConvert.DeserializeObject(json, new BinaryConverter()); + + Assert.AreEqual(new SqlBinary(TestData), sqlBinaryClass.SqlBinary); + Assert.AreEqual(new SqlBinary(TestData), sqlBinaryClass.NullableSqlBinary1); + Assert.AreEqual(null, sqlBinaryClass.NullableSqlBinary2); + } +#endif + + [Test] + public void DeserializeByteArrayClass() + { + string json = @"{ + ""ByteArray"": ""VGhpcyBpcyBzb21lIHRlc3QgZGF0YSEhIQ=="", + ""NullByteArray"": null +}"; + + ByteArrayClass byteArrayClass = JsonConvert.DeserializeObject(json, new BinaryConverter()); + + Assert.AreEqual(TestData, byteArrayClass.ByteArray); + Assert.AreEqual(null, byteArrayClass.NullByteArray); + } + + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/CustomCreationConverterTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/CustomCreationConverterTests.cs new file mode 100644 index 0000000..d66ddf3 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/CustomCreationConverterTests.cs @@ -0,0 +1,230 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class CustomCreationConverterTests : TestFixtureBase + { + public interface IPerson + { + string FirstName { get; set; } + string LastName { get; set; } + DateTime BirthDate { get; set; } + } + + public class Employee : IPerson + { + public string FirstName { get; set; } + public string LastName { get; set; } + public DateTime BirthDate { get; set; } + + public string Department { get; set; } + public string JobTitle { get; set; } + } + + public class PersonConverter : CustomCreationConverter + { + public override IPerson Create(Type objectType) + { + return new Employee(); + } + } + + public void DeserializeObject() + { + string json = JsonConvert.SerializeObject(new List + { + new Employee + { + BirthDate = new DateTime(1977, 12, 30, 1, 1, 1, DateTimeKind.Utc), + FirstName = "Maurice", + LastName = "Moss", + Department = "IT", + JobTitle = "Support" + }, + new Employee + { + BirthDate = new DateTime(1978, 3, 15, 1, 1, 1, DateTimeKind.Utc), + FirstName = "Jen", + LastName = "Barber", + Department = "IT", + JobTitle = "Manager" + } + }, Formatting.Indented); + + //[ + // { + // "FirstName": "Maurice", + // "LastName": "Moss", + // "BirthDate": "\/Date(252291661000)\/", + // "Department": "IT", + // "JobTitle": "Support" + // }, + // { + // "FirstName": "Jen", + // "LastName": "Barber", + // "BirthDate": "\/Date(258771661000)\/", + // "Department": "IT", + // "JobTitle": "Manager" + // } + //] + + List people = JsonConvert.DeserializeObject>(json, new PersonConverter()); + + IPerson person = people[0]; + + Console.WriteLine(person.GetType()); + // Newtonsoft.Json.Tests.Employee + + Console.WriteLine(person.FirstName); + // Maurice + + Employee employee = (Employee)person; + + Console.WriteLine(employee.JobTitle); + // Support + } + + public class MyClass + { + public string Value { get; set; } + + [JsonConverter(typeof(MyThingConverter))] + public IThing Thing { get; set; } + } + + public interface IThing + { + int Number { get; } + } + + public class MyThing : IThing + { + public int Number { get; set; } + } + + public class MyThingConverter : CustomCreationConverter + { + public override IThing Create(Type objectType) + { + return new MyThing(); + } + } + + [Test] + public void AssertDoesDeserialize() + { + const string json = @"{ +""Value"": ""A value"", +""Thing"": { +""Number"": 123 +} +} +"; + MyClass myClass = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(myClass); + Assert.AreEqual("A value", myClass.Value); + Assert.IsNotNull(myClass.Thing); + Assert.AreEqual(123, myClass.Thing.Number); + } + + [Test] + public void AssertShouldSerializeTest() + { + MyClass myClass = new MyClass + { + Value = "Foo", + Thing = new MyThing { Number = 456, } + }; + string json = JsonConvert.SerializeObject(myClass); // <-- Exception here + + const string expected = @"{""Value"":""Foo"",""Thing"":{""Number"":456}}"; + Assert.AreEqual(expected, json); + } + + internal interface IRange + { + T First { get; } + T Last { get; } + } + + internal class Range : IRange + { + public T First { get; set; } + public T Last { get; set; } + } + + internal class NullInterfaceTestClass + { + public virtual Guid Id { get; set; } + public virtual int? Year { get; set; } + public virtual string Company { get; set; } + public virtual IRange DecimalRange { get; set; } + public virtual IRange IntRange { get; set; } + public virtual IRange NullDecimalRange { get; set; } + } + + internal class DecimalRangeConverter : CustomCreationConverter> + { + public override IRange Create(Type objectType) + { + return new Range(); + } + } + + internal class IntRangeConverter : CustomCreationConverter> + { + public override IRange Create(Type objectType) + { + return new Range(); + } + } + + [Test] + public void DeserializeAndConvertNullValue() + { + NullInterfaceTestClass initial = new NullInterfaceTestClass + { + Company = "Company!", + DecimalRange = new Range { First = 0, Last = 1 }, + Id = new Guid(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), + IntRange = new Range { First = int.MinValue, Last = int.MaxValue }, + Year = 2010, + NullDecimalRange = null + }; + + string json = JsonConvert.SerializeObject(initial, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Id"": ""00000001-0002-0003-0405-060708090a0b"", + ""Year"": 2010, + ""Company"": ""Company!"", + ""DecimalRange"": { + ""First"": 0.0, + ""Last"": 1.0 + }, + ""IntRange"": { + ""First"": -2147483648, + ""Last"": 2147483647 + }, + ""NullDecimalRange"": null +}", json); + + NullInterfaceTestClass deserialized = JsonConvert.DeserializeObject( + json, new IntRangeConverter(), new DecimalRangeConverter()); + + Assert.AreEqual("Company!", deserialized.Company); + Assert.AreEqual(new Guid(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), deserialized.Id); + Assert.AreEqual(0, deserialized.DecimalRange.First); + Assert.AreEqual(1, deserialized.DecimalRange.Last); + Assert.AreEqual(int.MinValue, deserialized.IntRange.First); + Assert.AreEqual(int.MaxValue, deserialized.IntRange.Last); + Assert.AreEqual(null, deserialized.NullDecimalRange); + Assert.AreEqual(2010, deserialized.Year); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs new file mode 100644 index 0000000..eedfed4 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/DataSetConverterTests.cs @@ -0,0 +1,335 @@ +#if !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; +using System.Data; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class DataSetConverterTests : TestFixtureBase + { + [Test] + public void SerializeAndDeserialize() + { + DataSet dataSet = new DataSet("dataSet"); + dataSet.Namespace = "NetFrameWork"; + DataTable table = new DataTable(); + DataColumn idColumn = new DataColumn("id", typeof(int)); + idColumn.AutoIncrement = true; + + DataColumn itemColumn = new DataColumn("item"); + table.Columns.Add(idColumn); + table.Columns.Add(itemColumn); + dataSet.Tables.Add(table); + + for (int i = 0; i < 2; i++) + { + DataRow newRow = table.NewRow(); + newRow["item"] = "item " + i; + table.Rows.Add(newRow); + } + + dataSet.AcceptChanges(); + + string json = JsonConvert.SerializeObject(dataSet, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Table1"": [ + { + ""id"": 0, + ""item"": ""item 0"" + }, + { + ""id"": 1, + ""item"": ""item 1"" + } + ] +}", json); + + DataSet deserializedDataSet = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(deserializedDataSet); + + Assert.AreEqual(1, deserializedDataSet.Tables.Count); + + DataTable dt = deserializedDataSet.Tables[0]; + + Assert.AreEqual("Table1", dt.TableName); + Assert.AreEqual(2, dt.Columns.Count); + Assert.AreEqual("id", dt.Columns[0].ColumnName); + Assert.AreEqual(typeof(long), dt.Columns[0].DataType); + Assert.AreEqual("item", dt.Columns[1].ColumnName); + Assert.AreEqual(typeof(string), dt.Columns[1].DataType); + + Assert.AreEqual(2, dt.Rows.Count); + } + + [Test] + public void SerializeMultiTableDataSet() + { + DataSet ds = new DataSet(); + ds.Tables.Add(CreateDataTable("FirstTable", 2)); + ds.Tables.Add(CreateDataTable("SecondTable", 1)); + + string json = JsonConvert.SerializeObject(ds, Formatting.Indented, new IsoDateTimeConverter()); + // { + // "FirstTable": [ + // { + // "StringCol": "Item Name", + // "Int32Col": 1, + // "BooleanCol": true, + // "TimeSpanCol": "10.22:10:15.1000000", + // "DateTimeCol": "2000-12-29T00:00:00Z", + // "DecimalCol": 64.0021 + // }, + // { + // "StringCol": "Item Name", + // "Int32Col": 2, + // "BooleanCol": true, + // "TimeSpanCol": "10.22:10:15.1000000", + // "DateTimeCol": "2000-12-29T00:00:00Z", + // "DecimalCol": 64.0021 + // } + // ], + // "SecondTable": [ + // { + // "StringCol": "Item Name", + // "Int32Col": 1, + // "BooleanCol": true, + // "TimeSpanCol": "10.22:10:15.1000000", + // "DateTimeCol": "2000-12-29T00:00:00Z", + // "DecimalCol": 64.0021 + // } + // ] + // } + + DataSet deserializedDs = JsonConvert.DeserializeObject(json, new IsoDateTimeConverter()); + + Assert.AreEqual(@"{ + ""FirstTable"": [ + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 1, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00Z"", + ""DecimalCol"": 64.0021 + }, + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 2, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00Z"", + ""DecimalCol"": 64.0021 + } + ], + ""SecondTable"": [ + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 1, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00Z"", + ""DecimalCol"": 64.0021 + } + ] +}", json); + + Assert.IsNotNull(deserializedDs); + + } + + [Test] + public void DeserializeMultiTableDataSet() + { + string json = @"{ + ""FirstTable"": [ + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 2147483647, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00Z"", + ""DecimalCol"": 64.0021 + } + ], + ""SecondTable"": [ + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 2147483647, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00Z"", + ""DecimalCol"": 64.0021 + } + ] +}"; + + DataSet ds = JsonConvert.DeserializeObject(json, new IsoDateTimeConverter()); + Assert.IsNotNull(ds); + + Assert.AreEqual(2, ds.Tables.Count); + Assert.AreEqual("FirstTable", ds.Tables[0].TableName); + Assert.AreEqual("SecondTable", ds.Tables[1].TableName); + + DataTable dt = ds.Tables[0]; + Assert.AreEqual("StringCol", dt.Columns[0].ColumnName); + Assert.AreEqual(typeof(string), dt.Columns[0].DataType); + Assert.AreEqual("Int32Col", dt.Columns[1].ColumnName); + Assert.AreEqual(typeof(long), dt.Columns[1].DataType); + Assert.AreEqual("BooleanCol", dt.Columns[2].ColumnName); + Assert.AreEqual(typeof(bool), dt.Columns[2].DataType); + Assert.AreEqual("TimeSpanCol", dt.Columns[3].ColumnName); + Assert.AreEqual(typeof(string), dt.Columns[3].DataType); + Assert.AreEqual("DateTimeCol", dt.Columns[4].ColumnName); + Assert.AreEqual(typeof(string), dt.Columns[4].DataType); + Assert.AreEqual("DecimalCol", dt.Columns[5].ColumnName); + Assert.AreEqual(typeof(double), dt.Columns[5].DataType); + + Assert.AreEqual(1, ds.Tables[0].Rows.Count); + Assert.AreEqual(1, ds.Tables[1].Rows.Count); + } + + private DataTable CreateDataTable(string dataTableName, int rows) + { + // create a new DataTable. + DataTable myTable = new DataTable(dataTableName); + + // create DataColumn objects of data types. + DataColumn colString = new DataColumn("StringCol"); + colString.DataType = typeof(string); + myTable.Columns.Add(colString); + + DataColumn colInt32 = new DataColumn("Int32Col"); + colInt32.DataType = typeof(int); + myTable.Columns.Add(colInt32); + + DataColumn colBoolean = new DataColumn("BooleanCol"); + colBoolean.DataType = typeof(bool); + myTable.Columns.Add(colBoolean); + + DataColumn colTimeSpan = new DataColumn("TimeSpanCol"); + colTimeSpan.DataType = typeof(TimeSpan); + myTable.Columns.Add(colTimeSpan); + + DataColumn colDateTime = new DataColumn("DateTimeCol"); + colDateTime.DataType = typeof(DateTime); + colDateTime.DateTimeMode = DataSetDateTime.Utc; + myTable.Columns.Add(colDateTime); + + DataColumn colDecimal = new DataColumn("DecimalCol"); + colDecimal.DataType = typeof(decimal); + myTable.Columns.Add(colDecimal); + + for (int i = 1; i <= rows; i++) + { + DataRow myNewRow = myTable.NewRow(); + + myNewRow["StringCol"] = "Item Name"; + myNewRow["Int32Col"] = i; + myNewRow["BooleanCol"] = true; + myNewRow["TimeSpanCol"] = new TimeSpan(10, 22, 10, 15, 100); + myNewRow["DateTimeCol"] = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc); + myNewRow["DecimalCol"] = 64.0021; + myTable.Rows.Add(myNewRow); + } + + return myTable; + } + + public class DataSetAndTableTestClass + { + public string Before { get; set; } + public DataSet Set { get; set; } + public string Middle { get; set; } + public DataTable Table { get; set; } + public string After { get; set; } + } + + [Test] + public void Blah() + { + DataSet ds = new DataSet(); + ds.Tables.Add(CreateDataTable("FirstTable", 2)); + ds.Tables.Add(CreateDataTable("SecondTable", 1)); + + DataSetAndTableTestClass c = new DataSetAndTableTestClass + { + Before = "Before", + Set = ds, + Middle = "Middle", + Table = CreateDataTable("LoneTable", 2), + After = "After" + }; + + string json = JsonConvert.SerializeObject(c, Formatting.Indented, new IsoDateTimeConverter()); + + Assert.AreEqual(@"{ + ""Before"": ""Before"", + ""Set"": { + ""FirstTable"": [ + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 1, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00Z"", + ""DecimalCol"": 64.0021 + }, + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 2, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00Z"", + ""DecimalCol"": 64.0021 + } + ], + ""SecondTable"": [ + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 1, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00Z"", + ""DecimalCol"": 64.0021 + } + ] + }, + ""Middle"": ""Middle"", + ""Table"": [ + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 1, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00Z"", + ""DecimalCol"": 64.0021 + }, + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 2, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""2000-12-29T00:00:00Z"", + ""DecimalCol"": 64.0021 + } + ], + ""After"": ""After"" +}", json); + + DataSetAndTableTestClass c2 = JsonConvert.DeserializeObject(json, new IsoDateTimeConverter()); + + Assert.AreEqual(c.Before, c2.Before); + Assert.AreEqual(c.Set.Tables.Count, c2.Set.Tables.Count); + Assert.AreEqual(c.Middle, c2.Middle); + Assert.AreEqual(c.Table.Rows.Count, c2.Table.Rows.Count); + Assert.AreEqual(c.After, c2.After); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs new file mode 100644 index 0000000..3b578b6 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/DataTableConverterTests.cs @@ -0,0 +1,141 @@ +#if !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; +using System.Data; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class DataTableConverterTests : TestFixtureBase + { + [Test] + public void Deserialize() + { + string json = @"[ + { + ""id"": 0, + ""item"": ""item 0"" + }, + { + ""id"": 1, + ""item"": ""item 1"" + } +]"; + + DataTable deserializedDataTable = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(deserializedDataTable); + + Assert.AreEqual(string.Empty, deserializedDataTable.TableName); + Assert.AreEqual(2, deserializedDataTable.Columns.Count); + Assert.AreEqual("id", deserializedDataTable.Columns[0].ColumnName); + Assert.AreEqual(typeof(long), deserializedDataTable.Columns[0].DataType); + Assert.AreEqual("item", deserializedDataTable.Columns[1].ColumnName); + Assert.AreEqual(typeof(string), deserializedDataTable.Columns[1].DataType); + + Assert.AreEqual(2, deserializedDataTable.Rows.Count); + + DataRow dr1 = deserializedDataTable.Rows[0]; + Assert.AreEqual(0, dr1["id"]); + Assert.AreEqual("item 0", dr1["item"]); + + DataRow dr2 = deserializedDataTable.Rows[1]; + Assert.AreEqual(1, dr2["id"]); + Assert.AreEqual("item 1", dr2["item"]); + } + + [Test] + public void Serialize() + { + // create a new DataTable. + DataTable myTable = new DataTable("blah"); + + // create DataColumn objects of data types. + DataColumn colString = new DataColumn("StringCol"); + colString.DataType = typeof(string); + myTable.Columns.Add(colString); + + DataColumn colInt32 = new DataColumn("Int32Col"); + colInt32.DataType = typeof(int); + myTable.Columns.Add(colInt32); + + DataColumn colBoolean = new DataColumn("BooleanCol"); + colBoolean.DataType = typeof(bool); + myTable.Columns.Add(colBoolean); + + DataColumn colTimeSpan = new DataColumn("TimeSpanCol"); + colTimeSpan.DataType = typeof(TimeSpan); + myTable.Columns.Add(colTimeSpan); + + DataColumn colDateTime = new DataColumn("DateTimeCol"); + colDateTime.DataType = typeof(DateTime); + colDateTime.DateTimeMode = DataSetDateTime.Utc; + myTable.Columns.Add(colDateTime); + + DataColumn colDecimal = new DataColumn("DecimalCol"); + colDecimal.DataType = typeof(decimal); + myTable.Columns.Add(colDecimal); + + // populate one row with values. + DataRow myNewRow = myTable.NewRow(); + + myNewRow["StringCol"] = "Item Name"; + myNewRow["Int32Col"] = 2147483647; + myNewRow["BooleanCol"] = true; + myNewRow["TimeSpanCol"] = new TimeSpan(10, 22, 10, 15, 100); + myNewRow["DateTimeCol"] = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc); + myNewRow["DecimalCol"] = 64.0021; + myTable.Rows.Add(myNewRow); + + string json = JsonConvert.SerializeObject(myTable, Formatting.Indented); + Assert.AreEqual(@"[ + { + ""StringCol"": ""Item Name"", + ""Int32Col"": 2147483647, + ""BooleanCol"": true, + ""TimeSpanCol"": ""10.22:10:15.1000000"", + ""DateTimeCol"": ""\/Date(978048000000)\/"", + ""DecimalCol"": 64.0021 + } +]", json); + } + + public class TestDataTableConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + DataTable d = (DataTable) value; + writer.WriteValue(d.TableName); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + //reader.Read(); + DataTable d = new DataTable((string)reader.Value); + + return d; + } + + public override bool CanConvert(Type objectType) + { + return (objectType == typeof (DataTable)); + } + } + + [Test] + public void PassedInJsonConverterOverridesInternalConverter() + { + DataTable t1 = new DataTable("Custom"); + + string json = JsonConvert.SerializeObject(t1, Formatting.Indented, new TestDataTableConverter()); + Assert.AreEqual(@"""Custom""", json); + + DataTable t2 = JsonConvert.DeserializeObject(json, new TestDataTableConverter()); + Assert.AreEqual(t1.TableName, t2.TableName); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/ExpandoObjectConverterTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/ExpandoObjectConverterTests.cs new file mode 100644 index 0000000..bcb82a2 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/ExpandoObjectConverterTests.cs @@ -0,0 +1,158 @@ +#if !(NET35 || NET20 || WINDOWS_PHONE) + +using System; +using System.Collections.Generic; +#if !SILVERLIGHT && !PocketPC && !NET20 +using System.Data.Linq; +#endif +#if !SILVERLIGHT +using System.Data.SqlTypes; +#endif +using System.Dynamic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class ExpandoObjectConverterTests : TestFixtureBase + { + public class ExpandoContainer + { + public string Before { get; set; } + public ExpandoObject Expando { get; set; } + public string After { get; set; } + } + + [Test] + public void SerializeExpandoObject() + { + ExpandoContainer d = new ExpandoContainer + { + Before = "Before!", + Expando = new ExpandoObject(), + After = "After!" + }; + + dynamic o = d.Expando; + + o.String = "String!"; + o.Integer = 234; + o.Float = 1.23d; + o.List = new List {"First", "Second", "Third"}; + o.Object = new Dictionary + { + {"First", 1} + }; + + string json = JsonConvert.SerializeObject(d, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Before"": ""Before!"", + ""Expando"": { + ""String"": ""String!"", + ""Integer"": 234, + ""Float"": 1.23, + ""List"": [ + ""First"", + ""Second"", + ""Third"" + ], + ""Object"": { + ""First"": 1 + } + }, + ""After"": ""After!"" +}", json); + } + + [Test] + public void SerializeNullExpandoObject() + { + ExpandoContainer d = new ExpandoContainer(); + + string json = JsonConvert.SerializeObject(d, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Before"": null, + ""Expando"": null, + ""After"": null +}", json); + } + + [Test] + public void DeserializeExpandoObject() + { + string json = @"{ + ""Before"": ""Before!"", + ""Expando"": { + ""String"": ""String!"", + ""Integer"": 234, + ""Float"": 1.23, + ""List"": [ + ""First"", + ""Second"", + ""Third"" + ], + ""Object"": { + ""First"": 1 + } + }, + ""After"": ""After!"" +}"; + + ExpandoContainer o = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(o.Before, "Before!"); + Assert.AreEqual(o.After, "After!"); + Assert.IsNotNull(o.Expando); + + dynamic d = o.Expando; + Assert.IsInstanceOfType(typeof(ExpandoObject), d); + + Assert.AreEqual("String!", d.String); + Assert.IsInstanceOfType(typeof(string), d.String); + + Assert.AreEqual(234, d.Integer); + Assert.IsInstanceOfType(typeof(long), d.Integer); + + Assert.AreEqual(1.23, d.Float); + Assert.IsInstanceOfType(typeof(double), d.Float); + + Assert.IsNotNull(d.List); + Assert.AreEqual(3, d.List.Count); + Assert.IsInstanceOfType(typeof(List), d.List); + + Assert.AreEqual("First", d.List[0]); + Assert.IsInstanceOfType(typeof(string), d.List[0]); + + Assert.AreEqual("Second", d.List[1]); + Assert.AreEqual("Third", d.List[2]); + + Assert.IsNotNull(d.Object); + Assert.IsInstanceOfType(typeof(ExpandoObject), d.Object); + + Assert.AreEqual(1, d.Object.First); + Assert.IsInstanceOfType(typeof(long), d.Object.First); + } + + [Test] + public void DeserializeNullExpandoObject() + { + string json = @"{ + ""Before"": null, + ""Expando"": null, + ""After"": null +}"; + + ExpandoContainer c = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(null, c.Expando); + } + + } +} + +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/IsoDateTimeConverterTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/IsoDateTimeConverterTests.cs new file mode 100644 index 0000000..98e0e04 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/IsoDateTimeConverterTests.cs @@ -0,0 +1,312 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Utilities; +using System.Globalization; +using System.Xml; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class IsoDateTimeConverterTests : TestFixtureBase + { + [Test] + public void PropertiesShouldBeSet() + { + IsoDateTimeConverter converter = new IsoDateTimeConverter(); + Assert.AreEqual(CultureInfo.CurrentCulture, converter.Culture); + Assert.AreEqual(string.Empty, converter.DateTimeFormat); + Assert.AreEqual(DateTimeStyles.RoundtripKind, converter.DateTimeStyles); + + converter = new IsoDateTimeConverter() + { + DateTimeFormat = "F", + Culture = CultureInfo.InvariantCulture, + DateTimeStyles = DateTimeStyles.None + }; + + Assert.AreEqual(CultureInfo.InvariantCulture, converter.Culture); + Assert.AreEqual("F", converter.DateTimeFormat); + Assert.AreEqual(DateTimeStyles.None, converter.DateTimeStyles); + } + + [Test] + public void SerializeDateTime() + { + IsoDateTimeConverter converter = new IsoDateTimeConverter(); + + DateTime d = new DateTime(2000, 12, 15, 22, 11, 3, 55, DateTimeKind.Utc); + string result; + + result = JsonConvert.SerializeObject(d, converter); + Assert.AreEqual(@"""2000-12-15T22:11:03.055Z""", result); + + Assert.AreEqual(d, JsonConvert.DeserializeObject(result, converter)); + + d = new DateTime(2000, 12, 15, 22, 11, 3, 55, DateTimeKind.Local); + result = JsonConvert.SerializeObject(d, converter); + Assert.AreEqual(@"""2000-12-15T22:11:03.055" + d.GetLocalOffset() + @"""", result); + } + + [Test] + public void SerializeFormattedDateTimeInvariantCulture() + { + IsoDateTimeConverter converter = new IsoDateTimeConverter() { DateTimeFormat = "F", Culture = CultureInfo.InvariantCulture }; + + DateTime d = new DateTime(2000, 12, 15, 22, 11, 3, 0, DateTimeKind.Utc); + string result; + + result = JsonConvert.SerializeObject(d, converter); + Assert.AreEqual(@"""Friday, 15 December 2000 22:11:03""", result); + + Assert.AreEqual(d, JsonConvert.DeserializeObject(result, converter)); + + d = new DateTime(2000, 12, 15, 22, 11, 3, 0, DateTimeKind.Local); + result = JsonConvert.SerializeObject(d, converter); + Assert.AreEqual(@"""Friday, 15 December 2000 22:11:03""", result); + } + + [Test] + public void SerializeCustomFormattedDateTime() + { + IsoDateTimeConverter converter = new IsoDateTimeConverter + { + DateTimeFormat = "dd/MM/yyyy", + Culture = CultureInfo.InvariantCulture + }; + + string json = @"""09/12/2006"""; + + DateTime d = JsonConvert.DeserializeObject(json, converter); + + Assert.AreEqual(9, d.Day); + Assert.AreEqual(12, d.Month); + Assert.AreEqual(2006, d.Year); + } + +#if !SILVERLIGHT + [Test] + public void SerializeFormattedDateTimeNewZealandCulture() + { + IsoDateTimeConverter converter = new IsoDateTimeConverter() { DateTimeFormat = "F", Culture = CultureInfo.GetCultureInfo("en-NZ") }; + + DateTime d = new DateTime(2000, 12, 15, 22, 11, 3, 0, DateTimeKind.Utc); + string result; + + result = JsonConvert.SerializeObject(d, converter); + Assert.AreEqual(@"""Friday, 15 December 2000 10:11:03 p.m.""", result); + + Assert.AreEqual(d, JsonConvert.DeserializeObject(result, converter)); + + d = new DateTime(2000, 12, 15, 22, 11, 3, 0, DateTimeKind.Local); + result = JsonConvert.SerializeObject(d, converter); + Assert.AreEqual(@"""Friday, 15 December 2000 10:11:03 p.m.""", result); + } + + [Test] + public void SerializeDateTimeCulture() + { + IsoDateTimeConverter converter = new IsoDateTimeConverter() { Culture = CultureInfo.GetCultureInfo("en-NZ") }; + + string json = @"""09/12/2006"""; + + DateTime d = JsonConvert.DeserializeObject(json, converter); + + Assert.AreEqual(9, d.Day); + Assert.AreEqual(12, d.Month); + Assert.AreEqual(2006, d.Year); + } +#endif + +#if !PocketPC && !NET20 + [Test] + public void SerializeDateTimeOffset() + { + IsoDateTimeConverter converter = new IsoDateTimeConverter(); + + DateTimeOffset d = new DateTimeOffset(2000, 12, 15, 22, 11, 3, 55, TimeSpan.Zero); + string result; + + result = JsonConvert.SerializeObject(d, converter); + Assert.AreEqual(@"""2000-12-15T22:11:03.055+00:00""", result); + + Assert.AreEqual(d, JsonConvert.DeserializeObject(result, converter)); + } + + [Test] + public void SerializeUTC() + { + DateTimeTestClass c = new DateTimeTestClass(); + c.DateTimeField = new DateTime(2008, 12, 12, 12, 12, 12, 0, DateTimeKind.Utc).ToLocalTime(); + c.DateTimeOffsetField = new DateTime(2008, 12, 12, 12, 12, 12, 0, DateTimeKind.Utc).ToLocalTime(); + c.PreField = "Pre"; + c.PostField = "Post"; + string json = JsonConvert.SerializeObject(c, new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal }); + Assert.AreEqual(@"{""PreField"":""Pre"",""DateTimeField"":""2008-12-12T12:12:12Z"",""DateTimeOffsetField"":""2008-12-12T12:12:12+00:00"",""PostField"":""Post""}", json); + + //test the other edge case too + c.DateTimeField = new DateTime(2008, 1, 1, 1, 1, 1, 0, DateTimeKind.Utc).ToLocalTime(); + c.DateTimeOffsetField = new DateTime(2008, 1, 1, 1, 1, 1, 0, DateTimeKind.Utc).ToLocalTime(); + c.PreField = "Pre"; + c.PostField = "Post"; + json = JsonConvert.SerializeObject(c, new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal }); + Assert.AreEqual(@"{""PreField"":""Pre"",""DateTimeField"":""2008-01-01T01:01:01Z"",""DateTimeOffsetField"":""2008-01-01T01:01:01+00:00"",""PostField"":""Post""}", json); + } + + [Test] + public void NullableSerializeUTC() + { + NullableDateTimeTestClass c = new NullableDateTimeTestClass(); + c.DateTimeField = new DateTime(2008, 12, 12, 12, 12, 12, 0, DateTimeKind.Utc).ToLocalTime(); + c.DateTimeOffsetField = new DateTime(2008, 12, 12, 12, 12, 12, 0, DateTimeKind.Utc).ToLocalTime(); + c.PreField = "Pre"; + c.PostField = "Post"; + string json = JsonConvert.SerializeObject(c, new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal }); + Assert.AreEqual(@"{""PreField"":""Pre"",""DateTimeField"":""2008-12-12T12:12:12Z"",""DateTimeOffsetField"":""2008-12-12T12:12:12+00:00"",""PostField"":""Post""}", json); + + //test the other edge case too + c.DateTimeField = null; + c.DateTimeOffsetField = null; + c.PreField = "Pre"; + c.PostField = "Post"; + json = JsonConvert.SerializeObject(c, new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal }); + Assert.AreEqual(@"{""PreField"":""Pre"",""DateTimeField"":null,""DateTimeOffsetField"":null,""PostField"":""Post""}", json); + } + + [Test] + public void DeserializeUTC() + { + DateTimeTestClass c = + JsonConvert.DeserializeObject(@"{""PreField"":""Pre"",""DateTimeField"":""2008-12-12T12:12:12Z"",""DateTimeOffsetField"":""2008-12-12T12:12:12Z"",""PostField"":""Post""}", new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal }); + + Assert.AreEqual(new DateTime(2008, 12, 12, 12, 12, 12, 0, DateTimeKind.Utc).ToLocalTime(), c.DateTimeField); + Assert.AreEqual(new DateTimeOffset(2008, 12, 12, 12, 12, 12, 0, TimeSpan.Zero), c.DateTimeOffsetField); + Assert.AreEqual("Pre", c.PreField); + Assert.AreEqual("Post", c.PostField); + + DateTimeTestClass c2 = + JsonConvert.DeserializeObject(@"{""PreField"":""Pre"",""DateTimeField"":""2008-01-01T01:01:01Z"",""DateTimeOffsetField"":""2008-01-01T01:01:01Z"",""PostField"":""Post""}", new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal }); + + Assert.AreEqual(new DateTime(2008, 1, 1, 1, 1, 1, 0, DateTimeKind.Utc).ToLocalTime(), c2.DateTimeField); + Assert.AreEqual(new DateTimeOffset(2008, 1, 1, 1, 1, 1, 0, TimeSpan.Zero), c2.DateTimeOffsetField); + Assert.AreEqual("Pre", c2.PreField); + Assert.AreEqual("Post", c2.PostField); + } + + [Test] + public void NullableDeserializeUTC() + { + NullableDateTimeTestClass c = + JsonConvert.DeserializeObject(@"{""PreField"":""Pre"",""DateTimeField"":""2008-12-12T12:12:12Z"",""DateTimeOffsetField"":""2008-12-12T12:12:12Z"",""PostField"":""Post""}", new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal }); + + Assert.AreEqual(new DateTime(2008, 12, 12, 12, 12, 12, 0, DateTimeKind.Utc).ToLocalTime(), c.DateTimeField); + Assert.AreEqual(new DateTimeOffset(2008, 12, 12, 12, 12, 12, 0, TimeSpan.Zero), c.DateTimeOffsetField); + Assert.AreEqual("Pre", c.PreField); + Assert.AreEqual("Post", c.PostField); + + NullableDateTimeTestClass c2 = + JsonConvert.DeserializeObject(@"{""PreField"":""Pre"",""DateTimeField"":null,""DateTimeOffsetField"":null,""PostField"":""Post""}", new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal }); + + Assert.AreEqual(null, c2.DateTimeField); + Assert.AreEqual(null, c2.DateTimeOffsetField); + Assert.AreEqual("Pre", c2.PreField); + Assert.AreEqual("Post", c2.PostField); + } + + [Test] + public void NullableDeserializeEmptyString() + { + string json = @"{""DateTimeField"":""""}"; + + NullableDateTimeTestClass c = JsonConvert.DeserializeObject(json, + new JsonSerializerSettings { Converters = new [] {new IsoDateTimeConverter()}}); + Assert.AreEqual(null, c.DateTimeField); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Cannot convert null value to System.DateTime.")] + public void DeserializeNullToNonNullable() + { + DateTimeTestClass c2 = + JsonConvert.DeserializeObject(@"{""PreField"":""Pre"",""DateTimeField"":null,""DateTimeOffsetField"":null,""PostField"":""Post""}", new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal }); + } + + [Test] + public void SerializeShouldChangeNonUTCDates() + { + DateTime localDateTime = new DateTime(2008, 1, 1, 1, 1, 1, 0, DateTimeKind.Local); + + DateTimeTestClass c = new DateTimeTestClass(); + c.DateTimeField = localDateTime; + c.PreField = "Pre"; + c.PostField = "Post"; + string json = JsonConvert.SerializeObject(c, new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal }); //note that this fails without the Utc converter... + c.DateTimeField = new DateTime(2008, 1, 1, 1, 1, 1, 0, DateTimeKind.Utc); + string json2 = JsonConvert.SerializeObject(c, new IsoDateTimeConverter() { DateTimeStyles = DateTimeStyles.AssumeUniversal }); + + TimeSpan offset; + +#if SILVERLIGHT + offset = TimeZoneInfo.Local.GetUtcOffset(localDateTime); +#else + offset = TimeZone.CurrentTimeZone.GetUtcOffset(localDateTime); +#endif + + // if the current timezone is utc then local already equals utc + if (offset == TimeSpan.Zero) + Assert.AreEqual(json, json2); + else + Assert.AreNotEqual(json, json2); + } +#endif + + [Test] + public void BlogCodeSample() + { + Person p = new Person + { + Name = "Keith", + BirthDate = new DateTime(1980, 3, 8), + LastModified = new DateTime(2009, 4, 12, 20, 44, 55), + }; + + string jsonText = JsonConvert.SerializeObject(p, new IsoDateTimeConverter()); + // { + // "Name": "Keith", + // "BirthDate": "1980-03-08T00:00:00", + // "LastModified": "2009-04-12T20:44:55" + // } + + Console.WriteLine(jsonText); + + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/JavaScriptDateTimeConverterTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/JavaScriptDateTimeConverterTests.cs new file mode 100644 index 0000000..15094fb --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/JavaScriptDateTimeConverterTests.cs @@ -0,0 +1,126 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; +using Newtonsoft.Json.Converters; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class JavaScriptDateTimeConverterTests : TestFixtureBase + { + [Test] + public void SerializeDateTime() + { + JavaScriptDateTimeConverter converter = new JavaScriptDateTimeConverter(); + + DateTime d = new DateTime(2000, 12, 15, 22, 11, 3, 55, DateTimeKind.Utc); + string result; + + result = JsonConvert.SerializeObject(d, converter); + Assert.AreEqual("new Date(976918263055)", result); + } + +#if !PocketPC && !NET20 + [Test] + public void SerializeDateTimeOffset() + { + JavaScriptDateTimeConverter converter = new JavaScriptDateTimeConverter(); + + DateTimeOffset now = new DateTimeOffset(2000, 12, 15, 22, 11, 3, 55, TimeSpan.Zero); + string result; + + result = JsonConvert.SerializeObject(now, converter); + Assert.AreEqual("new Date(976918263055)", result); + } + + [Test] + public void sdfs() + { + int i = Convert.ToInt32("+1"); + Console.WriteLine(i); + } + + [Test] + public void SerializeNullableDateTimeClass() + { + NullableDateTimeTestClass t = new NullableDateTimeTestClass() + { + DateTimeField = null, + DateTimeOffsetField = null + }; + + JavaScriptDateTimeConverter converter = new JavaScriptDateTimeConverter(); + + string result; + + result = JsonConvert.SerializeObject(t, converter); + Assert.AreEqual(@"{""PreField"":null,""DateTimeField"":null,""DateTimeOffsetField"":null,""PostField"":null}", result); + + t = new NullableDateTimeTestClass() + { + DateTimeField = new DateTime(2000, 12, 15, 22, 11, 3, 55, DateTimeKind.Utc), + DateTimeOffsetField = new DateTimeOffset(2000, 12, 15, 22, 11, 3, 55, TimeSpan.Zero) + }; + + result = JsonConvert.SerializeObject(t, converter); + Assert.AreEqual(@"{""PreField"":null,""DateTimeField"":new Date(976918263055),""DateTimeOffsetField"":new Date(976918263055),""PostField"":null}", result); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Cannot convert null value to System.DateTime.")] + public void DeserializeNullToNonNullable() + { + DateTimeTestClass c2 = + JsonConvert.DeserializeObject(@"{""PreField"":""Pre"",""DateTimeField"":null,""DateTimeOffsetField"":null,""PostField"":""Post""}", new JavaScriptDateTimeConverter()); + } + + [Test] + public void DeserializeDateTimeOffset() + { + JavaScriptDateTimeConverter converter = new JavaScriptDateTimeConverter(); + DateTimeOffset start = new DateTimeOffset(2000, 12, 15, 22, 11, 3, 55, TimeSpan.Zero); + + string json = JsonConvert.SerializeObject(start, converter); + + DateTimeOffset result = JsonConvert.DeserializeObject(json, converter); + Assert.AreEqual(new DateTimeOffset(2000, 12, 15, 22, 11, 3, 55, TimeSpan.Zero), result); + } +#endif + + [Test] + public void DeserializeDateTime() + { + JavaScriptDateTimeConverter converter = new JavaScriptDateTimeConverter(); + + DateTime result = JsonConvert.DeserializeObject("new Date(976918263055)", converter); + Assert.AreEqual(new DateTime(2000, 12, 15, 22, 11, 3, 55, DateTimeKind.Utc), result); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/ObjectIdConverterTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/ObjectIdConverterTests.cs new file mode 100644 index 0000000..ae4602d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/ObjectIdConverterTests.cs @@ -0,0 +1,58 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Bson; +using Newtonsoft.Json.Tests.TestObjects; +using Newtonsoft.Json.Utilities; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class ObjectIdConverterTests : TestFixtureBase + { + public class ObjectIdTestClass + { + [JsonProperty("_id")] + public BsonObjectId Id { get; set; } + [JsonProperty("test")] + public string Test { get; set; } + } + + [Test] + public void Serialize() + { + ObjectIdTestClass c = new ObjectIdTestClass + { + Id = new BsonObjectId(MiscellaneousUtils.HexToBytes("4ABBED9D1D8B0F0218000001")), + Test = "1234£56" + }; + + MemoryStream ms = new MemoryStream(); + JsonSerializer serializer = new JsonSerializer(); + + // serialize product to BSON + BsonWriter writer = new BsonWriter(ms); + serializer.Serialize(writer, c); + + byte[] expected = MiscellaneousUtils.HexToBytes("29000000075F6964004ABBED9D1D8B0F02180000010274657374000900000031323334C2A335360000"); + + Assert.AreEqual(expected, ms.ToArray()); + } + + [Test] + public void Deserialize() + { + byte[] bson = MiscellaneousUtils.HexToBytes("29000000075F6964004ABBED9D1D8B0F02180000010274657374000900000031323334C2A335360000"); + + JsonSerializer serializer = new JsonSerializer(); + + BsonReader reader = new BsonReader(new MemoryStream(bson)); + ObjectIdTestClass c = serializer.Deserialize(reader); + + Assert.AreEqual(c.Id.Value, MiscellaneousUtils.HexToBytes("4ABBED9D1D8B0F0218000001")); + Assert.AreEqual(c.Test, "1234£56"); + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/RegexConverterTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/RegexConverterTests.cs new file mode 100644 index 0000000..d7d9314 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/RegexConverterTests.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +#if !SILVERLIGHT && !PocketPC && !NET20 +using System.Data.Linq; +#endif +#if !SILVERLIGHT +using System.Data.SqlTypes; +#endif +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json.Bson; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Utilities; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class RegexConverterTests : TestFixtureBase + { + public class RegexTestClass + { + public Regex Regex { get; set; } + } + + [Test] + public void SerializeToText() + { + Regex regex = new Regex("abc", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + + string json = JsonConvert.SerializeObject(regex, Formatting.Indented, new RegexConverter()); + + Assert.AreEqual(@"{ + ""Pattern"": ""abc"", + ""Options"": 513 +}", json); + } + + [Test] + public void SerializeToBson() + { + Regex regex = new Regex("abc", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + JsonSerializer serializer = new JsonSerializer(); + serializer.Converters.Add(new RegexConverter()); + + serializer.Serialize(writer, new RegexTestClass { Regex = regex }); + + string expected = "13-00-00-00-0B-52-65-67-65-78-00-61-62-63-00-69-75-00-00"; + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + + Assert.AreEqual(expected, bson); + } + + [Test] + public void DeserializeFromText() + { + string json = @"{ + ""Pattern"": ""abc"", + ""Options"": 513 +}"; + + Regex newRegex = JsonConvert.DeserializeObject(json, new RegexConverter()); + Assert.AreEqual("abc", newRegex.ToString()); + Assert.AreEqual(RegexOptions.IgnoreCase | RegexOptions.CultureInvariant, newRegex.Options); + } + + [Test] + public void DeserializeFromBson() + { + MemoryStream ms = new MemoryStream(MiscellaneousUtils.HexToBytes("13-00-00-00-0B-52-65-67-65-78-00-61-62-63-00-69-75-00-00")); + BsonReader reader = new BsonReader(ms); + JsonSerializer serializer = new JsonSerializer(); + serializer.Converters.Add(new RegexConverter()); + + RegexTestClass c = serializer.Deserialize(reader); + + Assert.AreEqual("abc", c.Regex.ToString()); + Assert.AreEqual(RegexOptions.IgnoreCase, c.Regex.Options); + } + + [Test] + public void ConvertEmptyRegexBson() + { + Regex regex = new Regex(string.Empty); + + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + JsonSerializer serializer = new JsonSerializer(); + serializer.Converters.Add(new RegexConverter()); + + serializer.Serialize(writer, new RegexTestClass { Regex = regex }); + + ms.Seek(0, SeekOrigin.Begin); + BsonReader reader = new BsonReader(ms); + serializer.Converters.Add(new RegexConverter()); + + RegexTestClass c = serializer.Deserialize(reader); + + Assert.AreEqual("", c.Regex.ToString()); + Assert.AreEqual(RegexOptions.None, c.Regex.Options); + } + + [Test] + public void ConvertRegexWithAllOptionsBson() + { + Regex regex = new Regex( + "/", + RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.ExplicitCapture); + + MemoryStream ms = new MemoryStream(); + BsonWriter writer = new BsonWriter(ms); + JsonSerializer serializer = new JsonSerializer(); + serializer.Converters.Add(new RegexConverter()); + + serializer.Serialize(writer, new RegexTestClass { Regex = regex }); + + string expected = "14-00-00-00-0B-52-65-67-65-78-00-2F-00-69-6D-73-75-78-00-00"; + string bson = MiscellaneousUtils.BytesToHex(ms.ToArray()); + + Assert.AreEqual(expected, bson); + + ms.Seek(0, SeekOrigin.Begin); + BsonReader reader = new BsonReader(ms); + serializer.Converters.Add(new RegexConverter()); + + RegexTestClass c = serializer.Deserialize(reader); + + Assert.AreEqual("/", c.Regex.ToString()); + Assert.AreEqual(RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.Multiline | RegexOptions.ExplicitCapture, c.Regex.Options); + } + + [Test] + public void ConvertEmptyRegexJson() + { + Regex regex = new Regex(""); + + string json = JsonConvert.SerializeObject(new RegexTestClass { Regex = regex }, Formatting.Indented, new RegexConverter()); + + Assert.AreEqual(@"{ + ""Regex"": { + ""Pattern"": """", + ""Options"": 0 + } +}", json); + + RegexTestClass newRegex = JsonConvert.DeserializeObject(json, new RegexConverter()); + Assert.AreEqual("", newRegex.Regex.ToString()); + Assert.AreEqual(RegexOptions.None, newRegex.Regex.Options); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs new file mode 100644 index 0000000..1a52421 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/StringEnumConverterTests.cs @@ -0,0 +1,260 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using Newtonsoft.Json.Converters; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; + +namespace Newtonsoft.Json.Tests.Converters +{ + public class StringEnumConverterTests : TestFixtureBase + { + public class EnumClass + { + public StoreColor StoreColor { get; set; } + public StoreColor? NullableStoreColor1 { get; set; } + public StoreColor? NullableStoreColor2 { get; set; } + } + + public class EnumContainer + { + public T Enum { get; set; } + } + + public enum NegativeEnum + { + Negative = -1, + Zero = 0, + Positive = 1 + } + +#if !NET20 + public enum NamedEnum + { + [EnumMember(Value = "@first")] + First, + [EnumMember(Value = "@second")] + Second, + Third + } + + public enum NamedEnumDuplicate + { + [EnumMember(Value = "Third")] + First, + [EnumMember(Value = "@second")] + Second, + Third + } +#endif + + public class NegativeEnumClass + { + public NegativeEnum Value1 { get; set; } + public NegativeEnum Value2 { get; set; } + } + +#if !NET20 + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Enum name 'Third' already exists on enum 'NamedEnumDuplicate'.")] + public void NamedEnumDuplicateTest() + { + EnumContainer c = new EnumContainer + { + Enum = NamedEnumDuplicate.First + }; + + JsonConvert.SerializeObject(c, Formatting.Indented, new StringEnumConverter()); + } + + [Test] + public void SerializeNameEnumTest() + { + EnumContainer c = new EnumContainer + { + Enum = NamedEnum.First + }; + + string json = JsonConvert.SerializeObject(c, Formatting.Indented, new StringEnumConverter()); + Assert.AreEqual(@"{ + ""Enum"": ""@first"" +}", json); + + c = new EnumContainer + { + Enum = NamedEnum.Third + }; + + json = JsonConvert.SerializeObject(c, Formatting.Indented, new StringEnumConverter()); + Assert.AreEqual(@"{ + ""Enum"": ""Third"" +}", json); + } + + [Test] + public void DeserializeNameEnumTest() + { + string json = @"{ + ""Enum"": ""@first"" +}"; + + EnumContainer c = JsonConvert.DeserializeObject>(json, new StringEnumConverter()); + Assert.AreEqual(NamedEnum.First, c.Enum); + + json = @"{ + ""Enum"": ""Third"" +}"; + + c = JsonConvert.DeserializeObject>(json, new StringEnumConverter()); + Assert.AreEqual(NamedEnum.Third, c.Enum); + } +#endif + + [Test] + public void SerializeEnumClass() + { + EnumClass enumClass = new EnumClass(); + enumClass.StoreColor = StoreColor.Red; + enumClass.NullableStoreColor1 = StoreColor.White; + enumClass.NullableStoreColor2 = null; + + string json = JsonConvert.SerializeObject(enumClass, Formatting.Indented, new StringEnumConverter()); + + Assert.AreEqual(@"{ + ""StoreColor"": ""Red"", + ""NullableStoreColor1"": ""White"", + ""NullableStoreColor2"": null +}", json); + } + + [Test] + public void SerializeEnumClassWithCamelCase() + { + EnumClass enumClass = new EnumClass(); + enumClass.StoreColor = StoreColor.Red; + enumClass.NullableStoreColor1 = StoreColor.DarkGoldenrod; + enumClass.NullableStoreColor2 = null; + + string json = JsonConvert.SerializeObject(enumClass, Formatting.Indented, new StringEnumConverter { CamelCaseText = true }); + + Assert.AreEqual(@"{ + ""StoreColor"": ""red"", + ""NullableStoreColor1"": ""darkGoldenrod"", + ""NullableStoreColor2"": null +}", json); + } + + [Test] + public void SerializeEnumClassUndefined() + { + EnumClass enumClass = new EnumClass(); + enumClass.StoreColor = (StoreColor)1000; + enumClass.NullableStoreColor1 = (StoreColor)1000; + enumClass.NullableStoreColor2 = null; + + string json = JsonConvert.SerializeObject(enumClass, Formatting.Indented, new StringEnumConverter()); + + Assert.AreEqual(@"{ + ""StoreColor"": 1000, + ""NullableStoreColor1"": 1000, + ""NullableStoreColor2"": null +}", json); + } + + [Test] + public void SerializeFlagEnum() + { + EnumClass enumClass = new EnumClass(); + enumClass.StoreColor = StoreColor.Red | StoreColor.White; + enumClass.NullableStoreColor1 = StoreColor.White & StoreColor.Yellow; + enumClass.NullableStoreColor2 = StoreColor.Red | StoreColor.White | StoreColor.Black; + + string json = JsonConvert.SerializeObject(enumClass, Formatting.Indented, new StringEnumConverter()); + + Assert.AreEqual(@"{ + ""StoreColor"": ""Red, White"", + ""NullableStoreColor1"": 0, + ""NullableStoreColor2"": ""Black, Red, White"" +}", json); + } + + [Test] + public void SerializeNegativeEnum() + { + NegativeEnumClass negativeEnumClass = new NegativeEnumClass(); + negativeEnumClass.Value1 = NegativeEnum.Negative; + negativeEnumClass.Value2 = (NegativeEnum) int.MinValue; + + string json = JsonConvert.SerializeObject(negativeEnumClass, Formatting.Indented, new StringEnumConverter()); + + Assert.AreEqual(@"{ + ""Value1"": ""Negative"", + ""Value2"": -2147483648 +}", json); + } + + [Test] + public void DeserializeNegativeEnum() + { + string json = @"{ + ""Value1"": ""Negative"", + ""Value2"": -2147483648 +}"; + + NegativeEnumClass negativeEnumClass = JsonConvert.DeserializeObject(json, new StringEnumConverter()); + + Assert.AreEqual(NegativeEnum.Negative, negativeEnumClass.Value1); + Assert.AreEqual((NegativeEnum)int.MinValue, negativeEnumClass.Value2); + } + + [Test] + public void DeserializeFlagEnum() + { + string json = @"{ + ""StoreColor"": ""Red, White"", + ""NullableStoreColor1"": 0, + ""NullableStoreColor2"": ""black, Red, White"" +}"; + + EnumClass enumClass = JsonConvert.DeserializeObject(json, new StringEnumConverter()); + + Assert.AreEqual(StoreColor.Red | StoreColor.White, enumClass.StoreColor); + Assert.AreEqual((StoreColor)0, enumClass.NullableStoreColor1); + Assert.AreEqual(StoreColor.Red | StoreColor.White | StoreColor.Black, enumClass.NullableStoreColor2); + } + + [Test] + public void DeserializeEnumClass() + { + string json = @"{ + ""StoreColor"": ""Red"", + ""NullableStoreColor1"": ""White"", + ""NullableStoreColor2"": null +}"; + + EnumClass enumClass = JsonConvert.DeserializeObject(json, new StringEnumConverter()); + + Assert.AreEqual(StoreColor.Red, enumClass.StoreColor); + Assert.AreEqual(StoreColor.White, enumClass.NullableStoreColor1); + Assert.AreEqual(null, enumClass.NullableStoreColor2); + } + + [Test] + public void DeserializeEnumClassUndefined() + { + string json = @"{ + ""StoreColor"": 1000, + ""NullableStoreColor1"": 1000, + ""NullableStoreColor2"": null +}"; + + EnumClass enumClass = JsonConvert.DeserializeObject(json, new StringEnumConverter()); + + Assert.AreEqual((StoreColor)1000, enumClass.StoreColor); + Assert.AreEqual((StoreColor)1000, enumClass.NullableStoreColor1); + Assert.AreEqual(null, enumClass.NullableStoreColor2); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs new file mode 100644 index 0000000..509a4e3 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Converters/XmlNodeConverterTest.cs @@ -0,0 +1,1224 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !SILVERLIGHT +using System; +using System.Collections.Generic; +using Newtonsoft.Json.Tests.Serialization; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; +using Newtonsoft.Json; +using System.IO; +using System.Xml; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; +#if !NET20 +using System.Xml.Linq; +#endif + +namespace Newtonsoft.Json.Tests.Converters +{ + public class XmlNodeConverterTest : TestFixtureBase + { + private string SerializeXmlNode(XmlNode node) + { + string json = JsonConvert.SerializeXmlNode(node, Formatting.Indented); + XmlNodeReader reader = new XmlNodeReader(node); + +#if !NET20 + XObject xNode; + if (node is XmlDocument) + { + xNode = XDocument.Load(reader); + } + else if (node is XmlAttribute) + { + XmlAttribute attribute = (XmlAttribute) node; + xNode = new XAttribute(XName.Get(attribute.LocalName, attribute.NamespaceURI), attribute.Value); + } + else + { + reader.MoveToContent(); + xNode = XNode.ReadFrom(reader); + } + + string linqJson = JsonConvert.SerializeXNode(xNode, Formatting.Indented); + + Assert.AreEqual(json, linqJson); +#endif + + return json; + } + + private XmlNode DeserializeXmlNode(string json) + { + return DeserializeXmlNode(json, null); + } + + private XmlNode DeserializeXmlNode(string json, string deserializeRootElementName) + { + JsonTextReader reader; + + reader = new JsonTextReader(new StringReader(json)); + reader.Read(); + XmlNodeConverter converter = new XmlNodeConverter(); + if (deserializeRootElementName != null) + converter.DeserializeRootElementName = deserializeRootElementName; + + XmlNode node = (XmlNode)converter.ReadJson(reader, typeof (XmlDocument), null, new JsonSerializer()); + +#if !NET20 + string xmlText = node.OuterXml; + + reader = new JsonTextReader(new StringReader(json)); + reader.Read(); + XDocument d = (XDocument) converter.ReadJson(reader, typeof (XDocument), null, new JsonSerializer()); + + string linqXmlText = d.ToString(SaveOptions.DisableFormatting); + if (d.Declaration != null) + linqXmlText = d.Declaration + linqXmlText; + + Assert.AreEqual(xmlText, linqXmlText); +#endif + + return node; + } + + [Test] + public void DocumentSerializeIndented() + { + string xml = @" + + + + Web 2.0 Conference + October 5 + 7 + Argent Hotel, San Francisco, CA + +"; + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + + string jsonText = SerializeXmlNode(doc); + string expected = @"{ + ""?xml"": { + ""@version"": ""1.0"", + ""@standalone"": ""no"" + }, + ""?xml-stylesheet"": ""href=\""classic.xsl\"" type=\""text/xml\"""", + ""span"": { + ""@class"": ""vevent"", + ""a"": { + ""@class"": ""url"", + ""@href"": ""http://www.web2con.com/"", + ""span"": [ + { + ""@class"": ""summary"", + ""#text"": ""Web 2.0 Conference"", + ""#cdata-section"": ""my escaped text"" + }, + { + ""@class"": ""location"", + ""#text"": ""Argent Hotel, San Francisco, CA"" + } + ], + ""abbr"": [ + { + ""@class"": ""dtstart"", + ""@title"": ""2005-10-05"", + ""#text"": ""October 5"" + }, + { + ""@class"": ""dtend"", + ""@title"": ""2005-10-08"", + ""#text"": ""7"" + } + ] + } + } +}"; + + Assert.AreEqual(expected, jsonText); + + Console.WriteLine("DocumentSerializeIndented"); + Console.WriteLine(jsonText); + Console.WriteLine(); + } + + [Test] + public void SerializeNodeTypes() + { + XmlDocument doc = new XmlDocument(); + string jsonText; + + Console.WriteLine("SerializeNodeTypes"); + + string xml = @" + + + +"; + + XmlDocument document = new XmlDocument(); + document.LoadXml(xml); + + // XmlAttribute + XmlAttribute attribute = document.DocumentElement.ChildNodes[0].Attributes["IsDataSet", "urn:schemas-microsoft-com:xml-msdata"]; + attribute.Value = "true"; + + jsonText = JsonConvert.SerializeXmlNode(attribute); + + Console.WriteLine(jsonText); + Assert.AreEqual(@"{""@msdata:IsDataSet"":""true""}", jsonText); + +#if !NET20 + XDocument d = XDocument.Parse(xml); + XAttribute a = d.Root.Element("{http://www.w3.org/2001/XMLSchema}element").Attribute("{urn:schemas-microsoft-com:xml-msdata}IsDataSet"); + + jsonText = JsonConvert.SerializeXNode(a); + + Assert.AreEqual(@"{""@msdata:IsDataSet"":""true""}", jsonText); +#endif + + // XmlProcessingInstruction + XmlProcessingInstruction instruction = doc.CreateProcessingInstruction("xml-stylesheet", @"href=""classic.xsl"" type=""text/xml"""); + + jsonText = JsonConvert.SerializeXmlNode(instruction); + + Console.WriteLine(jsonText); + Assert.AreEqual(@"{""?xml-stylesheet"":""href=\""classic.xsl\"" type=\""text/xml\""""}", jsonText); + + + // XmlProcessingInstruction + XmlCDataSection cDataSection = doc.CreateCDataSection("true"); + + jsonText = JsonConvert.SerializeXmlNode(cDataSection); + + Console.WriteLine(jsonText); + Assert.AreEqual(@"{""#cdata-section"":""true""}", jsonText); + + + // XmlElement + XmlElement element = doc.CreateElement("xs", "Choice", "http://www.w3.org/2001/XMLSchema"); + element.SetAttributeNode(doc.CreateAttribute("msdata", "IsDataSet", "urn:schemas-microsoft-com:xml-msdata")); + + XmlAttribute aa = doc.CreateAttribute(@"xmlns", "xs", "http://www.w3.org/2000/xmlns/"); + aa.Value = "http://www.w3.org/2001/XMLSchema"; + element.SetAttributeNode(aa); + + aa = doc.CreateAttribute(@"xmlns", "msdata", "http://www.w3.org/2000/xmlns/"); + aa.Value = "urn:schemas-microsoft-com:xml-msdata"; + element.SetAttributeNode(aa); + + element.AppendChild(instruction); + element.AppendChild(cDataSection); + + doc.AppendChild(element); + + jsonText = JsonConvert.SerializeXmlNode(element, Formatting.Indented); + + Assert.AreEqual(@"{ + ""xs:Choice"": { + ""@msdata:IsDataSet"": """", + ""@xmlns:xs"": ""http://www.w3.org/2001/XMLSchema"", + ""@xmlns:msdata"": ""urn:schemas-microsoft-com:xml-msdata"", + ""?xml-stylesheet"": ""href=\""classic.xsl\"" type=\""text/xml\"""", + ""#cdata-section"": ""true"" + } +}", jsonText); + } + + [Test] + public void DocumentFragmentSerialize() + { + XmlDocument doc = new XmlDocument(); + + XmlDocumentFragment fragement = doc.CreateDocumentFragment(); + + fragement.InnerXml = "widgetwidget"; + + string jsonText = JsonConvert.SerializeXmlNode(fragement); + + string expected = @"{""Item"":[""widget"",""widget""]}"; + + Assert.AreEqual(expected, jsonText); + + Console.WriteLine("DocumentFragmentSerialize"); + Console.WriteLine(jsonText); + Console.WriteLine(); + } + + [Test] + public void NamespaceSerializeDeserialize() + { + string xml = @" + + + + + + + + + + + + + + + + +"; + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + + string jsonText = SerializeXmlNode(doc); + + string expected = @"{ + ""?xml"": { + ""@version"": ""1.0"", + ""@encoding"": ""utf-8"" + }, + ""xs:schema"": { + ""@xs:id"": ""SomeID"", + ""@xmlns"": """", + ""@xmlns:xs"": ""http://www.w3.org/2001/XMLSchema"", + ""@xmlns:msdata"": ""urn:schemas-microsoft-com:xml-msdata"", + ""xs:element"": { + ""@name"": ""MyDataSet"", + ""@msdata:IsDataSet"": ""true"", + ""xs:complexType"": { + ""xs:choice"": { + ""@maxOccurs"": ""unbounded"", + ""xs:element"": { + ""@name"": ""customers"", + ""xs:complexType"": { + ""xs:sequence"": { + ""xs:element"": [ + { + ""@name"": ""CustomerID"", + ""@type"": ""xs:integer"", + ""@minOccurs"": ""0"" + }, + { + ""@name"": ""CompanyName"", + ""@type"": ""xs:string"", + ""@minOccurs"": ""0"" + }, + { + ""@name"": ""Phone"", + ""@type"": ""xs:string"" + } + ] + } + } + } + } + } + } + } +}"; + + Assert.AreEqual(expected, jsonText); + + XmlDocument deserializedDoc = (XmlDocument)DeserializeXmlNode(jsonText); + + Assert.AreEqual(doc.InnerXml, deserializedDoc.InnerXml); + + Console.WriteLine("NamespaceSerializeDeserialize"); + Console.WriteLine(jsonText); + Console.WriteLine(deserializedDoc.InnerXml); + Console.WriteLine(); + } + + [Test] + public void DocumentDeserialize() + { + string jsonText = @"{ + ""?xml"": { + ""@version"": ""1.0"", + ""@standalone"": ""no"" + }, + ""span"": { + ""@class"": ""vevent"", + ""a"": { + ""@class"": ""url"", + ""@href"": ""http://www.web2con.com/"", + ""span"": { + ""@class"": ""summary"", + ""#text"": ""Web 2.0 Conference"", + ""#cdata-section"": ""my escaped text"" + } + } + } +}"; + + XmlDocument doc = (XmlDocument)DeserializeXmlNode(jsonText); + + string expected = @" + + + Web 2.0 Conference + +"; + + string formattedXml = GetIndentedInnerXml(doc); + + Console.WriteLine("DocumentDeserialize"); + Console.WriteLine(formattedXml); + Console.WriteLine(); + + Assert.AreEqual(expected, formattedXml); + } + + private string GetIndentedInnerXml(XmlNode node) + { + XmlWriterSettings settings = new XmlWriterSettings(); + settings.Indent = true; + + StringWriter sw = new StringWriter(); + + using (XmlWriter writer = XmlWriter.Create(sw, settings)) + { + node.WriteTo(writer); + } + + return sw.ToString(); + } + + [Test] + public void SingleTextNode() + { + string xml = @" + + + Alan + http://www.google.com + + + Louis + http://www.yahoo.com + + "; + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + + string jsonText = SerializeXmlNode(doc); + + XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText); + + Assert.AreEqual(doc.InnerXml, newDoc.InnerXml); + } + + [Test] + public void EmptyNode() + { + string xml = @" + + + Alan + + + + Louis + http://www.yahoo.com + + "; + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + + string jsonText = SerializeXmlNode(doc); + + Console.WriteLine(jsonText); + + XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText); + + Assert.AreEqual(doc.InnerXml, newDoc.InnerXml); + } + + [Test] + public void OtherElementDataTypes() + { + string jsonText = @"{""?xml"":{""@version"":""1.0"",""@standalone"":""no""},""root"":{""person"":[{""@id"":""1"",""Float"":2.5,""Integer"":99},{""@id"":""2"",""Boolean"":true,""date"":""\/Date(954374400000)\/""}]}}"; + + XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText); + + string expected = @"2.599true2000-03-30T00:00:00Z"; + + Assert.AreEqual(expected, newDoc.InnerXml); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "XmlNodeConverter can only convert JSON that begins with an object.")] + public void NoRootObject() + { + XmlDocument newDoc = (XmlDocument)JsonConvert.DeserializeXmlNode(@"[1]"); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "JSON root object has multiple properties. The root object must have a single property in order to create a valid XML document. Consider specifing a DeserializeRootElementName.")] + public void RootObjectMultipleProperties() + { + XmlDocument newDoc = (XmlDocument)JsonConvert.DeserializeXmlNode(@"{Prop1:1,Prop2:2}"); + } + + [Test] + public void JavaScriptConstructor() + { + string jsonText = @"{root:{r:new Date(34343, 55)}}"; + + XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText); + + string expected = @"3434355"; + + Assert.AreEqual(expected, newDoc.InnerXml); + + string json = SerializeXmlNode(newDoc); + expected = @"{ + ""root"": { + ""r"": { + ""Date"": [ + ""34343"", + ""55"" + ] + } + } +}"; + + Assert.AreEqual(expected, json); + } + + [Test] + public void ForceJsonArray() + { + string arrayXml = @" + + Alan + http://www.google.com + Admin + + "; + + XmlDocument arrayDoc = new XmlDocument(); + arrayDoc.LoadXml(arrayXml); + + string arrayJsonText = SerializeXmlNode(arrayDoc); + string expected = @"{ + ""root"": { + ""person"": { + ""@id"": ""1"", + ""name"": ""Alan"", + ""url"": ""http://www.google.com"", + ""role"": [ + ""Admin"" + ] + } + } +}"; + Assert.AreEqual(expected, arrayJsonText); + + arrayXml = @" + + Alan + http://www.google.com + Admin1 + Admin2 + + "; + + arrayDoc = new XmlDocument(); + arrayDoc.LoadXml(arrayXml); + + arrayJsonText = SerializeXmlNode(arrayDoc); + expected = @"{ + ""root"": { + ""person"": { + ""@id"": ""1"", + ""name"": ""Alan"", + ""url"": ""http://www.google.com"", + ""role"": [ + ""Admin1"", + ""Admin2"" + ] + } + } +}"; + Assert.AreEqual(expected, arrayJsonText); + + arrayXml = @" + + Alan + http://www.google.com + Admin1 + + "; + + arrayDoc = new XmlDocument(); + arrayDoc.LoadXml(arrayXml); + + arrayJsonText = SerializeXmlNode(arrayDoc); + expected = @"{ + ""root"": { + ""person"": { + ""@id"": ""1"", + ""name"": ""Alan"", + ""url"": ""http://www.google.com"", + ""role"": ""Admin1"" + } + } +}"; + Assert.AreEqual(expected, arrayJsonText); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "JSON root object has multiple properties. The root object must have a single property in order to create a valid XML document. Consider specifing a DeserializeRootElementName.")] + public void MultipleRootPropertiesXmlDocument() + { + string json = @"{""count"": 773840,""photos"": null}"; + + JsonConvert.DeserializeXmlNode(json); + } + +#if !NET20 + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "JSON root object has multiple properties. The root object must have a single property in order to create a valid XML document. Consider specifing a DeserializeRootElementName.")] + public void MultipleRootPropertiesXDocument() + { + string json = @"{""count"": 773840,""photos"": null}"; + + JsonConvert.DeserializeXNode(json); + } +#endif + + [Test] + public void MultipleRootPropertiesAddRootElement() + { + string json = @"{""count"": 773840,""photos"": 773840}"; + + XmlDocument newDoc = JsonConvert.DeserializeXmlNode(json, "myRoot"); + + Assert.AreEqual(@"773840773840", newDoc.InnerXml); + +#if !NET20 + XDocument newXDoc = JsonConvert.DeserializeXNode(json, "myRoot"); + + Assert.AreEqual(@"773840773840", newXDoc.ToString(SaveOptions.DisableFormatting)); +#endif + } + + [Test] + public void NestedArrays() + { + string json = @"{ + ""available_sizes"": [ + [ + ""assets/images/resized/0001/1070/11070v1-max-150x150.jpg"", + ""assets/images/resized/0001/1070/11070v1-max-150x150.jpg"" + ], + [ + ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"", + ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"" + ], + [ + ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"" + ] + ] +}"; + + XmlDocument newDoc = JsonConvert.DeserializeXmlNode(json, "myRoot"); + + string xml = IndentXml(newDoc.InnerXml); + + Assert.AreEqual(@" + + assets/images/resized/0001/1070/11070v1-max-150x150.jpg + assets/images/resized/0001/1070/11070v1-max-150x150.jpg + + + assets/images/resized/0001/1070/11070v1-max-250x250.jpg + assets/images/resized/0001/1070/11070v1-max-250x250.jpg + + + assets/images/resized/0001/1070/11070v1-max-250x250.jpg + +", IndentXml(newDoc.InnerXml)); + +#if !NET20 + XDocument newXDoc = JsonConvert.DeserializeXNode(json, "myRoot"); + + Assert.AreEqual(@" + + assets/images/resized/0001/1070/11070v1-max-150x150.jpg + assets/images/resized/0001/1070/11070v1-max-150x150.jpg + + + assets/images/resized/0001/1070/11070v1-max-250x250.jpg + assets/images/resized/0001/1070/11070v1-max-250x250.jpg + + + assets/images/resized/0001/1070/11070v1-max-250x250.jpg + +", IndentXml(newXDoc.ToString(SaveOptions.DisableFormatting))); +#endif + + string newJson = JsonConvert.SerializeXmlNode(newDoc, Formatting.Indented); + Console.WriteLine(newJson); + } + + [Test] + public void RoundTripNestedArrays() + { + string json = @"{ + ""available_sizes"": [ + [ + ""assets/images/resized/0001/1070/11070v1-max-150x150.jpg"", + ""assets/images/resized/0001/1070/11070v1-max-150x150.jpg"" + ], + [ + ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"", + ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"" + ], + [ + ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"" + ] + ] +}"; + + XmlDocument newDoc = JsonConvert.DeserializeXmlNode(json, "myRoot", true); + + Assert.AreEqual(@" + + assets/images/resized/0001/1070/11070v1-max-150x150.jpg + assets/images/resized/0001/1070/11070v1-max-150x150.jpg + + + assets/images/resized/0001/1070/11070v1-max-250x250.jpg + assets/images/resized/0001/1070/11070v1-max-250x250.jpg + + + assets/images/resized/0001/1070/11070v1-max-250x250.jpg + +", IndentXml(newDoc.InnerXml)); + +#if !NET20 + XDocument newXDoc = JsonConvert.DeserializeXNode(json, "myRoot", true); + + Console.WriteLine(IndentXml(newXDoc.ToString(SaveOptions.DisableFormatting))); + + Assert.AreEqual(@" + + assets/images/resized/0001/1070/11070v1-max-150x150.jpg + assets/images/resized/0001/1070/11070v1-max-150x150.jpg + + + assets/images/resized/0001/1070/11070v1-max-250x250.jpg + assets/images/resized/0001/1070/11070v1-max-250x250.jpg + + + assets/images/resized/0001/1070/11070v1-max-250x250.jpg + +", IndentXml(newXDoc.ToString(SaveOptions.DisableFormatting))); +#endif + + string newJson = JsonConvert.SerializeXmlNode(newDoc, Formatting.Indented, true); + Assert.AreEqual(json, newJson); + } + + [Test] + public void MultipleNestedArraysToXml() + { + string json = @"{ + ""available_sizes"": [ + [ + [113, 150], + ""assets/images/resized/0001/1070/11070v1-max-150x150.jpg"" + ], + [ + [189, 250], + ""assets/images/resized/0001/1070/11070v1-max-250x250.jpg"" + ], + [ + [341, 450], + ""assets/images/resized/0001/1070/11070v1-max-450x450.jpg"" + ] + ] +}"; + + XmlDocument newDoc = JsonConvert.DeserializeXmlNode(json, "myRoot"); + + Assert.AreEqual(@"113150assets/images/resized/0001/1070/11070v1-max-150x150.jpg189250assets/images/resized/0001/1070/11070v1-max-250x250.jpg341450assets/images/resized/0001/1070/11070v1-max-450x450.jpg", newDoc.InnerXml); + +#if !NET20 + XDocument newXDoc = JsonConvert.DeserializeXNode(json, "myRoot"); + + Assert.AreEqual(@"113150assets/images/resized/0001/1070/11070v1-max-150x150.jpg189250assets/images/resized/0001/1070/11070v1-max-250x250.jpg341450assets/images/resized/0001/1070/11070v1-max-450x450.jpg", newXDoc.ToString(SaveOptions.DisableFormatting)); +#endif + } + + [Test] + public void Encoding() + { + XmlDocument doc = new XmlDocument(); + + doc.LoadXml(@"O""Connor"); // i use "" so it will be easier to see the problem + + string json = SerializeXmlNode(doc); + Assert.AreEqual(@"{ + ""name"": ""O\""Connor"" +}", json); + } + + [Test] + public void SerializeComment() + { + string xml = @" + Text +"; + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + + string jsonText = SerializeXmlNode(doc); + + string expected = @"{ + ""span"": { + ""@class"": ""vevent"", + ""a"": { + ""@class"": ""url"", + ""@href"": ""http://www.web2con.com/"", + ""#text"": ""Text"" + }/* Hi! */ + } +}"; + + Assert.AreEqual(expected, jsonText); + + XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText); + Assert.AreEqual(@"Text", newDoc.InnerXml); + } + + [Test] + public void SerializeExample() + { + string xml = @" + + + Alan + http://www.google.com + + + Louis + http://www.yahoo.com + + "; + + XmlDocument doc = new XmlDocument(); + doc.LoadXml(xml); + + string jsonText = SerializeXmlNode(doc); + // { + // "?xml": { + // "@version": "1.0", + // "@standalone": "no" + // }, + // "root": { + // "person": [ + // { + // "@id": "1", + // "name": "Alan", + // "url": "http://www.google.com" + // }, + // { + // "@id": "2", + // "name": "Louis", + // "url": "http://www.yahoo.com" + // } + // ] + // } + // } + + // format + jsonText = JObject.Parse(jsonText).ToString(); + + Assert.AreEqual(@"{ + ""?xml"": { + ""@version"": ""1.0"", + ""@standalone"": ""no"" + }, + ""root"": { + ""person"": [ + { + ""@id"": ""1"", + ""name"": ""Alan"", + ""url"": ""http://www.google.com"" + }, + { + ""@id"": ""2"", + ""name"": ""Louis"", + ""url"": ""http://www.yahoo.com"" + } + ] + } +}", jsonText); + + XmlDocument newDoc = (XmlDocument)DeserializeXmlNode(jsonText); + + Assert.AreEqual(doc.InnerXml, newDoc.InnerXml); + } + + [Test] + public void DeserializeExample() + { + string json = @"{ + ""?xml"": { + ""@version"": ""1.0"", + ""@standalone"": ""no"" + }, + ""root"": { + ""person"": [ + { + ""@id"": ""1"", + ""name"": ""Alan"", + ""url"": ""http://www.google.com"" + }, + { + ""@id"": ""2"", + ""name"": ""Louis"", + ""url"": ""http://www.yahoo.com"" + } + ] + } + }"; + + XmlDocument doc = (XmlDocument)DeserializeXmlNode(json); + // + // + // + // Alan + // http://www.google.com + // + // + // Louis + // http://www.yahoo.com + // + // + + Assert.AreEqual(@" + + +Alan +http://www.google.com + + +Louis +http://www.yahoo.com + +".Replace(Environment.NewLine, string.Empty), doc.InnerXml); + } + + [Test] + public void SerializeDeserializeSpecialProperties() + { + PreserveReferencesHandlingTests.CircularDictionary circularDictionary = new PreserveReferencesHandlingTests.CircularDictionary(); + circularDictionary.Add("other", new PreserveReferencesHandlingTests.CircularDictionary { { "blah", null } }); + circularDictionary.Add("self", circularDictionary); + + string json = JsonConvert.SerializeObject(circularDictionary, Formatting.Indented, + new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All }); + + Assert.AreEqual(@"{ + ""$id"": ""1"", + ""other"": { + ""$id"": ""2"", + ""blah"": null + }, + ""self"": { + ""$ref"": ""1"" + } +}", json); + + XmlNode node = DeserializeXmlNode(json, "root"); + string xml = GetIndentedInnerXml(node); + string expected = @" + + + + + +"; + + Assert.AreEqual(expected, xml); + + string xmlJson = SerializeXmlNode(node); + string expectedXmlJson = @"{ + ""root"": { + ""$id"": ""1"", + ""other"": { + ""$id"": ""2"", + ""blah"": null + }, + ""self"": { + ""$ref"": ""1"" + } + } +}"; + + Assert.AreEqual(expectedXmlJson, xmlJson); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "XmlNodeConverter cannot convert JSON with an empty property name to XML.")] + public void EmptyPropertyName() + { + string json = @"{ + ""8452309520V2"": { + """": { + ""CLIENT"": { + ""ID_EXPIRATION_1"": { + ""VALUE"": ""12/12/2000"", + ""DATATYPE"": ""D"", + ""MSG"": ""Missing Identification Exp. Date 1"" + }, + ""ID_ISSUEDATE_1"": { + ""VALUE"": """", + ""DATATYPE"": ""D"", + ""MSG"": ""Missing Identification Issue Date 1"" + } + } + }, + ""457463534534"": { + ""ACCOUNT"": { + ""FUNDING_SOURCE"": { + ""VALUE"": ""FS0"", + ""DATATYPE"": ""L"", + ""MSG"": ""Missing Source of Funds"" + } + } + } + } +}{ + ""34534634535345"": { + """": { + ""CLIENT"": { + ""ID_NUMBER_1"": { + ""VALUE"": """", + ""DATATYPE"": ""S"", + ""MSG"": ""Missing Picture ID"" + }, + ""ID_EXPIRATION_1"": { + ""VALUE"": ""12/12/2000"", + ""DATATYPE"": ""D"", + ""MSG"": ""Missing Picture ID"" + }, + ""WALK_IN"": { + ""VALUE"": """", + ""DATATYPE"": ""L"", + ""MSG"": ""Missing Walk in"" + }, + ""PERSONAL_MEETING"": { + ""VALUE"": ""PM1"", + ""DATATYPE"": ""L"", + ""MSG"": ""Missing Met Client in Person"" + }, + ""ID_ISSUEDATE_1"": { + ""VALUE"": """", + ""DATATYPE"": ""D"", + ""MSG"": ""Missing Picture ID"" + }, + ""PHOTO_ID"": { + ""VALUE"": """", + ""DATATYPE"": ""L"", + ""MSG"": ""Missing Picture ID"" + }, + ""ID_TYPE_1"": { + ""VALUE"": """", + ""DATATYPE"": ""L"", + ""MSG"": ""Missing Picture ID"" + } + } + }, + ""45635624523"": { + ""ACCOUNT"": { + ""FUNDING_SOURCE"": { + ""VALUE"": ""FS1"", + ""DATATYPE"": ""L"", + ""MSG"": ""Missing Source of Funds"" + } + } + } + } +}"; + + DeserializeXmlNode(json); + } + + [Test] + public void SingleItemArrayPropertySerialization() + { + Product product = new Product(); + + product.Name = "Apple"; + product.ExpiryDate = new DateTime(2008, 12, 28, 0, 0, 0, DateTimeKind.Utc); + product.Price = 3.99M; + product.Sizes = new string[] { "Small" }; + + string output = JsonConvert.SerializeObject(product, new IsoDateTimeConverter()); + + XmlDocument xmlProduct = JsonConvert.DeserializeXmlNode(output, "product", true); + + Assert.AreEqual(@" + Apple + 2008-12-28T00:00:00Z + 3.99 + Small +", IndentXml(xmlProduct.InnerXml)); + + string output2 = JsonConvert.SerializeXmlNode(xmlProduct.DocumentElement, Formatting.Indented); + + Assert.AreEqual(@"{ + ""product"": { + ""Name"": ""Apple"", + ""ExpiryDate"": ""2008-12-28T00:00:00Z"", + ""Price"": ""3.99"", + ""Sizes"": [ + ""Small"" + ] + } +}", output2); + } + + public class TestComplexArrayClass + { + public string Name { get; set; } + public IList Products { get; set; } + } + + [Test] + public void ComplexSingleItemArrayPropertySerialization() + { + TestComplexArrayClass o = new TestComplexArrayClass + { + Name = "Hi", + Products = new List + { + new Product { Name = "First" } + } + }; + + string output = JsonConvert.SerializeObject(o, new IsoDateTimeConverter()); + + XmlDocument xmlProduct = JsonConvert.DeserializeXmlNode(output, "test", true); + + Assert.AreEqual(@" + Hi + + First + 2000-01-01T00:00:00Z + 0 + + +", IndentXml(xmlProduct.InnerXml)); + + string output2 = JsonConvert.SerializeXmlNode(xmlProduct.DocumentElement, Formatting.Indented, true); + + Assert.AreEqual(@"{ + ""Name"": ""Hi"", + ""Products"": [ + { + ""Name"": ""First"", + ""ExpiryDate"": ""2000-01-01T00:00:00Z"", + ""Price"": ""0"", + ""Sizes"": null + } + ] +}", output2); + } + + private string IndentXml(string xml) + { + XmlReader reader = XmlReader.Create(new StringReader(xml)); + + StringWriter sw = new StringWriter(); + XmlWriter writer = XmlWriter.Create(sw, new XmlWriterSettings { Indent = true, OmitXmlDeclaration = true }); + + while (reader.Read()) + { + writer.WriteNode(reader, false); + } + + writer.Flush(); + + return sw.ToString(); + } + + [Test] + public void OmitRootObject() + { + string xml = @" + Hi + Hi + + First + 2000-01-01T00:00:00Z + 0 + + +"; + + XmlDocument d = new XmlDocument(); + d.LoadXml(xml); + + string output = JsonConvert.SerializeXmlNode(d, Formatting.Indented, true); + + Assert.AreEqual(@"{ + ""Name"": [ + ""Hi"", + ""Hi"" + ], + ""Products"": [ + { + ""Name"": ""First"", + ""ExpiryDate"": ""2000-01-01T00:00:00Z"", + ""Price"": ""0"", + ""Sizes"": null + } + ] +}", output); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/ExceptionTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/ExceptionTests.cs new file mode 100644 index 0000000..2330089 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/ExceptionTests.cs @@ -0,0 +1,72 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Schema; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests +{ + public class ExceptionTests : TestFixtureBase + { + [Test] + public void JsonSerializationException() + { + JsonSerializationException exception = new JsonSerializationException(); + Assert.AreEqual("Exception of type 'Newtonsoft.Json.JsonSerializationException' was thrown.", exception.Message); + + exception = new JsonSerializationException("Message!"); + Assert.AreEqual("Message!", exception.Message); + Assert.AreEqual(null, exception.InnerException); + + exception = new JsonSerializationException("Message!", new Exception("Inner!")); + Assert.AreEqual("Message!", exception.Message); + Assert.AreEqual("Inner!", exception.InnerException.Message); + } + + [Test] + public void JsonWriterException() + { + JsonWriterException exception = new JsonWriterException(); + Assert.AreEqual("Exception of type 'Newtonsoft.Json.JsonWriterException' was thrown.", exception.Message); + + exception = new JsonWriterException("Message!"); + Assert.AreEqual("Message!", exception.Message); + Assert.AreEqual(null, exception.InnerException); + + exception = new JsonWriterException("Message!", new Exception("Inner!")); + Assert.AreEqual("Message!", exception.Message); + Assert.AreEqual("Inner!", exception.InnerException.Message); + } + + [Test] + public void JsonReaderException() + { + JsonReaderException exception = new JsonReaderException(); + Assert.AreEqual("Exception of type 'Newtonsoft.Json.JsonReaderException' was thrown.", exception.Message); + + exception = new JsonReaderException("Message!"); + Assert.AreEqual("Message!", exception.Message); + Assert.AreEqual(null, exception.InnerException); + + exception = new JsonReaderException("Message!", new Exception("Inner!")); + Assert.AreEqual("Message!", exception.Message); + Assert.AreEqual("Inner!", exception.InnerException.Message); + } + + [Test] + public void JsonSchemaException() + { + JsonSchemaException exception = new JsonSchemaException(); + Assert.AreEqual("Exception of type 'Newtonsoft.Json.Schema.JsonSchemaException' was thrown.", exception.Message); + + exception = new JsonSchemaException("Message!"); + Assert.AreEqual("Message!", exception.Message); + Assert.AreEqual(null, exception.InnerException); + + exception = new JsonSchemaException("Message!", new Exception("Inner!")); + Assert.AreEqual("Message!", exception.Message); + Assert.AreEqual("Inner!", exception.InnerException.Message); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/FileSystemEntityModel.Designer.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/FileSystemEntityModel.Designer.cs new file mode 100644 index 0000000..b5de9bf --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/FileSystemEntityModel.Designer.cs @@ -0,0 +1,524 @@ +//------------------------------------------------------------------------------ +// +// This code was generated from a template. +// +// Manual changes to this file may cause unexpected behavior in your application. +// Manual changes to this file will be overwritten if the code is regenerated. +// +//------------------------------------------------------------------------------ + +using System; +using System.Data.Objects; +using System.Data.Objects.DataClasses; +using System.Data.EntityClient; +using System.ComponentModel; +using System.Xml.Serialization; +using System.Runtime.Serialization; + +[assembly: EdmSchemaAttribute()] +#region EDM Relationship Metadata + +[assembly: EdmRelationshipAttribute("DataServicesTestDatabaseModel", "FK_File_Folder", "Folder", System.Data.Metadata.Edm.RelationshipMultiplicity.One, typeof(Newtonsoft.Json.Tests.Folder), "File", System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(Newtonsoft.Json.Tests.File))] +[assembly: EdmRelationshipAttribute("DataServicesTestDatabaseModel", "FK_Folder_Folder", "Folder", System.Data.Metadata.Edm.RelationshipMultiplicity.ZeroOrOne, typeof(Newtonsoft.Json.Tests.Folder), "Folder1", System.Data.Metadata.Edm.RelationshipMultiplicity.Many, typeof(Newtonsoft.Json.Tests.Folder))] + +#endregion + +namespace Newtonsoft.Json.Tests +{ + #region Contexts + + /// + /// No Metadata Documentation available. + /// + public partial class DataServicesTestDatabaseEntities : ObjectContext + { + #region Constructors + + /// + /// Initializes a new DataServicesTestDatabaseEntities object using the connection string found in the 'DataServicesTestDatabaseEntities' section of the application configuration file. + /// + public DataServicesTestDatabaseEntities() : base("name=DataServicesTestDatabaseEntities", "DataServicesTestDatabaseEntities") + { + OnContextCreated(); + } + + /// + /// Initialize a new DataServicesTestDatabaseEntities object. + /// + public DataServicesTestDatabaseEntities(string connectionString) : base(connectionString, "DataServicesTestDatabaseEntities") + { + OnContextCreated(); + } + + /// + /// Initialize a new DataServicesTestDatabaseEntities object. + /// + public DataServicesTestDatabaseEntities(EntityConnection connection) : base(connection, "DataServicesTestDatabaseEntities") + { + OnContextCreated(); + } + + #endregion + + #region Partial Methods + + partial void OnContextCreated(); + + #endregion + + #region ObjectSet Properties + + /// + /// No Metadata Documentation available. + /// + public ObjectSet File + { + get + { + if ((_File == null)) + { + _File = base.CreateObjectSet("File"); + } + return _File; + } + } + private ObjectSet _File; + + /// + /// No Metadata Documentation available. + /// + public ObjectSet Folder + { + get + { + if ((_Folder == null)) + { + _Folder = base.CreateObjectSet("Folder"); + } + return _Folder; + } + } + private ObjectSet _Folder; + + #endregion + #region AddTo Methods + + /// + /// Deprecated Method for adding a new object to the File EntitySet. Consider using the .Add method of the associated ObjectSet<T> property instead. + /// + public void AddToFile(File file) + { + base.AddObject("File", file); + } + + /// + /// Deprecated Method for adding a new object to the Folder EntitySet. Consider using the .Add method of the associated ObjectSet<T> property instead. + /// + public void AddToFolder(Folder folder) + { + base.AddObject("Folder", folder); + } + + #endregion + } + + + #endregion + + #region Entities + + /// + /// No Metadata Documentation available. + /// + [EdmEntityTypeAttribute(NamespaceName="DataServicesTestDatabaseModel", Name="File")] + [Serializable()] + [DataContractAttribute(IsReference=true)] + public partial class File : EntityObject + { + #region Factory Method + + /// + /// Create a new File object. + /// + /// Initial value of the FileId property. + /// Initial value of the Name property. + /// Initial value of the Description property. + /// Initial value of the CreatedDate property. + public static File CreateFile(global::System.Guid fileId, global::System.String name, global::System.String description, global::System.DateTime createdDate) + { + File file = new File(); + file.FileId = fileId; + file.Name = name; + file.Description = description; + file.CreatedDate = createdDate; + return file; + } + + #endregion + #region Primitive Properties + + /// + /// No Metadata Documentation available. + /// + [EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)] + [DataMemberAttribute()] + public global::System.Guid FileId + { + get + { + return _FileId; + } + set + { + if (_FileId != value) + { + OnFileIdChanging(value); + ReportPropertyChanging("FileId"); + _FileId = StructuralObject.SetValidValue(value); + ReportPropertyChanged("FileId"); + OnFileIdChanged(); + } + } + } + private global::System.Guid _FileId; + partial void OnFileIdChanging(global::System.Guid value); + partial void OnFileIdChanged(); + + /// + /// No Metadata Documentation available. + /// + [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)] + [DataMemberAttribute()] + public global::System.String Name + { + get + { + return _Name; + } + set + { + OnNameChanging(value); + ReportPropertyChanging("Name"); + _Name = StructuralObject.SetValidValue(value, false); + ReportPropertyChanged("Name"); + OnNameChanged(); + } + } + private global::System.String _Name; + partial void OnNameChanging(global::System.String value); + partial void OnNameChanged(); + + /// + /// No Metadata Documentation available. + /// + [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)] + [DataMemberAttribute()] + public global::System.String Description + { + get + { + return _Description; + } + set + { + OnDescriptionChanging(value); + ReportPropertyChanging("Description"); + _Description = StructuralObject.SetValidValue(value, false); + ReportPropertyChanged("Description"); + OnDescriptionChanged(); + } + } + private global::System.String _Description; + partial void OnDescriptionChanging(global::System.String value); + partial void OnDescriptionChanged(); + + /// + /// No Metadata Documentation available. + /// + [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)] + [DataMemberAttribute()] + public global::System.DateTime CreatedDate + { + get + { + return _CreatedDate; + } + set + { + OnCreatedDateChanging(value); + ReportPropertyChanging("CreatedDate"); + _CreatedDate = StructuralObject.SetValidValue(value); + ReportPropertyChanged("CreatedDate"); + OnCreatedDateChanged(); + } + } + private global::System.DateTime _CreatedDate; + partial void OnCreatedDateChanging(global::System.DateTime value); + partial void OnCreatedDateChanged(); + + #endregion + + #region Navigation Properties + + /// + /// No Metadata Documentation available. + /// + [XmlIgnoreAttribute()] + [SoapIgnoreAttribute()] + [DataMemberAttribute()] + [EdmRelationshipNavigationPropertyAttribute("DataServicesTestDatabaseModel", "FK_File_Folder", "Folder")] + public Folder Folder + { + get + { + return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference("DataServicesTestDatabaseModel.FK_File_Folder", "Folder").Value; + } + set + { + ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference("DataServicesTestDatabaseModel.FK_File_Folder", "Folder").Value = value; + } + } + /// + /// No Metadata Documentation available. + /// + [BrowsableAttribute(false)] + [DataMemberAttribute()] + public EntityReference FolderReference + { + get + { + return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference("DataServicesTestDatabaseModel.FK_File_Folder", "Folder"); + } + set + { + if ((value != null)) + { + ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedReference("DataServicesTestDatabaseModel.FK_File_Folder", "Folder", value); + } + } + } + + #endregion + } + + /// + /// No Metadata Documentation available. + /// + [EdmEntityTypeAttribute(NamespaceName="DataServicesTestDatabaseModel", Name="Folder")] + [Serializable()] + [DataContractAttribute(IsReference=true)] + public partial class Folder : EntityObject + { + #region Factory Method + + /// + /// Create a new Folder object. + /// + /// Initial value of the FolderId property. + /// Initial value of the Name property. + /// Initial value of the Description property. + /// Initial value of the CreatedDate property. + public static Folder CreateFolder(global::System.Guid folderId, global::System.String name, global::System.String description, global::System.DateTime createdDate) + { + Folder folder = new Folder(); + folder.FolderId = folderId; + folder.Name = name; + folder.Description = description; + folder.CreatedDate = createdDate; + return folder; + } + + #endregion + #region Primitive Properties + + /// + /// No Metadata Documentation available. + /// + [EdmScalarPropertyAttribute(EntityKeyProperty=true, IsNullable=false)] + [DataMemberAttribute()] + public global::System.Guid FolderId + { + get + { + return _FolderId; + } + set + { + if (_FolderId != value) + { + OnFolderIdChanging(value); + ReportPropertyChanging("FolderId"); + _FolderId = StructuralObject.SetValidValue(value); + ReportPropertyChanged("FolderId"); + OnFolderIdChanged(); + } + } + } + private global::System.Guid _FolderId; + partial void OnFolderIdChanging(global::System.Guid value); + partial void OnFolderIdChanged(); + + /// + /// No Metadata Documentation available. + /// + [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)] + [DataMemberAttribute()] + public global::System.String Name + { + get + { + return _Name; + } + set + { + OnNameChanging(value); + ReportPropertyChanging("Name"); + _Name = StructuralObject.SetValidValue(value, false); + ReportPropertyChanged("Name"); + OnNameChanged(); + } + } + private global::System.String _Name; + partial void OnNameChanging(global::System.String value); + partial void OnNameChanged(); + + /// + /// No Metadata Documentation available. + /// + [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)] + [DataMemberAttribute()] + public global::System.String Description + { + get + { + return _Description; + } + set + { + OnDescriptionChanging(value); + ReportPropertyChanging("Description"); + _Description = StructuralObject.SetValidValue(value, false); + ReportPropertyChanged("Description"); + OnDescriptionChanged(); + } + } + private global::System.String _Description; + partial void OnDescriptionChanging(global::System.String value); + partial void OnDescriptionChanged(); + + /// + /// No Metadata Documentation available. + /// + [EdmScalarPropertyAttribute(EntityKeyProperty=false, IsNullable=false)] + [DataMemberAttribute()] + public global::System.DateTime CreatedDate + { + get + { + return _CreatedDate; + } + set + { + OnCreatedDateChanging(value); + ReportPropertyChanging("CreatedDate"); + _CreatedDate = StructuralObject.SetValidValue(value); + ReportPropertyChanged("CreatedDate"); + OnCreatedDateChanged(); + } + } + private global::System.DateTime _CreatedDate; + partial void OnCreatedDateChanging(global::System.DateTime value); + partial void OnCreatedDateChanged(); + + #endregion + + #region Navigation Properties + + /// + /// No Metadata Documentation available. + /// + [XmlIgnoreAttribute()] + [SoapIgnoreAttribute()] + [DataMemberAttribute()] + [EdmRelationshipNavigationPropertyAttribute("DataServicesTestDatabaseModel", "FK_File_Folder", "File")] + public EntityCollection Files + { + get + { + return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection("DataServicesTestDatabaseModel.FK_File_Folder", "File"); + } + set + { + if ((value != null)) + { + ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection("DataServicesTestDatabaseModel.FK_File_Folder", "File", value); + } + } + } + + /// + /// No Metadata Documentation available. + /// + [XmlIgnoreAttribute()] + [SoapIgnoreAttribute()] + [DataMemberAttribute()] + [EdmRelationshipNavigationPropertyAttribute("DataServicesTestDatabaseModel", "FK_Folder_Folder", "Folder1")] + public EntityCollection ChildFolders + { + get + { + return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedCollection("DataServicesTestDatabaseModel.FK_Folder_Folder", "Folder1"); + } + set + { + if ((value != null)) + { + ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedCollection("DataServicesTestDatabaseModel.FK_Folder_Folder", "Folder1", value); + } + } + } + + /// + /// No Metadata Documentation available. + /// + [XmlIgnoreAttribute()] + [SoapIgnoreAttribute()] + [DataMemberAttribute()] + [EdmRelationshipNavigationPropertyAttribute("DataServicesTestDatabaseModel", "FK_Folder_Folder", "Folder")] + public Folder ParentFolder + { + get + { + return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference("DataServicesTestDatabaseModel.FK_Folder_Folder", "Folder").Value; + } + set + { + ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference("DataServicesTestDatabaseModel.FK_Folder_Folder", "Folder").Value = value; + } + } + /// + /// No Metadata Documentation available. + /// + [BrowsableAttribute(false)] + [DataMemberAttribute()] + public EntityReference ParentFolderReference + { + get + { + return ((IEntityWithRelationships)this).RelationshipManager.GetRelatedReference("DataServicesTestDatabaseModel.FK_Folder_Folder", "Folder"); + } + set + { + if ((value != null)) + { + ((IEntityWithRelationships)this).RelationshipManager.InitializeRelatedReference("DataServicesTestDatabaseModel.FK_Folder_Folder", "Folder", value); + } + } + } + + #endregion + } + + #endregion + +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/FileSystemEntityModel.edmx b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/FileSystemEntityModel.edmx new file mode 100644 index 0000000..f0a34e3 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/FileSystemEntityModel.edmx @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonArrayAttributeTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonArrayAttributeTests.cs new file mode 100644 index 0000000..61d89b4 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonArrayAttributeTests.cs @@ -0,0 +1,63 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests +{ + public class JsonArrayAttributeTests : TestFixtureBase + { + [Test] + public void IsReferenceTest() + { + JsonPropertyAttribute attribute = new JsonPropertyAttribute(); + Assert.AreEqual(null, attribute._isReference); + Assert.AreEqual(false, attribute.IsReference); + + attribute.IsReference = false; + Assert.AreEqual(false, attribute._isReference); + Assert.AreEqual(false, attribute.IsReference); + + attribute.IsReference = true; + Assert.AreEqual(true, attribute._isReference); + Assert.AreEqual(true, attribute.IsReference); + } + + [Test] + public void NullValueHandlingTest() + { + JsonPropertyAttribute attribute = new JsonPropertyAttribute(); + Assert.AreEqual(null, attribute._nullValueHandling); + Assert.AreEqual(NullValueHandling.Include, attribute.NullValueHandling); + + attribute.NullValueHandling = NullValueHandling.Ignore; + Assert.AreEqual(NullValueHandling.Ignore, attribute._nullValueHandling); + Assert.AreEqual(NullValueHandling.Ignore, attribute.NullValueHandling); + } + + [Test] + public void DefaultValueHandlingTest() + { + JsonPropertyAttribute attribute = new JsonPropertyAttribute(); + Assert.AreEqual(null, attribute._defaultValueHandling); + Assert.AreEqual(DefaultValueHandling.Include, attribute.DefaultValueHandling); + + attribute.DefaultValueHandling = DefaultValueHandling.Ignore; + Assert.AreEqual(DefaultValueHandling.Ignore, attribute._defaultValueHandling); + Assert.AreEqual(DefaultValueHandling.Ignore, attribute.DefaultValueHandling); + } + + [Test] + public void ReferenceLoopHandlingTest() + { + JsonPropertyAttribute attribute = new JsonPropertyAttribute(); + Assert.AreEqual(null, attribute._defaultValueHandling); + Assert.AreEqual(ReferenceLoopHandling.Error, attribute.ReferenceLoopHandling); + + attribute.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + Assert.AreEqual(ReferenceLoopHandling.Ignore, attribute._referenceLoopHandling); + Assert.AreEqual(ReferenceLoopHandling.Ignore, attribute.ReferenceLoopHandling); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs new file mode 100644 index 0000000..73a5a69 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonConvertTest.cs @@ -0,0 +1,342 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using Newtonsoft.Json.Utilities; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests +{ + public class JsonConvertTest : TestFixtureBase + { +#if Entities + [Test] + public void EntitiesTest() + { + Purchase purchase = new Purchase() { Id = 1 }; + purchase.PurchaseLine.Add(new PurchaseLine() { Id = 1, Purchase = purchase }); + purchase.PurchaseLine.Add(new PurchaseLine() { Id = 2, Purchase = purchase }); + + StringWriter sw = new StringWriter(); + JsonSerializer serializer = new JsonSerializer(); + serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + + using (JsonWriter jw = new JsonTextWriter(sw)) + { + jw.Formatting = Formatting.Indented; + + serializer.Serialize(jw, purchase); + } + + string json = sw.ToString(); + + Assert.AreEqual(@"{ + ""Id"": 1, + ""PurchaseLine"": [ + { + ""Id"": 1, + ""PurchaseReference"": { + ""EntityKey"": null, + ""RelationshipName"": ""EntityDataModel.PurchasePurchaseLine"", + ""SourceRoleName"": ""PurchaseLine"", + ""TargetRoleName"": ""Purchase"", + ""RelationshipSet"": null, + ""IsLoaded"": false + }, + ""EntityState"": 1, + ""EntityKey"": null + }, + { + ""Id"": 2, + ""PurchaseReference"": { + ""EntityKey"": null, + ""RelationshipName"": ""EntityDataModel.PurchasePurchaseLine"", + ""SourceRoleName"": ""PurchaseLine"", + ""TargetRoleName"": ""Purchase"", + ""RelationshipSet"": null, + ""IsLoaded"": false + }, + ""EntityState"": 1, + ""EntityKey"": null + } + ], + ""EntityState"": 1, + ""EntityKey"": null +}", json); + + Purchase newPurchase = JsonConvert.DeserializeObject(json); + Assert.AreEqual(1, newPurchase.Id); + + Assert.AreEqual(2, newPurchase.PurchaseLine.Count); + Assert.AreEqual(1, newPurchase.PurchaseLine.ElementAt(0).Id); + Assert.AreEqual(2, newPurchase.PurchaseLine.ElementAt(1).Id); + } +#endif + + [Test] + public void EscapeJavaScriptString() + { + string result; + + result = JavaScriptUtils.ToEscapedJavaScriptString("How now brown cow?", '"', true); + Assert.AreEqual(@"""How now brown cow?""", result); + + result = JavaScriptUtils.ToEscapedJavaScriptString("How now 'brown' cow?", '"', true); + Assert.AreEqual(@"""How now 'brown' cow?""", result); + + result = JavaScriptUtils.ToEscapedJavaScriptString("How now cow?", '"', true); + Assert.AreEqual(@"""How now cow?""", result); + + result = JavaScriptUtils.ToEscapedJavaScriptString(@"How +now brown cow?", '"', true); + Assert.AreEqual(@"""How \r\nnow brown cow?""", result); + + result = JavaScriptUtils.ToEscapedJavaScriptString("\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007", '"', true); + Assert.AreEqual(@"""\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007""", result); + + result = + JavaScriptUtils.ToEscapedJavaScriptString("\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013", '"', true); + Assert.AreEqual(@"""\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013""", result); + + result = + JavaScriptUtils.ToEscapedJavaScriptString( + "\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f ", '"', true); + Assert.AreEqual(@"""\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f """, result); + + result = + JavaScriptUtils.ToEscapedJavaScriptString( + "!\"#$%&\u0027()*+,-./0123456789:;\u003c=\u003e?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]", '"', true); + Assert.AreEqual(@"""!\""#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]""", result); + + result = JavaScriptUtils.ToEscapedJavaScriptString("^_`abcdefghijklmnopqrstuvwxyz{|}~", '"', true); + Assert.AreEqual(@"""^_`abcdefghijklmnopqrstuvwxyz{|}~""", result); + + string data = + "\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\"#$%&\u0027()*+,-./0123456789:;\u003c=\u003e?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + string expected = + @"""\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\b\t\n\u000b\f\r\u000e\u000f\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f !\""#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"""; + + result = JavaScriptUtils.ToEscapedJavaScriptString(data, '"', true); + Assert.AreEqual(expected, result); + + result = JavaScriptUtils.ToEscapedJavaScriptString("Fred's cat.", '\'', true); + Assert.AreEqual(result, @"'Fred\'s cat.'"); + + result = JavaScriptUtils.ToEscapedJavaScriptString(@"""How are you gentlemen?"" said Cats.", '"', true); + Assert.AreEqual(result, @"""\""How are you gentlemen?\"" said Cats."""); + + result = JavaScriptUtils.ToEscapedJavaScriptString(@"""How are' you gentlemen?"" said Cats.", '"', true); + Assert.AreEqual(result, @"""\""How are' you gentlemen?\"" said Cats."""); + + result = JavaScriptUtils.ToEscapedJavaScriptString(@"Fred's ""cat"".", '\'', true); + Assert.AreEqual(result, @"'Fred\'s ""cat"".'"); + + result = JavaScriptUtils.ToEscapedJavaScriptString("\u001farray\u003caddress"); + Assert.AreEqual(result, @"""\u001farray(serialized); + } + + [Test] + public void DeserializeValueObjects() + { + int i = JsonConvert.DeserializeObject("1"); + Assert.AreEqual(1, i); + +#if !PocketPC && !NET20 + DateTimeOffset d = JsonConvert.DeserializeObject(@"""\/Date(-59011455539000+0000)\/"""); + Assert.AreEqual(new DateTimeOffset(new DateTime(100, 1, 1, 1, 1, 1, DateTimeKind.Utc)), d); +#endif + + bool b = JsonConvert.DeserializeObject("true"); + Assert.AreEqual(true, b); + + object n = JsonConvert.DeserializeObject("null"); + Assert.AreEqual(null, n); + + object u = JsonConvert.DeserializeObject("undefined"); + Assert.AreEqual(null, u); + } + + [Test] + public void FloatToString() + { + Assert.AreEqual("1.1", JsonConvert.ToString(1.1)); + Assert.AreEqual("1.11", JsonConvert.ToString(1.11)); + Assert.AreEqual("1.111", JsonConvert.ToString(1.111)); + Assert.AreEqual("1.1111", JsonConvert.ToString(1.1111)); + Assert.AreEqual("1.11111", JsonConvert.ToString(1.11111)); + Assert.AreEqual("1.111111", JsonConvert.ToString(1.111111)); + Assert.AreEqual("1.0", JsonConvert.ToString(1.0)); + Assert.AreEqual("1.0", JsonConvert.ToString(1d)); + Assert.AreEqual("-1.0", JsonConvert.ToString(-1d)); + Assert.AreEqual("1.01", JsonConvert.ToString(1.01)); + Assert.AreEqual("1.001", JsonConvert.ToString(1.001)); + Assert.AreEqual(JsonConvert.PositiveInfinity, JsonConvert.ToString(double.PositiveInfinity)); + Assert.AreEqual(JsonConvert.NegativeInfinity, JsonConvert.ToString(double.NegativeInfinity)); + Assert.AreEqual(JsonConvert.NaN, JsonConvert.ToString(double.NaN)); + } + + [Test] + public void DecimalToString() + { + Assert.AreEqual("1.1", JsonConvert.ToString(1.1m)); + Assert.AreEqual("1.11", JsonConvert.ToString(1.11m)); + Assert.AreEqual("1.111", JsonConvert.ToString(1.111m)); + Assert.AreEqual("1.1111", JsonConvert.ToString(1.1111m)); + Assert.AreEqual("1.11111", JsonConvert.ToString(1.11111m)); + Assert.AreEqual("1.111111", JsonConvert.ToString(1.111111m)); + Assert.AreEqual("1.0", JsonConvert.ToString(1.0m)); + Assert.AreEqual("-1.0", JsonConvert.ToString(-1.0m)); + Assert.AreEqual("-1.0", JsonConvert.ToString(-1m)); + Assert.AreEqual("1.0", JsonConvert.ToString(1m)); + Assert.AreEqual("1.01", JsonConvert.ToString(1.01m)); + Assert.AreEqual("1.001", JsonConvert.ToString(1.001m)); + Assert.AreEqual("79228162514264337593543950335.0", JsonConvert.ToString(decimal.MaxValue)); + Assert.AreEqual("-79228162514264337593543950335.0", JsonConvert.ToString(decimal.MinValue)); + } + + [Test] + public void StringEscaping() + { + string v = @"It's a good day +""sunshine"""; + + string json = JsonConvert.ToString(v); + Assert.AreEqual(@"""It's a good day\r\n\""sunshine\""""", json); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs new file mode 100644 index 0000000..9578af4 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonTextReaderTest.cs @@ -0,0 +1,826 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json; +using System.IO; +using System.Xml; + +namespace Newtonsoft.Json.Tests +{ + public class JsonTextReaderTest : TestFixtureBase + { + [Test] + public void CloseInput() + { + MemoryStream ms = new MemoryStream(); + JsonTextReader reader = new JsonTextReader(new StreamReader(ms)); + + Assert.IsTrue(ms.CanRead); + reader.Close(); + Assert.IsFalse(ms.CanRead); + + ms = new MemoryStream(); + reader = new JsonTextReader(new StreamReader(ms)) { CloseInput = false }; + + Assert.IsTrue(ms.CanRead); + reader.Close(); + Assert.IsTrue(ms.CanRead); + } + + [Test] + public void YahooFinance() + { + string input = @"{ +""matches"" : [ +{""t"":""C"", ""n"":""Citigroup Inc."", ""e"":""NYSE"", ""id"":""662713""} +,{""t"":""CHL"", ""n"":""China Mobile Ltd. (ADR)"", ""e"":""NYSE"", ""id"":""660998""} +,{""t"":""PTR"", ""n"":""PetroChina Company Limited (ADR)"", ""e"":""NYSE"", ""id"":""664536""} +,{""t"":""RIO"", ""n"":""Companhia Vale do Rio Doce (ADR)"", ""e"":""NYSE"", ""id"":""671472""} +,{""t"":""RIOPR"", ""n"":""Companhia Vale do Rio Doce (ADR)"", ""e"":""NYSE"", ""id"":""3512643""} +,{""t"":""CSCO"", ""n"":""Cisco Systems, Inc."", ""e"":""NASDAQ"", ""id"":""99624""} +,{""t"":""CVX"", ""n"":""Chevron Corporation"", ""e"":""NYSE"", ""id"":""667226""} +,{""t"":""TM"", ""n"":""Toyota Motor Corporation (ADR)"", ""e"":""NYSE"", ""id"":""655880""} +,{""t"":""JPM"", ""n"":""JPMorgan Chase \\x26 Co."", ""e"":""NYSE"", ""id"":""665639""} +,{""t"":""COP"", ""n"":""ConocoPhillips"", ""e"":""NYSE"", ""id"":""1691168""} +,{""t"":""LFC"", ""n"":""China Life Insurance Company Ltd. (ADR)"", ""e"":""NYSE"", ""id"":""688679""} +,{""t"":""NOK"", ""n"":""Nokia Corporation (ADR)"", ""e"":""NYSE"", ""id"":""657729""} +,{""t"":""KO"", ""n"":""The Coca-Cola Company"", ""e"":""NYSE"", ""id"":""6550""} +,{""t"":""VZ"", ""n"":""Verizon Communications Inc."", ""e"":""NYSE"", ""id"":""664887""} +,{""t"":""AMX"", ""n"":""America Movil S.A.B de C.V. (ADR)"", ""e"":""NYSE"", ""id"":""665834""}], +""all"" : false +} +"; + + using (JsonReader jsonReader = new JsonTextReader(new StringReader(input))) + { + while (jsonReader.Read()) + { + Console.WriteLine(jsonReader.Value); + } + } + } + + [Test] + public void ReadingIndented() + { + string input = @"{ + CPU: 'Intel', + Drives: [ + 'DVD read/writer', + ""500 gigabyte hard drive"" + ] +}"; + + StringReader sr = new StringReader(input); + + using (JsonTextReader jsonReader = new JsonTextReader(sr)) + { + Assert.AreEqual(jsonReader.TokenType, JsonToken.None); + Assert.AreEqual(0, jsonReader.LineNumber); + Assert.AreEqual(0, jsonReader.LinePosition); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.StartObject); + Assert.AreEqual(1, jsonReader.LineNumber); + Assert.AreEqual(1, jsonReader.LinePosition); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); + Assert.AreEqual(jsonReader.Value, "CPU"); + Assert.AreEqual(2, jsonReader.LineNumber); + Assert.AreEqual(6, jsonReader.LinePosition); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.String); + Assert.AreEqual(jsonReader.Value, "Intel"); + Assert.AreEqual(2, jsonReader.LineNumber); + Assert.AreEqual(14, jsonReader.LinePosition); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); + Assert.AreEqual(jsonReader.Value, "Drives"); + Assert.AreEqual(3, jsonReader.LineNumber); + Assert.AreEqual(9, jsonReader.LinePosition); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.StartArray); + Assert.AreEqual(3, jsonReader.LineNumber); + Assert.AreEqual(11, jsonReader.LinePosition); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.String); + Assert.AreEqual(jsonReader.Value, "DVD read/writer"); + Assert.AreEqual(jsonReader.QuoteChar, '\''); + Assert.AreEqual(4, jsonReader.LineNumber); + Assert.AreEqual(21, jsonReader.LinePosition); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.String); + Assert.AreEqual(jsonReader.Value, "500 gigabyte hard drive"); + Assert.AreEqual(jsonReader.QuoteChar, '"'); + Assert.AreEqual(5, jsonReader.LineNumber); + Assert.AreEqual(29, jsonReader.LinePosition); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.EndArray); + Assert.AreEqual(6, jsonReader.LineNumber); + Assert.AreEqual(3, jsonReader.LinePosition); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.EndObject); + Assert.AreEqual(7, jsonReader.LineNumber); + Assert.AreEqual(1, jsonReader.LinePosition); + + Assert.IsFalse(jsonReader.Read()); + } + } + + [Test] + public void Depth() + { + string input = "{value:'Purple\\r \\n monkey\\'s:\\tdishwasher',array:[1,2]}"; + + StringReader sr = new StringReader(input); + + using (JsonReader jsonReader = new JsonTextReader(sr)) + { + Assert.AreEqual(0, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.StartObject); + Assert.AreEqual(0, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); + Assert.AreEqual(1, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.String); + Assert.AreEqual(jsonReader.Value, "Purple\r \n monkey's:\tdishwasher"); + Assert.AreEqual(jsonReader.QuoteChar, '\''); + Assert.AreEqual(1, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); + Assert.AreEqual(1, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.StartArray); + Assert.AreEqual(2, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.Integer); + Assert.AreEqual(1, jsonReader.Value); + Assert.AreEqual(3, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.Integer); + Assert.AreEqual(2, jsonReader.Value); + Assert.AreEqual(3, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.EndArray); + Assert.AreEqual(1, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.EndObject); + Assert.AreEqual(0, jsonReader.Depth); + } + } + + [Test] + [ExpectedException(typeof(ArgumentNullException), ExpectedMessage = @"Value cannot be null. +Parameter name: reader")] + public void NullTextReader() + { + new JsonTextReader(null); + } + + [Test] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unterminated string. Expected delimiter: '. Line 1, position 3.")] + public void UnexpectedEndOfString() + { + JsonReader reader = new JsonTextReader(new StringReader("'hi")); + reader.Read(); + } + + [Test] + public void ReadNullTerminatorStrings() + { + JsonReader reader = new JsonTextReader(new StringReader("'h\0i'")); + Assert.IsTrue(reader.Read()); + + Assert.AreEqual("h\0i", reader.Value); + } + + [Test] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unexpected end while parsing unicode character. Line 1, position 7.")] + public void UnexpectedEndOfHex() + { + JsonReader reader = new JsonTextReader(new StringReader(@"'h\u006")); + reader.Read(); + } + + [Test] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unterminated string. Expected delimiter: '. Line 1, position 3.")] + public void UnexpectedEndOfControlCharacter() + { + JsonReader reader = new JsonTextReader(new StringReader(@"'h\")); + reader.Read(); + } + + [Test] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unexpected token when reading bytes: Boolean. Line 1, position 4.")] + public void ReadBytesWithBadCharacter() + { + JsonReader reader = new JsonTextReader(new StringReader(@"true")); + reader.ReadAsBytes(); + } + + [Test] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unterminated string. Expected delimiter: '. Line 1, position 17.")] + public void ReadBytesWithUnexpectedEnd() + { + string helloWorld = "Hello world!"; + byte[] helloWorldData = Encoding.UTF8.GetBytes(helloWorld); + + JsonReader reader = new JsonTextReader(new StringReader(@"'" + Convert.ToBase64String(helloWorldData))); + reader.ReadAsBytes(); + } + + [Test] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unexpected end when reading bytes: Line 1, position 3.")] + public void ReadBytesNoStartWithUnexpectedEnd() + { + JsonReader reader = new JsonTextReader(new StringReader(@"[ ")); + Assert.IsTrue(reader.Read()); + reader.ReadAsBytes(); + } + + [Test] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Unexpected end when parsing unquoted property name. Line 1, position 4.")] + public void UnexpectedEndWhenParsingUnquotedProperty() + { + JsonReader reader = new JsonTextReader(new StringReader(@"{aww")); + Assert.IsTrue(reader.Read()); + reader.Read(); + } + + [Test] + public void ParsingQuotedPropertyWithControlCharacters() + { + JsonReader reader = new JsonTextReader(new StringReader(@"{'hi\r\nbye':1}")); + Assert.IsTrue(reader.Read()); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual(@"hi +bye", reader.Value); + Assert.IsTrue(reader.Read()); + Assert.IsTrue(reader.Read()); + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadBytesFollowingNumberInArray() + { + string helloWorld = "Hello world!"; + byte[] helloWorldData = Encoding.UTF8.GetBytes(helloWorld); + + JsonReader reader = new JsonTextReader(new StringReader(@"[1,'" + Convert.ToBase64String(helloWorldData) + @"']")); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + byte[] data = reader.ReadAsBytes(); + Assert.AreEqual(helloWorldData, data); + Assert.AreEqual(JsonToken.Bytes, reader.TokenType); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadBytesFollowingNumberInObject() + { + string helloWorld = "Hello world!"; + byte[] helloWorldData = Encoding.UTF8.GetBytes(helloWorld); + + JsonReader reader = new JsonTextReader(new StringReader(@"{num:1,data:'" + Convert.ToBase64String(helloWorldData) + @"'}")); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + Assert.IsTrue(reader.Read()); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.IsTrue(reader.Read()); + byte[] data = reader.ReadAsBytes(); + Assert.AreEqual(helloWorldData, data); + Assert.AreEqual(JsonToken.Bytes, reader.TokenType); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadingEscapedStrings() + { + string input = "{value:'Purple\\r \\n monkey\\'s:\\tdishwasher'}"; + + StringReader sr = new StringReader(input); + + using (JsonReader jsonReader = new JsonTextReader(sr)) + { + Assert.AreEqual(0, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.StartObject); + Assert.AreEqual(0, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); + Assert.AreEqual(1, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.String); + Assert.AreEqual(jsonReader.Value, "Purple\r \n monkey's:\tdishwasher"); + Assert.AreEqual(jsonReader.QuoteChar, '\''); + Assert.AreEqual(1, jsonReader.Depth); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.EndObject); + Assert.AreEqual(0, jsonReader.Depth); + } + } + + [Test] + public void ReadNewlineLastCharacter() + { + string input = @"{ + CPU: 'Intel', + Drives: [ /* Com*ment */ + 'DVD read/writer', + ""500 gigabyte hard drive"" + ] +}" + '\n'; + + object o = JsonConvert.DeserializeObject(input); + } + + [Test] + public void WriteReadWrite() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteStartArray(); + jsonWriter.WriteValue(true); + + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("integer"); + jsonWriter.WriteValue(99); + jsonWriter.WritePropertyName("string"); + jsonWriter.WriteValue("how now brown cow?"); + jsonWriter.WritePropertyName("array"); + + jsonWriter.WriteStartArray(); + for (int i = 0; i < 5; i++) + { + jsonWriter.WriteValue(i); + } + + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("decimal"); + jsonWriter.WriteValue(990.00990099m); + jsonWriter.WriteEndObject(); + + jsonWriter.WriteValue(5); + jsonWriter.WriteEndArray(); + + jsonWriter.WriteEndObject(); + + jsonWriter.WriteValue("This is a string."); + jsonWriter.WriteNull(); + jsonWriter.WriteNull(); + jsonWriter.WriteEndArray(); + } + + string json = sb.ToString(); + + JsonSerializer serializer = new JsonSerializer(); + + object jsonObject = serializer.Deserialize(new JsonTextReader(new StringReader(json))); + + sb = new StringBuilder(); + sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + serializer.Serialize(sw, jsonObject); + } + + Assert.AreEqual(json, sb.ToString()); + } + + [Test] + public void FloatingPointNonFiniteNumbers() + { + string input = @"[ + NaN, + Infinity, + -Infinity +]"; + + StringReader sr = new StringReader(input); + + using (JsonReader jsonReader = new JsonTextReader(sr)) + { + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.StartArray); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.Float); + Assert.AreEqual(jsonReader.Value, double.NaN); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.Float); + Assert.AreEqual(jsonReader.Value, double.PositiveInfinity); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.Float); + Assert.AreEqual(jsonReader.Value, double.NegativeInfinity); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.EndArray); + } + } + + [Test] + public void LongStringTest() + { + int length = 20000; + string json = @"[""" + new string(' ', length) + @"""]"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + + reader.Read(); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + reader.Read(); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(typeof(string), reader.ValueType); + Assert.AreEqual(20000, reader.Value.ToString().Length); + + reader.Read(); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + reader.Read(); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + } + + [Test] + public void EscapedUnicodeText() + { + string json = @"[""\u003c"",""\u5f20""]"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + + reader.Read(); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + reader.Read(); + Assert.AreEqual("<", reader.Value); + + reader.Read(); + Assert.AreEqual(24352, Convert.ToInt32(Convert.ToChar((string)reader.Value))); + + reader.Read(); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + } + + [Test] + public void ReadFloatingPointNumber() + { + string json = + @"[0.0,0.0,0.1,1.0,1.000001,1E-06,4.94065645841247E-324,Infinity,-Infinity,NaN,1.7976931348623157E+308,-1.7976931348623157E+308,Infinity,-Infinity,NaN]"; + + using (JsonReader jsonReader = new JsonTextReader(new StringReader(json))) + { + jsonReader.Read(); + Assert.AreEqual(JsonToken.StartArray, jsonReader.TokenType); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(0.0, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(0.0, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(0.1, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(1.0, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(1.000001, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(1E-06, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(4.94065645841247E-324, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(double.PositiveInfinity, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(double.NegativeInfinity, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(double.NaN, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(double.MaxValue, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(double.MinValue, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(double.PositiveInfinity, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(double.NegativeInfinity, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Float, jsonReader.TokenType); + Assert.AreEqual(double.NaN, jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.EndArray, jsonReader.TokenType); + } + } + + [Test] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = @"Invalid character after parsing property name. Expected ':' but got: "". Line 3, position 9.")] + public void MissingColon() + { + string json = @"{ + ""A"" : true, + ""B"" ""hello"", // Notice the colon is missing + ""C"" : ""bye"" +}"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + + reader.Read(); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + reader.Read(); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + reader.Read(); + Assert.AreEqual(JsonToken.Boolean, reader.TokenType); + + reader.Read(); + } + + [Test] + public void ReadSingleBytes() + { + StringReader s = new StringReader(@"""SGVsbG8gd29ybGQu"""); + JsonTextReader reader = new JsonTextReader(s); + + byte[] data = reader.ReadAsBytes(); + Assert.IsNotNull(data); + + string text = Encoding.UTF8.GetString(data, 0, data.Length); + Assert.AreEqual("Hello world.", text); + } + + [Test] + public void ReadOctalNumber() + { + StringReader s = new StringReader(@"[0372, 0xFA, 0XFA]"); + JsonTextReader jsonReader = new JsonTextReader(s); + + Assert.IsTrue(jsonReader.Read()); + Assert.AreEqual(JsonToken.StartArray, jsonReader.TokenType); + + Assert.IsTrue(jsonReader.Read()); + Assert.AreEqual(JsonToken.Integer, jsonReader.TokenType); + Assert.AreEqual(250, jsonReader.Value); + + Assert.IsTrue(jsonReader.Read()); + Assert.AreEqual(JsonToken.Integer, jsonReader.TokenType); + Assert.AreEqual(250, jsonReader.Value); + + Assert.IsTrue(jsonReader.Read()); + Assert.AreEqual(JsonToken.Integer, jsonReader.TokenType); + Assert.AreEqual(250, jsonReader.Value); + + Assert.IsTrue(jsonReader.Read()); + Assert.AreEqual(JsonToken.EndArray, jsonReader.TokenType); + + Assert.IsFalse(jsonReader.Read()); + } + + [Test] + public void ReadUnicode() + { + string json = @"{""Message"":""Hi,I\u0092ve send you smth""}"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("Message", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(@"Hi,I" + '\u0092' + "ve send you smth", reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ReadHexidecimalWithAllLetters() + { + string json = @"{""text"":0xabcdef12345}"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual(11806310474565, reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + } + +#if !NET20 + [Test] + public void ReadAsDateTimeOffset() + { + string json = "{\"Offset\":\"\\/Date(946663200000+0600)\\/\"}"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + reader.ReadAsDateTimeOffset(); + Assert.AreEqual(JsonToken.Date, reader.TokenType); + Assert.AreEqual(typeof(DateTimeOffset), reader.ValueType); + Assert.AreEqual(new DateTimeOffset(new DateTime(2000, 1, 1), TimeSpan.FromHours(6)), reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + } + + [Test] + public void ReadAsDateTimeOffsetNegative() + { + string json = @"{""Offset"":""\/Date(946706400000-0600)\/""}"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + reader.ReadAsDateTimeOffset(); + Assert.AreEqual(JsonToken.Date, reader.TokenType); + Assert.AreEqual(typeof(DateTimeOffset), reader.ValueType); + Assert.AreEqual(new DateTimeOffset(new DateTime(2000, 1, 1), TimeSpan.FromHours(-6)), reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + } + + [Test] + public void ReadAsDateTimeOffsetHoursOnly() + { + string json = "{\"Offset\":\"\\/Date(946663200000+06)\\/\"}"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + reader.ReadAsDateTimeOffset(); + Assert.AreEqual(JsonToken.Date, reader.TokenType); + Assert.AreEqual(typeof(DateTimeOffset), reader.ValueType); + Assert.AreEqual(new DateTimeOffset(new DateTime(2000, 1, 1), TimeSpan.FromHours(6)), reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + } + + [Test] + public void ReadAsDateTimeOffsetWithMinutes() + { + string json = @"{""Offset"":""\/Date(946708260000-0631)\/""}"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + reader.ReadAsDateTimeOffset(); + Assert.AreEqual(JsonToken.Date, reader.TokenType); + Assert.AreEqual(typeof(DateTimeOffset), reader.ValueType); + Assert.AreEqual(new DateTimeOffset(new DateTime(2000, 1, 1), TimeSpan.FromHours(-6).Add(TimeSpan.FromMinutes(-31))), reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + } +#endif + + [Test] + public void ReadAsDecimal() + { + string json = @"{""decimal"":-7.92281625142643E+28}"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + decimal? d = reader.ReadAsDecimal(); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(typeof(decimal), reader.ValueType); + Assert.AreEqual(-79228162514264300000000000000m, d); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs new file mode 100644 index 0000000..1165400 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonTextWriterTest.cs @@ -0,0 +1,622 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json; +using System.IO; + +namespace Newtonsoft.Json.Tests +{ + public class JsonTextWriterTest : TestFixtureBase + { + [Test] + public void CloseOutput() + { + MemoryStream ms = new MemoryStream(); + JsonTextWriter writer = new JsonTextWriter(new StreamWriter(ms)); + + Assert.IsTrue(ms.CanRead); + writer.Close(); + Assert.IsFalse(ms.CanRead); + + ms = new MemoryStream(); + writer = new JsonTextWriter(new StreamWriter(ms)) { CloseOutput = false }; + + Assert.IsTrue(ms.CanRead); + writer.Close(); + Assert.IsTrue(ms.CanRead); + } + + [Test] + public void ValueFormatting() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteStartArray(); + jsonWriter.WriteValue('@'); + jsonWriter.WriteValue("\r\n\t\f\b?{\\r\\n\"\'"); + jsonWriter.WriteValue(true); + jsonWriter.WriteValue(10); + jsonWriter.WriteValue(10.99); + jsonWriter.WriteValue(0.99); + jsonWriter.WriteValue(0.000000000000000001d); + jsonWriter.WriteValue(0.000000000000000001m); + jsonWriter.WriteValue((string)null); + jsonWriter.WriteValue((object)null); + jsonWriter.WriteValue("This is a string."); + jsonWriter.WriteNull(); + jsonWriter.WriteUndefined(); + jsonWriter.WriteEndArray(); + } + + string expected = @"[""@"",""\r\n\t\f\b?{\\r\\n\""'"",true,10,10.99,0.99,1E-18,0.000000000000000001,null,null,""This is a string."",null,undefined]"; + string result = sb.ToString(); + + Console.WriteLine("ValueFormatting"); + Console.WriteLine(result); + + Assert.AreEqual(expected, result); + } + + [Test] + public void NullableValueFormatting() + { + StringWriter sw = new StringWriter(); + using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteStartArray(); + jsonWriter.WriteValue((char?)null); + jsonWriter.WriteValue((char?)'c'); + jsonWriter.WriteValue((bool?)null); + jsonWriter.WriteValue((bool?)true); + jsonWriter.WriteValue((byte?)null); + jsonWriter.WriteValue((byte?)1); + jsonWriter.WriteValue((sbyte?)null); + jsonWriter.WriteValue((sbyte?)1); + jsonWriter.WriteValue((short?)null); + jsonWriter.WriteValue((short?)1); + jsonWriter.WriteValue((ushort?)null); + jsonWriter.WriteValue((ushort?)1); + jsonWriter.WriteValue((int?)null); + jsonWriter.WriteValue((int?)1); + jsonWriter.WriteValue((uint?)null); + jsonWriter.WriteValue((uint?)1); + jsonWriter.WriteValue((long?)null); + jsonWriter.WriteValue((long?)1); + jsonWriter.WriteValue((ulong?)null); + jsonWriter.WriteValue((ulong?)1); + jsonWriter.WriteValue((double?)null); + jsonWriter.WriteValue((double?)1.1); + jsonWriter.WriteValue((float?)null); + jsonWriter.WriteValue((float?)1.1); + jsonWriter.WriteValue((decimal?)null); + jsonWriter.WriteValue((decimal?)1.1m); + jsonWriter.WriteValue((DateTime?)null); + jsonWriter.WriteValue((DateTime?)new DateTime(JsonConvert.InitialJavaScriptDateTicks, DateTimeKind.Utc)); +#if !PocketPC && !NET20 + jsonWriter.WriteValue((DateTimeOffset?)null); + jsonWriter.WriteValue((DateTimeOffset?)new DateTimeOffset(JsonConvert.InitialJavaScriptDateTicks, TimeSpan.Zero)); +#endif + jsonWriter.WriteEndArray(); + } + + string json = sw.ToString(); + string expected; + +#if !PocketPC && !NET20 + expected = @"[null,""c"",null,true,null,1,null,1,null,1,null,1,null,1,null,1,null,1,null,1,null,1.1,null,1.1,null,1.1,null,""\/Date(0)\/"",null,""\/Date(0+0000)\/""]"; +#else + expected = @"[null,""c"",null,true,null,1,null,1,null,1,null,1,null,1,null,1,null,1,null,1,null,1.1,null,1.1,null,1.1,null,""\/Date(0)\/""]"; +#endif + + Assert.AreEqual(expected, json); + } + + [Test] + public void WriteValueObjectWithNullable() + { + StringWriter sw = new StringWriter(); + using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) + { + char? value = 'c'; + + jsonWriter.WriteStartArray(); + jsonWriter.WriteValue((object)value); + jsonWriter.WriteEndArray(); + } + + string json = sw.ToString(); + string expected = @"[""c""]"; + + Assert.AreEqual(expected, json); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"Unsupported type: System.Version. Use the JsonSerializer class to get the object's JSON representation.")] + public void WriteValueObjectWithUnsupportedValue() + { + StringWriter sw = new StringWriter(); + using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteStartArray(); + jsonWriter.WriteValue(new Version(1, 1, 1, 1)); + jsonWriter.WriteEndArray(); + } + } + + [Test] + public void StringEscaping() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteStartArray(); + jsonWriter.WriteValue(@"""These pretzels are making me thirsty!"""); + jsonWriter.WriteValue("Jeff's house was burninated."); + jsonWriter.WriteValue(@"1. You don't talk about fight club. +2. You don't talk about fight club."); + jsonWriter.WriteValue("35% of\t statistics\n are made\r up."); + jsonWriter.WriteEndArray(); + } + + string expected = @"[""\""These pretzels are making me thirsty!\"""",""Jeff's house was burninated."",""1. You don't talk about fight club.\r\n2. You don't talk about fight club."",""35% of\t statistics\n are made\r up.""]"; + string result = sb.ToString(); + + Console.WriteLine("StringEscaping"); + Console.WriteLine(result); + + Assert.AreEqual(expected, result); + } + + [Test] + public void Indenting() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = Formatting.Indented; + + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("CPU"); + jsonWriter.WriteValue("Intel"); + jsonWriter.WritePropertyName("PSU"); + jsonWriter.WriteValue("500W"); + jsonWriter.WritePropertyName("Drives"); + jsonWriter.WriteStartArray(); + jsonWriter.WriteValue("DVD read/writer"); + jsonWriter.WriteComment("(broken)"); + jsonWriter.WriteValue("500 gigabyte hard drive"); + jsonWriter.WriteValue("200 gigabype hard drive"); + jsonWriter.WriteEnd(); + jsonWriter.WriteEndObject(); + } + + // { + // "CPU": "Intel", + // "PSU": "500W", + // "Drives": [ + // "DVD read/writer" + // /*(broken)*/, + // "500 gigabyte hard drive", + // "200 gigabype hard drive" + // ] + // } + + string expected = @"{ + ""CPU"": ""Intel"", + ""PSU"": ""500W"", + ""Drives"": [ + ""DVD read/writer"" + /*(broken)*/, + ""500 gigabyte hard drive"", + ""200 gigabype hard drive"" + ] +}"; + string result = sb.ToString(); + + Console.WriteLine("Indenting"); + Console.WriteLine(result); + + Assert.AreEqual(expected, result); + } + + [Test] + public void State() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + Assert.AreEqual(WriteState.Start, jsonWriter.WriteState); + + jsonWriter.WriteStartObject(); + Assert.AreEqual(WriteState.Object, jsonWriter.WriteState); + + jsonWriter.WritePropertyName("CPU"); + Assert.AreEqual(WriteState.Property, jsonWriter.WriteState); + + jsonWriter.WriteValue("Intel"); + Assert.AreEqual(WriteState.Object, jsonWriter.WriteState); + + jsonWriter.WritePropertyName("Drives"); + Assert.AreEqual(WriteState.Property, jsonWriter.WriteState); + + jsonWriter.WriteStartArray(); + Assert.AreEqual(WriteState.Array, jsonWriter.WriteState); + + jsonWriter.WriteValue("DVD read/writer"); + Assert.AreEqual(WriteState.Array, jsonWriter.WriteState); + + jsonWriter.WriteEnd(); + Assert.AreEqual(WriteState.Object, jsonWriter.WriteState); + + jsonWriter.WriteEndObject(); + Assert.AreEqual(WriteState.Start, jsonWriter.WriteState); + } + } + + [Test] + public void FloatingPointNonFiniteNumbers() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = Formatting.Indented; + + jsonWriter.WriteStartArray(); + jsonWriter.WriteValue(double.NaN); + jsonWriter.WriteValue(double.PositiveInfinity); + jsonWriter.WriteValue(double.NegativeInfinity); + jsonWriter.WriteValue(float.NaN); + jsonWriter.WriteValue(float.PositiveInfinity); + jsonWriter.WriteValue(float.NegativeInfinity); + jsonWriter.WriteEndArray(); + + jsonWriter.Flush(); + } + + string expected = @"[ + NaN, + Infinity, + -Infinity, + NaN, + Infinity, + -Infinity +]"; + string result = sb.ToString(); + + Assert.AreEqual(expected, result); + } + + [Test] + public void WriteRawInStart() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = Formatting.Indented; + + jsonWriter.WriteRaw("[1,2,3,4,5]"); + jsonWriter.WriteWhitespace(" "); + jsonWriter.WriteStartArray(); + jsonWriter.WriteValue(double.NaN); + jsonWriter.WriteEndArray(); + } + + string expected = @"[1,2,3,4,5] [ + NaN +]"; + string result = sb.ToString(); + + Assert.AreEqual(expected, result); + } + + [Test] + public void WriteRawInArray() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = Formatting.Indented; + + jsonWriter.WriteStartArray(); + jsonWriter.WriteValue(double.NaN); + jsonWriter.WriteRaw(",[1,2,3,4,5]"); + jsonWriter.WriteRaw(",[1,2,3,4,5]"); + jsonWriter.WriteValue(float.NaN); + jsonWriter.WriteEndArray(); + } + + string expected = @"[ + NaN,[1,2,3,4,5],[1,2,3,4,5], + NaN +]"; + string result = sb.ToString(); + + Assert.AreEqual(expected, result); + } + + [Test] + public void WriteRawInObject() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = Formatting.Indented; + + jsonWriter.WriteStartObject(); + jsonWriter.WriteRaw(@"""PropertyName"":[1,2,3,4,5]"); + jsonWriter.WriteEnd(); + } + + string expected = @"{""PropertyName"":[1,2,3,4,5]}"; + string result = sb.ToString(); + + Assert.AreEqual(expected, result); + } + + [Test] + public void WriteToken() + { + JsonTextReader reader = new JsonTextReader(new StringReader("[1,2,3,4,5]")); + reader.Read(); + reader.Read(); + + StringWriter sw = new StringWriter(); + JsonTextWriter writer = new JsonTextWriter(sw); + writer.WriteToken(reader); + + Assert.AreEqual("1", sw.ToString()); + } + + [Test] + public void WriteRawValue() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + int i = 0; + string rawJson = "[1,2]"; + + jsonWriter.WriteStartObject(); + + while (i < 3) + { + jsonWriter.WritePropertyName("d" + i); + jsonWriter.WriteRawValue(rawJson); + + i++; + } + + jsonWriter.WriteEndObject(); + } + + Assert.AreEqual(@"{""d0"":[1,2],""d1"":[1,2],""d2"":[1,2]}", sb.ToString()); + } + + [Test] + public void WriteObjectNestedInConstructor() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("con"); + + jsonWriter.WriteStartConstructor("Ext.data.JsonStore"); + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("aa"); + jsonWriter.WriteValue("aa"); + jsonWriter.WriteEndObject(); + jsonWriter.WriteEndConstructor(); + + jsonWriter.WriteEndObject(); + } + + Assert.AreEqual(@"{""con"":new Ext.data.JsonStore({""aa"":""aa""})}", sb.ToString()); + } + + [Test] + public void WriteFloatingPointNumber() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteStartArray(); + + jsonWriter.WriteValue(0.0); + jsonWriter.WriteValue(0f); + jsonWriter.WriteValue(0.1); + jsonWriter.WriteValue(1.0); + jsonWriter.WriteValue(1.000001); + jsonWriter.WriteValue(0.000001); + jsonWriter.WriteValue(double.Epsilon); + jsonWriter.WriteValue(double.PositiveInfinity); + jsonWriter.WriteValue(double.NegativeInfinity); + jsonWriter.WriteValue(double.NaN); + jsonWriter.WriteValue(double.MaxValue); + jsonWriter.WriteValue(double.MinValue); + jsonWriter.WriteValue(float.PositiveInfinity); + jsonWriter.WriteValue(float.NegativeInfinity); + jsonWriter.WriteValue(float.NaN); + + jsonWriter.WriteEndArray(); + } + + Assert.AreEqual(@"[0.0,0.0,0.1,1.0,1.000001,1E-06,4.94065645841247E-324,Infinity,-Infinity,NaN,1.7976931348623157E+308,-1.7976931348623157E+308,Infinity,-Infinity,NaN]", sb.ToString()); + } + + [Test] + [ExpectedException(typeof(JsonWriterException), ExpectedMessage = "No token to close.")] + public void BadWriteEndArray() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteStartArray(); + + jsonWriter.WriteValue(0.0); + + jsonWriter.WriteEndArray(); + jsonWriter.WriteEndArray(); + } + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"Invalid JavaScript string quote character. Valid quote characters are ' and "".")] + public void InvalidQuoteChar() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = Formatting.Indented; + jsonWriter.QuoteChar = '*'; + } + } + + [Test] + public void Indentation() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = Formatting.Indented; + Assert.AreEqual(Formatting.Indented, jsonWriter.Formatting); + + jsonWriter.Indentation = 5; + Assert.AreEqual(5, jsonWriter.Indentation); + jsonWriter.IndentChar = '_'; + Assert.AreEqual('_', jsonWriter.IndentChar); + jsonWriter.QuoteName = true; + Assert.AreEqual(true, jsonWriter.QuoteName); + jsonWriter.QuoteChar = '\''; + Assert.AreEqual('\'', jsonWriter.QuoteChar); + + jsonWriter.WriteStartObject(); + jsonWriter.WritePropertyName("propertyName"); + jsonWriter.WriteValue(double.NaN); + jsonWriter.WriteEndObject(); + } + + string expected = @"{ +_____'propertyName': NaN +}"; + string result = sb.ToString(); + + Assert.AreEqual(expected, result); + } + + [Test] + public void WriteSingleBytes() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + string text = "Hello world."; + byte[] data = Encoding.UTF8.GetBytes(text); + + using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = Formatting.Indented; + Assert.AreEqual(Formatting.Indented, jsonWriter.Formatting); + + jsonWriter.WriteValue(data); + } + + string expected = @"""SGVsbG8gd29ybGQu"""; + string result = sb.ToString(); + + Assert.AreEqual(expected, result); + + byte[] d2 = Convert.FromBase64String(result.Trim('"')); + + Assert.AreEqual(text, Encoding.UTF8.GetString(d2, 0, d2.Length)); + } + + [Test] + public void WriteBytesInArray() + { + StringBuilder sb = new StringBuilder(); + StringWriter sw = new StringWriter(sb); + + string text = "Hello world."; + byte[] data = Encoding.UTF8.GetBytes(text); + + using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = Formatting.Indented; + Assert.AreEqual(Formatting.Indented, jsonWriter.Formatting); + + jsonWriter.WriteStartArray(); + jsonWriter.WriteValue(data); + jsonWriter.WriteValue(data); + jsonWriter.WriteValue((object)data); + jsonWriter.WriteValue((byte[])null); + jsonWriter.WriteEndArray(); + } + + string expected = @"[ + ""SGVsbG8gd29ybGQu"", + ""SGVsbG8gd29ybGQu"", + ""SGVsbG8gd29ybGQu"", + null +]"; + string result = sb.ToString(); + + Assert.AreEqual(expected, result); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonValidatingReaderTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonValidatingReaderTests.cs new file mode 100644 index 0000000..c8ce1d9 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/JsonValidatingReaderTests.cs @@ -0,0 +1,1213 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using NUnit.Framework; +using System.Xml; +using System.Xml.Schema; +using Newtonsoft.Json.Schema; + +namespace Newtonsoft.Json.Tests +{ + public class JsonValidatingReaderTests : TestFixtureBase + { + [Test] + public void CheckInnerReader() + { + string json = "{'name':'James','hobbies':['pie','cake']}"; + JsonReader reader = new JsonTextReader(new StringReader(json)); + + JsonValidatingReader validatingReader = new JsonValidatingReader(reader); + Assert.AreEqual(reader, validatingReader.Reader); + } + + [Test] + public void ValidateTypes() + { + string schemaJson = @"{ + ""description"":""A person"", + ""type"":""object"", + ""properties"": + { + ""name"":{""type"":""string""}, + ""hobbies"": + { + ""type"":""array"", + ""items"": {""type"":""string""} + } + } +}"; + + string json = @"{'name':""James"",'hobbies':[""pie"",'cake']}"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + JsonSchema schema = JsonSchema.Parse(schemaJson); + reader.Schema = schema; + Assert.AreEqual(schema, reader.Schema); + + Assert.AreEqual(0, reader.Depth); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("name", reader.Value.ToString()); + + Assert.AreEqual(1, reader.Depth); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("James", reader.Value.ToString()); + Assert.AreEqual(typeof(string), reader.ValueType); + Assert.AreEqual('"', reader.QuoteChar); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("hobbies", reader.Value.ToString()); + Assert.AreEqual('\'', reader.QuoteChar); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("pie", reader.Value.ToString()); + Assert.AreEqual('"', reader.QuoteChar); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("cake", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsNull(validationEventArgs); + } + + [Test] + public void ValidateUnrestrictedArray() + { + string schemaJson = @"{ + ""type"":""array"" +}"; + + string json = "['pie','cake',['nested1','nested2'],{'nestedproperty1':1.1,'nestedproperty2':[null]}]"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("pie", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("cake", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("nested1", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("nested2", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("nestedproperty1", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(1.1, reader.Value); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("nestedproperty2", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Null, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsNull(validationEventArgs); + } + + [Test] + public void StringLessThanMinimumLength() + { + string schemaJson = @"{ + ""type"":""string"", + ""minLength"":5, + ""maxLength"":50, +}"; + + string json = "'pie'"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("String 'pie' is less than minimum length of 5. Line 1, position 5.", validationEventArgs.Message); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void StringGreaterThanMaximumLength() + { + string schemaJson = @"{ + ""type"":""string"", + ""minLength"":5, + ""maxLength"":10 +}"; + + string json = "'The quick brown fox jumps over the lazy dog.'"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("String 'The quick brown fox jumps over the lazy dog.' exceeds maximum length of 10. Line 1, position 46.", validationEventArgs.Message); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void StringIsNotInEnum() + { + string schemaJson = @"{ + ""type"":""array"", + ""items"":{ + ""type"":""string"", + ""enum"":[""one"",""two""] + }, + ""maxItems"":3 +}"; + + string json = "['one','two','THREE']"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(@"Value ""THREE"" is not defined in enum. Line 1, position 20.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void StringDoesNotMatchPattern() + { + string schemaJson = @"{ + ""type"":""string"", + ""pattern"":""foo"" +}"; + + string json = "'The quick brown fox jumps over the lazy dog.'"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("String 'The quick brown fox jumps over the lazy dog.' does not match regex pattern 'foo'. Line 1, position 46.", validationEventArgs.Message); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void IntegerGreaterThanMaximumValue() + { + string schemaJson = @"{ + ""type"":""integer"", + ""maximum"":5 +}"; + + string json = "10"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual("Integer 10 exceeds maximum value of 5. Line 1, position 2.", validationEventArgs.Message); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + [ExpectedException(typeof(JsonSchemaException), ExpectedMessage = "Integer 10 exceeds maximum value of 5. Line 1, position 2.")] + public void ThrowExceptionWhenNoValidationEventHandler() + { + string schemaJson = @"{ + ""type"":""integer"", + ""maximum"":5 +}"; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader("10"))); + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + } + + [Test] + public void IntegerLessThanMinimumValue() + { + string schemaJson = @"{ + ""type"":""integer"", + ""minimum"":5 +}"; + + string json = "1"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual("Integer 1 is less than minimum value of 5. Line 1, position 1.", validationEventArgs.Message); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void IntegerIsNotInEnum() + { + string schemaJson = @"{ + ""type"":""array"", + ""items"":{ + ""type"":""integer"", + ""enum"":[1,2] + }, + ""maxItems"":3 +}"; + + string json = "[1,2,3]"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual(@"Value 3 is not defined in enum. Line 1, position 7.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void FloatGreaterThanMaximumValue() + { + string schemaJson = @"{ + ""type"":""number"", + ""maximum"":5 +}"; + + string json = "10.0"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual("Float 10.0 exceeds maximum value of 5. Line 1, position 4.", validationEventArgs.Message); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void FloatLessThanMinimumValue() + { + string schemaJson = @"{ + ""type"":""number"", + ""minimum"":5 +}"; + + string json = "1.1"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual("Float 1.1 is less than minimum value of 5. Line 1, position 3.", validationEventArgs.Message); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void FloatIsNotInEnum() + { + string schemaJson = @"{ + ""type"":""array"", + ""items"":{ + ""type"":""number"", + ""enum"":[1.1,2.2] + }, + ""maxItems"":3 +}"; + + string json = "[1.1,2.2,3.0]"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(@"Value 3.0 is not defined in enum. Line 1, position 13.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void FloatExceedsMaxDecimalPlaces() + { + string schemaJson = @"{ + ""type"":""array"", + ""items"":{ + ""type"":""number"", + ""divisibleBy"":0.1 + } +}"; + + string json = "[1.1,2.2,4.001]"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(@"Float 4.001 is not evenly divisible by 0.1. Line 1, position 15.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void NullNotInEnum() + { + string schemaJson = @"{ + ""type"":""array"", + ""items"":{ + ""type"":""null"", + ""enum"":[] + }, + ""maxItems"":3 +}"; + + string json = "[null]"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Null, reader.TokenType); + Assert.AreEqual(@"Value null is not defined in enum. Line 1, position 5.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void BooleanNotInEnum() + { + string schemaJson = @"{ + ""type"":""array"", + ""items"":{ + ""type"":""boolean"", + ""enum"":[true] + }, + ""maxItems"":3 +}"; + + string json = "[true,false]"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Boolean, reader.TokenType); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Boolean, reader.TokenType); + Assert.AreEqual(@"Value false is not defined in enum. Line 1, position 11.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void ArrayCountGreaterThanMaximumItems() + { + string schemaJson = @"{ + ""type"":""array"", + ""minItems"":2, + ""maxItems"":3 +}"; + + string json = "[null,null,null,null]"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Null, reader.TokenType); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Null, reader.TokenType); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Null, reader.TokenType); + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Null, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + Assert.AreEqual("Array item count 4 exceeds maximum count of 3. Line 1, position 21.", validationEventArgs.Message); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void ArrayCountLessThanMinimumItems() + { + string schemaJson = @"{ + ""type"":""array"", + ""minItems"":2, + ""maxItems"":3 +}"; + + string json = "[null]"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Null, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + Assert.AreEqual("Array item count 1 is less than minimum count of 2. Line 1, position 6.", validationEventArgs.Message); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void InvalidDataType() + { + string schemaJson = @"{ + ""type"":""string"", + ""minItems"":2, + ""maxItems"":3, + ""items"":{} +}"; + + string json = "[null,null,null,null]"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + Assert.AreEqual(@"Invalid type. Expected String but got Array. Line 1, position 1.", validationEventArgs.Message); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void StringDisallowed() + { + string schemaJson = @"{ + ""type"":""array"", + ""items"":{ + ""disallow"":[""number""] + }, + ""maxItems"":3 +}"; + + string json = "['pie',1.1]"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Float, reader.TokenType); + Assert.AreEqual(@"Type Float is disallowed. Line 1, position 11.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void MissingRequiredProperties() + { + string schemaJson = @"{ + ""description"":""A person"", + ""type"":""object"", + ""properties"": + { + ""name"":{""type"":""string""}, + ""hobbies"":{""type"":""string"",""required"":true}, + ""age"":{""type"":""integer"",""required"":true} + } +}"; + + string json = "{'name':'James'}"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("name", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("James", reader.Value.ToString()); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + Assert.AreEqual("Required properties are missing from object: hobbies, age. Line 1, position 16.", validationEventArgs.Message); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void MissingNonRequiredProperties() + { + string schemaJson = @"{ + ""description"":""A person"", + ""type"":""object"", + ""properties"": + { + ""name"":{""type"":""string"",""required"":true}, + ""hobbies"":{""type"":""string"",""required"":false}, + ""age"":{""type"":""integer""} + } +}"; + + string json = "{'name':'James'}"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("name", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("James", reader.Value.ToString()); + Assert.IsNull(validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsNull(validationEventArgs); + } + + [Test] + public void DisableAdditionalProperties() + { + string schemaJson = @"{ + ""description"":""A person"", + ""type"":""object"", + ""properties"": + { + ""name"":{""type"":""string""} + }, + ""additionalProperties"":false +}"; + + string json = "{'name':'James','additionalProperty1':null,'additionalProperty2':null}"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("name", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("James", reader.Value.ToString()); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("additionalProperty1", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Null, reader.TokenType); + Assert.AreEqual(null, reader.Value); + Assert.AreEqual("Property 'additionalProperty1' has not been defined and the schema does not allow additional properties. Line 1, position 38.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("additionalProperty2", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Null, reader.TokenType); + Assert.AreEqual(null, reader.Value); + Assert.AreEqual("Property 'additionalProperty2' has not been defined and the schema does not allow additional properties. Line 1, position 65.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsNotNull(validationEventArgs); + } + + [Test] + public void ExtendsStringGreaterThanMaximumLength() + { + string schemaJson = @"{ + ""extends"":{ + ""type"":""string"", + ""minLength"":5, + ""maxLength"":10 + }, + ""maxLength"":9 +}"; + + List errors = new List(); + string json = "'The quick brown fox jumps over the lazy dog.'"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; errors.Add(validationEventArgs.Message); }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual(1, errors.Count); + Assert.AreEqual("String 'The quick brown fox jumps over the lazy dog.' exceeds maximum length of 9. Line 1, position 46.", errors[0]); + + Assert.IsNotNull(validationEventArgs); + } + + private JsonSchema GetExtendedSchema() + { + string first = @"{ + ""id"":""first"", + ""type"":""object"", + ""properties"": + { + ""firstproperty"":{""type"":""string"",""required"":true} + }, + ""additionalProperties"":{} +}"; + + string second = @"{ + ""id"":""second"", + ""type"":""object"", + ""extends"":{""$ref"":""first""}, + ""properties"": + { + ""secondproperty"":{""type"":""string"",""required"":true} + }, + ""additionalProperties"":false +}"; + + JsonSchemaResolver resolver = new JsonSchemaResolver(); + JsonSchema firstSchema = JsonSchema.Parse(first, resolver); + JsonSchema secondSchema = JsonSchema.Parse(second, resolver); + + return secondSchema; + } + + [Test] + public void ExtendsDisallowAdditionProperties() + { + string json = "{'firstproperty':'blah','secondproperty':'blah2','additional':'blah3','additional2':'blah4'}"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = GetExtendedSchema(); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("firstproperty", reader.Value.ToString()); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("blah", reader.Value.ToString()); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("secondproperty", reader.Value.ToString()); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("blah2", reader.Value.ToString()); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("additional", reader.Value.ToString()); + Assert.AreEqual("Property 'additional' has not been defined and the schema does not allow additional properties. Line 1, position 62.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("blah3", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("additional2", reader.Value.ToString()); + Assert.AreEqual("Property 'additional2' has not been defined and the schema does not allow additional properties. Line 1, position 84.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("blah4", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ExtendsMissingRequiredProperties() + { + string json = "{}"; + + List errors = new List(); + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { errors.Add(args.Message); }; + reader.Schema = GetExtendedSchema(); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.AreEqual(1, errors.Count); + Assert.AreEqual("Required properties are missing from object: secondproperty, firstproperty. Line 1, position 2.", errors[0]); + } + + [Test] + public void NoAdditionalProperties() + { + string schemaJson = @"{ + ""type"":""array"", + ""items"": [{""type"":""string""},{""type"":""integer""}], + ""additionalProperties"": false +}"; + + string json = @"[1, 'a', null]"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartArray, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual("Invalid type. Expected String but got Integer. Line 1, position 3.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("Invalid type. Expected Integer but got String. Line 1, position 7.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Null, reader.TokenType); + Assert.AreEqual("Index 3 has not been defined and the schema does not allow additional items. Line 1, position 13.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndArray, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void PatternPropertiesNoAdditionalProperties() + { + string schemaJson = @"{ + ""type"":""object"", + ""patternProperties"": { + ""hi"": {""type"":""string""}, + ""ho"": {""type"":""string""} + }, + ""additionalProperties"": false +}"; + + string json = @"{ + ""hi"": ""A string!"", + ""hide"": ""A string!"", + ""ho"": 1, + ""hey"": ""A string!"" +}"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; }; + reader.Schema = JsonSchema.Parse(schemaJson); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Integer, reader.TokenType); + Assert.AreEqual("Invalid type. Expected String but got Integer. Line 4, position 10.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("Property 'hey' has not been defined and the schema does not allow additional properties. Line 5, position 8.", validationEventArgs.Message); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void ExtendedComplex() + { + string first = @"{ + ""id"":""first"", + ""type"":""object"", + ""properties"": + { + ""firstproperty"":{""type"":""string""}, + ""secondproperty"":{""type"":""string"",""maxLength"":10}, + ""thirdproperty"":{ + ""type"":""object"", + ""properties"": + { + ""thirdproperty_firstproperty"":{""type"":""string"",""maxLength"":10,""minLength"":7} + } + } + }, + ""additionalProperties"":{} +}"; + + string second = @"{ + ""id"":""second"", + ""type"":""object"", + ""extends"":{""$ref"":""first""}, + ""properties"": + { + ""secondproperty"":{""type"":""any""}, + ""thirdproperty"":{ + ""extends"":{ + ""properties"": + { + ""thirdproperty_firstproperty"":{""maxLength"":9,""minLength"":6,""pattern"":""hi2u""} + }, + ""additionalProperties"":{""maxLength"":9,""minLength"":6,""enum"":[""one"",""two""]} + }, + ""type"":""object"", + ""properties"": + { + ""thirdproperty_firstproperty"":{""pattern"":""hi""} + }, + ""additionalProperties"":{""type"":""string"",""enum"":[""two"",""three""]} + }, + ""fourthproperty"":{""type"":""string""} + }, + ""additionalProperties"":false +}"; + + JsonSchemaResolver resolver = new JsonSchemaResolver(); + JsonSchema firstSchema = JsonSchema.Parse(first, resolver); + JsonSchema secondSchema = JsonSchema.Parse(second, resolver); + + JsonSchemaModelBuilder modelBuilder = new JsonSchemaModelBuilder(); + + string json = @"{ + 'firstproperty':'blahblahblahblahblahblah', + 'secondproperty':'secasecasecasecaseca', + 'thirdproperty':{ + 'thirdproperty_firstproperty':'aaa', + 'additional':'three' + } +}"; + + Json.Schema.ValidationEventArgs validationEventArgs = null; + List errors = new List(); + + JsonValidatingReader reader = new JsonValidatingReader(new JsonTextReader(new StringReader(json))); + reader.ValidationEventHandler += (sender, args) => { validationEventArgs = args; errors.Add(validationEventArgs.Message); }; + reader.Schema = secondSchema; + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("firstproperty", reader.Value.ToString()); + Assert.AreEqual(null, validationEventArgs); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("blahblahblahblahblahblah", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("secondproperty", reader.Value.ToString()); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("secasecasecasecaseca", reader.Value.ToString()); + Assert.AreEqual(1, errors.Count); + Assert.AreEqual("String 'secasecasecasecaseca' exceeds maximum length of 10. Line 3, position 41.", errors[0]); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("thirdproperty", reader.Value.ToString()); + Assert.AreEqual(1, errors.Count); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + Assert.AreEqual(1, errors.Count); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("thirdproperty_firstproperty", reader.Value.ToString()); + Assert.AreEqual(1, errors.Count); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("aaa", reader.Value.ToString()); + Assert.AreEqual(4, errors.Count); + Assert.AreEqual("String 'aaa' is less than minimum length of 7. Line 5, position 39.", errors[1]); + Assert.AreEqual("String 'aaa' does not match regex pattern 'hi'. Line 5, position 39.", errors[2]); + Assert.AreEqual("String 'aaa' does not match regex pattern 'hi2u'. Line 5, position 39.", errors[3]); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + Assert.AreEqual("additional", reader.Value.ToString()); + Assert.AreEqual(4, errors.Count); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + Assert.AreEqual("three", reader.Value.ToString()); + Assert.AreEqual(5, errors.Count); + Assert.AreEqual("String 'three' is less than minimum length of 6. Line 6, position 24.", errors[4]); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/BindingTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/BindingTests.cs new file mode 100644 index 0000000..c7adbef --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/BindingTests.cs @@ -0,0 +1,78 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC && !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using System.Web.UI; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Linq.ComponentModel +{ + public class BindingTests : TestFixtureBase + { + [Test] + public void DataBinderEval() + { + JObject o = new JObject( + new JProperty("First", "String!"), + new JProperty("Second", 12345.6789m), + new JProperty("Third", new JArray( + 1, + 2, + 3, + 4, + 5, + new JObject( + new JProperty("Fourth", "String!"), + new JProperty("Fifth", new JObject( + new JProperty("Sixth", "String!"))))))); + + object value; + + value = (string)DataBinder.Eval(o, "First.Value"); + Assert.AreEqual(value, (string)o["First"]); + + value = DataBinder.Eval(o, "Second.Value"); + Assert.AreEqual(value, (decimal)o["Second"]); + + value = DataBinder.Eval(o, "Third"); + Assert.AreEqual(value, o["Third"]); + + value = DataBinder.Eval(o, "Third[0].Value"); + Assert.AreEqual((int)value, (int)o["Third"][0]); + + value = DataBinder.Eval(o, "Third[5].Fourth.Value"); + Assert.AreEqual(value, (string)o["Third"][5]["Fourth"]); + + value = DataBinder.Eval(o, "Third[5].Fifth.Sixth.Value"); + Assert.AreEqual(value, (string)o["Third"][5]["Fifth"]["Sixth"]); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs new file mode 100644 index 0000000..b8ee7ae --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/ComponentModel/JPropertyDescriptorTests.cs @@ -0,0 +1,91 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Linq.ComponentModel; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Linq.ComponentModel +{ + public class JPropertyDescriptorTests : TestFixtureBase + { + [Test] + public void GetValue() + { + JObject o = JObject.Parse("{prop1:'12345!',prop2:[1,'two','III']}"); + + JPropertyDescriptor prop1 = new JPropertyDescriptor("prop1", typeof(string)); + JPropertyDescriptor prop2 = new JPropertyDescriptor("prop2", typeof(JArray)); + + Assert.AreEqual("12345!", ((JValue) prop1.GetValue(o)).Value); + Assert.AreEqual(o["prop2"], prop2.GetValue(o)); + } + + [Test] + public void SetValue() + { + JObject o = JObject.Parse("{prop1:'12345!'}"); + + JPropertyDescriptor propertyDescriptor1 = new JPropertyDescriptor("prop1", typeof(string)); + + propertyDescriptor1.SetValue(o, "54321!"); + + Assert.AreEqual("54321!", (string)o["prop1"]); + } + + [Test] + public void ResetValue() + { + JObject o = JObject.Parse("{prop1:'12345!'}"); + + JPropertyDescriptor propertyDescriptor1 = new JPropertyDescriptor("prop1", typeof(string)); + propertyDescriptor1.ResetValue(o); + + Assert.AreEqual("12345!", (string)o["prop1"]); + } + + [Test] + public void IsReadOnly() + { + JPropertyDescriptor propertyDescriptor1 = new JPropertyDescriptor("prop1", typeof(string)); + + Assert.AreEqual(false, propertyDescriptor1.IsReadOnly); + } + + [Test] + public void PropertyType() + { + JPropertyDescriptor propertyDescriptor1 = new JPropertyDescriptor("prop1", typeof(string)); + + Assert.AreEqual(typeof(string), propertyDescriptor1.PropertyType); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs new file mode 100644 index 0000000..32f9df1 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/DynamicTests.cs @@ -0,0 +1,662 @@ +#if !(NET35 || NET20) +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Linq; +using NUnit.Framework; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class DynamicTests : TestFixtureBase + { + [Test] + public void JObjectPropertyNames() + { + JObject o = new JObject( + new JProperty("ChildValue", "blah blah")); + + dynamic d = o; + + d.First = "A value!"; + + Assert.AreEqual(new JValue("A value!"), d.First); + Assert.AreEqual("A value!", (string)d.First); + + d.First = null; + Assert.AreEqual(JTokenType.Null, d.First.Type); + + Assert.IsTrue(d.Remove("First")); + Assert.IsNull(d.First); + + JValue v1 = d.ChildValue; + JValue v2 = d["ChildValue"]; + Assert.AreEqual(v1, v2); + + JValue newValue1 = new JValue("Blah blah"); + d.NewValue = newValue1; + JValue newValue2 = d.NewValue; + + Assert.IsTrue(ReferenceEquals(newValue1, newValue2)); + } + + [Test] + public void JObjectEnumerator() + { + JObject o = new JObject( + new JProperty("ChildValue", "blah blah")); + + dynamic d = o; + + foreach (JProperty value in d) + { + Assert.AreEqual("ChildValue", value.Name); + Assert.AreEqual("blah blah", (string)value.Value); + } + + foreach (dynamic value in d) + { + Assert.AreEqual("ChildValue", value.Name); + Assert.AreEqual("blah blah", (string)value.Value); + } + } + + [Test] + public void JObjectPropertyNameWithJArray() + { + JObject o = new JObject( + new JProperty("ChildValue", "blah blah")); + + dynamic d = o; + + d.First = new JArray(); + d.First.Add("Hi"); + + Assert.AreEqual(1, d.First.Count); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Could not determine JSON object type for type System.String[].")] + public void JObjectPropertyNameWithNonToken() + { + dynamic d = new JObject(); + + d.First = new [] {"One", "II", "3"}; + } + + [Test] + public void JObjectMethods() + { + JObject o = new JObject( + new JProperty("ChildValue", "blah blah")); + + dynamic d = o; + + d.Add("NewValue", 1); + + object count = d.Count; + + Assert.IsNull(count); + Assert.IsNull(d["Count"]); + + JToken v; + Assert.IsTrue(d.TryGetValue("ChildValue", out v)); + Assert.AreEqual("blah blah", (string)v); + } + + [Test] + public void JValueEquals() + { + JObject o = new JObject( + new JProperty("Null", new JValue(null, JTokenType.Null)), + new JProperty("Integer", new JValue(1)), + new JProperty("Float", new JValue(1.1d)), + new JProperty("Decimal", new JValue(1.1m)), + new JProperty("DateTime", new JValue(new DateTime(2000, 12, 29, 23, 51, 10, DateTimeKind.Utc))), + new JProperty("Boolean", new JValue(true)), + new JProperty("String", new JValue("A string lol!")), + new JProperty("Bytes", new JValue(Encoding.UTF8.GetBytes("A string lol!"))) + ); + + dynamic d = o; + + Assert.IsTrue(d.Null == d.Null); + Assert.IsTrue(d.Null == null); + Assert.IsTrue(d.Null == new JValue(null, JTokenType.Null)); + Assert.IsFalse(d.Null == 1); + + Assert.IsTrue(d.Integer == d.Integer); + Assert.IsTrue(d.Integer > 0); + Assert.IsTrue(d.Integer > 0.0m); + Assert.IsTrue(d.Integer > 0.0f); + Assert.IsTrue(d.Integer > null); + Assert.IsTrue(d.Integer >= null); + Assert.IsTrue(d.Integer == 1); + Assert.IsTrue(d.Integer == 1m); + Assert.IsTrue(d.Integer != 1.1f); + Assert.IsTrue(d.Integer != 1.1d); + + Assert.IsTrue(d.Decimal == d.Decimal); + Assert.IsTrue(d.Decimal > 0); + Assert.IsTrue(d.Decimal > 0.0m); + Assert.IsTrue(d.Decimal > 0.0f); + Assert.IsTrue(d.Decimal > null); + Assert.IsTrue(d.Decimal >= null); + Assert.IsTrue(d.Decimal == 1.1); + Assert.IsTrue(d.Decimal == 1.1m); + Assert.IsTrue(d.Decimal != 1.0f); + Assert.IsTrue(d.Decimal != 1.0d); + + Assert.IsTrue(d.Float == d.Float); + Assert.IsTrue(d.Float > 0); + Assert.IsTrue(d.Float > 0.0m); + Assert.IsTrue(d.Float > 0.0f); + Assert.IsTrue(d.Float > null); + Assert.IsTrue(d.Float >= null); + Assert.IsTrue(d.Float < 2); + Assert.IsTrue(d.Float <= 1.1); + Assert.IsTrue(d.Float == 1.1); + Assert.IsTrue(d.Float == 1.1m); + Assert.IsTrue(d.Float != 1.0f); + Assert.IsTrue(d.Float != 1.0d); + + Assert.IsTrue(d.Bytes == d.Bytes); + Assert.IsTrue(d.Bytes == Encoding.UTF8.GetBytes("A string lol!")); + Assert.IsTrue(d.Bytes == new JValue(Encoding.UTF8.GetBytes("A string lol!"))); + } + + [Test] + public void JValueAddition() + { + JObject o = new JObject( + new JProperty("Null", new JValue(null, JTokenType.Null)), + new JProperty("Integer", new JValue(1)), + new JProperty("Float", new JValue(1.1d)), + new JProperty("Decimal", new JValue(1.1m)), + new JProperty("DateTime", new JValue(new DateTime(2000, 12, 29, 23, 51, 10, DateTimeKind.Utc))), + new JProperty("Boolean", new JValue(true)), + new JProperty("String", new JValue("A string lol!")), + new JProperty("Bytes", new JValue(Encoding.UTF8.GetBytes("A string lol!"))) + ); + + dynamic d = o; + dynamic r; + + #region Add + r = d.String + " LAMO!"; + Assert.AreEqual("A string lol! LAMO!", (string)r); + r += " gg"; + Assert.AreEqual("A string lol! LAMO! gg", (string)r); + + r = d.String + null; + Assert.AreEqual("A string lol!", (string)r); + r += null; + Assert.AreEqual("A string lol!", (string)r); + + r = d.Integer + 1; + Assert.AreEqual(2, (int)r); + r += 2; + Assert.AreEqual(4, (int)r); + + r = d.Integer + 1.1; + Assert.AreEqual(2.1, (double)r); + r += 2; + Assert.AreEqual(4.1, (double)r); + + r = d.Integer + 1.1d; + Assert.AreEqual(2.1, (decimal)r); + r += 2; + Assert.AreEqual(4.1, (decimal)r); + + r = d.Integer + null; + Assert.AreEqual(null, r.Value); + r += 2; + Assert.AreEqual(null, r.Value); + + r = d.Float + 1; + Assert.AreEqual(2.1, (double)r); + r += 2; + Assert.AreEqual(4.1, (double)r); + + r = d.Float + 1.1; + Assert.AreEqual(2.2, (double)r); + r += 2; + Assert.AreEqual(4.2, (double)r); + + r = d.Float + 1.1d; + Assert.AreEqual(2.2, (decimal)r); + r += 2; + Assert.AreEqual(4.2, (decimal)r); + + r = d.Float + null; + Assert.AreEqual(null, r.Value); + r += 2; + Assert.AreEqual(null, r.Value); + + r = d.Decimal + 1; + Assert.AreEqual(2.1, (decimal)r); + r += 2; + Assert.AreEqual(4.1, (decimal)r); + + r = d.Decimal + 1.1; + Assert.AreEqual(2.2, (decimal)r); + r += 2; + Assert.AreEqual(4.2, (decimal)r); + + r = d.Decimal + 1.1d; + Assert.AreEqual(2.2, (decimal)r); + r += 2; + Assert.AreEqual(4.2, (decimal)r); + + r = d.Decimal + null; + Assert.AreEqual(null, r.Value); + r += 2; + Assert.AreEqual(null, r.Value); + #endregion + + #region Subtract + r = d.Integer - 1; + Assert.AreEqual(0, (int)r); + r -= 2; + Assert.AreEqual(-2, (int)r); + + r = d.Integer - 1.1; + Assert.AreEqual(-0.1, (double)r, 0.00001); + r -= 2; + Assert.AreEqual(-2.1, (double)r); + + r = d.Integer - 1.1d; + Assert.AreEqual(-0.1, (decimal)r); + r -= 2; + Assert.AreEqual(-2.1, (decimal)r); + + r = d.Integer - null; + Assert.AreEqual(null, r.Value); + r -= 2; + Assert.AreEqual(null, r.Value); + + r = d.Float - 1; + Assert.AreEqual(0.1, (double)r, 0.00001); + r -= 2; + Assert.AreEqual(-1.9, (double)r); + + r = d.Float - 1.1; + Assert.AreEqual(0, (double)r); + r -= 2; + Assert.AreEqual(-2, (double)r); + + r = d.Float - 1.1d; + Assert.AreEqual(0, (decimal)r); + r -= 2; + Assert.AreEqual(-2, (decimal)r); + + r = d.Float - null; + Assert.AreEqual(null, r.Value); + r -= 2; + Assert.AreEqual(null, r.Value); + + r = d.Decimal - 1; + Assert.AreEqual(0.1, (decimal)r); + r -= 2; + Assert.AreEqual(-1.9, (decimal)r); + + r = d.Decimal - 1.1; + Assert.AreEqual(0, (decimal)r); + r -= 2; + Assert.AreEqual(-2, (decimal)r); + + r = d.Decimal - 1.1d; + Assert.AreEqual(0, (decimal)r); + r -= 2; + Assert.AreEqual(-2, (decimal)r); + + r = d.Decimal - null; + Assert.AreEqual(null, r.Value); + r -= 2; + Assert.AreEqual(null, r.Value); + #endregion + + #region Multiply + r = d.Integer * 1; + Assert.AreEqual(1, (int)r); + r *= 2; + Assert.AreEqual(2, (int)r); + + r = d.Integer * 1.1; + Assert.AreEqual(1.1, (double)r); + r *= 2; + Assert.AreEqual(2.2, (double)r); + + r = d.Integer * 1.1d; + Assert.AreEqual(1.1, (decimal)r); + r *= 2; + Assert.AreEqual(2.2, (decimal)r); + + r = d.Integer * null; + Assert.AreEqual(null, r.Value); + r *= 2; + Assert.AreEqual(null, r.Value); + + r = d.Float * 1; + Assert.AreEqual(1.1, (double)r); + r *= 2; + Assert.AreEqual(2.2, (double)r); + + r = d.Float * 1.1; + Assert.AreEqual(1.21, (double)r, 0.00001); + r *= 2; + Assert.AreEqual(2.42, (double)r, 0.00001); + + r = d.Float * 1.1d; + Assert.AreEqual(1.21, (decimal)r); + r *= 2; + Assert.AreEqual(2.42, (decimal)r); + + r = d.Float * null; + Assert.AreEqual(null, r.Value); + r *= 2; + Assert.AreEqual(null, r.Value); + + r = d.Decimal * 1; + Assert.AreEqual(1.1, (decimal)r); + r *= 2; + Assert.AreEqual(2.2, (decimal)r); + + r = d.Decimal * 1.1; + Assert.AreEqual(1.21, (decimal)r); + r *= 2; + Assert.AreEqual(2.42, (decimal)r); + + r = d.Decimal * 1.1d; + Assert.AreEqual(1.21, (decimal)r); + r *= 2; + Assert.AreEqual(2.42, (decimal)r); + + r = d.Decimal * null; + Assert.AreEqual(null, r.Value); + r *= 2; + Assert.AreEqual(null, r.Value); + #endregion + + #region Divide + r = d.Integer / 1; + Assert.AreEqual(1, (int)r); + r /= 2; + Assert.AreEqual(0, (int)r); + + r = d.Integer / 1.1; + Assert.AreEqual(0.9090909090909091, (double)r); + r /= 2; + Assert.AreEqual(0.454545454545455, (double)r, 0.00001); + + r = d.Integer / 1.1d; + Assert.AreEqual(0.909090909090909m, (decimal)r); + r /= 2; + Assert.AreEqual(0.454545454545454m, (decimal)r); + + r = d.Integer / null; + Assert.AreEqual(null, r.Value); + r /= 2; + Assert.AreEqual(null, r.Value); + + r = d.Float / 1; + Assert.AreEqual(1.1, (double)r); + r /= 2; + Assert.AreEqual(0.55, (double)r); + + r = d.Float / 1.1; + Assert.AreEqual(1, (double)r, 0.00001); + r /= 2; + Assert.AreEqual(0.5, (double)r, 0.00001); + + r = d.Float / 1.1d; + Assert.AreEqual(1m, (decimal)r); + r /= 2; + Assert.AreEqual(0.5m, (decimal)r); + + r = d.Float / null; + Assert.AreEqual(null, r.Value); + r /= 2; + Assert.AreEqual(null, r.Value); + + r = d.Decimal / 1; + Assert.AreEqual(1.1d, (decimal)r); + r /= 2; + Assert.AreEqual(0.55d, (decimal)r); + + r = d.Decimal / 1.1; + Assert.AreEqual(1d, (decimal)r); + r /= 2; + Assert.AreEqual(0.5d, (decimal)r); + + r = d.Decimal / 1.1d; + Assert.AreEqual(1d, (decimal)r); + r /= 2; + Assert.AreEqual(0.5d, (decimal)r); + + r = d.Decimal / null; + Assert.AreEqual(null, r.Value); + r /= 2; + Assert.AreEqual(null, r.Value); + #endregion + } + + [Test] + public void JValueToString() + { + JObject o = new JObject( + new JProperty("Null", new JValue(null, JTokenType.Null)), + new JProperty("Integer", new JValue(1)), + new JProperty("Float", new JValue(1.1)), + new JProperty("DateTime", new JValue(new DateTime(2000, 12, 29, 23, 51, 10, DateTimeKind.Utc))), + new JProperty("Boolean", new JValue(true)), + new JProperty("String", new JValue("A string lol!")), + new JProperty("Bytes", new JValue(Encoding.UTF8.GetBytes("A string lol!"))) + ); + + dynamic d = o; + + Assert.AreEqual("", d.Null.ToString()); + Assert.AreEqual("1", d.Integer.ToString()); + Assert.AreEqual("1.1", d.Float.ToString(CultureInfo.InvariantCulture)); + Assert.AreEqual("12/29/2000 23:51:10", d.DateTime.ToString(null, CultureInfo.InvariantCulture)); + Assert.AreEqual("True", d.Boolean.ToString()); + Assert.AreEqual("A string lol!", d.String.ToString()); + Assert.AreEqual("System.Byte[]", d.Bytes.ToString()); + } + + [Test] + public void JObjectGetDynamicPropertyNames() + { + JObject o = new JObject( + new JProperty("ChildValue", "blah blah"), + new JProperty("Hello Joe", null)); + + dynamic d = o; + + List memberNames = o.GetDynamicMemberNames().ToList(); + + Assert.AreEqual(2, memberNames.Count); + Assert.AreEqual("ChildValue", memberNames[0]); + Assert.AreEqual("Hello Joe", memberNames[1]); + + o = new JObject( + new JProperty("ChildValue1", "blah blah"), + new JProperty("Hello Joe1", null)); + + d = o; + + memberNames = o.GetDynamicMemberNames().ToList(); + + Assert.AreEqual(2, memberNames.Count); + Assert.AreEqual("ChildValue1", memberNames[0]); + Assert.AreEqual("Hello Joe1", memberNames[1]); + } + + [Test] + public void JValueConvert() + { + AssertValueConverted(true); + AssertValueConverted(true); + AssertValueConverted(false); + AssertValueConverted(null); + AssertValueConverted("true", true); + AssertValueConverted(null); + AssertValueConverted(Encoding.UTF8.GetBytes("blah")); + AssertValueConverted(new DateTime(2000, 12, 20, 23, 59, 2, DateTimeKind.Utc)); + AssertValueConverted(new DateTime(2000, 12, 20, 23, 59, 2, DateTimeKind.Utc)); + AssertValueConverted(null); + AssertValueConverted(new DateTimeOffset(2000, 12, 20, 23, 59, 2, TimeSpan.FromHours(1))); + AssertValueConverted(new DateTimeOffset(2000, 12, 20, 23, 59, 2, TimeSpan.FromHours(1))); + AssertValueConverted(null); + AssertValueConverted(99.9m); + AssertValueConverted(99.9m); + AssertValueConverted(1); + AssertValueConverted(1.1f, 1.1m); + AssertValueConverted("1.1", 1.1m); + AssertValueConverted(99.9); + AssertValueConverted(99.9m); + AssertValueConverted(99.9); + AssertValueConverted(99.9f); + AssertValueConverted(99.9f); + AssertValueConverted(int.MinValue); + AssertValueConverted(int.MinValue); + AssertValueConverted(long.MaxValue); + AssertValueConverted(long.MaxValue); + AssertValueConverted(short.MaxValue); + AssertValueConverted(short.MaxValue); + AssertValueConverted("blah"); + AssertValueConverted(null); + AssertValueConverted(1, "1"); + AssertValueConverted(uint.MinValue); + AssertValueConverted(uint.MinValue); + AssertValueConverted("1", 1); + AssertValueConverted(ulong.MaxValue); + AssertValueConverted(ulong.MaxValue); + AssertValueConverted(ushort.MinValue); + AssertValueConverted(ushort.MinValue); + AssertValueConverted(null); + } + + private static void AssertValueConverted(object value) + { + AssertValueConverted(value, value); + } + + private static void AssertValueConverted(object value, object expected) + { + JValue v = new JValue(value); + dynamic d = v; + + T t = d; + Assert.AreEqual(expected, t); + } + + [Test] + public void DynamicSerializerExample() + { + dynamic value = new DynamicDictionary(); + + value.Name = "Arine Admin"; + value.Enabled = true; + value.Roles = new[] {"Admin", "User"}; + + string json = JsonConvert.SerializeObject(value, Formatting.Indented); + // { + // "Name": "Arine Admin", + // "Enabled": true, + // "Roles": [ + // "Admin", + // "User" + // ] + // } + + dynamic newValue = JsonConvert.DeserializeObject(json); + + string role = newValue.Roles[0]; + // Admin + } + + [Test] + public void DynamicLinqExample() + { + JObject oldAndBusted = new JObject(); + oldAndBusted["Name"] = "Arnie Admin"; + oldAndBusted["Enabled"] = true; + oldAndBusted["Roles"] = new JArray(new[] { "Admin", "User" }); + + string oldRole = (string) oldAndBusted["Roles"][0]; + // Admin + + + dynamic newHotness = new JObject(); + newHotness.Name = "Arnie Admin"; + newHotness.Enabled = true; + newHotness.Roles = new JArray(new[] { "Admin", "User" }); + + string newRole = newHotness.Roles[0]; + // Admin + } + + [Test] + public void ImprovedDynamicLinqExample() + { + dynamic product = new JObject(); + product.ProductName = "Elbow Grease"; + product.Enabled = true; + product.Price = 4.90m; + product.StockCount = 9000; + product.StockValue = 44100; + + // All Elbow Grease must go sale! + // 50% off price + + product.Price = product.Price / 2; + product.StockValue = product.StockCount * product.Price; + product.ProductName = product.ProductName + " (SALE)"; + + string json = product.ToString(); + // { + // "ProductName": "Elbow Grease (SALE)", + // "Enabled": true, + // "Price": 2.45, + // "StockCount": 9000, + // "StockValue": 22050.0 + // } + + Assert.AreEqual(@"{ + ""ProductName"": ""Elbow Grease (SALE)"", + ""Enabled"": true, + ""Price"": 2.45, + ""StockCount"": 9000, + ""StockValue"": 22050.0 +}", json); + } + + public class DynamicDictionary : DynamicObject + { + private readonly IDictionary _values = new Dictionary(); + + public override IEnumerable GetDynamicMemberNames() + { + return _values.Keys; + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + result = _values[binder.Name]; + return true; + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + _values[binder.Name] = value; + return true; + } + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs new file mode 100644 index 0000000..7666502 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JArrayTests.cs @@ -0,0 +1,440 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JArrayTests : TestFixtureBase + { + [Test] + public void Clear() + { + JArray a = new JArray { 1 }; + Assert.AreEqual(1, a.Count); + + a.Clear(); + Assert.AreEqual(0, a.Count); + } + + [Test] + public void Contains() + { + JValue v = new JValue(1); + + JArray a = new JArray { v }; + + Assert.AreEqual(false, a.Contains(new JValue(2))); + Assert.AreEqual(false, a.Contains(new JValue(1))); + Assert.AreEqual(false, a.Contains(null)); + Assert.AreEqual(true, a.Contains(v)); + } + + [Test] + public void GenericCollectionCopyTo() + { + JArray j = new JArray(); + j.Add(new JValue(1)); + j.Add(new JValue(2)); + j.Add(new JValue(3)); + Assert.AreEqual(3, j.Count); + + JToken[] a = new JToken[5]; + + ((ICollection)j).CopyTo(a, 1); + + Assert.AreEqual(null, a[0]); + + Assert.AreEqual(1, (int)a[1]); + + Assert.AreEqual(2, (int)a[2]); + + Assert.AreEqual(3, (int)a[3]); + + Assert.AreEqual(null, a[4]); + + } + + [Test] + [ExpectedException(typeof(ArgumentNullException), ExpectedMessage = @"Value cannot be null. +Parameter name: array")] + public void GenericCollectionCopyToNullArrayShouldThrow() + { + JArray j = new JArray(); + ((ICollection)j).CopyTo(null, 0); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"arrayIndex is less than 0. +Parameter name: arrayIndex")] + public void GenericCollectionCopyToNegativeArrayIndexShouldThrow() + { + JArray j = new JArray(); + ((ICollection)j).CopyTo(new JToken[1], -1); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"arrayIndex is equal to or greater than the length of array.")] + public void GenericCollectionCopyToArrayIndexEqualGreaterToArrayLengthShouldThrow() + { + JArray j = new JArray(); + ((ICollection)j).CopyTo(new JToken[1], 1); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array.")] + public void GenericCollectionCopyToInsufficientArrayCapacity() + { + JArray j = new JArray(); + j.Add(new JValue(1)); + j.Add(new JValue(2)); + j.Add(new JValue(3)); + + ((ICollection)j).CopyTo(new JToken[3], 1); + } + + [Test] + public void Remove() + { + JValue v = new JValue(1); + JArray j = new JArray(); + j.Add(v); + + Assert.AreEqual(1, j.Count); + + Assert.AreEqual(false, j.Remove(new JValue(1))); + Assert.AreEqual(false, j.Remove(null)); + Assert.AreEqual(true, j.Remove(v)); + Assert.AreEqual(false, j.Remove(v)); + + Assert.AreEqual(0, j.Count); + } + + [Test] + public void IndexOf() + { + JValue v1 = new JValue(1); + JValue v2 = new JValue(1); + JValue v3 = new JValue(1); + + JArray j = new JArray(); + + j.Add(v1); + Assert.AreEqual(0, j.IndexOf(v1)); + + j.Add(v2); + Assert.AreEqual(0, j.IndexOf(v1)); + Assert.AreEqual(1, j.IndexOf(v2)); + + j.AddFirst(v3); + Assert.AreEqual(1, j.IndexOf(v1)); + Assert.AreEqual(2, j.IndexOf(v2)); + Assert.AreEqual(0, j.IndexOf(v3)); + + v3.Remove(); + Assert.AreEqual(0, j.IndexOf(v1)); + Assert.AreEqual(1, j.IndexOf(v2)); + Assert.AreEqual(-1, j.IndexOf(v3)); + } + + [Test] + public void RemoveAt() + { + JValue v1 = new JValue(1); + JValue v2 = new JValue(1); + JValue v3 = new JValue(1); + + JArray j = new JArray(); + + j.Add(v1); + j.Add(v2); + j.Add(v3); + + Assert.AreEqual(true, j.Contains(v1)); + j.RemoveAt(0); + Assert.AreEqual(false, j.Contains(v1)); + + Assert.AreEqual(true, j.Contains(v3)); + j.RemoveAt(1); + Assert.AreEqual(false, j.Contains(v3)); + + Assert.AreEqual(1, j.Count); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"index is equal to or greater than Count. +Parameter name: index")] + public void RemoveAtOutOfRangeIndexShouldError() + { + JArray j = new JArray(); + j.RemoveAt(0); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"index is less than 0. +Parameter name: index")] + public void RemoveAtNegativeIndexShouldError() + { + JArray j = new JArray(); + j.RemoveAt(-1); + } + + [Test] + public void Insert() + { + JValue v1 = new JValue(1); + JValue v2 = new JValue(2); + JValue v3 = new JValue(3); + JValue v4 = new JValue(4); + + JArray j = new JArray(); + + j.Add(v1); + j.Add(v2); + j.Add(v3); + j.Insert(1, v4); + + Assert.AreEqual(0, j.IndexOf(v1)); + Assert.AreEqual(1, j.IndexOf(v4)); + Assert.AreEqual(2, j.IndexOf(v2)); + Assert.AreEqual(3, j.IndexOf(v3)); + } + + [Test] + public void AddFirstAddedTokenShouldBeFirst() + { + JValue v1 = new JValue(1); + JValue v2 = new JValue(2); + JValue v3 = new JValue(3); + + JArray j = new JArray(); + Assert.AreEqual(null, j.First); + Assert.AreEqual(null, j.Last); + + j.AddFirst(v1); + Assert.AreEqual(v1, j.First); + Assert.AreEqual(v1, j.Last); + + j.AddFirst(v2); + Assert.AreEqual(v2, j.First); + Assert.AreEqual(v1, j.Last); + + j.AddFirst(v3); + Assert.AreEqual(v3, j.First); + Assert.AreEqual(v1, j.Last); + } + + [Test] + public void InsertShouldInsertAtZeroIndex() + { + JValue v1 = new JValue(1); + JValue v2 = new JValue(2); + + JArray j = new JArray(); + + j.Insert(0, v1); + Assert.AreEqual(0, j.IndexOf(v1)); + + j.Insert(0, v2); + Assert.AreEqual(1, j.IndexOf(v1)); + Assert.AreEqual(0, j.IndexOf(v2)); + } + + [Test] + public void InsertNull() + { + JArray j = new JArray(); + j.Insert(0, null); + + Assert.AreEqual(null, ((JValue)j[0]).Value); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"Specified argument was out of the range of valid values. +Parameter name: index")] + public void InsertNegativeIndexShouldThrow() + { + JArray j = new JArray(); + j.Insert(-1, new JValue(1)); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"Specified argument was out of the range of valid values. +Parameter name: index")] + public void InsertOutOfRangeIndexShouldThrow() + { + JArray j = new JArray(); + j.Insert(2, new JValue(1)); + } + + [Test] + public void Item() + { + JValue v1 = new JValue(1); + JValue v2 = new JValue(2); + JValue v3 = new JValue(3); + JValue v4 = new JValue(4); + + JArray j = new JArray(); + + j.Add(v1); + j.Add(v2); + j.Add(v3); + + j[1] = v4; + + Assert.AreEqual(null, v2.Parent); + Assert.AreEqual(-1, j.IndexOf(v2)); + Assert.AreEqual(j, v4.Parent); + Assert.AreEqual(1, j.IndexOf(v4)); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Error reading JArray from JsonReader. Current JsonReader item is not an array: StartObject")] + public void Parse_ShouldThrowOnUnexpectedToken() + { + string json = @"{""prop"":""value""}"; + JArray.Parse(json); + } + + public class ListItemFields + { + public string ListItemText { get; set; } + public object ListItemValue { get; set; } + } + + [Test] + public void ArrayOrder() + { + string itemZeroText = "Zero text"; + + IEnumerable t = new List + { + new ListItemFields { ListItemText = "First", ListItemValue = 1 }, + new ListItemFields { ListItemText = "Second", ListItemValue = 2 }, + new ListItemFields { ListItemText = "Third", ListItemValue = 3 } + }; + + JObject optionValues = + new JObject( + new JProperty("options", + new JArray( + new JObject( + new JProperty("text", itemZeroText), + new JProperty("value", "0")), + from r in t + orderby r.ListItemValue + select new JObject( + new JProperty("text", r.ListItemText), + new JProperty("value", r.ListItemValue.ToString()))))); + + string result = "myOptions = " + optionValues.ToString(); + + Assert.AreEqual(@"myOptions = { + ""options"": [ + { + ""text"": ""Zero text"", + ""value"": ""0"" + }, + { + ""text"": ""First"", + ""value"": ""1"" + }, + { + ""text"": ""Second"", + ""value"": ""2"" + }, + { + ""text"": ""Third"", + ""value"": ""3"" + } + ] +}", result); + } + + [Test] + public void Iterate() + { + JArray a = new JArray(1, 2, 3, 4, 5); + + int i = 1; + foreach (JToken token in a) + { + Assert.AreEqual(i, (int)token); + i++; + } + } + + +#if !SILVERLIGHT + [Test] + public void ITypedListGetItemProperties() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + ITypedList a = new JArray(new JObject(p1, p2)); + + PropertyDescriptorCollection propertyDescriptors = a.GetItemProperties(null); + Assert.IsNotNull(propertyDescriptors); + Assert.AreEqual(2, propertyDescriptors.Count); + Assert.AreEqual("Test1", propertyDescriptors[0].Name); + Assert.AreEqual("Test2", propertyDescriptors[1].Name); + } +#endif + + [Test] + public void AddArrayToSelf() + { + JArray a = new JArray(1, 2); + a.Add(a); + + Assert.AreEqual(3, a.Count); + Assert.AreEqual(1, (int)a[0]); + Assert.AreEqual(2, (int)a[1]); + Assert.AreNotSame(a, a[2]); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"Set JArray values with invalid key value: ""badvalue"". Array position index expected.")] + public void SetValueWithInvalidIndex() + { + JArray a = new JArray(); + a["badvalue"] = new JValue(3); + } + + [Test] + public void SetValue() + { + object key = 0; + + JArray a = new JArray((object)null); + a[key] = new JValue(3); + + Assert.AreEqual(3, (int)a[key]); + } + + [Test] + public void ReplaceAll() + { + JArray a = new JArray(new [] { 1, 2, 3 }); + Assert.AreEqual(3, a.Count); + Assert.AreEqual(1, (int)a[0]); + Assert.AreEqual(2, (int)a[1]); + Assert.AreEqual(3, (int)a[2]); + + a.ReplaceAll(1); + Assert.AreEqual(1, a.Count); + Assert.AreEqual(1, (int)a[0]); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Unexpected end of content while loading JArray.")] + public void ParseIncomplete() + { + JArray.Parse("[1"); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JConstructorTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JConstructorTests.cs new file mode 100644 index 0000000..43b981a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JConstructorTests.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Linq; +using NUnit.Framework; +using System.IO; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JConstructorTests : TestFixtureBase + { + [Test] + public void Load() + { + JsonReader reader = new JsonTextReader(new StringReader("new Date(123)")); + reader.Read(); + + JConstructor constructor = JConstructor.Load(reader); + Assert.AreEqual("Date", constructor.Name); + Assert.IsTrue(JToken.DeepEquals(new JValue(123), constructor.Values().ElementAt(0))); + } + + [Test] + public void CreateWithMultiValue() + { + JConstructor constructor = new JConstructor("Test", new List { 1, 2, 3 }); + Assert.AreEqual("Test", constructor.Name); + Assert.AreEqual(3, constructor.Children().Count()); + Assert.AreEqual(1, (int)constructor.Children().ElementAt(0)); + Assert.AreEqual(2, (int)constructor.Children().ElementAt(1)); + Assert.AreEqual(3, (int)constructor.Children().ElementAt(2)); + } + + [Test] + public void Iterate() + { + JConstructor c = new JConstructor("MrConstructor", 1, 2, 3, 4, 5); + + int i = 1; + foreach (JToken token in c) + { + Assert.AreEqual(i, (int)token); + i++; + } + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"Set JConstructor values with invalid key value: ""badvalue"". Argument position index expected.")] + public void SetValueWithInvalidIndex() + { + JConstructor c = new JConstructor(); + c["badvalue"] = new JValue(3); + } + + [Test] + public void SetValue() + { + object key = 0; + + JConstructor c = new JConstructor(); + c.Name = "con"; + c.Add(null); + c[key] = new JValue(3); + + Assert.AreEqual(3, (int)c[key]); + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs new file mode 100644 index 0000000..6a9216d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JObjectTests.cs @@ -0,0 +1,1595 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Converters; +using System.IO; +using System.Collections; +using System.Collections.Specialized; +#if !PocketPC && !SILVERLIGHT +using System.Web.UI; +#endif + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JObjectTests : TestFixtureBase + { + [Test] + public void TryGetValue() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", new JValue(1)); + Assert.AreEqual(1, o.Children().Count()); + + JToken t; + Assert.AreEqual(false, o.TryGetValue("sdf", out t)); + Assert.AreEqual(null, t); + + Assert.AreEqual(false, o.TryGetValue(null, out t)); + Assert.AreEqual(null, t); + + Assert.AreEqual(true, o.TryGetValue("PropertyNameValue", out t)); + Assert.AreEqual(true, JToken.DeepEquals(new JValue(1), t)); + } + + [Test] + public void DictionaryItemShouldSet() + { + JObject o = new JObject(); + o["PropertyNameValue"] = new JValue(1); + Assert.AreEqual(1, o.Children().Count()); + + JToken t; + Assert.AreEqual(true, o.TryGetValue("PropertyNameValue", out t)); + Assert.AreEqual(true, JToken.DeepEquals(new JValue(1), t)); + + o["PropertyNameValue"] = new JValue(2); + Assert.AreEqual(1, o.Children().Count()); + + Assert.AreEqual(true, o.TryGetValue("PropertyNameValue", out t)); + Assert.AreEqual(true, JToken.DeepEquals(new JValue(2), t)); + + o["PropertyNameValue"] = null; + Assert.AreEqual(1, o.Children().Count()); + + Assert.AreEqual(true, o.TryGetValue("PropertyNameValue", out t)); + Assert.AreEqual(true, JToken.DeepEquals(new JValue((object)null), t)); + } + + [Test] + public void Remove() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", new JValue(1)); + Assert.AreEqual(1, o.Children().Count()); + + Assert.AreEqual(false, o.Remove("sdf")); + Assert.AreEqual(false, o.Remove(null)); + Assert.AreEqual(true, o.Remove("PropertyNameValue")); + + Assert.AreEqual(0, o.Children().Count()); + } + + [Test] + public void GenericCollectionRemove() + { + JValue v = new JValue(1); + JObject o = new JObject(); + o.Add("PropertyNameValue", v); + Assert.AreEqual(1, o.Children().Count()); + + Assert.AreEqual(false, ((ICollection>)o).Remove(new KeyValuePair("PropertyNameValue1", new JValue(1)))); + Assert.AreEqual(false, ((ICollection>)o).Remove(new KeyValuePair("PropertyNameValue", new JValue(2)))); + Assert.AreEqual(false, ((ICollection>)o).Remove(new KeyValuePair("PropertyNameValue", new JValue(1)))); + Assert.AreEqual(true, ((ICollection>)o).Remove(new KeyValuePair("PropertyNameValue", v))); + + Assert.AreEqual(0, o.Children().Count()); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add property PropertyNameValue to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.")] + public void DuplicatePropertyNameShouldThrow() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", null); + o.Add("PropertyNameValue", null); + } + + [Test] + public void GenericDictionaryAdd() + { + JObject o = new JObject(); + + o.Add("PropertyNameValue", new JValue(1)); + Assert.AreEqual(1, (int)o["PropertyNameValue"]); + + o.Add("PropertyNameValue1", null); + Assert.AreEqual(null, ((JValue)o["PropertyNameValue1"]).Value); + + Assert.AreEqual(2, o.Children().Count()); + } + + [Test] + public void GenericCollectionAdd() + { + JObject o = new JObject(); + ((ICollection>)o).Add(new KeyValuePair("PropertyNameValue", new JValue(1))); + + Assert.AreEqual(1, (int)o["PropertyNameValue"]); + Assert.AreEqual(1, o.Children().Count()); + } + + [Test] + public void GenericCollectionClear() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", new JValue(1)); + Assert.AreEqual(1, o.Children().Count()); + + JProperty p = (JProperty)o.Children().ElementAt(0); + + ((ICollection>)o).Clear(); + Assert.AreEqual(0, o.Children().Count()); + + Assert.AreEqual(null, p.Parent); + } + + [Test] + public void GenericCollectionContains() + { + JValue v = new JValue(1); + JObject o = new JObject(); + o.Add("PropertyNameValue", v); + Assert.AreEqual(1, o.Children().Count()); + + bool contains = ((ICollection>)o).Contains(new KeyValuePair("PropertyNameValue", new JValue(1))); + Assert.AreEqual(false, contains); + + contains = ((ICollection>)o).Contains(new KeyValuePair("PropertyNameValue", v)); + Assert.AreEqual(true, contains); + + contains = ((ICollection>)o).Contains(new KeyValuePair("PropertyNameValue", new JValue(2))); + Assert.AreEqual(false, contains); + + contains = ((ICollection>)o).Contains(new KeyValuePair("PropertyNameValue1", new JValue(1))); + Assert.AreEqual(false, contains); + + contains = ((ICollection>)o).Contains(default(KeyValuePair)); + Assert.AreEqual(false, contains); + } + + [Test] + public void GenericDictionaryContains() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", new JValue(1)); + Assert.AreEqual(1, o.Children().Count()); + + bool contains = ((IDictionary)o).ContainsKey("PropertyNameValue"); + Assert.AreEqual(true, contains); + } + + [Test] + public void GenericCollectionCopyTo() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", new JValue(1)); + o.Add("PropertyNameValue2", new JValue(2)); + o.Add("PropertyNameValue3", new JValue(3)); + Assert.AreEqual(3, o.Children().Count()); + + KeyValuePair[] a = new KeyValuePair[5]; + + ((ICollection>)o).CopyTo(a, 1); + + Assert.AreEqual(default(KeyValuePair), a[0]); + + Assert.AreEqual("PropertyNameValue", a[1].Key); + Assert.AreEqual(1, (int)a[1].Value); + + Assert.AreEqual("PropertyNameValue2", a[2].Key); + Assert.AreEqual(2, (int)a[2].Value); + + Assert.AreEqual("PropertyNameValue3", a[3].Key); + Assert.AreEqual(3, (int)a[3].Value); + + Assert.AreEqual(default(KeyValuePair), a[4]); + } + + [Test] + [ExpectedException(typeof(ArgumentNullException), ExpectedMessage = @"Value cannot be null. +Parameter name: array")] + public void GenericCollectionCopyToNullArrayShouldThrow() + { + JObject o = new JObject(); + ((ICollection>)o).CopyTo(null, 0); + } + + [Test] + [ExpectedException(typeof(ArgumentOutOfRangeException), ExpectedMessage = @"arrayIndex is less than 0. +Parameter name: arrayIndex")] + public void GenericCollectionCopyToNegativeArrayIndexShouldThrow() + { + JObject o = new JObject(); + ((ICollection>)o).CopyTo(new KeyValuePair[1], -1); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"arrayIndex is equal to or greater than the length of array.")] + public void GenericCollectionCopyToArrayIndexEqualGreaterToArrayLengthShouldThrow() + { + JObject o = new JObject(); + ((ICollection>)o).CopyTo(new KeyValuePair[1], 1); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array.")] + public void GenericCollectionCopyToInsufficientArrayCapacity() + { + JObject o = new JObject(); + o.Add("PropertyNameValue", new JValue(1)); + o.Add("PropertyNameValue2", new JValue(2)); + o.Add("PropertyNameValue3", new JValue(3)); + + ((ICollection>)o).CopyTo(new KeyValuePair[3], 1); + } + + [Test] + public void FromObjectRaw() + { + PersonRaw raw = new PersonRaw + { + FirstName = "FirstNameValue", + RawContent = new JRaw("[1,2,3,4,5]"), + LastName = "LastNameValue" + }; + + JObject o = JObject.FromObject(raw); + + Assert.AreEqual("FirstNameValue", (string)o["first_name"]); + Assert.AreEqual(JTokenType.Raw, ((JValue)o["RawContent"]).Type); + Assert.AreEqual("[1,2,3,4,5]", (string)o["RawContent"]); + Assert.AreEqual("LastNameValue", (string)o["last_name"]); + } + + [Test] + public void JTokenReader() + { + PersonRaw raw = new PersonRaw + { + FirstName = "FirstNameValue", + RawContent = new JRaw("[1,2,3,4,5]"), + LastName = "LastNameValue" + }; + + JObject o = JObject.FromObject(raw); + + JsonReader reader = new JTokenReader(o); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.Raw, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.PropertyName, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.String, reader.TokenType); + + Assert.IsTrue(reader.Read()); + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + Assert.IsFalse(reader.Read()); + } + + [Test] + public void DeserializeFromRaw() + { + PersonRaw raw = new PersonRaw + { + FirstName = "FirstNameValue", + RawContent = new JRaw("[1,2,3,4,5]"), + LastName = "LastNameValue" + }; + + JObject o = JObject.FromObject(raw); + + JsonReader reader = new JTokenReader(o); + JsonSerializer serializer = new JsonSerializer(); + raw = (PersonRaw)serializer.Deserialize(reader, typeof(PersonRaw)); + + Assert.AreEqual("FirstNameValue", raw.FirstName); + Assert.AreEqual("LastNameValue", raw.LastName); + Assert.AreEqual("[1,2,3,4,5]", raw.RawContent.Value); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray")] + public void Parse_ShouldThrowOnUnexpectedToken() + { + string json = @"[""prop""]"; + JObject.Parse(json); + } + + [Test] + public void ParseJavaScriptDate() + { + string json = @"[new Date(1207285200000)]"; + + JArray a = (JArray)JsonConvert.DeserializeObject(json); + JValue v = (JValue)a[0]; + + Assert.AreEqual(JsonConvert.ConvertJavaScriptTicksToDateTime(1207285200000), (DateTime)v); + } + + [Test] + public void GenericValueCast() + { + string json = @"{""foo"":true}"; + JObject o = (JObject)JsonConvert.DeserializeObject(json); + bool? value = o.Value("foo"); + Assert.AreEqual(true, value); + + json = @"{""foo"":null}"; + o = (JObject)JsonConvert.DeserializeObject(json); + value = o.Value("foo"); + Assert.AreEqual(null, value); + } + + [Test] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Invalid property identifier character: ]. Line 3, position 9.")] + public void Blog() + { + JObject person = JObject.Parse(@"{ + ""name"": ""James"", + ]!#$THIS IS: BAD JSON![{}}}}] + }"); + + // Invalid property identifier character: ]. Line 3, position 9. + } + + [Test] + public void RawChildValues() + { + JObject o = new JObject(); + o["val1"] = new JRaw("1"); + o["val2"] = new JRaw("1"); + + string json = o.ToString(); + + Assert.AreEqual(@"{ + ""val1"": 1, + ""val2"": 1 +}", json); + } + + [Test] + public void Iterate() + { + JObject o = new JObject(); + o.Add("PropertyNameValue1", new JValue(1)); + o.Add("PropertyNameValue2", new JValue(2)); + + JToken t = o; + + int i = 1; + foreach (JProperty property in t) + { + Assert.AreEqual("PropertyNameValue" + i, property.Name); + Assert.AreEqual(i, (int)property.Value); + + i++; + } + } + + [Test] + public void KeyValuePairIterate() + { + JObject o = new JObject(); + o.Add("PropertyNameValue1", new JValue(1)); + o.Add("PropertyNameValue2", new JValue(2)); + + int i = 1; + foreach (KeyValuePair pair in o) + { + Assert.AreEqual("PropertyNameValue" + i, pair.Key); + Assert.AreEqual(i, (int)pair.Value); + + i++; + } + } + + [Test] + public void WriteObjectNullStringValue() + { + string s = null; + JValue v = new JValue(s); + Assert.AreEqual(null, v.Value); + Assert.AreEqual(JTokenType.String, v.Type); + + JObject o = new JObject(); + o["title"] = v; + + string output = o.ToString(); + + Assert.AreEqual(@"{ + ""title"": null +}", output); + } + + [Test] + public void Example() + { + string json = @"{ + ""Name"": ""Apple"", + ""Expiry"": new Date(1230422400000), + ""Price"": 3.99, + ""Sizes"": [ + ""Small"", + ""Medium"", + ""Large"" + ] + }"; + + JObject o = JObject.Parse(json); + + string name = (string)o["Name"]; + // Apple + + JArray sizes = (JArray)o["Sizes"]; + + string smallest = (string)sizes[0]; + // Small + + Console.WriteLine(name); + Console.WriteLine(smallest); + } + + [Test] + public void DeserializeClassManually() + { + string jsonText = @"{ + ""short"": + { + ""original"":""http://www.foo.com/"", + ""short"":""krehqk"", + ""error"": + { + ""code"":0, + ""msg"":""No action taken"" + } + } +}"; + + JObject json = JObject.Parse(jsonText); + + Shortie shortie = new Shortie + { + Original = (string)json["short"]["original"], + Short = (string)json["short"]["short"], + Error = new ShortieException + { + Code = (int)json["short"]["error"]["code"], + ErrorMessage = (string)json["short"]["error"]["msg"] + } + }; + + Console.WriteLine(shortie.Original); + // http://www.foo.com/ + + Console.WriteLine(shortie.Error.ErrorMessage); + // No action taken + + Assert.AreEqual("http://www.foo.com/", shortie.Original); + Assert.AreEqual("krehqk", shortie.Short); + Assert.AreEqual(null, shortie.Shortened); + Assert.AreEqual(0, shortie.Error.Code); + Assert.AreEqual("No action taken", shortie.Error.ErrorMessage); + } + + [Test] + public void JObjectContainingHtml() + { + JObject o = new JObject(); + o["rc"] = new JValue(200); + o["m"] = new JValue(""); + o["o"] = new JValue(@"
+
+ asdf
+ 0 +
+
+

+ 444444444 +

+
+
+
+
"); + + Assert.AreEqual(@"{ + ""rc"": 200, + ""m"": """", + ""o"": ""
\r\n
\r\n asdf
\r\n 0\r\n
\r\n
\r\n

\r\n 444444444\r\n

\r\n
\r\n
\r\n
\r\n
"" +}", o.ToString()); + } + + [Test] + public void ImplicitValueConversions() + { + JObject moss = new JObject(); + moss["FirstName"] = new JValue("Maurice"); + moss["LastName"] = new JValue("Moss"); + moss["BirthDate"] = new JValue(new DateTime(1977, 12, 30)); + moss["Department"] = new JValue("IT"); + moss["JobTitle"] = new JValue("Support"); + + Console.WriteLine(moss.ToString()); + //{ + // "FirstName": "Maurice", + // "LastName": "Moss", + // "BirthDate": "\/Date(252241200000+1300)\/", + // "Department": "IT", + // "JobTitle": "Support" + //} + + + JObject jen = new JObject(); + jen["FirstName"] = "Jen"; + jen["LastName"] = "Barber"; + jen["BirthDate"] = new DateTime(1978, 3, 15); + jen["Department"] = "IT"; + jen["JobTitle"] = "Manager"; + + Console.WriteLine(jen.ToString()); + //{ + // "FirstName": "Jen", + // "LastName": "Barber", + // "BirthDate": "\/Date(258721200000+1300)\/", + // "Department": "IT", + // "JobTitle": "Manager" + //} + } + + [Test] + public void ReplaceJPropertyWithJPropertyWithSameName() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + + JObject o = new JObject(p1, p2); + IList l = o; + Assert.AreEqual(p1, l[0]); + Assert.AreEqual(p2, l[1]); + + JProperty p3 = new JProperty("Test1", "III"); + + p1.Replace(p3); + Assert.AreEqual(null, p1.Parent); + Assert.AreEqual(l, p3.Parent); + + Assert.AreEqual(p3, l[0]); + Assert.AreEqual(p2, l[1]); + + Assert.AreEqual(2, l.Count); + Assert.AreEqual(2, o.Properties().Count()); + + JProperty p4 = new JProperty("Test4", "IV"); + + p2.Replace(p4); + Assert.AreEqual(null, p2.Parent); + Assert.AreEqual(l, p4.Parent); + + Assert.AreEqual(p3, l[0]); + Assert.AreEqual(p4, l[1]); + } + +#if !PocketPC && !SILVERLIGHT && !NET20 + [Test] + public void PropertyChanging() + { + object changing = null; + object changed = null; + int changingCount = 0; + int changedCount = 0; + + JObject o = new JObject(); + o.PropertyChanging += (sender, args) => + { + JObject s = (JObject) sender; + changing = (s[args.PropertyName] != null) ? ((JValue)s[args.PropertyName]).Value : null; + changingCount++; + }; + o.PropertyChanged += (sender, args) => + { + JObject s = (JObject)sender; + changed = (s[args.PropertyName] != null) ? ((JValue)s[args.PropertyName]).Value : null; + changedCount++; + }; + + o["StringValue"] = "value1"; + Assert.AreEqual(null, changing); + Assert.AreEqual("value1", changed); + Assert.AreEqual("value1", (string)o["StringValue"]); + Assert.AreEqual(1, changingCount); + Assert.AreEqual(1, changedCount); + + o["StringValue"] = "value1"; + Assert.AreEqual(1, changingCount); + Assert.AreEqual(1, changedCount); + + o["StringValue"] = "value2"; + Assert.AreEqual("value1", changing); + Assert.AreEqual("value2", changed); + Assert.AreEqual("value2", (string)o["StringValue"]); + Assert.AreEqual(2, changingCount); + Assert.AreEqual(2, changedCount); + + o["StringValue"] = null; + Assert.AreEqual("value2", changing); + Assert.AreEqual(null, changed); + Assert.AreEqual(null, (string)o["StringValue"]); + Assert.AreEqual(3, changingCount); + Assert.AreEqual(3, changedCount); + + o["NullValue"] = null; + Assert.AreEqual(null, changing); + Assert.AreEqual(null, changed); + Assert.AreEqual(new JValue((object)null), o["NullValue"]); + Assert.AreEqual(4, changingCount); + Assert.AreEqual(4, changedCount); + + o["NullValue"] = null; + Assert.AreEqual(4, changingCount); + Assert.AreEqual(4, changedCount); + } +#endif + + [Test] + public void PropertyChanged() + { + object changed = null; + int changedCount = 0; + + JObject o = new JObject(); + o.PropertyChanged += (sender, args) => + { + JObject s = (JObject)sender; + changed = (s[args.PropertyName] != null) ? ((JValue)s[args.PropertyName]).Value : null; + changedCount++; + }; + + o["StringValue"] = "value1"; + Assert.AreEqual("value1", changed); + Assert.AreEqual("value1", (string)o["StringValue"]); + Assert.AreEqual(1, changedCount); + + o["StringValue"] = "value1"; + Assert.AreEqual(1, changedCount); + + o["StringValue"] = "value2"; + Assert.AreEqual("value2", changed); + Assert.AreEqual("value2", (string)o["StringValue"]); + Assert.AreEqual(2, changedCount); + + o["StringValue"] = null; + Assert.AreEqual(null, changed); + Assert.AreEqual(null, (string)o["StringValue"]); + Assert.AreEqual(3, changedCount); + + o["NullValue"] = null; + Assert.AreEqual(null, changed); + Assert.AreEqual(new JValue((object)null), o["NullValue"]); + Assert.AreEqual(4, changedCount); + + o["NullValue"] = null; + Assert.AreEqual(4, changedCount); + } + + [Test] + public void IListContains() + { + JProperty p = new JProperty("Test", 1); + IList l = new JObject(p); + + Assert.IsTrue(l.Contains(p)); + Assert.IsFalse(l.Contains(new JProperty("Test", 1))); + } + + [Test] + public void IListIndexOf() + { + JProperty p = new JProperty("Test", 1); + IList l = new JObject(p); + + Assert.AreEqual(0, l.IndexOf(p)); + Assert.AreEqual(-1, l.IndexOf(new JProperty("Test", 1))); + } + + [Test] + public void IListClear() + { + JProperty p = new JProperty("Test", 1); + IList l = new JObject(p); + + Assert.AreEqual(1, l.Count); + + l.Clear(); + + Assert.AreEqual(0, l.Count); + } + + [Test] + public void IListCopyTo() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + object[] a = new object[l.Count]; + + l.CopyTo(a, 0); + + Assert.AreEqual(p1, a[0]); + Assert.AreEqual(p2, a[1]); + } + + [Test] + public void IListAdd() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l.Add(p3); + + Assert.AreEqual(3, l.Count); + Assert.AreEqual(p3, l[2]); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.")] + public void IListAddBadToken() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + l.Add(new JValue("Bad!")); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Argument is not a JToken.")] + public void IListAddBadValue() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + l.Add("Bad!"); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add property Test2 to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.")] + public void IListAddPropertyWithExistingName() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test2", "II"); + + l.Add(p3); + } + + [Test] + public void IListRemove() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + // won't do anything + l.Remove(p3); + Assert.AreEqual(2, l.Count); + + l.Remove(p1); + Assert.AreEqual(1, l.Count); + Assert.IsFalse(l.Contains(p1)); + Assert.IsTrue(l.Contains(p2)); + + l.Remove(p2); + Assert.AreEqual(0, l.Count); + Assert.IsFalse(l.Contains(p2)); + Assert.AreEqual(null, p2.Parent); + } + + [Test] + public void IListRemoveAt() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + // won't do anything + l.RemoveAt(0); + + l.Remove(p1); + Assert.AreEqual(1, l.Count); + + l.Remove(p2); + Assert.AreEqual(0, l.Count); + } + + [Test] + public void IListInsert() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l.Insert(1, p3); + Assert.AreEqual(l, p3.Parent); + + Assert.AreEqual(p1, l[0]); + Assert.AreEqual(p3, l[1]); + Assert.AreEqual(p2, l[2]); + } + + [Test] + public void IListIsReadOnly() + { + IList l = new JObject(); + Assert.IsFalse(l.IsReadOnly); + } + + [Test] + public void IListIsFixedSize() + { + IList l = new JObject(); + Assert.IsFalse(l.IsFixedSize); + } + + [Test] + public void IListSetItem() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l[0] = p3; + + Assert.AreEqual(p3, l[0]); + Assert.AreEqual(p2, l[1]); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add property Test3 to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.")] + public void IListSetItemAlreadyExists() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l[0] = p3; + l[1] = p3; + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.")] + public void IListSetItemInvalid() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + l[0] = new JValue(true); + } + + [Test] + public void IListSyncRoot() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + Assert.IsNotNull(l.SyncRoot); + } + + [Test] + public void IListIsSynchronized() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + Assert.IsFalse(l.IsSynchronized); + } + + [Test] + public void GenericListJTokenContains() + { + JProperty p = new JProperty("Test", 1); + IList l = new JObject(p); + + Assert.IsTrue(l.Contains(p)); + Assert.IsFalse(l.Contains(new JProperty("Test", 1))); + } + + [Test] + public void GenericListJTokenIndexOf() + { + JProperty p = new JProperty("Test", 1); + IList l = new JObject(p); + + Assert.AreEqual(0, l.IndexOf(p)); + Assert.AreEqual(-1, l.IndexOf(new JProperty("Test", 1))); + } + + [Test] + public void GenericListJTokenClear() + { + JProperty p = new JProperty("Test", 1); + IList l = new JObject(p); + + Assert.AreEqual(1, l.Count); + + l.Clear(); + + Assert.AreEqual(0, l.Count); + } + + [Test] + public void GenericListJTokenCopyTo() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JToken[] a = new JToken[l.Count]; + + l.CopyTo(a, 0); + + Assert.AreEqual(p1, a[0]); + Assert.AreEqual(p2, a[1]); + } + + [Test] + public void GenericListJTokenAdd() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l.Add(p3); + + Assert.AreEqual(3, l.Count); + Assert.AreEqual(p3, l[2]); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.")] + public void GenericListJTokenAddBadToken() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + l.Add(new JValue("Bad!")); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.")] + public void GenericListJTokenAddBadValue() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + // string is implicitly converted to JValue + l.Add("Bad!"); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add property Test2 to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.")] + public void GenericListJTokenAddPropertyWithExistingName() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test2", "II"); + + l.Add(p3); + } + + [Test] + public void GenericListJTokenRemove() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + // won't do anything + Assert.IsFalse(l.Remove(p3)); + Assert.AreEqual(2, l.Count); + + Assert.IsTrue(l.Remove(p1)); + Assert.AreEqual(1, l.Count); + Assert.IsFalse(l.Contains(p1)); + Assert.IsTrue(l.Contains(p2)); + + Assert.IsTrue(l.Remove(p2)); + Assert.AreEqual(0, l.Count); + Assert.IsFalse(l.Contains(p2)); + Assert.AreEqual(null, p2.Parent); + } + + [Test] + public void GenericListJTokenRemoveAt() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + // won't do anything + l.RemoveAt(0); + + l.Remove(p1); + Assert.AreEqual(1, l.Count); + + l.Remove(p2); + Assert.AreEqual(0, l.Count); + } + + [Test] + public void GenericListJTokenInsert() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l.Insert(1, p3); + Assert.AreEqual(l, p3.Parent); + + Assert.AreEqual(p1, l[0]); + Assert.AreEqual(p3, l[1]); + Assert.AreEqual(p2, l[2]); + } + + [Test] + public void GenericListJTokenIsReadOnly() + { + IList l = new JObject(); + Assert.IsFalse(l.IsReadOnly); + } + + [Test] + public void GenericListJTokenSetItem() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l[0] = p3; + + Assert.AreEqual(p3, l[0]); + Assert.AreEqual(p2, l[1]); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add property Test3 to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.")] + public void GenericListJTokenSetItemAlreadyExists() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + IList l = new JObject(p1, p2); + + JProperty p3 = new JProperty("Test3", "III"); + + l[0] = p3; + l[1] = p3; + } + +#if !SILVERLIGHT + [Test] + public void IBindingListSortDirection() + { + IBindingList l = new JObject(); + Assert.AreEqual(ListSortDirection.Ascending, l.SortDirection); + } + + [Test] + public void IBindingListSortProperty() + { + IBindingList l = new JObject(); + Assert.AreEqual(null, l.SortProperty); + } + + [Test] + public void IBindingListSupportsChangeNotification() + { + IBindingList l = new JObject(); + Assert.AreEqual(true, l.SupportsChangeNotification); + } + + [Test] + public void IBindingListSupportsSearching() + { + IBindingList l = new JObject(); + Assert.AreEqual(false, l.SupportsSearching); + } + + [Test] + public void IBindingListSupportsSorting() + { + IBindingList l = new JObject(); + Assert.AreEqual(false, l.SupportsSorting); + } + + [Test] + public void IBindingListAllowEdit() + { + IBindingList l = new JObject(); + Assert.AreEqual(true, l.AllowEdit); + } + + [Test] + public void IBindingListAllowNew() + { + IBindingList l = new JObject(); + Assert.AreEqual(true, l.AllowNew); + } + + [Test] + public void IBindingListAllowRemove() + { + IBindingList l = new JObject(); + Assert.AreEqual(true, l.AllowRemove); + } + + [Test] + public void IBindingListAddIndex() + { + IBindingList l = new JObject(); + // do nothing + l.AddIndex(null); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void IBindingListApplySort() + { + IBindingList l = new JObject(); + l.ApplySort(null, ListSortDirection.Ascending); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void IBindingListRemoveSort() + { + IBindingList l = new JObject(); + l.RemoveSort(); + } + + [Test] + public void IBindingListRemoveIndex() + { + IBindingList l = new JObject(); + // do nothing + l.RemoveIndex(null); + } + + [Test] + [ExpectedException(typeof(NotSupportedException))] + public void IBindingListFind() + { + IBindingList l = new JObject(); + l.Find(null, null); + } + + [Test] + public void IBindingListIsSorted() + { + IBindingList l = new JObject(); + Assert.AreEqual(false, l.IsSorted); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Could not determine new value to add to 'Newtonsoft.Json.Linq.JObject'.")] + public void IBindingListAddNew() + { + IBindingList l = new JObject(); + l.AddNew(); + } + + [Test] + public void IBindingListAddNewWithEvent() + { + JObject o = new JObject(); + o.AddingNew += (s, e) => e.NewObject = new JProperty("Property!"); + + IBindingList l = o; + object newObject = l.AddNew(); + Assert.IsNotNull(newObject); + + JProperty p = (JProperty) newObject; + Assert.AreEqual("Property!", p.Name); + Assert.AreEqual(o, p.Parent); + } + + [Test] + public void ITypedListGetListName() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + ITypedList l = new JObject(p1, p2); + + Assert.AreEqual(string.Empty, l.GetListName(null)); + } + + [Test] + public void ITypedListGetItemProperties() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + ITypedList l = new JObject(p1, p2); + + PropertyDescriptorCollection propertyDescriptors = l.GetItemProperties(null); + Assert.IsNull(propertyDescriptors); + } + + [Test] + public void ListChanged() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + JObject o = new JObject(p1, p2); + + ListChangedType? changedType = null; + int? index = null; + + o.ListChanged += (s, a) => + { + changedType = a.ListChangedType; + index = a.NewIndex; + }; + + JProperty p3 = new JProperty("Test3", "III"); + + o.Add(p3); + Assert.AreEqual(changedType, ListChangedType.ItemAdded); + Assert.AreEqual(index, 2); + Assert.AreEqual(p3, ((IList)o)[index.Value]); + + JProperty p4 = new JProperty("Test4", "IV"); + + ((IList) o)[index.Value] = p4; + Assert.AreEqual(changedType, ListChangedType.ItemChanged); + Assert.AreEqual(index, 2); + Assert.AreEqual(p4, ((IList)o)[index.Value]); + Assert.IsFalse(((IList)o).Contains(p3)); + Assert.IsTrue(((IList)o).Contains(p4)); + + o["Test1"] = 2; + Assert.AreEqual(changedType, ListChangedType.ItemChanged); + Assert.AreEqual(index, 0); + Assert.AreEqual(2, (int)o["Test1"]); + } +#endif +#if SILVERLIGHT || !(NET20 || NET35) + [Test] + public void CollectionChanged() + { + JProperty p1 = new JProperty("Test1", 1); + JProperty p2 = new JProperty("Test2", "Two"); + JObject o = new JObject(p1, p2); + + NotifyCollectionChangedAction? changedType = null; + int? index = null; + + o.CollectionChanged += (s, a) => + { + changedType = a.Action; + index = a.NewStartingIndex; + }; + + JProperty p3 = new JProperty("Test3", "III"); + + o.Add(p3); + Assert.AreEqual(changedType, NotifyCollectionChangedAction.Add); + Assert.AreEqual(index, 2); + Assert.AreEqual(p3, ((IList)o)[index.Value]); + + JProperty p4 = new JProperty("Test4", "IV"); + + ((IList)o)[index.Value] = p4; + Assert.AreEqual(changedType, NotifyCollectionChangedAction.Replace); + Assert.AreEqual(index, 2); + Assert.AreEqual(p4, ((IList)o)[index.Value]); + Assert.IsFalse(((IList)o).Contains(p3)); + Assert.IsTrue(((IList)o).Contains(p4)); + + o["Test1"] = 2; + Assert.AreEqual(changedType, NotifyCollectionChangedAction.Replace); + Assert.AreEqual(index, 0); + Assert.AreEqual(2, (int)o["Test1"]); + } +#endif + + [Test] + public void GetGeocodeAddress() + { + string json = @"{ + ""name"": ""Address: 435 North Mulford Road Rockford, IL 61107"", + ""Status"": { + ""code"": 200, + ""request"": ""geocode"" + }, + ""Placemark"": [ { + ""id"": ""p1"", + ""address"": ""435 N Mulford Rd, Rockford, IL 61107, USA"", + ""AddressDetails"": { + ""Accuracy"" : 8, + ""Country"" : { + ""AdministrativeArea"" : { + ""AdministrativeAreaName"" : ""IL"", + ""SubAdministrativeArea"" : { + ""Locality"" : { + ""LocalityName"" : ""Rockford"", + ""PostalCode"" : { + ""PostalCodeNumber"" : ""61107"" + }, + ""Thoroughfare"" : { + ""ThoroughfareName"" : ""435 N Mulford Rd"" + } + }, + ""SubAdministrativeAreaName"" : ""Winnebago"" + } + }, + ""CountryName"" : ""USA"", + ""CountryNameCode"" : ""US"" + } +}, + ""ExtendedData"": { + ""LatLonBox"": { + ""north"": 42.2753076, + ""south"": 42.2690124, + ""east"": -88.9964645, + ""west"": -89.0027597 + } + }, + ""Point"": { + ""coordinates"": [ -88.9995886, 42.2721596, 0 ] + } + } ] +}"; + + JObject o = JObject.Parse(json); + + string searchAddress = (string)o["Placemark"][0]["AddressDetails"]["Country"]["AdministrativeArea"]["SubAdministrativeArea"]["Locality"]["Thoroughfare"]["ThoroughfareName"]; + Assert.AreEqual("435 N Mulford Rd", searchAddress); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Set JObject values with invalid key value: 0. Object property name expected.")] + public void SetValueWithInvalidPropertyName() + { + JObject o = new JObject(); + o[0] = new JValue(3); + } + + [Test] + public void SetValue() + { + object key = "TestKey"; + + JObject o = new JObject(); + o[key] = new JValue(3); + + Assert.AreEqual(3, (int)o[key]); + } + + [Test] + public void ParseMultipleProperties() + { + string json = @"{ + ""Name"": ""Name1"", + ""Name"": ""Name2"" + }"; + + JObject o = JObject.Parse(json); + string value = (string)o["Name"]; + + Assert.AreEqual("Name2", value); + } + + [Test] + public void WriteObjectNullDBNullValue() + { + DBNull dbNull = DBNull.Value; + JValue v = new JValue(dbNull); + Assert.AreEqual(DBNull.Value, v.Value); + Assert.AreEqual(JTokenType.Null, v.Type); + + JObject o = new JObject(); + o["title"] = v; + + string output = o.ToString(); + + Assert.AreEqual(@"{ + ""title"": null +}", output); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not convert Object to String.")] + public void InvalidValueCastExceptionMessage() + { + string json = @"{ + ""responseData"": {}, + ""responseDetails"": null, + ""responseStatus"": 200 +}"; + + JObject o = JObject.Parse(json); + + string name = (string)o["responseData"]; + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not convert Object to String.")] + public void InvalidPropertyValueCastExceptionMessage() + { + string json = @"{ + ""responseData"": {}, + ""responseDetails"": null, + ""responseStatus"": 200 +}"; + + JObject o = JObject.Parse(json); + + string name = (string)o.Property("responseData"); + } + + [Test] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "JSON integer 307953220000517141511 is too large or small for an Int64.")] + public void NumberTooBigForInt64() + { + string json = @"{""code"": 307953220000517141511}"; + + JObject.Parse(json); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Unexpected end of content while loading JObject.")] + public void ParseIncomplete() + { + JObject.Parse("{ foo:"); + } + + [Test] + public void LoadFromNestedObject() + { + string jsonText = @"{ + ""short"": + { + ""error"": + { + ""code"":0, + ""msg"":""No action taken"" + } + } +}"; + + JsonReader reader = new JsonTextReader(new StringReader(jsonText)); + reader.Read(); + reader.Read(); + reader.Read(); + reader.Read(); + reader.Read(); + + JObject o = (JObject)JToken.ReadFrom(reader); + Assert.IsNotNull(o); + Assert.AreEqual(@"{ + ""code"": 0, + ""msg"": ""No action taken"" +}", o.ToString(Formatting.Indented)); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Unexpected end of content while loading JObject.")] + public void LoadFromNestedObjectIncomplete() + { + string jsonText = @"{ + ""short"": + { + ""error"": + { + ""code"":0"; + + JsonReader reader = new JsonTextReader(new StringReader(jsonText)); + reader.Read(); + reader.Read(); + reader.Read(); + reader.Read(); + reader.Read(); + + JToken.ReadFrom(reader); + } + +#if !SILVERLIGHT + [Test] + public void GetProperties() + { + JObject o = JObject.Parse("{'prop1':12,'prop2':'hi!','prop3':null,'prop4':[1,2,3]}"); + + ICustomTypeDescriptor descriptor = o; + + PropertyDescriptorCollection properties = descriptor.GetProperties(); + Assert.AreEqual(4, properties.Count); + + PropertyDescriptor prop1 = properties[0]; + Assert.AreEqual("prop1", prop1.Name); + Assert.AreEqual(typeof(long), prop1.PropertyType); + Assert.AreEqual(typeof(JObject), prop1.ComponentType); + Assert.AreEqual(false, prop1.CanResetValue(o)); + Assert.AreEqual(false, prop1.ShouldSerializeValue(o)); + + PropertyDescriptor prop2 = properties[1]; + Assert.AreEqual("prop2", prop2.Name); + Assert.AreEqual(typeof(string), prop2.PropertyType); + Assert.AreEqual(typeof(JObject), prop2.ComponentType); + Assert.AreEqual(false, prop2.CanResetValue(o)); + Assert.AreEqual(false, prop2.ShouldSerializeValue(o)); + + PropertyDescriptor prop3 = properties[2]; + Assert.AreEqual("prop3", prop3.Name); + Assert.AreEqual(typeof(object), prop3.PropertyType); + Assert.AreEqual(typeof(JObject), prop3.ComponentType); + Assert.AreEqual(false, prop3.CanResetValue(o)); + Assert.AreEqual(false, prop3.ShouldSerializeValue(o)); + + PropertyDescriptor prop4 = properties[3]; + Assert.AreEqual("prop4", prop4.Name); + Assert.AreEqual(typeof(JArray), prop4.PropertyType); + Assert.AreEqual(typeof(JObject), prop4.ComponentType); + Assert.AreEqual(false, prop4.CanResetValue(o)); + Assert.AreEqual(false, prop4.ShouldSerializeValue(o)); + } +#endif + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JPathTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JPathTests.cs new file mode 100644 index 0000000..b5eeefd --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JPathTests.cs @@ -0,0 +1,322 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Converters; +using System.IO; +using System.Collections; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JPathTests : TestFixtureBase + { + [Test] + public void SingleProperty() + { + JPath path = new JPath("Blah"); + Assert.AreEqual(1, path.Parts.Count); + Assert.AreEqual("Blah", path.Parts[0]); + } + + [Test] + public void TwoProperties() + { + JPath path = new JPath("Blah.Two"); + Assert.AreEqual(2, path.Parts.Count); + Assert.AreEqual("Blah", path.Parts[0]); + Assert.AreEqual("Two", path.Parts[1]); + } + + [Test] + public void SinglePropertyAndIndexer() + { + JPath path = new JPath("Blah[0]"); + Assert.AreEqual(2, path.Parts.Count); + Assert.AreEqual("Blah", path.Parts[0]); + Assert.AreEqual(0, path.Parts[1]); + } + + [Test] + public void MultiplePropertiesAndIndexers() + { + JPath path = new JPath("Blah[0].Two.Three[1].Four"); + Assert.AreEqual(6, path.Parts.Count); + Assert.AreEqual("Blah", path.Parts[0]); + Assert.AreEqual(0, path.Parts[1]); + Assert.AreEqual("Two", path.Parts[2]); + Assert.AreEqual("Three", path.Parts[3]); + Assert.AreEqual(1, path.Parts[4]); + Assert.AreEqual("Four", path.Parts[5]); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = @"Unexpected character while parsing path indexer: [")] + public void BadCharactersInIndexer() + { + new JPath("Blah[[0]].Two.Three[1].Four"); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = @"Path ended with open indexer. Expected ]")] + public void UnclosedIndexer() + { + new JPath("Blah[0"); + } + + [Test] + public void AdditionalDots() + { + JPath path = new JPath(".Blah..[0]..Two.Three....[1].Four."); + Assert.AreEqual(6, path.Parts.Count); + Assert.AreEqual("Blah", path.Parts[0]); + Assert.AreEqual(0, path.Parts[1]); + Assert.AreEqual("Two", path.Parts[2]); + Assert.AreEqual("Three", path.Parts[3]); + Assert.AreEqual(1, path.Parts[4]); + Assert.AreEqual("Four", path.Parts[5]); + } + + [Test] + public void IndexerOnly() + { + JPath path = new JPath("[111119990]"); + Assert.AreEqual(1, path.Parts.Count); + Assert.AreEqual(111119990, path.Parts[0]); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Empty path indexer.")] + public void EmptyIndexer() + { + new JPath("[]"); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Unexpected character while parsing path: ]")] + public void IndexerCloseInProperty() + { + new JPath("]"); + } + + [Test] + public void AdjacentIndexers() + { + JPath path = new JPath("[1][0][0][" + int.MaxValue + "]"); + Assert.AreEqual(4, path.Parts.Count); + Assert.AreEqual(1, path.Parts[0]); + Assert.AreEqual(0, path.Parts[1]); + Assert.AreEqual(0, path.Parts[2]); + Assert.AreEqual(int.MaxValue, path.Parts[3]); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Unexpected character following indexer: B")] + public void MissingDotAfterIndexer() + { + new JPath("[1]Blah"); + } + + [Test] + public void EvaluateSingleProperty() + { + JObject o = new JObject( + new JProperty("Blah", 1)); + + JToken t = o.SelectToken("Blah"); + Assert.IsNotNull(t); + Assert.AreEqual(JTokenType.Integer, t.Type); + Assert.AreEqual(1, (int)t); + } + + [Test] + public void EvaluateMissingProperty() + { + JObject o = new JObject( + new JProperty("Blah", 1)); + + JToken t = o.SelectToken("Missing[1]"); + Assert.IsNull(t); + } + + [Test] + public void EvaluateIndexerOnObject() + { + JObject o = new JObject( + new JProperty("Blah", 1)); + + JToken t = o.SelectToken("[1]"); + Assert.IsNull(t); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = @"Index 1 not valid on JObject.")] + public void EvaluateIndexerOnObjectWithError() + { + JObject o = new JObject( + new JProperty("Blah", 1)); + + o.SelectToken("[1]", true); + } + + [Test] + public void EvaluatePropertyOnArray() + { + JArray a = new JArray(1, 2, 3, 4, 5); + + JToken t = a.SelectToken("BlahBlah"); + Assert.IsNull(t); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = @"Property 'BlahBlah' not valid on JArray.")] + public void EvaluatePropertyOnArrayWithError() + { + JArray a = new JArray(1, 2, 3, 4, 5); + + a.SelectToken("BlahBlah", true); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = @"Index 1 not valid on JConstructor.")] + public void EvaluateIndexerOnConstructorWithError() + { + JConstructor c = new JConstructor("Blah"); + + c.SelectToken("[1]", true); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Property 'Missing' does not exist on JObject.")] + public void EvaluateMissingPropertyWithError() + { + JObject o = new JObject( + new JProperty("Blah", 1)); + + o.SelectToken("Missing", true); + } + + [Test] + public void EvaluateOutOfBoundsIndxer() + { + JArray a = new JArray(1, 2, 3, 4, 5); + + JToken t = a.SelectToken("[1000].Ha"); + Assert.IsNull(t); + } + + [Test] + [ExpectedException(typeof(IndexOutOfRangeException), ExpectedMessage = "Index 1000 outside the bounds of JArray.")] + public void EvaluateOutOfBoundsIndxerWithError() + { + JArray a = new JArray(1, 2, 3, 4, 5); + + a.SelectToken("[1000].Ha", true); + } + + [Test] + public void EvaluateArray() + { + JArray a = new JArray(1, 2, 3, 4); + + JToken t = a.SelectToken("[1]"); + Assert.IsNotNull(t); + Assert.AreEqual(JTokenType.Integer, t.Type); + Assert.AreEqual(2, (int)t); + } + + [Test] + public void EvaluateSinglePropertyReturningArray() + { + JObject o = new JObject( + new JProperty("Blah", new [] { 1, 2, 3 })); + + JToken t = o.SelectToken("Blah"); + Assert.IsNotNull(t); + Assert.AreEqual(JTokenType.Array, t.Type); + + t = o.SelectToken("Blah[2]"); + Assert.AreEqual(JTokenType.Integer, t.Type); + Assert.AreEqual(3, (int)t); + } + + [Test] + public void EvaluateLastSingleCharacterProperty() + { + JObject o2 = JObject.Parse("{'People':[{'N':'Jeff'}]}"); + string a2 = (string)o2.SelectToken("People[0].N"); + + Assert.AreEqual("Jeff", a2); + } + + [Test] + public void Example() + { + JObject o = JObject.Parse(@"{ + ""Stores"": [ + ""Lambton Quay"", + ""Willis Street"" + ], + ""Manufacturers"": [ + { + ""Name"": ""Acme Co"", + ""Products"": [ + { + ""Name"": ""Anvil"", + ""Price"": 50 + } + ] + }, + { + ""Name"": ""Contoso"", + ""Products"": [ + { + ""Name"": ""Elbow Grease"", + ""Price"": 99.95 + }, + { + ""Name"": ""Headlight Fluid"", + ""Price"": 4 + } + ] + } + ] + }"); + + string name = (string)o.SelectToken("Manufacturers[0].Name"); + // Acme Co + + decimal productPrice = (decimal)o.SelectToken("Manufacturers[0].Products[0].Price"); + // 50 + + string productName = (string)o.SelectToken("Manufacturers[1].Products[0].Name"); + // Elbow Grease + + Assert.AreEqual("Acme Co", name); + Assert.AreEqual(50m, productPrice); + Assert.AreEqual("Elbow Grease", productName); + + IList storeNames = o.SelectToken("Stores").Select(s => (string)s).ToList(); + // Lambton Quay + // Willis Street + + IList firstProductNames = o["Manufacturers"].Select(m => (string)m.SelectToken("Products[1].Name")).ToList(); + // null + // Headlight Fluid + + decimal totalPrice = o["Manufacturers"].Sum(m => (decimal)m.SelectToken("Products[0].Price")); + // 149.95 + + Assert.AreEqual(2, storeNames.Count); + Assert.AreEqual("Lambton Quay", storeNames[0]); + Assert.AreEqual("Willis Street", storeNames[1]); + Assert.AreEqual(2, firstProductNames.Count); + Assert.AreEqual(null, firstProductNames[0]); + Assert.AreEqual("Headlight Fluid", firstProductNames[1]); + Assert.AreEqual(149.95m, totalPrice); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JPropertyTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JPropertyTests.cs new file mode 100644 index 0000000..693d21e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JPropertyTests.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Linq; +using NUnit.Framework; +using System.IO; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JPropertyTests : TestFixtureBase + { + [Test] + public void NullValue() + { + JProperty p = new JProperty("TestProperty", null); + Assert.IsNotNull(p.Value); + Assert.AreEqual(JTokenType.Null, p.Value.Type); + Assert.AreEqual(p, p.Value.Parent); + + p.Value = null; + Assert.IsNotNull(p.Value); + Assert.AreEqual(JTokenType.Null, p.Value.Type); + Assert.AreEqual(p, p.Value.Parent); + } + +#if !SILVERLIGHT + [Test] + public void ListChanged() + { + JProperty p = new JProperty("TestProperty", null); + IBindingList l = p; + + ListChangedType? listChangedType = null; + int? index = null; + + l.ListChanged += (sender, args) => + { + listChangedType = args.ListChangedType; + index = args.NewIndex; + }; + + p.Value = 1; + + Assert.AreEqual(ListChangedType.ItemChanged, listChangedType.Value); + Assert.AreEqual(0, index.Value); + } +#endif + + [Test] + public void IListCount() + { + JProperty p = new JProperty("TestProperty", null); + IList l = p; + + Assert.AreEqual(1, l.Count); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Cannot add or remove items from Newtonsoft.Json.Linq.JProperty.")] + public void IListClear() + { + JProperty p = new JProperty("TestProperty", null); + IList l = p; + + l.Clear(); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Newtonsoft.Json.Linq.JProperty cannot have multiple values.")] + public void IListAdd() + { + JProperty p = new JProperty("TestProperty", null); + IList l = p; + + l.Add(null); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Cannot add or remove items from Newtonsoft.Json.Linq.JProperty.")] + public void IListRemove() + { + JProperty p = new JProperty("TestProperty", null); + IList l = p; + + l.Remove(p.Value); + } + + [Test] + public void Load() + { + JsonReader reader = new JsonTextReader(new StringReader("{'propertyname':['value1']}")); + reader.Read(); + + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + reader.Read(); + + JProperty property = JProperty.Load(reader); + Assert.AreEqual("propertyname", property.Name); + Assert.IsTrue(JToken.DeepEquals(JArray.Parse("['value1']"), property.Value)); + + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + + reader = new JsonTextReader(new StringReader("{'propertyname':null}")); + reader.Read(); + + Assert.AreEqual(JsonToken.StartObject, reader.TokenType); + reader.Read(); + + property = JProperty.Load(reader); + Assert.AreEqual("propertyname", property.Name); + Assert.IsTrue(JToken.DeepEquals(new JValue(null, JTokenType.Null), property.Value)); + + Assert.AreEqual(JsonToken.EndObject, reader.TokenType); + } + + [Test] + public void MultiContentConstructor() + { + JProperty p = new JProperty("error", new List { "one", "two" }); + JArray a = (JArray) p.Value; + + Assert.AreEqual(a.Count, 2); + Assert.AreEqual("one", (string)a[0]); + Assert.AreEqual("two", (string)a[1]); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Newtonsoft.Json.Linq.JProperty cannot have multiple values.")] + public void IListGenericAdd() + { + IList t = new JProperty("error", new List { "one", "two" }); + t.Add(1); + t.Add(2); + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JRawTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JRawTests.cs new file mode 100644 index 0000000..1cd8c74 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JRawTests.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JRawTests : TestFixtureBase + { + [Test] + public void RawEquals() + { + JRaw r1 = new JRaw("raw1"); + JRaw r2 = new JRaw("raw1"); + JRaw r3 = new JRaw("raw2"); + + Assert.IsTrue(JToken.DeepEquals(r1, r2)); + Assert.IsFalse(JToken.DeepEquals(r1, r3)); + } + + [Test] + public void RawClone() + { + JRaw r1 = new JRaw("raw1"); + JToken r2 = r1.CloneToken(); + + Assert.IsInstanceOfType(typeof(JRaw), r2); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JTokenEqualityComparerTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JTokenEqualityComparerTests.cs new file mode 100644 index 0000000..30a0e41 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JTokenEqualityComparerTests.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JTokenEqualityComparerTests : TestFixtureBase + { + [Test] + public void JValueDictionary() + { + Dictionary dic = new Dictionary(JToken.EqualityComparer); + JValue v11 = new JValue(1); + JValue v12 = new JValue(1); + + dic[v11] = 1; + dic[v12] += 1; + Assert.AreEqual(2, dic[v11]); + } + + [Test] + public void JArrayDictionary() + { + Dictionary dic = new Dictionary(JToken.EqualityComparer); + JArray v11 = new JArray(); + JArray v12 = new JArray(); + + dic[v11] = 1; + dic[v12] += 1; + Assert.AreEqual(2, dic[v11]); + } + + [Test] + public void JObjectDictionary() + { + Dictionary dic = new Dictionary(JToken.EqualityComparer); + JObject v11 = new JObject() { { "Test", new JValue(1) }, { "Test1", new JValue(1) } }; + JObject v12 = new JObject() { { "Test", new JValue(1) }, { "Test1", new JValue(1) } }; + + dic[v11] = 1; + dic[v12] += 1; + Assert.AreEqual(2, dic[v11]); + } + + [Test] + public void JConstructorDictionary() + { + Dictionary dic = new Dictionary(JToken.EqualityComparer); + JConstructor v11 = new JConstructor("ConstructorValue"); + JConstructor v12 = new JConstructor("ConstructorValue"); + + dic[v11] = 1; + dic[v12] += 1; + Assert.AreEqual(2, dic[v11]); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JTokenReaderTest.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JTokenReaderTest.cs new file mode 100644 index 0000000..d881c8d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JTokenReaderTest.cs @@ -0,0 +1,309 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json; +using System.IO; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JTokenReaderTest : TestFixtureBase + { +#if !PocketPC && !NET20 + [Test] + public void YahooFinance() + { + JObject o = + new JObject( + new JProperty("Test1", new DateTime(2000, 10, 15, 5, 5, 5, DateTimeKind.Utc)), + new JProperty("Test2", new DateTimeOffset(2000, 10, 15, 5, 5, 5, new TimeSpan(11, 11, 0))), + new JProperty("Test3", "Test3Value"), + new JProperty("Test4", null) + ); + + using (JTokenReader jsonReader = new JTokenReader(o)) + { + IJsonLineInfo lineInfo = jsonReader; + + jsonReader.Read(); + Assert.AreEqual(JsonToken.StartObject, jsonReader.TokenType); + Assert.AreEqual(false, lineInfo.HasLineInfo()); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.PropertyName, jsonReader.TokenType); + Assert.AreEqual("Test1", jsonReader.Value); + Assert.AreEqual(false, lineInfo.HasLineInfo()); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Date, jsonReader.TokenType); + Assert.AreEqual(new DateTime(2000, 10, 15, 5, 5, 5, DateTimeKind.Utc), jsonReader.Value); + Assert.AreEqual(false, lineInfo.HasLineInfo()); + Assert.AreEqual(0, lineInfo.LinePosition); + Assert.AreEqual(0, lineInfo.LineNumber); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.PropertyName, jsonReader.TokenType); + Assert.AreEqual("Test2", jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Date, jsonReader.TokenType); + Assert.AreEqual(new DateTimeOffset(2000, 10, 15, 5, 5, 5, new TimeSpan(11, 11, 0)), jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.PropertyName, jsonReader.TokenType); + Assert.AreEqual("Test3", jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.String, jsonReader.TokenType); + Assert.AreEqual("Test3Value", jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.PropertyName, jsonReader.TokenType); + Assert.AreEqual("Test4", jsonReader.Value); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.Null, jsonReader.TokenType); + Assert.AreEqual(null, jsonReader.Value); + + Assert.IsTrue(jsonReader.Read()); + Assert.AreEqual(JsonToken.EndObject, jsonReader.TokenType); + + Assert.IsFalse(jsonReader.Read()); + } + + using (JsonReader jsonReader = new JTokenReader(o.Property("Test2"))) + { + Assert.IsTrue(jsonReader.Read()); + Assert.AreEqual(JsonToken.PropertyName, jsonReader.TokenType); + Assert.AreEqual("Test2", jsonReader.Value); + + Assert.IsTrue(jsonReader.Read()); + Assert.AreEqual(JsonToken.Date, jsonReader.TokenType); + Assert.AreEqual(new DateTimeOffset(2000, 10, 15, 5, 5, 5, new TimeSpan(11, 11, 0)), jsonReader.Value); + + Assert.IsFalse(jsonReader.Read()); + } + } +#endif + + [Test] + public void ReadLineInfo() + { + string input = @"{ + CPU: 'Intel', + Drives: [ + 'DVD read/writer', + ""500 gigabyte hard drive"" + ] +}"; + + StringReader sr = new StringReader(input); + + JObject o = JObject.Parse(input); + + using (JTokenReader jsonReader = new JTokenReader(o)) + { + IJsonLineInfo lineInfo = jsonReader; + + Assert.AreEqual(jsonReader.TokenType, JsonToken.None); + Assert.AreEqual(0, lineInfo.LineNumber); + Assert.AreEqual(0, lineInfo.LinePosition); + Assert.AreEqual(false, lineInfo.HasLineInfo()); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.StartObject); + Assert.AreEqual(1, lineInfo.LineNumber); + Assert.AreEqual(1, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); + Assert.AreEqual(jsonReader.Value, "CPU"); + Assert.AreEqual(2, lineInfo.LineNumber); + Assert.AreEqual(6, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.String); + Assert.AreEqual(jsonReader.Value, "Intel"); + Assert.AreEqual(2, lineInfo.LineNumber); + Assert.AreEqual(14, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.PropertyName); + Assert.AreEqual(jsonReader.Value, "Drives"); + Assert.AreEqual(3, lineInfo.LineNumber); + Assert.AreEqual(9, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.StartArray); + Assert.AreEqual(3, lineInfo.LineNumber); + Assert.AreEqual(11, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.String); + Assert.AreEqual(jsonReader.Value, "DVD read/writer"); + Assert.AreEqual(4, lineInfo.LineNumber); + Assert.AreEqual(21, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.String); + Assert.AreEqual(jsonReader.Value, "500 gigabyte hard drive"); + Assert.AreEqual(5, lineInfo.LineNumber); + Assert.AreEqual(29, lineInfo.LinePosition); + Assert.AreEqual(true, lineInfo.HasLineInfo()); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.EndArray); + Assert.AreEqual(0, lineInfo.LineNumber); + Assert.AreEqual(0, lineInfo.LinePosition); + Assert.AreEqual(false, lineInfo.HasLineInfo()); + + jsonReader.Read(); + Assert.AreEqual(jsonReader.TokenType, JsonToken.EndObject); + Assert.AreEqual(0, lineInfo.LineNumber); + Assert.AreEqual(0, lineInfo.LinePosition); + Assert.AreEqual(false, lineInfo.HasLineInfo()); + } + } + + [Test] + public void ReadBytes() + { + byte[] data = Encoding.UTF8.GetBytes("Hello world!"); + + JObject o = + new JObject( + new JProperty("Test1", data) + ); + + using (JTokenReader jsonReader = new JTokenReader(o)) + { + jsonReader.Read(); + Assert.AreEqual(JsonToken.StartObject, jsonReader.TokenType); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.PropertyName, jsonReader.TokenType); + Assert.AreEqual("Test1", jsonReader.Value); + + byte[] readBytes = jsonReader.ReadAsBytes(); + Assert.AreEqual(data, readBytes); + + Assert.IsTrue(jsonReader.Read()); + Assert.AreEqual(JsonToken.EndObject, jsonReader.TokenType); + + Assert.IsFalse(jsonReader.Read()); + } + } + + [Test] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = "Error reading bytes. Expected bytes but got Integer.")] + public void ReadBytesFailure() + { + JObject o = + new JObject( + new JProperty("Test1", 1) + ); + + using (JTokenReader jsonReader = new JTokenReader(o)) + { + jsonReader.Read(); + Assert.AreEqual(JsonToken.StartObject, jsonReader.TokenType); + + jsonReader.Read(); + Assert.AreEqual(JsonToken.PropertyName, jsonReader.TokenType); + Assert.AreEqual("Test1", jsonReader.Value); + + jsonReader.ReadAsBytes(); + } + } + + public class HasBytes + { + public byte[] Bytes { get; set; } + } + + [Test] + public void ReadBytesFromString() + { + var bytes = new HasBytes { Bytes = new byte[] { 1, 2, 3, 4 } }; + var json = JsonConvert.SerializeObject(bytes); + + TextReader textReader = new StringReader(json); + JsonReader jsonReader = new JsonTextReader(textReader); + + var jToken = JToken.ReadFrom(jsonReader); + + jsonReader = new JTokenReader(jToken); + + var result2 = (HasBytes)JsonSerializer.Create(null) + .Deserialize(jsonReader, typeof(HasBytes)); + + Assert.AreEqual(new byte[] { 1, 2, 3, 4 }, result2.Bytes); + } + + [Test] + public void ReadBytesFromEmptyString() + { + var bytes = new HasBytes { Bytes = new byte[0] }; + var json = JsonConvert.SerializeObject(bytes); + + TextReader textReader = new StringReader(json); + JsonReader jsonReader = new JsonTextReader(textReader); + + var jToken = JToken.ReadFrom(jsonReader); + + jsonReader = new JTokenReader(jToken); + + var result2 = (HasBytes)JsonSerializer.Create(null) + .Deserialize(jsonReader, typeof(HasBytes)); + + Assert.AreEqual(new byte[0], result2.Bytes); + } + + public class ReadAsBytesTestObject + { + public byte[] Data; + } + + [Test] + public void ReadAsBytesNull() + { + JsonSerializer s = new JsonSerializer(); + + JToken nullToken = JToken.ReadFrom(new JsonTextReader(new StringReader("{ Data: null }"))); + ReadAsBytesTestObject x = s.Deserialize(new JTokenReader(nullToken)); + Assert.IsNull(x.Data); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs new file mode 100644 index 0000000..7d6947b --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JTokenTests.cs @@ -0,0 +1,740 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; +using NUnit.Framework; +using Newtonsoft.Json.Linq; +using System.IO; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JTokenTests : TestFixtureBase + { + [Test] + public void ReadFrom() + { + JObject o = (JObject)JToken.ReadFrom(new JsonTextReader(new StringReader("{'pie':true}"))); + Assert.AreEqual(true, (bool)o["pie"]); + + JArray a = (JArray)JToken.ReadFrom(new JsonTextReader(new StringReader("[1,2,3]"))); + Assert.AreEqual(1, (int)a[0]); + Assert.AreEqual(2, (int)a[1]); + Assert.AreEqual(3, (int)a[2]); + + JsonReader reader = new JsonTextReader(new StringReader("{'pie':true}")); + reader.Read(); + reader.Read(); + + JProperty p = (JProperty)JToken.ReadFrom(reader); + Assert.AreEqual("pie", p.Name); + Assert.AreEqual(true, (bool)p.Value); + + JConstructor c = (JConstructor)JToken.ReadFrom(new JsonTextReader(new StringReader("new Date(1)"))); + Assert.AreEqual("Date", c.Name); + Assert.IsTrue(JToken.DeepEquals(new JValue(1), c.Values().ElementAt(0))); + + JValue v; + + v = (JValue)JToken.ReadFrom(new JsonTextReader(new StringReader(@"""stringvalue"""))); + Assert.AreEqual("stringvalue", (string)v); + + v = (JValue)JToken.ReadFrom(new JsonTextReader(new StringReader(@"1"))); + Assert.AreEqual(1, (int)v); + + v = (JValue)JToken.ReadFrom(new JsonTextReader(new StringReader(@"1.1"))); + Assert.AreEqual(1.1, (double)v); + } + + [Test] + public void Load() + { + JObject o = (JObject)JToken.Load(new JsonTextReader(new StringReader("{'pie':true}"))); + Assert.AreEqual(true, (bool)o["pie"]); + } + + [Test] + public void Parse() + { + JObject o = (JObject)JToken.Parse("{'pie':true}"); + Assert.AreEqual(true, (bool)o["pie"]); + } + + [Test] + public void Parent() + { + JArray v = new JArray(new JConstructor("TestConstructor"), new JValue(new DateTime(2000, 12, 20))); + + Assert.AreEqual(null, v.Parent); + + JObject o = + new JObject( + new JProperty("Test1", v), + new JProperty("Test2", "Test2Value"), + new JProperty("Test3", "Test3Value"), + new JProperty("Test4", null) + ); + + Assert.AreEqual(o.Property("Test1"), v.Parent); + + JProperty p = new JProperty("NewProperty", v); + + // existing value should still have same parent + Assert.AreEqual(o.Property("Test1"), v.Parent); + + // new value should be cloned + Assert.AreNotSame(p.Value, v); + + Assert.AreEqual((DateTime)((JValue)p.Value[1]).Value, (DateTime)((JValue)v[1]).Value); + + Assert.AreEqual(v, o["Test1"]); + + Assert.AreEqual(null, o.Parent); + JProperty o1 = new JProperty("O1", o); + Assert.AreEqual(o, o1.Value); + + Assert.AreNotEqual(null, o.Parent); + JProperty o2 = new JProperty("O2", o); + + Assert.AreNotSame(o1.Value, o2.Value); + Assert.AreEqual(o1.Value.Children().Count(), o2.Value.Children().Count()); + Assert.AreEqual(false, JToken.DeepEquals(o1, o2)); + Assert.AreEqual(true, JToken.DeepEquals(o1.Value, o2.Value)); + } + + [Test] + public void Next() + { + JArray a = + new JArray( + 5, + 6, + new JArray(7, 8), + new JArray(9, 10) + ); + + JToken next = a[0].Next; + Assert.AreEqual(6, (int)next); + + next = next.Next; + Assert.IsTrue(JToken.DeepEquals(new JArray(7, 8), next)); + + next = next.Next; + Assert.IsTrue(JToken.DeepEquals(new JArray(9, 10), next)); + + next = next.Next; + Assert.IsNull(next); + } + + [Test] + public void Previous() + { + JArray a = + new JArray( + 5, + 6, + new JArray(7, 8), + new JArray(9, 10) + ); + + JToken previous = a[3].Previous; + Assert.IsTrue(JToken.DeepEquals(new JArray(7, 8), previous)); + + previous = previous.Previous; + Assert.AreEqual(6, (int)previous); + + previous = previous.Previous; + Assert.AreEqual(5, (int)previous); + + previous = previous.Previous; + Assert.IsNull(previous); + } + + [Test] + public void Children() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3) + ); + + Assert.AreEqual(4, a.Count()); + Assert.AreEqual(3, a.Children().Count()); + } + + [Test] + public void BeforeAfter() + { + JArray a = + new JArray( + 5, + new JArray(1, 2, 3), + new JArray(1, 2, 3), + new JArray(1, 2, 3) + ); + + Assert.AreEqual(5, (int)a[1].Previous); + Assert.AreEqual(2, a[2].BeforeSelf().Count()); + //Assert.AreEqual(2, a[2].AfterSelf().Count()); + } + + [Test] + public void Casting() + { + Assert.AreEqual(new DateTime(2000, 12, 20), (DateTime)new JValue(new DateTime(2000, 12, 20))); +#if !PocketPC && !NET20 + Assert.AreEqual(new DateTimeOffset(2000, 12, 20, 23, 50, 10, TimeSpan.Zero), (DateTimeOffset)new JValue(new DateTimeOffset(2000, 12, 20, 23, 50, 10, TimeSpan.Zero))); + Assert.AreEqual(null, (DateTimeOffset?)new JValue((DateTimeOffset?)null)); + Assert.AreEqual(null, (DateTimeOffset?)(JValue)null); +#endif + Assert.AreEqual(true, (bool)new JValue(true)); + Assert.AreEqual(true, (bool?)new JValue(true)); + Assert.AreEqual(null, (bool?)((JValue)null)); + Assert.AreEqual(null, (bool?)new JValue((object)null)); + Assert.AreEqual(10, (long)new JValue(10)); + Assert.AreEqual(null, (long?)new JValue((long?)null)); + Assert.AreEqual(null, (long?)(JValue)null); + Assert.AreEqual(null, (int?)new JValue((int?)null)); + Assert.AreEqual(null, (int?)(JValue)null); + Assert.AreEqual(null, (DateTime?)new JValue((DateTime?)null)); + Assert.AreEqual(null, (DateTime?)(JValue)null); + Assert.AreEqual(null, (short?)new JValue((short?)null)); + Assert.AreEqual(null, (short?)(JValue)null); + Assert.AreEqual(null, (float?)new JValue((float?)null)); + Assert.AreEqual(null, (float?)(JValue)null); + Assert.AreEqual(null, (double?)new JValue((double?)null)); + Assert.AreEqual(null, (double?)(JValue)null); + Assert.AreEqual(null, (decimal?)new JValue((decimal?)null)); + Assert.AreEqual(null, (decimal?)(JValue)null); + Assert.AreEqual(null, (uint?)new JValue((uint?)null)); + Assert.AreEqual(null, (uint?)(JValue)null); + Assert.AreEqual(null, (sbyte?)new JValue((sbyte?)null)); + Assert.AreEqual(null, (sbyte?)(JValue)null); + Assert.AreEqual(null, (ulong?)new JValue((ulong?)null)); + Assert.AreEqual(null, (ulong?)(JValue)null); + Assert.AreEqual(null, (uint?)new JValue((uint?)null)); + Assert.AreEqual(null, (uint?)(JValue)null); + Assert.AreEqual(11.1f, (float)new JValue(11.1)); + Assert.AreEqual(float.MinValue, (float)new JValue(float.MinValue)); + Assert.AreEqual(1.1, (double)new JValue(1.1)); + Assert.AreEqual(uint.MaxValue, (uint)new JValue(uint.MaxValue)); + Assert.AreEqual(ulong.MaxValue, (ulong)new JValue(ulong.MaxValue)); + Assert.AreEqual(ulong.MaxValue, (ulong)new JProperty("Test", new JValue(ulong.MaxValue))); + Assert.AreEqual(null, (string)new JValue((string)null)); + Assert.AreEqual(5m, (decimal)(new JValue(5L))); + Assert.AreEqual(5m, (decimal?)(new JValue(5L))); + Assert.AreEqual(5f, (float)(new JValue(5L))); + Assert.AreEqual(5f, (float)(new JValue(5m))); + Assert.AreEqual(5f, (float?)(new JValue(5m))); + + byte[] data = new byte[0]; + Assert.AreEqual(data, (byte[])(new JValue(data))); + + Assert.AreEqual(5, (int)(new JValue(StringComparison.OrdinalIgnoreCase))); + } + + [Test] + public void ImplicitCastingTo() + { + Assert.IsTrue(JToken.DeepEquals(new JValue(new DateTime(2000, 12, 20)), (JValue)new DateTime(2000, 12, 20))); +#if !PocketPC && !NET20 + Assert.IsTrue(JToken.DeepEquals(new JValue(new DateTimeOffset(2000, 12, 20, 23, 50, 10, TimeSpan.Zero)), (JValue)new DateTimeOffset(2000, 12, 20, 23, 50, 10, TimeSpan.Zero))); + Assert.IsTrue(JToken.DeepEquals(new JValue((DateTimeOffset?)null), (JValue)(DateTimeOffset?)null)); +#endif + + Assert.IsTrue(JToken.DeepEquals(new JValue(true), (JValue)true)); + Assert.IsTrue(JToken.DeepEquals(new JValue(true), (JValue)(bool?)true)); + Assert.IsTrue(JToken.DeepEquals(new JValue((bool?)null), (JValue)(bool?)null)); + Assert.IsTrue(JToken.DeepEquals(new JValue(10), (JValue)10)); + Assert.IsTrue(JToken.DeepEquals(new JValue((long?)null), (JValue)(long?)null)); + Assert.IsTrue(JToken.DeepEquals(new JValue((DateTime?)null), (JValue)(DateTime?)null)); + Assert.IsTrue(JToken.DeepEquals(new JValue(long.MaxValue), (JValue)long.MaxValue)); + Assert.IsTrue(JToken.DeepEquals(new JValue((int?)null), (JValue)(int?)null)); + Assert.IsTrue(JToken.DeepEquals(new JValue((short?)null), (JValue)(short?)null)); + Assert.IsTrue(JToken.DeepEquals(new JValue((double?)null), (JValue)(double?)null)); + Assert.IsTrue(JToken.DeepEquals(new JValue((uint?)null), (JValue)(uint?)null)); + Assert.IsTrue(JToken.DeepEquals(new JValue((decimal?)null), (JValue)(decimal?)null)); + Assert.IsTrue(JToken.DeepEquals(new JValue((ulong?)null), (JValue)(ulong?)null)); + Assert.IsTrue(JToken.DeepEquals(new JValue((sbyte?)null), (JValue)(sbyte?)null)); + Assert.IsTrue(JToken.DeepEquals(new JValue((ushort?)null), (JValue)(ushort?)null)); + Assert.IsTrue(JToken.DeepEquals(new JValue(ushort.MaxValue), (JValue)ushort.MaxValue)); + Assert.IsTrue(JToken.DeepEquals(new JValue(11.1f), (JValue)11.1f)); + Assert.IsTrue(JToken.DeepEquals(new JValue(float.MinValue), (JValue)float.MinValue)); + Assert.IsTrue(JToken.DeepEquals(new JValue(double.MinValue), (JValue)double.MinValue)); + Assert.IsTrue(JToken.DeepEquals(new JValue(uint.MaxValue), (JValue)uint.MaxValue)); + Assert.IsTrue(JToken.DeepEquals(new JValue(ulong.MaxValue), (JValue)ulong.MaxValue)); + Assert.IsTrue(JToken.DeepEquals(new JValue(ulong.MinValue), (JValue)ulong.MinValue)); + Assert.IsTrue(JToken.DeepEquals(new JValue((string)null), (JValue)(string)null)); + Assert.IsTrue(JToken.DeepEquals(new JValue((DateTime?)null), (JValue)(DateTime?)null)); + Assert.IsTrue(JToken.DeepEquals(new JValue(decimal.MaxValue), (JValue)decimal.MaxValue)); + Assert.IsTrue(JToken.DeepEquals(new JValue(decimal.MaxValue), (JValue)(decimal?)decimal.MaxValue)); + Assert.IsTrue(JToken.DeepEquals(new JValue(decimal.MinValue), (JValue)decimal.MinValue)); + Assert.IsTrue(JToken.DeepEquals(new JValue(float.MaxValue), (JValue)(float?)float.MaxValue)); + Assert.IsTrue(JToken.DeepEquals(new JValue(double.MaxValue), (JValue)(double?)double.MaxValue)); + Assert.IsTrue(JToken.DeepEquals(new JValue((object)null), (JValue)(double?)null)); + + Assert.IsFalse(JToken.DeepEquals(new JValue(true), (JValue)(bool?)null)); + Assert.IsFalse(JToken.DeepEquals(new JValue((object)null), (JValue)(object)null)); + + byte[] emptyData = new byte[0]; + Assert.IsTrue(JToken.DeepEquals(new JValue(emptyData), (JValue)emptyData)); + Assert.IsFalse(JToken.DeepEquals(new JValue(emptyData), (JValue)new byte[1])); + Assert.IsTrue(JToken.DeepEquals(new JValue(Encoding.UTF8.GetBytes("Hi")), (JValue)Encoding.UTF8.GetBytes("Hi"))); + } + + [Test] + public void Root() + { + JArray a = + new JArray( + 5, + 6, + new JArray(7, 8), + new JArray(9, 10) + ); + + Assert.AreEqual(a, a.Root); + Assert.AreEqual(a, a[0].Root); + Assert.AreEqual(a, ((JArray)a[2])[0].Root); + } + + [Test] + public void Remove() + { + JToken t; + JArray a = + new JArray( + 5, + 6, + new JArray(7, 8), + new JArray(9, 10) + ); + + a[0].Remove(); + + Assert.AreEqual(6, (int)a[0]); + + a[1].Remove(); + + Assert.AreEqual(6, (int)a[0]); + Assert.IsTrue(JToken.DeepEquals(new JArray(9, 10), a[1])); + Assert.AreEqual(2, a.Count()); + + t = a[1]; + t.Remove(); + Assert.AreEqual(6, (int)a[0]); + Assert.IsNull(t.Next); + Assert.IsNull(t.Previous); + Assert.IsNull(t.Parent); + + t = a[0]; + t.Remove(); + Assert.AreEqual(0, a.Count()); + + Assert.IsNull(t.Next); + Assert.IsNull(t.Previous); + Assert.IsNull(t.Parent); + } + + [Test] + public void AfterSelf() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3) + ); + + JToken t = a[1]; + List afterTokens = t.AfterSelf().ToList(); + + Assert.AreEqual(2, afterTokens.Count); + Assert.IsTrue(JToken.DeepEquals(new JArray(1, 2), afterTokens[0])); + Assert.IsTrue(JToken.DeepEquals(new JArray(1, 2, 3), afterTokens[1])); + } + + [Test] + public void BeforeSelf() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3) + ); + + JToken t = a[2]; + List beforeTokens = t.BeforeSelf().ToList(); + + Assert.AreEqual(2, beforeTokens.Count); + Assert.IsTrue(JToken.DeepEquals(new JValue(5), beforeTokens[0])); + Assert.IsTrue(JToken.DeepEquals(new JArray(1), beforeTokens[1])); + } + + [Test] + public void HasValues() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3) + ); + + Assert.IsTrue(a.HasValues); + } + + [Test] + public void Ancestors() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3) + ); + + JToken t = a[1][0]; + List ancestors = t.Ancestors().ToList(); + Assert.AreEqual(2, ancestors.Count()); + Assert.AreEqual(a[1], ancestors[0]); + Assert.AreEqual(a, ancestors[1]); + } + + [Test] + public void Descendants() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3) + ); + + List descendants = a.Descendants().ToList(); + Assert.AreEqual(10, descendants.Count()); + Assert.AreEqual(5, (int)descendants[0]); + Assert.IsTrue(JToken.DeepEquals(new JArray(1, 2, 3), descendants[descendants.Count - 4])); + Assert.AreEqual(1, (int)descendants[descendants.Count - 3]); + Assert.AreEqual(2, (int)descendants[descendants.Count - 2]); + Assert.AreEqual(3, (int)descendants[descendants.Count - 1]); + } + + [Test] + public void CreateWriter() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3) + ); + + JsonWriter writer = a.CreateWriter(); + Assert.IsNotNull(writer); + Assert.AreEqual(4, a.Count()); + + writer.WriteValue("String"); + Assert.AreEqual(5, a.Count()); + Assert.AreEqual("String", (string)a[4]); + + writer.WriteStartObject(); + writer.WritePropertyName("Property"); + writer.WriteValue("PropertyValue"); + writer.WriteEnd(); + + Assert.AreEqual(6, a.Count()); + Assert.IsTrue(JToken.DeepEquals(new JObject(new JProperty("Property", "PropertyValue")), a[5])); + } + + [Test] + public void AddFirst() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3) + ); + + a.AddFirst("First"); + + Assert.AreEqual("First", (string)a[0]); + Assert.AreEqual(a, a[0].Parent); + Assert.AreEqual(a[1], a[0].Next); + Assert.AreEqual(5, a.Count()); + + a.AddFirst("NewFirst"); + Assert.AreEqual("NewFirst", (string)a[0]); + Assert.AreEqual(a, a[0].Parent); + Assert.AreEqual(a[1], a[0].Next); + Assert.AreEqual(6, a.Count()); + + Assert.AreEqual(a[0], a[0].Next.Previous); + } + + [Test] + public void RemoveAll() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3) + ); + + JToken first = a.First; + Assert.AreEqual(5, (int)first); + + a.RemoveAll(); + Assert.AreEqual(0, a.Count()); + + Assert.IsNull(first.Parent); + Assert.IsNull(first.Next); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add Newtonsoft.Json.Linq.JProperty to Newtonsoft.Json.Linq.JArray.")] + public void AddPropertyToArray() + { + JArray a = new JArray(); + a.Add(new JProperty("PropertyName")); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not add Newtonsoft.Json.Linq.JValue to Newtonsoft.Json.Linq.JObject.")] + public void AddValueToObject() + { + JObject o = new JObject(); + o.Add(5); + } + + [Test] + public void Replace() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3) + ); + + a[0].Replace(new JValue(int.MaxValue)); + Assert.AreEqual(int.MaxValue, (int)a[0]); + Assert.AreEqual(4, a.Count()); + + a[1][0].Replace(new JValue("Test")); + Assert.AreEqual("Test", (string)a[1][0]); + + a[2].Replace(new JValue(int.MaxValue)); + Assert.AreEqual(int.MaxValue, (int)a[2]); + Assert.AreEqual(4, a.Count()); + + Assert.IsTrue(JToken.DeepEquals(new JArray(int.MaxValue, new JArray("Test"), int.MaxValue, new JArray(1, 2, 3)), a)); + } + + [Test] + public void ToStringWithConverters() + { + JArray a = + new JArray( + new JValue(new DateTime(2009, 2, 15, 0, 0, 0, DateTimeKind.Utc)) + ); + + string json = a.ToString(Formatting.Indented, new IsoDateTimeConverter()); + + Assert.AreEqual(@"[ + ""2009-02-15T00:00:00Z"" +]", json); + + json = JsonConvert.SerializeObject(a, new IsoDateTimeConverter()); + + Assert.AreEqual(@"[""2009-02-15T00:00:00Z""]", json); + } + + [Test] + public void ToStringWithNoIndenting() + { + JArray a = + new JArray( + new JValue(new DateTime(2009, 2, 15, 0, 0, 0, DateTimeKind.Utc)) + ); + + string json = a.ToString(Formatting.None, new IsoDateTimeConverter()); + + Assert.AreEqual(@"[""2009-02-15T00:00:00Z""]", json); + } + + [Test] + public void AddAfterSelf() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3) + ); + + a[1].AddAfterSelf("pie"); + + Assert.AreEqual(5, (int)a[0]); + Assert.AreEqual(1, a[1].Count()); + Assert.AreEqual("pie", (string)a[2]); + Assert.AreEqual(5, a.Count()); + + a[4].AddAfterSelf("lastpie"); + + Assert.AreEqual("lastpie", (string)a[5]); + Assert.AreEqual("lastpie", (string)a.Last); + } + + [Test] + public void AddBeforeSelf() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3) + ); + + a[1].AddBeforeSelf("pie"); + + Assert.AreEqual(5, (int)a[0]); + Assert.AreEqual("pie", (string)a[1]); + Assert.AreEqual(a, a[1].Parent); + Assert.AreEqual(a[2], a[1].Next); + Assert.AreEqual(5, a.Count()); + + a[0].AddBeforeSelf("firstpie"); + + Assert.AreEqual("firstpie", (string)a[0]); + Assert.AreEqual(5, (int)a[1]); + Assert.AreEqual("pie", (string)a[2]); + Assert.AreEqual(a, a[0].Parent); + Assert.AreEqual(a[1], a[0].Next); + Assert.AreEqual(6, a.Count()); + + a.Last.AddBeforeSelf("secondlastpie"); + + Assert.AreEqual("secondlastpie", (string)a[5]); + Assert.AreEqual(7, a.Count()); + } + + [Test] + public void DeepClone() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3), + new JObject( + new JProperty("First", new JValue(Encoding.UTF8.GetBytes("Hi"))), + new JProperty("Second", 1), + new JProperty("Third", null), + new JProperty("Fourth", new JConstructor("Date", 12345)), + new JProperty("Fifth", double.PositiveInfinity), + new JProperty("Sixth", double.NaN) + ) + ); + + JArray a2 = (JArray)a.DeepClone(); + + Console.WriteLine(a2.ToString(Formatting.Indented)); + + Assert.IsTrue(a.DeepEquals(a2)); + } + +#if !SILVERLIGHT + [Test] + public void Clone() + { + JArray a = + new JArray( + 5, + new JArray(1), + new JArray(1, 2), + new JArray(1, 2, 3), + new JObject( + new JProperty("First", new JValue(Encoding.UTF8.GetBytes("Hi"))), + new JProperty("Second", 1), + new JProperty("Third", null), + new JProperty("Fourth", new JConstructor("Date", 12345)), + new JProperty("Fifth", double.PositiveInfinity), + new JProperty("Sixth", double.NaN) + ) + ); + + ICloneable c = a; + + JArray a2 = (JArray) c.Clone(); + + Assert.IsTrue(a.DeepEquals(a2)); + } +#endif + + [Test] + public void DoubleDeepEquals() + { + JArray a = + new JArray( + double.NaN, + double.PositiveInfinity, + double.NegativeInfinity + ); + + JArray a2 = (JArray)a.DeepClone(); + + Assert.IsTrue(a.DeepEquals(a2)); + + double d = 1 + 0.1 + 0.1 + 0.1; + + JValue v1 = new JValue(d); + JValue v2 = new JValue(1.3); + + Assert.IsTrue(v1.DeepEquals(v2)); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs new file mode 100644 index 0000000..52c4e49 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JTokenWriterTest.cs @@ -0,0 +1,167 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json; +using System.IO; +using Newtonsoft.Json.Linq; +using System.Linq; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JTokenWriterTest : TestFixtureBase + { + [Test] + public void ValueFormatting() + { + byte[] data = Encoding.UTF8.GetBytes("Hello world."); + + JToken root; + using (JTokenWriter jsonWriter = new JTokenWriter()) + { + jsonWriter.WriteStartArray(); + jsonWriter.WriteValue('@'); + jsonWriter.WriteValue("\r\n\t\f\b?{\\r\\n\"\'"); + jsonWriter.WriteValue(true); + jsonWriter.WriteValue(10); + jsonWriter.WriteValue(10.99); + jsonWriter.WriteValue(0.99); + jsonWriter.WriteValue(0.000000000000000001d); + jsonWriter.WriteValue(0.000000000000000001m); + jsonWriter.WriteValue((string)null); + jsonWriter.WriteValue("This is a string."); + jsonWriter.WriteNull(); + jsonWriter.WriteUndefined(); + jsonWriter.WriteValue(data); + jsonWriter.WriteEndArray(); + + root = jsonWriter.Token; + } + + Assert.IsInstanceOfType(typeof(JArray), root); + Assert.AreEqual(13, root.Children().Count()); + Assert.AreEqual("@", (string)root[0]); + Assert.AreEqual("\r\n\t\f\b?{\\r\\n\"\'", (string)root[1]); + Assert.AreEqual(true, (bool)root[2]); + Assert.AreEqual(10, (int)root[3]); + Assert.AreEqual(10.99, (double)root[4]); + Assert.AreEqual(0.99, (double)root[5]); + Assert.AreEqual(0.000000000000000001d, (double)root[6]); + Assert.AreEqual(0.000000000000000001m, (decimal)root[7]); + Assert.AreEqual(string.Empty, (string)root[8]); + Assert.AreEqual("This is a string.", (string)root[9]); + Assert.AreEqual(null, ((JValue)root[10]).Value); + Assert.AreEqual(null, ((JValue)root[11]).Value); + Assert.AreEqual(data, (byte[])root[12]); + } + + [Test] + public void State() + { + using (JsonWriter jsonWriter = new JTokenWriter()) + { + Assert.AreEqual(WriteState.Start, jsonWriter.WriteState); + + jsonWriter.WriteStartObject(); + Assert.AreEqual(WriteState.Object, jsonWriter.WriteState); + + jsonWriter.WritePropertyName("CPU"); + Assert.AreEqual(WriteState.Property, jsonWriter.WriteState); + + jsonWriter.WriteValue("Intel"); + Assert.AreEqual(WriteState.Object, jsonWriter.WriteState); + + jsonWriter.WritePropertyName("Drives"); + Assert.AreEqual(WriteState.Property, jsonWriter.WriteState); + + jsonWriter.WriteStartArray(); + Assert.AreEqual(WriteState.Array, jsonWriter.WriteState); + + jsonWriter.WriteValue("DVD read/writer"); + Assert.AreEqual(WriteState.Array, jsonWriter.WriteState); + + jsonWriter.WriteValue(new byte[0]); + Assert.AreEqual(WriteState.Array, jsonWriter.WriteState); + + jsonWriter.WriteEnd(); + Assert.AreEqual(WriteState.Object, jsonWriter.WriteState); + + jsonWriter.WriteEndObject(); + Assert.AreEqual(WriteState.Start, jsonWriter.WriteState); + } + } + + [Test] + public void WriteComment() + { + JTokenWriter writer = new JTokenWriter(); + + writer.WriteStartArray(); + writer.WriteComment("fail"); + writer.WriteEndArray(); + + Assert.AreEqual(@"[ + /*fail*/]", writer.Token.ToString()); + } + + [Test] + public void WriteRaw() + { + JTokenWriter writer = new JTokenWriter(); + + writer.WriteStartArray(); + writer.WriteRaw("fail"); + writer.WriteRaw("fail"); + writer.WriteEndArray(); + + // this is a bug. write raw shouldn't be autocompleting like this + // hard to fix without introducing Raw and RawValue token types + // meh + Assert.AreEqual(@"[ + fail, + fail +]", writer.Token.ToString()); + } + + [Test] + public void WriteRawValue() + { + JTokenWriter writer = new JTokenWriter(); + + writer.WriteStartArray(); + writer.WriteRawValue("fail"); + writer.WriteRawValue("fail"); + writer.WriteEndArray(); + + Assert.AreEqual(@"[ + fail, + fail +]", writer.Token.ToString()); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs new file mode 100644 index 0000000..74126d9 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/JValueTests.cs @@ -0,0 +1,242 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Linq; +using System.Globalization; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class JValueTests : TestFixtureBase + { + [Test] + public void ChangeValue() + { + JValue v = new JValue(true); + Assert.AreEqual(true, v.Value); + Assert.AreEqual(JTokenType.Boolean, v.Type); + + v.Value = "Pie"; + Assert.AreEqual("Pie", v.Value); + Assert.AreEqual(JTokenType.String, v.Type); + + v.Value = null; + Assert.AreEqual(null, v.Value); + Assert.AreEqual(JTokenType.Null, v.Type); + + v.Value = (int?)null; + Assert.AreEqual(null, v.Value); + Assert.AreEqual(JTokenType.Null, v.Type); + + v.Value = "Pie"; + Assert.AreEqual("Pie", v.Value); + Assert.AreEqual(JTokenType.String, v.Type); + + v.Value = DBNull.Value; + Assert.AreEqual(DBNull.Value, v.Value); + Assert.AreEqual(JTokenType.Null, v.Type); + + byte[] data = new byte[0]; + v.Value = data; + + Assert.AreEqual(data, v.Value); + Assert.AreEqual(JTokenType.Bytes, v.Type); + + v.Value = StringComparison.OrdinalIgnoreCase; + Assert.AreEqual(StringComparison.OrdinalIgnoreCase, v.Value); + Assert.AreEqual(JTokenType.Integer, v.Type); + } + + [Test] + public void CreateComment() + { + JValue commentValue = JValue.CreateComment(null); + Assert.AreEqual(null, commentValue.Value); + Assert.AreEqual(JTokenType.Comment, commentValue.Type); + + commentValue.Value = "Comment"; + Assert.AreEqual("Comment", commentValue.Value); + Assert.AreEqual(JTokenType.Comment, commentValue.Type); + } + + [Test] + public void CreateString() + { + JValue stringValue = JValue.CreateString(null); + Assert.AreEqual(null, stringValue.Value); + Assert.AreEqual(JTokenType.String, stringValue.Type); + } + + [Test] + public void ToString() + { + JValue v; + + v = new JValue(true); + Assert.AreEqual("True", v.ToString()); + + v = new JValue(Encoding.UTF8.GetBytes("Blah")); + Assert.AreEqual("System.Byte[]", v.ToString(null, CultureInfo.InvariantCulture)); + + v = new JValue("I am a string!"); + Assert.AreEqual("I am a string!", v.ToString()); + + v = new JValue(null, JTokenType.Null); + Assert.AreEqual("", v.ToString()); + + v = new JValue(null, JTokenType.Null); + Assert.AreEqual("", v.ToString(null, CultureInfo.InvariantCulture)); + + v = new JValue(new DateTime(2000, 12, 12, 20, 59, 59, DateTimeKind.Utc), JTokenType.Date); + Assert.AreEqual("12/12/2000 20:59:59", v.ToString(null, CultureInfo.InvariantCulture)); + } + + [Test] + [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot access child value on Newtonsoft.Json.Linq.JValue.")] + public void Last() + { + JValue v = new JValue(true); + JToken last = v.Last; + } + + [Test] + [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot access child value on Newtonsoft.Json.Linq.JValue.")] + public void Children() + { + JValue v = new JValue(true); + var c = v.Children(); + } + + [Test] + [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot access child value on Newtonsoft.Json.Linq.JValue.")] + public void First() + { + JValue v = new JValue(true); + JToken first = v.First; + } + + [Test] + [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot access child value on Newtonsoft.Json.Linq.JValue.")] + public void Item() + { + JValue v = new JValue(true); + JToken first = v[0]; + } + + [Test] + [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot access child value on Newtonsoft.Json.Linq.JValue.")] + public void Values() + { + JValue v = new JValue(true); + v.Values(); + } + + [Test] + [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "The parent is missing.")] + public void RemoveParentNull() + { + JValue v = new JValue(true); + v.Remove(); + } + + [Test] + public void Root() + { + JValue v = new JValue(true); + Assert.AreEqual(v, v.Root); + } + + [Test] + public void Previous() + { + JValue v = new JValue(true); + Assert.IsNull(v.Previous); + } + + [Test] + public void Next() + { + JValue v = new JValue(true); + Assert.IsNull(v.Next); + } + + [Test] + public void DeepEquals() + { + Assert.IsTrue(JToken.DeepEquals(new JValue(5L), new JValue(5))); + Assert.IsFalse(JToken.DeepEquals(new JValue(5M), new JValue(5))); + Assert.IsTrue(JToken.DeepEquals(new JValue((ulong)long.MaxValue), new JValue(long.MaxValue))); + } + + [Test] + public void HasValues() + { + Assert.IsFalse((new JValue(5L)).HasValues); + } + + [Test] + [ExpectedException(typeof(InvalidOperationException), ExpectedMessage = "Cannot set child value on Newtonsoft.Json.Linq.JValue.")] + public void SetValue() + { + JToken t = new JValue(5L); + t[0] = new JValue(3); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Can not convert Null to Int32.")] + public void CastNullValueToNonNullable() + { + JValue v = new JValue((object)null); + int i = (int) v; + } + + [Test] + public void ConvertValueToCompatibleType() + { + IComparable c = (new JValue(1).Value()); + Assert.AreEqual(1, c); + } + + [Test] + public void Ordering() + { + JObject o = new JObject( + new JProperty("Integer", new JValue(1)), + new JProperty("Float", new JValue(1.2d)), + new JProperty("Decimal", new JValue(1.1m)) + ); + + IList orderedValues = o.Values().Cast().OrderBy(v => v).Select(v => v.Value).ToList(); + + Assert.AreEqual(1, orderedValues[0]); + Assert.AreEqual(1.1, orderedValues[1]); + Assert.AreEqual(1.2, orderedValues[2]); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs new file mode 100644 index 0000000..ae47b57 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Linq/LinqToJsonTest.cs @@ -0,0 +1,786 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Linq; +using System.Xml; +using System.IO; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Tests.TestObjects; + +namespace Newtonsoft.Json.Tests.Linq +{ + public class LinqToJsonTest : TestFixtureBase + { + [Test] + public void DoubleValue() + { + JArray j = JArray.Parse("[-1E+4,100.0e-2]"); + + double value = (double)j[0]; + Assert.AreEqual(-10000d, value); + + value = (double)j[1]; + Assert.AreEqual(1d, value); + } + + [Test] + public void Manual() + { + JArray array = new JArray(); + JValue text = new JValue("Manual text"); + JValue date = new JValue(new DateTime(2000, 5, 23)); + + array.Add(text); + array.Add(date); + + string json = array.ToString(); + // [ + // "Manual text", + // "\/Date(958996800000+1200)\/" + // ] + } + + [Test] + public void LinqToJsonDeserialize() + { + JObject o = new JObject( + new JProperty("Name", "John Smith"), + new JProperty("BirthDate", new DateTime(1983, 3, 20)) + ); + + JsonSerializer serializer = new JsonSerializer(); + Person p = (Person)serializer.Deserialize(new JTokenReader(o), typeof(Person)); + + // John Smith + Console.WriteLine(p.Name); + } + + [Test] + public void ObjectParse() + { + string json = @"{ + CPU: 'Intel', + Drives: [ + 'DVD read/writer', + ""500 gigabyte hard drive"" + ] + }"; + + JObject o = JObject.Parse(json); + IList properties = o.Properties().ToList(); + + Assert.AreEqual("CPU", properties[0].Name); + Assert.AreEqual("Intel", (string)properties[0].Value); + Assert.AreEqual("Drives", properties[1].Name); + + JArray list = (JArray)properties[1].Value; + Assert.AreEqual(2, list.Children().Count()); + Assert.AreEqual("DVD read/writer", (string)list.Children().ElementAt(0)); + Assert.AreEqual("500 gigabyte hard drive", (string)list.Children().ElementAt(1)); + + List parameterValues = + (from p in o.Properties() + where p.Value is JValue + select ((JValue)p.Value).Value).ToList(); + + Assert.AreEqual(1, parameterValues.Count); + Assert.AreEqual("Intel", parameterValues[0]); + } + + [Test] + public void CreateLongArray() + { + string json = @"[0,1,2,3,4,5,6,7,8,9]"; + + JArray a = JArray.Parse(json); + List list = a.Values().ToList(); + + List expected = new List() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + CollectionAssert.AreEqual(expected, list); + } + + [Test] + public void GoogleSearchAPI() + { + #region GoogleJson + string json = @"{ + results: + [ + { + GsearchResultClass:""GwebSearch"", + unescapedUrl : ""http://www.google.com/"", + url : ""http://www.google.com/"", + visibleUrl : ""www.google.com"", + cacheUrl : +""http://www.google.com/search?q=cache:zhool8dxBV4J:www.google.com"", + title : ""Google"", + titleNoFormatting : ""Google"", + content : ""Enables users to search the Web, Usenet, and +images. Features include PageRank, caching and translation of +results, and an option to find similar pages."" + }, + { + GsearchResultClass:""GwebSearch"", + unescapedUrl : ""http://news.google.com/"", + url : ""http://news.google.com/"", + visibleUrl : ""news.google.com"", + cacheUrl : +""http://www.google.com/search?q=cache:Va_XShOz_twJ:news.google.com"", + title : ""Google News"", + titleNoFormatting : ""Google News"", + content : ""Aggregated headlines and a search engine of many of the world's news sources."" + }, + + { + GsearchResultClass:""GwebSearch"", + unescapedUrl : ""http://groups.google.com/"", + url : ""http://groups.google.com/"", + visibleUrl : ""groups.google.com"", + cacheUrl : +""http://www.google.com/search?q=cache:x2uPD3hfkn0J:groups.google.com"", + title : ""Google Groups"", + titleNoFormatting : ""Google Groups"", + content : ""Enables users to search and browse the Usenet +archives which consist of over 700 million messages, and post new +comments."" + }, + + { + GsearchResultClass:""GwebSearch"", + unescapedUrl : ""http://maps.google.com/"", + url : ""http://maps.google.com/"", + visibleUrl : ""maps.google.com"", + cacheUrl : +""http://www.google.com/search?q=cache:dkf5u2twBXIJ:maps.google.com"", + title : ""Google Maps"", + titleNoFormatting : ""Google Maps"", + content : ""Provides directions, interactive maps, and +satellite/aerial imagery of the United States. Can also search by +keyword such as type of business."" + } + ], + + adResults: + [ + { + GsearchResultClass:""GwebSearch.ad"", + title : ""Gartner Symposium/ITxpo"", + content1 : ""Meet brilliant Gartner IT analysts"", + content2 : ""20-23 May 2007- Barcelona, Spain"", + url : +""http://www.google.com/url?sa=L&ai=BVualExYGRo3hD5ianAPJvejjD8-s6ye7kdTwArbI4gTAlrECEAEYASDXtMMFOAFQubWAjvr_____AWDXw_4EiAEBmAEAyAEBgAIB&num=1&q=http://www.gartner.com/it/sym/2007/spr8/spr8.jsp%3Fsrc%3D_spain_07_%26WT.srch%3D1&usg=__CxRH06E4Xvm9Muq13S4MgMtnziY="", + + impressionUrl : +""http://www.google.com/uds/css/ad-indicator-on.gif?ai=BVualExYGRo3hD5ianAPJvejjD8-s6ye7kdTwArbI4gTAlrECEAEYASDXtMMFOAFQubWAjvr_____AWDXw_4EiAEBmAEAyAEBgAIB"", + + unescapedUrl : +""http://www.google.com/url?sa=L&ai=BVualExYGRo3hD5ianAPJvejjD8-s6ye7kdTwArbI4gTAlrECEAEYASDXtMMFOAFQubWAjvr_____AWDXw_4EiAEBmAEAyAEBgAIB&num=1&q=http://www.gartner.com/it/sym/2007/spr8/spr8.jsp%3Fsrc%3D_spain_07_%26WT.srch%3D1&usg=__CxRH06E4Xvm9Muq13S4MgMtnziY="", + + visibleUrl : ""www.gartner.com"" + } + ] +} +"; + #endregion + + JObject o = JObject.Parse(json); + + List resultObjects = o["results"].Children().ToList(); + + Assert.AreEqual(32, resultObjects.Properties().Count()); + + Assert.AreEqual(32, resultObjects.Cast().Values().Count()); + + Assert.AreEqual(4, resultObjects.Cast().Values("GsearchResultClass").Count()); + + Assert.AreEqual(5, o.PropertyValues().Cast().Children().Count()); + + List resultUrls = o["results"].Children().Values("url").ToList(); + + List expectedUrls = new List() { "http://www.google.com/", "http://news.google.com/", "http://groups.google.com/", "http://maps.google.com/" }; + + CollectionAssert.AreEqual(expectedUrls, resultUrls); + + List descendants = o.Descendants().ToList(); + Assert.AreEqual(89, descendants.Count); + } + + [Test] + public void JTokenToString() + { + string json = @"{ + CPU: 'Intel', + Drives: [ + 'DVD read/writer', + ""500 gigabyte hard drive"" + ] +}"; + + JObject o = JObject.Parse(json); + + Assert.AreEqual(@"{ + ""CPU"": ""Intel"", + ""Drives"": [ + ""DVD read/writer"", + ""500 gigabyte hard drive"" + ] +}", o.ToString()); + + JArray list = o.Value("Drives"); + + Assert.AreEqual(@"[ + ""DVD read/writer"", + ""500 gigabyte hard drive"" +]", list.ToString()); + + JProperty cpuProperty = o.Property("CPU"); + Assert.AreEqual(@"""CPU"": ""Intel""", cpuProperty.ToString()); + + JProperty drivesProperty = o.Property("Drives"); + Assert.AreEqual(@"""Drives"": [ + ""DVD read/writer"", + ""500 gigabyte hard drive"" +]", drivesProperty.ToString()); + } + + [Test] + public void JTokenToStringTypes() + { + string json = @"{""Color"":2,""Establised"":new Date(1264118400000),""Width"":1.1,""Employees"":999,""RoomsPerFloor"":[1,2,3,4,5,6,7,8,9],""Open"":false,""Symbol"":""@"",""Mottos"":[""Hello World"",""öäüÖÄÜ\\'{new Date(12345);}[222]_µ@²³~"",null,"" ""],""Cost"":100980.1,""Escape"":""\r\n\t\f\b?{\\r\\n\""'"",""product"":[{""Name"":""Rocket"",""ExpiryDate"":new Date(949532490000),""Price"":0},{""Name"":""Alien"",""ExpiryDate"":new Date(-62135596800000),""Price"":0}]}"; + + JObject o = JObject.Parse(json); + + Assert.AreEqual(@"""Establised"": new Date( + 1264118400000 +)", o.Property("Establised").ToString()); + Assert.AreEqual(@"new Date( + 1264118400000 +)", o.Property("Establised").Value.ToString()); + Assert.AreEqual(@"""Width"": 1.1", o.Property("Width").ToString()); + Assert.AreEqual(@"1.1", ((JValue)o.Property("Width").Value).ToString(CultureInfo.InvariantCulture)); + Assert.AreEqual(@"""Open"": false", o.Property("Open").ToString()); + Assert.AreEqual(@"False", o.Property("Open").Value.ToString()); + + json = @"[null,undefined]"; + + JArray a = JArray.Parse(json); + Assert.AreEqual(@"[ + null, + undefined +]", a.ToString()); + Assert.AreEqual(@"", a.Children().ElementAt(0).ToString()); + Assert.AreEqual(@"", a.Children().ElementAt(1).ToString()); + } + + [Test] + public void CreateJTokenTree() + { + JObject o = + new JObject( + new JProperty("Test1", "Test1Value"), + new JProperty("Test2", "Test2Value"), + new JProperty("Test3", "Test3Value"), + new JProperty("Test4", null) + ); + + Assert.AreEqual(4, o.Properties().Count()); + + Assert.AreEqual(@"{ + ""Test1"": ""Test1Value"", + ""Test2"": ""Test2Value"", + ""Test3"": ""Test3Value"", + ""Test4"": null +}", o.ToString()); + + JArray a = + new JArray( + o, + new DateTime(2000, 10, 10, 0, 0, 0, DateTimeKind.Utc), + 55, + new JArray( + "1", + 2, + 3.0, + new DateTime(4, 5, 6, 7, 8, 9, DateTimeKind.Utc) + ), + new JConstructor( + "ConstructorName", + "param1", + 2, + 3.0 + ) + ); + + Assert.AreEqual(5, a.Count()); + Assert.AreEqual(@"[ + { + ""Test1"": ""Test1Value"", + ""Test2"": ""Test2Value"", + ""Test3"": ""Test3Value"", + ""Test4"": null + }, + ""\/Date(971136000000)\/"", + 55, + [ + ""1"", + 2, + 3.0, + ""\/Date(-62030076711000)\/"" + ], + new ConstructorName( + ""param1"", + 2, + 3.0 + ) +]", a.ToString()); + } + + private class Post + { + public string Title { get; set; } + public string Description { get; set; } + public string Link { get; set; } + public IList Categories { get; set; } + } + + private List GetPosts() + { + return new List() + { + new Post() + { + Title = "LINQ to JSON beta", + Description = "Annoucing LINQ to JSON", + Link = "http://james.newtonking.com/projects/json-net.aspx", + Categories = new List() { "Json.NET", "LINQ" } + }, + new Post() + { + Title = "Json.NET 1.3 + New license + Now on CodePlex", + Description = "Annoucing the release of Json.NET 1.3, the MIT license and the source being available on CodePlex", + Link = "http://james.newtonking.com/projects/json-net.aspx", + Categories = new List() { "Json.NET", "CodePlex" } + } + }; + } + + [Test] + public void CreateJTokenTreeNested() + { + List posts = GetPosts(); + + JObject rss = + new JObject( + new JProperty("channel", + new JObject( + new JProperty("title", "James Newton-King"), + new JProperty("link", "http://james.newtonking.com"), + new JProperty("description", "James Newton-King's blog."), + new JProperty("item", + new JArray( + from p in posts + orderby p.Title + select new JObject( + new JProperty("title", p.Title), + new JProperty("description", p.Description), + new JProperty("link", p.Link), + new JProperty("category", + new JArray( + from c in p.Categories + select new JValue(c))))))))); + + Console.WriteLine(rss.ToString()); + + //{ + // "channel": { + // "title": "James Newton-King", + // "link": "http://james.newtonking.com", + // "description": "James Newton-King's blog.", + // "item": [ + // { + // "title": "Json.NET 1.3 + New license + Now on CodePlex", + // "description": "Annoucing the release of Json.NET 1.3, the MIT license and the source being available on CodePlex", + // "link": "http://james.newtonking.com/projects/json-net.aspx", + // "category": [ + // "Json.NET", + // "CodePlex" + // ] + // }, + // { + // "title": "LINQ to JSON beta", + // "description": "Annoucing LINQ to JSON", + // "link": "http://james.newtonking.com/projects/json-net.aspx", + // "category": [ + // "Json.NET", + // "LINQ" + // ] + // } + // ] + // } + //} + + var postTitles = + from p in rss["channel"]["item"] + select p.Value("title"); + + foreach (var item in postTitles) + { + Console.WriteLine(item); + } + + //LINQ to JSON beta + //Json.NET 1.3 + New license + Now on CodePlex + + var categories = + from c in rss["channel"]["item"].Children()["category"].Values() + group c by c into g + orderby g.Count() descending + select new { Category = g.Key, Count = g.Count() }; + + foreach (var c in categories) + { + Console.WriteLine(c.Category + " - Count: " + c.Count); + } + + //Json.NET - Count: 2 + //LINQ - Count: 1 + //CodePlex - Count: 1 + } + + [Test] + public void BasicQuerying() + { + string json = @"{ + ""channel"": { + ""title"": ""James Newton-King"", + ""link"": ""http://james.newtonking.com"", + ""description"": ""James Newton-King's blog."", + ""item"": [ + { + ""title"": ""Json.NET 1.3 + New license + Now on CodePlex"", + ""description"": ""Annoucing the release of Json.NET 1.3, the MIT license and the source being available on CodePlex"", + ""link"": ""http://james.newtonking.com/projects/json-net.aspx"", + ""category"": [ + ""Json.NET"", + ""CodePlex"" + ] + }, + { + ""title"": ""LINQ to JSON beta"", + ""description"": ""Annoucing LINQ to JSON"", + ""link"": ""http://james.newtonking.com/projects/json-net.aspx"", + ""category"": [ + ""Json.NET"", + ""LINQ"" + ] + } + ] + } + }"; + + JObject o = JObject.Parse(json); + + Assert.AreEqual(null, o["purple"]); + Assert.AreEqual(null, o.Value("purple")); + + Assert.IsInstanceOfType(typeof(JArray), o["channel"]["item"]); + + Assert.AreEqual(2, o["channel"]["item"].Children()["title"].Count()); + Assert.AreEqual(0, o["channel"]["item"].Children()["monkey"].Count()); + + Assert.AreEqual("Json.NET 1.3 + New license + Now on CodePlex", (string)o["channel"]["item"][0]["title"]); + + CollectionAssert.AreEqual(new string[] { "Json.NET 1.3 + New license + Now on CodePlex", "LINQ to JSON beta" }, o["channel"]["item"].Children().Values("title").ToArray()); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = "Accessed JObject values with invalid key value: 0. Object property name expected.")] + public void JObjectIntIndex() + { + JObject o = new JObject(); + Assert.AreEqual(null, o[0]); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"Accessed JArray values with invalid key value: ""purple"". Array position index expected.")] + public void JArrayStringIndex() + { + JArray a = new JArray(); + Assert.AreEqual(null, a["purple"]); + } + + [Test] + [ExpectedException(typeof(ArgumentException), ExpectedMessage = @"Accessed JConstructor values with invalid key value: ""purple"". Argument position index expected.")] + public void JConstructorStringIndex() + { + JConstructor c = new JConstructor("ConstructorValue"); + Assert.AreEqual(null, c["purple"]); + } + +#if !PocketPC && !NET20 + [Test] + public void ToStringJsonConverter() + { + JObject o = + new JObject( + new JProperty("Test1", new DateTime(2000, 10, 15, 5, 5, 5, DateTimeKind.Utc)), + new JProperty("Test2", new DateTimeOffset(2000, 10, 15, 5, 5, 5, new TimeSpan(11, 11, 0))), + new JProperty("Test3", "Test3Value"), + new JProperty("Test4", null) + ); + + JsonSerializer serializer = new JsonSerializer(); + serializer.Converters.Add(new JavaScriptDateTimeConverter()); + StringWriter sw = new StringWriter(); + JsonWriter writer = new JsonTextWriter(sw); + writer.Formatting = Formatting.Indented; + serializer.Serialize(writer, o); + + string json = sw.ToString(); + + Assert.AreEqual(@"{ + ""Test1"": new Date( + 971586305000 + ), + ""Test2"": new Date( + 971546045000 + ), + ""Test3"": ""Test3Value"", + ""Test4"": null +}", json); + } + + [Test] + public void DateTimeOffset() + { + List testDates = new List { + new DateTimeOffset(new DateTime(100, 1, 1, 1, 1, 1, DateTimeKind.Utc)), + new DateTimeOffset(2000, 1, 1, 1, 1, 1, TimeSpan.Zero), + new DateTimeOffset(2000, 1, 1, 1, 1, 1, TimeSpan.FromHours(13)), + new DateTimeOffset(2000, 1, 1, 1, 1, 1, TimeSpan.FromHours(-3.5)), + }; + + JsonSerializer jsonSerializer = new JsonSerializer(); + + JTokenWriter jsonWriter; + using (jsonWriter = new JTokenWriter()) + { + jsonSerializer.Serialize(jsonWriter, testDates); + } + + Assert.AreEqual(4, jsonWriter.Token.Children().Count()); + } +#endif + + [Test] + public void FromObject() + { + List posts = GetPosts(); + + JObject o = JObject.FromObject(new + { + channel = new + { + title = "James Newton-King", + link = "http://james.newtonking.com", + description = "James Newton-King's blog.", + item = + from p in posts + orderby p.Title + select new + { + title = p.Title, + description = p.Description, + link = p.Link, + category = p.Categories + } + } + }); + + Console.WriteLine(o.ToString()); + Assert.IsInstanceOfType(typeof(JObject), o); + Assert.IsInstanceOfType(typeof(JObject), o["channel"]); + Assert.AreEqual("James Newton-King", (string)o["channel"]["title"]); + Assert.AreEqual(2, o["channel"]["item"].Children().Count()); + + JArray a = JArray.FromObject(new List() { 0, 1, 2, 3, 4 }); + Assert.IsInstanceOfType(typeof(JArray), a); + Assert.AreEqual(5, a.Count()); + } + + [Test] + public void FromAnonDictionary() + { + List posts = GetPosts(); + + JObject o = JObject.FromObject(new + { + channel = new Dictionary + { + { "title", "James Newton-King" }, + { "link", "http://james.newtonking.com" }, + { "description", "James Newton-King's blog." }, + { "item", + (from p in posts + orderby p.Title + select new + { + title = p.Title, + description = p.Description, + link = p.Link, + category = p.Categories + }) + } + } + }); + + Console.WriteLine(o.ToString()); + Assert.IsInstanceOfType(typeof(JObject), o); + Assert.IsInstanceOfType(typeof(JObject), o["channel"]); + Assert.AreEqual("James Newton-King", (string)o["channel"]["title"]); + Assert.AreEqual(2, o["channel"]["item"].Children().Count()); + + JArray a = JArray.FromObject(new List() { 0, 1, 2, 3, 4 }); + Assert.IsInstanceOfType(typeof(JArray), a); + Assert.AreEqual(5, a.Count()); + } + + [Test] + public void AsJEnumerable() + { + JObject o = null; + IJEnumerable enumerable = null; + + enumerable = o.AsJEnumerable(); + Assert.IsNull(enumerable); + + o = + new JObject( + new JProperty("Test1", new DateTime(2000, 10, 15, 5, 5, 5, DateTimeKind.Utc)), + new JProperty("Test2", "Test2Value"), + new JProperty("Test3", null) + ); + + enumerable = o.AsJEnumerable(); + Assert.IsNotNull(enumerable); + Assert.AreEqual(o, enumerable); + + DateTime d = enumerable["Test1"].Value(); + + Assert.AreEqual(new DateTime(2000, 10, 15, 5, 5, 5, DateTimeKind.Utc), d); + } + +#if !(NET20 || NET35 || SILVERLIGHT) + [Test] + public void CovariantIJEnumerable() + { + IEnumerable o = new[] + { + JObject.FromObject(new {First = 1, Second = 2}), + JObject.FromObject(new {First = 1, Second = 2}) + }; + + IJEnumerable values = o.Properties(); + Assert.AreEqual(4, values.Count()); + } +#endif + + [Test] + public void ChildrenExtension() + { + string json = @"[ + { + ""title"": ""James Newton-King"", + ""link"": ""http://james.newtonking.com"", + ""description"": ""James Newton-King's blog."", + ""item"": [ + { + ""title"": ""Json.NET 1.3 + New license + Now on CodePlex"", + ""description"": ""Annoucing the release of Json.NET 1.3, the MIT license and the source being available on CodePlex"", + ""link"": ""http://james.newtonking.com/projects/json-net.aspx"", + ""category"": [ + ""Json.NET"", + ""CodePlex"" + ] + }, + { + ""title"": ""LINQ to JSON beta"", + ""description"": ""Annoucing LINQ to JSON"", + ""link"": ""http://james.newtonking.com/projects/json-net.aspx"", + ""category"": [ + ""Json.NET"", + ""LINQ"" + ] + } + ] + }, + { + ""title"": ""James Newton-King"", + ""link"": ""http://james.newtonking.com"", + ""description"": ""James Newton-King's blog."", + ""item"": [ + { + ""title"": ""Json.NET 1.3 + New license + Now on CodePlex"", + ""description"": ""Annoucing the release of Json.NET 1.3, the MIT license and the source being available on CodePlex"", + ""link"": ""http://james.newtonking.com/projects/json-net.aspx"", + ""category"": [ + ""Json.NET"", + ""CodePlex"" + ] + }, + { + ""title"": ""LINQ to JSON beta"", + ""description"": ""Annoucing LINQ to JSON"", + ""link"": ""http://james.newtonking.com/projects/json-net.aspx"", + ""category"": [ + ""Json.NET"", + ""LINQ"" + ] + } + ] + } + ]"; + + JArray o = JArray.Parse(json); + + Assert.AreEqual(4, o.Children()["item"].Children()["title"].Count()); + CollectionAssert.AreEqual(new string[] + { + "Json.NET 1.3 + New license + Now on CodePlex", + "LINQ to JSON beta", + "Json.NET 1.3 + New license + Now on CodePlex", + "LINQ to JSON beta" + }, + o.Children()["item"].Children()["title"].Values().ToArray()); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/Department.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/Department.cs new file mode 100644 index 0000000..99b8f52 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/Department.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Data.Linq; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.LinqToSql +{ + [MetadataType(typeof(DepartmentMetadata))] + public partial class Department + { + [JsonConverter(typeof(DepartmentConverter))] + public class DepartmentMetadata + { + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/DepartmentConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/DepartmentConverter.cs new file mode 100644 index 0000000..3aee048 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/DepartmentConverter.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.LinqToSql +{ + public class DepartmentConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + Department department = (Department)value; + + JObject o = new JObject(); + o["DepartmentId"] = new JValue(department.DepartmentId.ToString()); + o["Name"] = new JValue(new string(department.Name.Reverse().ToArray())); + + o.WriteTo(writer); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + JObject o = JObject.Load(reader); + + Department department = new Department(); + department.DepartmentId = new Guid((string)o["DepartmentId"]); + department.Name = new string(((string) o["Name"]).Reverse().ToArray()); + + return department; + } + + public override bool CanConvert(Type objectType) + { + return (objectType == typeof (Department)); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/GuidByteArrayConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/GuidByteArrayConverter.cs new file mode 100644 index 0000000..a52c69f --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/GuidByteArrayConverter.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.LinqToSql +{ + public class GuidByteArrayConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + Guid guid = (Guid) value; + writer.WriteValue(Convert.ToBase64String(guid.ToByteArray())); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + string encodedData = (string) reader.Value; + byte[] data = Convert.FromBase64String(encodedData); + return new Guid(data); + } + + public override bool CanConvert(Type objectType) + { + return (objectType == typeof (Guid)); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml new file mode 100644 index 0000000..4929619 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml @@ -0,0 +1,35 @@ + + + + + + + + + + +
+ + + + + + +
+ + + + + + + + +
+ + + + + + +
+
\ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml.layout b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml.layout new file mode 100644 index 0000000..36b0ff0 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.dbml.layout @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.designer.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.designer.cs new file mode 100644 index 0000000..7ffc689 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClasses.designer.cs @@ -0,0 +1,726 @@ +#pragma warning disable 1591 +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Newtonsoft.Json.Tests.LinqToSql +{ + using System.Data.Linq; + using System.Data.Linq.Mapping; + using System.Data; + using System.Collections.Generic; + using System.Reflection; + using System.Linq; + using System.Linq.Expressions; + using System.ComponentModel; + using System; + + + public partial class LinqToSqlClassesDataContext : System.Data.Linq.DataContext + { + + private static System.Data.Linq.Mapping.MappingSource mappingSource = new AttributeMappingSource(); + + #region Extensibility Method Definitions + partial void OnCreated(); + partial void InsertPerson(Person instance); + partial void UpdatePerson(Person instance); + partial void DeletePerson(Person instance); + partial void InsertRole(Role instance); + partial void UpdateRole(Role instance); + partial void DeleteRole(Role instance); + partial void InsertPersonRole(PersonRole instance); + partial void UpdatePersonRole(PersonRole instance); + partial void DeletePersonRole(PersonRole instance); + partial void InsertDepartment(Department instance); + partial void UpdateDepartment(Department instance); + partial void DeleteDepartment(Department instance); + #endregion + + public LinqToSqlClassesDataContext(string connection) : + base(connection, mappingSource) + { + OnCreated(); + } + + public LinqToSqlClassesDataContext(System.Data.IDbConnection connection) : + base(connection, mappingSource) + { + OnCreated(); + } + + public LinqToSqlClassesDataContext(string connection, System.Data.Linq.Mapping.MappingSource mappingSource) : + base(connection, mappingSource) + { + OnCreated(); + } + + public LinqToSqlClassesDataContext(System.Data.IDbConnection connection, System.Data.Linq.Mapping.MappingSource mappingSource) : + base(connection, mappingSource) + { + OnCreated(); + } + + public System.Data.Linq.Table Persons + { + get + { + return this.GetTable(); + } + } + + public System.Data.Linq.Table Roles + { + get + { + return this.GetTable(); + } + } + + public System.Data.Linq.Table PersonRoles + { + get + { + return this.GetTable(); + } + } + + public System.Data.Linq.Table Departments + { + get + { + return this.GetTable(); + } + } + } + + [global::System.Data.Linq.Mapping.TableAttribute(Name="")] + public partial class Person : INotifyPropertyChanging, INotifyPropertyChanged + { + + private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty); + + private string _FirstName; + + private string _LastName; + + private System.Guid _PersonId; + + private System.Guid _DepartmentId; + + private EntitySet _PersonRoles; + + private EntityRef _Department; + + #region Extensibility Method Definitions + partial void OnLoaded(); + partial void OnValidate(System.Data.Linq.ChangeAction action); + partial void OnCreated(); + partial void OnFirstNameChanging(string value); + partial void OnFirstNameChanged(); + partial void OnLastNameChanging(string value); + partial void OnLastNameChanged(); + partial void OnPersonIdChanging(System.Guid value); + partial void OnPersonIdChanged(); + partial void OnDepartmentIdChanging(System.Guid value); + partial void OnDepartmentIdChanged(); + #endregion + + public Person() + { + this._PersonRoles = new EntitySet(new Action(this.attach_PersonRoles), new Action(this.detach_PersonRoles)); + this._Department = default(EntityRef); + OnCreated(); + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_FirstName", CanBeNull=false)] + public string FirstName + { + get + { + return this._FirstName; + } + set + { + if ((this._FirstName != value)) + { + this.OnFirstNameChanging(value); + this.SendPropertyChanging(); + this._FirstName = value; + this.SendPropertyChanged("FirstName"); + this.OnFirstNameChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_LastName", CanBeNull=false)] + public string LastName + { + get + { + return this._LastName; + } + set + { + if ((this._LastName != value)) + { + this.OnLastNameChanging(value); + this.SendPropertyChanging(); + this._LastName = value; + this.SendPropertyChanged("LastName"); + this.OnLastNameChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_PersonId", IsPrimaryKey=true)] + public System.Guid PersonId + { + get + { + return this._PersonId; + } + set + { + if ((this._PersonId != value)) + { + this.OnPersonIdChanging(value); + this.SendPropertyChanging(); + this._PersonId = value; + this.SendPropertyChanged("PersonId"); + this.OnPersonIdChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_DepartmentId")] + public System.Guid DepartmentId + { + get + { + return this._DepartmentId; + } + set + { + if ((this._DepartmentId != value)) + { + if (this._Department.HasLoadedOrAssignedValue) + { + throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException(); + } + this.OnDepartmentIdChanging(value); + this.SendPropertyChanging(); + this._DepartmentId = value; + this.SendPropertyChanged("DepartmentId"); + this.OnDepartmentIdChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.AssociationAttribute(Name="Person_PersonRole", Storage="_PersonRoles", ThisKey="PersonId", OtherKey="PersonId")] + public EntitySet PersonRoles + { + get + { + return this._PersonRoles; + } + set + { + this._PersonRoles.Assign(value); + } + } + + [global::System.Data.Linq.Mapping.AssociationAttribute(Name="Department_Person", Storage="_Department", ThisKey="DepartmentId", OtherKey="DepartmentId", IsForeignKey=true)] + public Department Department + { + get + { + return this._Department.Entity; + } + set + { + Department previousValue = this._Department.Entity; + if (((previousValue != value) + || (this._Department.HasLoadedOrAssignedValue == false))) + { + this.SendPropertyChanging(); + if ((previousValue != null)) + { + this._Department.Entity = null; + previousValue.Persons.Remove(this); + } + this._Department.Entity = value; + if ((value != null)) + { + value.Persons.Add(this); + this._DepartmentId = value.DepartmentId; + } + else + { + this._DepartmentId = default(System.Guid); + } + this.SendPropertyChanged("Department"); + } + } + } + + public event PropertyChangingEventHandler PropertyChanging; + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void SendPropertyChanging() + { + if ((this.PropertyChanging != null)) + { + this.PropertyChanging(this, emptyChangingEventArgs); + } + } + + protected virtual void SendPropertyChanged(String propertyName) + { + if ((this.PropertyChanged != null)) + { + this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + + private void attach_PersonRoles(PersonRole entity) + { + this.SendPropertyChanging(); + entity.Person = this; + } + + private void detach_PersonRoles(PersonRole entity) + { + this.SendPropertyChanging(); + entity.Person = null; + } + } + + [global::System.Data.Linq.Mapping.TableAttribute(Name="")] + public partial class Role : INotifyPropertyChanging, INotifyPropertyChanged + { + + private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty); + + private string _Name; + + private System.Guid _RoleId; + + private EntitySet _PersonRoles; + + #region Extensibility Method Definitions + partial void OnLoaded(); + partial void OnValidate(System.Data.Linq.ChangeAction action); + partial void OnCreated(); + partial void OnNameChanging(string value); + partial void OnNameChanged(); + partial void OnRoleIdChanging(System.Guid value); + partial void OnRoleIdChanged(); + #endregion + + public Role() + { + this._PersonRoles = new EntitySet(new Action(this.attach_PersonRoles), new Action(this.detach_PersonRoles)); + OnCreated(); + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Name", CanBeNull=false)] + public string Name + { + get + { + return this._Name; + } + set + { + if ((this._Name != value)) + { + this.OnNameChanging(value); + this.SendPropertyChanging(); + this._Name = value; + this.SendPropertyChanged("Name"); + this.OnNameChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_RoleId", IsPrimaryKey=true)] + public System.Guid RoleId + { + get + { + return this._RoleId; + } + set + { + if ((this._RoleId != value)) + { + this.OnRoleIdChanging(value); + this.SendPropertyChanging(); + this._RoleId = value; + this.SendPropertyChanged("RoleId"); + this.OnRoleIdChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.AssociationAttribute(Name="Role_PersonRole", Storage="_PersonRoles", ThisKey="RoleId", OtherKey="RoleId")] + public EntitySet PersonRoles + { + get + { + return this._PersonRoles; + } + set + { + this._PersonRoles.Assign(value); + } + } + + public event PropertyChangingEventHandler PropertyChanging; + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void SendPropertyChanging() + { + if ((this.PropertyChanging != null)) + { + this.PropertyChanging(this, emptyChangingEventArgs); + } + } + + protected virtual void SendPropertyChanged(String propertyName) + { + if ((this.PropertyChanged != null)) + { + this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + + private void attach_PersonRoles(PersonRole entity) + { + this.SendPropertyChanging(); + entity.Role = this; + } + + private void detach_PersonRoles(PersonRole entity) + { + this.SendPropertyChanging(); + entity.Role = null; + } + } + + [global::System.Data.Linq.Mapping.TableAttribute(Name="")] + public partial class PersonRole : INotifyPropertyChanging, INotifyPropertyChanged + { + + private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty); + + private System.Guid _PersonId; + + private System.Guid _RoleId; + + private System.Guid _PersonRoleId; + + private EntityRef _Person; + + private EntityRef _Role; + + #region Extensibility Method Definitions + partial void OnLoaded(); + partial void OnValidate(System.Data.Linq.ChangeAction action); + partial void OnCreated(); + partial void OnPersonIdChanging(System.Guid value); + partial void OnPersonIdChanged(); + partial void OnRoleIdChanging(System.Guid value); + partial void OnRoleIdChanged(); + partial void OnPersonRoleIdChanging(System.Guid value); + partial void OnPersonRoleIdChanged(); + #endregion + + public PersonRole() + { + this._Person = default(EntityRef); + this._Role = default(EntityRef); + OnCreated(); + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_PersonId")] + public System.Guid PersonId + { + get + { + return this._PersonId; + } + set + { + if ((this._PersonId != value)) + { + if (this._Person.HasLoadedOrAssignedValue) + { + throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException(); + } + this.OnPersonIdChanging(value); + this.SendPropertyChanging(); + this._PersonId = value; + this.SendPropertyChanged("PersonId"); + this.OnPersonIdChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_RoleId")] + public System.Guid RoleId + { + get + { + return this._RoleId; + } + set + { + if ((this._RoleId != value)) + { + if (this._Role.HasLoadedOrAssignedValue) + { + throw new System.Data.Linq.ForeignKeyReferenceAlreadyHasValueException(); + } + this.OnRoleIdChanging(value); + this.SendPropertyChanging(); + this._RoleId = value; + this.SendPropertyChanged("RoleId"); + this.OnRoleIdChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_PersonRoleId", IsPrimaryKey=true)] + public System.Guid PersonRoleId + { + get + { + return this._PersonRoleId; + } + set + { + if ((this._PersonRoleId != value)) + { + this.OnPersonRoleIdChanging(value); + this.SendPropertyChanging(); + this._PersonRoleId = value; + this.SendPropertyChanged("PersonRoleId"); + this.OnPersonRoleIdChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.AssociationAttribute(Name="Person_PersonRole", Storage="_Person", ThisKey="PersonId", OtherKey="PersonId", IsForeignKey=true)] + public Person Person + { + get + { + return this._Person.Entity; + } + set + { + Person previousValue = this._Person.Entity; + if (((previousValue != value) + || (this._Person.HasLoadedOrAssignedValue == false))) + { + this.SendPropertyChanging(); + if ((previousValue != null)) + { + this._Person.Entity = null; + previousValue.PersonRoles.Remove(this); + } + this._Person.Entity = value; + if ((value != null)) + { + value.PersonRoles.Add(this); + this._PersonId = value.PersonId; + } + else + { + this._PersonId = default(System.Guid); + } + this.SendPropertyChanged("Person"); + } + } + } + + [global::System.Data.Linq.Mapping.AssociationAttribute(Name="Role_PersonRole", Storage="_Role", ThisKey="RoleId", OtherKey="RoleId", IsForeignKey=true)] + public Role Role + { + get + { + return this._Role.Entity; + } + set + { + Role previousValue = this._Role.Entity; + if (((previousValue != value) + || (this._Role.HasLoadedOrAssignedValue == false))) + { + this.SendPropertyChanging(); + if ((previousValue != null)) + { + this._Role.Entity = null; + previousValue.PersonRoles.Remove(this); + } + this._Role.Entity = value; + if ((value != null)) + { + value.PersonRoles.Add(this); + this._RoleId = value.RoleId; + } + else + { + this._RoleId = default(System.Guid); + } + this.SendPropertyChanged("Role"); + } + } + } + + public event PropertyChangingEventHandler PropertyChanging; + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void SendPropertyChanging() + { + if ((this.PropertyChanging != null)) + { + this.PropertyChanging(this, emptyChangingEventArgs); + } + } + + protected virtual void SendPropertyChanged(String propertyName) + { + if ((this.PropertyChanged != null)) + { + this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + } + + [global::System.Data.Linq.Mapping.TableAttribute(Name="")] + public partial class Department : INotifyPropertyChanging, INotifyPropertyChanged + { + + private static PropertyChangingEventArgs emptyChangingEventArgs = new PropertyChangingEventArgs(String.Empty); + + private System.Guid _DepartmentId; + + private string _Name; + + private EntitySet _Persons; + + #region Extensibility Method Definitions + partial void OnLoaded(); + partial void OnValidate(System.Data.Linq.ChangeAction action); + partial void OnCreated(); + partial void OnDepartmentIdChanging(System.Guid value); + partial void OnDepartmentIdChanged(); + partial void OnNameChanging(string value); + partial void OnNameChanged(); + #endregion + + public Department() + { + this._Persons = new EntitySet(new Action(this.attach_Persons), new Action(this.detach_Persons)); + OnCreated(); + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_DepartmentId", IsPrimaryKey=true)] + public System.Guid DepartmentId + { + get + { + return this._DepartmentId; + } + set + { + if ((this._DepartmentId != value)) + { + this.OnDepartmentIdChanging(value); + this.SendPropertyChanging(); + this._DepartmentId = value; + this.SendPropertyChanged("DepartmentId"); + this.OnDepartmentIdChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.ColumnAttribute(Storage="_Name", CanBeNull=false)] + public string Name + { + get + { + return this._Name; + } + set + { + if ((this._Name != value)) + { + this.OnNameChanging(value); + this.SendPropertyChanging(); + this._Name = value; + this.SendPropertyChanged("Name"); + this.OnNameChanged(); + } + } + } + + [global::System.Data.Linq.Mapping.AssociationAttribute(Name="Department_Person", Storage="_Persons", ThisKey="DepartmentId", OtherKey="DepartmentId")] + public EntitySet Persons + { + get + { + return this._Persons; + } + set + { + this._Persons.Assign(value); + } + } + + public event PropertyChangingEventHandler PropertyChanging; + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void SendPropertyChanging() + { + if ((this.PropertyChanging != null)) + { + this.PropertyChanging(this, emptyChangingEventArgs); + } + } + + protected virtual void SendPropertyChanged(String propertyName) + { + if ((this.PropertyChanged != null)) + { + this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + } + + private void attach_Persons(Person entity) + { + this.SendPropertyChanging(); + entity.Department = this; + } + + private void detach_Persons(Person entity) + { + this.SendPropertyChanging(); + entity.Department = null; + } + } +} +#pragma warning restore 1591 diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClassesSerializationTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClassesSerializationTests.cs new file mode 100644 index 0000000..08bc646 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/LinqToSqlClassesSerializationTests.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Tests.LinqToSql; +using NUnit.Framework; +using System.Reflection; +using System.ComponentModel; +using Newtonsoft.Json.Serialization; +using System.Data.Linq.Mapping; + +namespace Newtonsoft.Json.Tests.LinqToSql +{ + public class LinqToSqlClassesSerializationTests : TestFixtureBase + { + [Test] + public void Serialize() + { + Role role = new Role(); + role.Name = "Role1"; + role.RoleId = new Guid("67EA92B7-4BD3-4718-BD75-3C7EDF800B34"); + + Person person = new Person(); + person.FirstName = "FirstName!"; + person.LastName = "LastName!"; + person.PersonId = new Guid("7AA027AA-C995-4986-908D-999D8063599F"); + person.PersonRoles.Add(new PersonRole + { + PersonRoleId = new Guid("B012DD41-71DF-4839-B8D5-D1333FB886BC"), + Role = role + }); + + person.Department = new Department + { + DepartmentId = new Guid("08F68BF9-929B-4434-BC47-C9489D22112B"), + Name = "Name!" + }; + + string json = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); + + Assert.AreEqual(@"{ + ""first_name"": ""FirstName!"", + ""LastName"": ""LastName!"", + ""PersonId"": ""7aa027aa-c995-4986-908d-999d8063599f"", + ""DepartmentId"": ""08f68bf9-929b-4434-bc47-c9489d22112b"", + ""PersonRoles"": [ + { + ""PersonId"": ""7aa027aa-c995-4986-908d-999d8063599f"", + ""RoleId"": ""67ea92b7-4bd3-4718-bd75-3c7edf800b34"", + ""PersonRoleId"": ""b012dd41-71df-4839-b8d5-d1333fb886bc"", + ""Role"": { + ""Name"": ""Role1"", + ""RoleId"": ""t5LqZ9NLGEe9dTx+34ALNA=="" + } + } + ], + ""Department"": { + ""DepartmentId"": ""08f68bf9-929b-4434-bc47-c9489d22112b"", + ""Name"": ""!emaN"" + } +}", json); + } + + [Test] + public void Deserialize() + { + string json = @"{ + ""first_name"": ""FirstName!"", + ""LastName"": ""LastName!"", + ""PersonId"": ""7aa027aa-c995-4986-908d-999d8063599f"", + ""PersonRoles"": [ + { + ""PersonId"": ""7aa027aa-c995-4986-908d-999d8063599f"", + ""RoleId"": ""67ea92b7-4bd3-4718-bd75-3c7edf800b34"", + ""PersonRoleId"": ""b012dd41-71df-4839-b8d5-d1333fb886bc"", + ""Role"": { + ""Name"": ""Role1"", + ""RoleId"": ""t5LqZ9NLGEe9dTx+34ALNA=="" + } + } + ], + ""Department"": { + ""DepartmentId"": ""08f68bf9-929b-4434-bc47-c9489d22112b"", + ""Name"": ""!emaN"" + } +}"; + + Person person = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(person); + + Assert.AreEqual(new Guid("7AA027AA-C995-4986-908D-999D8063599F"), person.PersonId); + Assert.AreEqual("FirstName!", person.FirstName); + Assert.AreEqual("LastName!", person.LastName); + Assert.AreEqual(1, person.PersonRoles.Count); + Assert.AreEqual(person.PersonId, person.PersonRoles[0].PersonId); + Assert.AreEqual(new Guid("67EA92B7-4BD3-4718-BD75-3C7EDF800B34"), person.PersonRoles[0].RoleId); + Assert.IsNotNull(person.PersonRoles[0].Role); + Assert.AreEqual(1, person.PersonRoles[0].Role.PersonRoles.Count); + + Assert.AreEqual("Name!", person.Department.Name); + + TableAttribute tableAttribute = JsonTypeReflector.GetAttribute(typeof(Person)); + Assert.AreEqual("", tableAttribute.Name); + + ColumnAttribute columnAttribute = JsonTypeReflector.GetAttribute(typeof(Person).GetProperty("FirstName")); + Assert.AreEqual("_FirstName", columnAttribute.Storage); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/Person.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/Person.cs new file mode 100644 index 0000000..57ec3dc --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/Person.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.LinqToSql +{ + [MetadataType(typeof(PersonMetadata))] + public partial class Person + { + public class PersonMetadata + { + [JsonProperty("first_name")] + public string FirstName { get; set; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/Role.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/Role.cs new file mode 100644 index 0000000..b7108ed --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/LinqToSql/Role.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Data.Linq; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.LinqToSql +{ + [MetadataType(typeof(RoleMetadata))] + public partial class Role + { + public class RoleMetadata + { + [JsonConverter(typeof(GuidByteArrayConverter))] + public Guid RoleId { get; set; } + [JsonIgnore] + public EntitySet PersonRoles { get; set; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj new file mode 100644 index 0000000..44b5612 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net20.csproj @@ -0,0 +1,262 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {3E6E2335-B079-4B5B-A65A-9D586914BCBB} + Library + Properties + Newtonsoft.Json.Tests + Newtonsoft.Json.Tests.Net20 + + + + + + + + + + + 3.5 + + + v2.0 + false + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + true + full + false + bin\Debug\Net20\ + TRACE;DEBUG;NET20 + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\Net20\ + TRACE + prompt + 4 + AllRules.ruleset + + + + False + ..\Lib\LinqBridge.dll + + + False + ..\Lib\NUnit\DotNet\nunit.framework.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D} + Newtonsoft.Json.Net20 + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj new file mode 100644 index 0000000..05cb732 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Net35.csproj @@ -0,0 +1,321 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {3E6E2335-B079-4B5B-A65A-9D586914BCBB} + Library + Properties + Newtonsoft.Json.Tests.Net35 + Newtonsoft.Json.Tests.Net35 + + + + + + + + + + + 3.5 + + + v3.5 + false + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + true + full + false + bin\Debug\Net35\ + TRACE;DEBUG;NET35 + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\Net35\ + TRACE;NET35 + prompt + 4 + AllRules.ruleset + + + + False + ..\Lib\NUnit\DotNet\nunit.framework.dll + + + + 3.5 + + + 3.5 + + + + 3.5 + + + 3.5 + + + + 3.0 + + + + 3.0 + + + 3.5 + + + + 3.5 + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + True + True + LinqToSqlClasses.dbml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + MSLinqToSQLGenerator + LinqToSqlClasses.designer.cs + Designer + + + + + + + + LinqToSqlClasses.dbml + + + + + Always + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D} + Newtonsoft.Json.Net35 + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj new file mode 100644 index 0000000..dbd6848 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.Silverlight.csproj @@ -0,0 +1,291 @@ + + + + v3.5 + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {0D8C3C2E-62C6-4C93-9377-6F74DD6BFD93} + {A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Newtonsoft.Json.Tests.Silverlight + Newtonsoft.Json.Tests.Silverlight + v4.0 + false + + + 3.5 + + + Silverlight + $(TargetFrameworkVersion) + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + false + + + true + full + false + Bin\Debug\Silverlight\ + DEBUG;TRACE;SILVERLIGHT + true + true + prompt + 4 + AllRules.ruleset + + + pdbonly + true + Bin\Release\Silverlight\ + TRACE;SILVERLIGHT + true + true + prompt + 4 + AllRules.ruleset + + + + True + + + False + ..\Lib\NUnit\Silverlight\nunit.framework.dll + True + + + True + + + True + + + True + + + True + + + + True + + + True + + + True + + + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {DC3C6F3D-2CA1-4278-9B79-63770FB3EA2D} + Newtonsoft.Json.Silverlight + True + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj new file mode 100644 index 0000000..a5cfd2c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.WindowsPhone.csproj @@ -0,0 +1,230 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {5ED71C8C-D0A6-487C-9A8C-BB855ECEAF75} + {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Newtonsoft.Json.Tests.WindowsPhone + Newtonsoft.Json.Tests.WindowsPhone + v4.0 + $(TargetFrameworkVersion) + WindowsPhone + Silverlight + false + true + true + + + true + full + false + Bin\Debug\WindowsPhone\ + DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE + true + true + prompt + 4 + + + pdbonly + true + Bin\Release\WindowsPhone\ + TRACE;SILVERLIGHT;WINDOWS_PHONE + true + true + prompt + 4 + + + + ..\Lib\NUnit\Silverlight\nunit.framework.dll + + + True + + + True + + + True + + + True + + + True + + + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {7A7F70AB-5C07-47ED-BDD2-ECC14DBACA5E} + Newtonsoft.Json.WindowsPhone + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj new file mode 100644 index 0000000..0f19d56 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Newtonsoft.Json.Tests.csproj @@ -0,0 +1,331 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {3E6E2335-B079-4B5B-A65A-9D586914BCBB} + Library + Properties + Newtonsoft.Json.Tests + Newtonsoft.Json.Tests + + + + + + + + + + + 3.5 + + + v4.0 + false + + + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + + + + true + full + false + bin\Debug\Net\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\Net\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + False + ..\Lib\NUnit\DotNet\nunit.framework.dll + + + + 3.5 + + + 3.5 + + + + 3.5 + + + 3.5 + + + + 3.0 + + + + 3.0 + + + 3.5 + + + + 3.5 + + + + 3.5 + + + + + + + + + + + + + + + True + True + FileSystemEntityModel.edmx + + + + + + + + + + True + True + LinqToSqlClasses.dbml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D} + Newtonsoft.Json + + + + + MSLinqToSQLGenerator + LinqToSqlClasses.designer.cs + Designer + + + + + + + + EntityModelCodeGenerator + FileSystemEntityModel.Designer.cs + + + LinqToSqlClasses.dbml + + + + + Always + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/PerformanceTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/PerformanceTests.cs new file mode 100644 index 0000000..06e85ce --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/PerformanceTests.cs @@ -0,0 +1,636 @@ +#if !SILVERLIGHT && !PocketPC && !NET20 && !NET35 +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Diagnostics; +using System.IO; +using System.Runtime.Serialization; +using System.Web.Script.Serialization; +using System.Xml.Serialization; +using Newtonsoft.Json.Utilities; +using NUnit.Framework; +using System.Runtime.Serialization.Json; +using System.Text; +using Newtonsoft.Json.Bson; +using System.Runtime.Serialization.Formatters.Binary; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Converters; + +namespace Newtonsoft.Json.Tests +{ + [Serializable] + [DataContract] + public class Image + { + [DataMember] + public string FileName { get; set; } + [DataMember] + public string Author { get; set; } + [DataMember] + public string Caption { get; set; } + [DataMember] + public byte[] Data { get; set; } + } + + public class PerformanceTests : TestFixtureBase + { + private const int Iterations = 100; + //private const int Iterations = 5000; + + #region Data + private const string BsonHex = + @"A9-01-00-00-04-73-74-72-69-6E-67-73-00-2B-00-00-00-0A-30-00-02-31-00-19-00-00-00-4D-61-72-6B-75-73-20-65-67-67-65-72-20-5D-3E-3C-5B-2C-20-28-32-6E-64-29-00-0A-32-00-00-03-64-69-63-74-69-6F-6E-61-72-79-00-37-00-00-00-10-56-61-6C-20-26-20-61-73-64-31-00-01-00-00-00-10-56-61-6C-32-20-26-20-61-73-64-31-00-03-00-00-00-10-56-61-6C-33-20-26-20-61-73-64-31-00-04-00-00-00-00-02-4E-61-6D-65-00-05-00-00-00-52-69-63-6B-00-09-4E-6F-77-00-EF-BD-69-EC-25-01-00-00-01-42-69-67-4E-75-6D-62-65-72-00-E7-7B-CC-26-96-C7-1F-42-03-41-64-64-72-65-73-73-31-00-47-00-00-00-02-53-74-72-65-65-74-00-0B-00-00-00-66-66-66-20-53-74-72-65-65-74-00-02-50-68-6F-6E-65-00-0F-00-00-00-28-35-30-33-29-20-38-31-34-2D-36-33-33-35-00-09-45-6E-74-65-72-65-64-00-6F-FF-31-53-26-01-00-00-00-04-41-64-64-72-65-73-73-65-73-00-A2-00-00-00-03-30-00-4B-00-00-00-02-53-74-72-65-65-74-00-0F-00-00-00-1F-61-72-72-61-79-3C-61-64-64-72-65-73-73-00-02-50-68-6F-6E-65-00-0F-00-00-00-28-35-30-33-29-20-38-31-34-2D-36-33-33-35-00-09-45-6E-74-65-72-65-64-00-6F-73-0C-E7-25-01-00-00-00-03-31-00-4C-00-00-00-02-53-74-72-65-65-74-00-10-00-00-00-61-72-72-61-79-20-32-20-61-64-64-72-65-73-73-00-02-50-68-6F-6E-65-00-0F-00-00-00-28-35-30-33-29-20-38-31-34-2D-36-33-33-35-00-09-45-6E-74-65-72-65-64-00-6F-17-E6-E1-25-01-00-00-00-00-00"; + + private const string BinaryFormatterHex = + @"00-01-00-00-00-FF-FF-FF-FF-01-00-00-00-00-00-00-00-0C-02-00-00-00-4C-4E-65-77-74-6F-6E-73-6F-66-74-2E-4A-73-6F-6E-2E-54-65-73-74-73-2C-20-56-65-72-73-69-6F-6E-3D-33-2E-35-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-6E-75-6C-6C-05-01-00-00-00-1F-4E-65-77-74-6F-6E-73-6F-66-74-2E-4A-73-6F-6E-2E-54-65-73-74-73-2E-54-65-73-74-43-6C-61-73-73-07-00-00-00-05-5F-4E-61-6D-65-04-5F-4E-6F-77-0A-5F-42-69-67-4E-75-6D-62-65-72-09-5F-41-64-64-72-65-73-73-31-0A-5F-41-64-64-72-65-73-73-65-73-07-73-74-72-69-6E-67-73-0A-64-69-63-74-69-6F-6E-61-72-79-01-00-00-04-03-03-03-0D-05-1D-4E-65-77-74-6F-6E-73-6F-66-74-2E-4A-73-6F-6E-2E-54-65-73-74-73-2E-41-64-64-72-65-73-73-02-00-00-00-90-01-53-79-73-74-65-6D-2E-43-6F-6C-6C-65-63-74-69-6F-6E-73-2E-47-65-6E-65-72-69-63-2E-4C-69-73-74-60-31-5B-5B-4E-65-77-74-6F-6E-73-6F-66-74-2E-4A-73-6F-6E-2E-54-65-73-74-73-2E-41-64-64-72-65-73-73-2C-20-4E-65-77-74-6F-6E-73-6F-66-74-2E-4A-73-6F-6E-2E-54-65-73-74-73-2C-20-56-65-72-73-69-6F-6E-3D-33-2E-35-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-6E-75-6C-6C-5D-5D-7F-53-79-73-74-65-6D-2E-43-6F-6C-6C-65-63-74-69-6F-6E-73-2E-47-65-6E-65-72-69-63-2E-4C-69-73-74-60-31-5B-5B-53-79-73-74-65-6D-2E-53-74-72-69-6E-67-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-5D-E1-01-53-79-73-74-65-6D-2E-43-6F-6C-6C-65-63-74-69-6F-6E-73-2E-47-65-6E-65-72-69-63-2E-44-69-63-74-69-6F-6E-61-72-79-60-32-5B-5B-53-79-73-74-65-6D-2E-53-74-72-69-6E-67-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-2C-5B-53-79-73-74-65-6D-2E-49-6E-74-33-32-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-5D-02-00-00-00-06-03-00-00-00-04-52-69-63-6B-B6-25-3A-D1-C5-59-CC-88-0F-33-34-31-32-33-31-32-33-31-32-33-2E-31-32-31-09-04-00-00-00-09-05-00-00-00-09-06-00-00-00-09-07-00-00-00-05-04-00-00-00-1D-4E-65-77-74-6F-6E-73-6F-66-74-2E-4A-73-6F-6E-2E-54-65-73-74-73-2E-41-64-64-72-65-73-73-03-00-00-00-07-5F-73-74-72-65-65-74-06-5F-50-68-6F-6E-65-08-5F-45-6E-74-65-72-65-64-01-01-00-0D-02-00-00-00-06-08-00-00-00-0A-66-66-66-20-53-74-72-65-65-74-06-09-00-00-00-0E-28-35-30-33-29-20-38-31-34-2D-36-33-33-35-B6-BD-B8-BF-74-69-CC-88-04-05-00-00-00-90-01-53-79-73-74-65-6D-2E-43-6F-6C-6C-65-63-74-69-6F-6E-73-2E-47-65-6E-65-72-69-63-2E-4C-69-73-74-60-31-5B-5B-4E-65-77-74-6F-6E-73-6F-66-74-2E-4A-73-6F-6E-2E-54-65-73-74-73-2E-41-64-64-72-65-73-73-2C-20-4E-65-77-74-6F-6E-73-6F-66-74-2E-4A-73-6F-6E-2E-54-65-73-74-73-2C-20-56-65-72-73-69-6F-6E-3D-33-2E-35-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-6E-75-6C-6C-5D-5D-03-00-00-00-06-5F-69-74-65-6D-73-05-5F-73-69-7A-65-08-5F-76-65-72-73-69-6F-6E-04-00-00-1F-4E-65-77-74-6F-6E-73-6F-66-74-2E-4A-73-6F-6E-2E-54-65-73-74-73-2E-41-64-64-72-65-73-73-5B-5D-02-00-00-00-08-08-09-0A-00-00-00-02-00-00-00-02-00-00-00-04-06-00-00-00-7F-53-79-73-74-65-6D-2E-43-6F-6C-6C-65-63-74-69-6F-6E-73-2E-47-65-6E-65-72-69-63-2E-4C-69-73-74-60-31-5B-5B-53-79-73-74-65-6D-2E-53-74-72-69-6E-67-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-5D-03-00-00-00-06-5F-69-74-65-6D-73-05-5F-73-69-7A-65-08-5F-76-65-72-73-69-6F-6E-06-00-00-08-08-09-0B-00-00-00-03-00-00-00-03-00-00-00-04-07-00-00-00-E1-01-53-79-73-74-65-6D-2E-43-6F-6C-6C-65-63-74-69-6F-6E-73-2E-47-65-6E-65-72-69-63-2E-44-69-63-74-69-6F-6E-61-72-79-60-32-5B-5B-53-79-73-74-65-6D-2E-53-74-72-69-6E-67-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-2C-5B-53-79-73-74-65-6D-2E-49-6E-74-33-32-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-5D-04-00-00-00-07-56-65-72-73-69-6F-6E-08-43-6F-6D-70-61-72-65-72-08-48-61-73-68-53-69-7A-65-0D-4B-65-79-56-61-6C-75-65-50-61-69-72-73-00-03-00-03-08-92-01-53-79-73-74-65-6D-2E-43-6F-6C-6C-65-63-74-69-6F-6E-73-2E-47-65-6E-65-72-69-63-2E-47-65-6E-65-72-69-63-45-71-75-61-6C-69-74-79-43-6F-6D-70-61-72-65-72-60-31-5B-5B-53-79-73-74-65-6D-2E-53-74-72-69-6E-67-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-5D-08-E5-01-53-79-73-74-65-6D-2E-43-6F-6C-6C-65-63-74-69-6F-6E-73-2E-47-65-6E-65-72-69-63-2E-4B-65-79-56-61-6C-75-65-50-61-69-72-60-32-5B-5B-53-79-73-74-65-6D-2E-53-74-72-69-6E-67-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-2C-5B-53-79-73-74-65-6D-2E-49-6E-74-33-32-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-5D-5B-5D-03-00-00-00-09-0C-00-00-00-03-00-00-00-09-0D-00-00-00-07-0A-00-00-00-00-01-00-00-00-04-00-00-00-04-1D-4E-65-77-74-6F-6E-73-6F-66-74-2E-4A-73-6F-6E-2E-54-65-73-74-73-2E-41-64-64-72-65-73-73-02-00-00-00-09-0E-00-00-00-09-0F-00-00-00-0D-02-11-0B-00-00-00-04-00-00-00-0A-06-10-00-00-00-18-4D-61-72-6B-75-73-20-65-67-67-65-72-20-5D-3E-3C-5B-2C-20-28-32-6E-64-29-0D-02-04-0C-00-00-00-92-01-53-79-73-74-65-6D-2E-43-6F-6C-6C-65-63-74-69-6F-6E-73-2E-47-65-6E-65-72-69-63-2E-47-65-6E-65-72-69-63-45-71-75-61-6C-69-74-79-43-6F-6D-70-61-72-65-72-60-31-5B-5B-53-79-73-74-65-6D-2E-53-74-72-69-6E-67-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-5D-00-00-00-00-07-0D-00-00-00-00-01-00-00-00-03-00-00-00-03-E3-01-53-79-73-74-65-6D-2E-43-6F-6C-6C-65-63-74-69-6F-6E-73-2E-47-65-6E-65-72-69-63-2E-4B-65-79-56-61-6C-75-65-50-61-69-72-60-32-5B-5B-53-79-73-74-65-6D-2E-53-74-72-69-6E-67-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-2C-5B-53-79-73-74-65-6D-2E-49-6E-74-33-32-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-5D-04-EF-FF-FF-FF-E3-01-53-79-73-74-65-6D-2E-43-6F-6C-6C-65-63-74-69-6F-6E-73-2E-47-65-6E-65-72-69-63-2E-4B-65-79-56-61-6C-75-65-50-61-69-72-60-32-5B-5B-53-79-73-74-65-6D-2E-53-74-72-69-6E-67-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-2C-5B-53-79-73-74-65-6D-2E-49-6E-74-33-32-2C-20-6D-73-63-6F-72-6C-69-62-2C-20-56-65-72-73-69-6F-6E-3D-32-2E-30-2E-30-2E-30-2C-20-43-75-6C-74-75-72-65-3D-6E-65-75-74-72-61-6C-2C-20-50-75-62-6C-69-63-4B-65-79-54-6F-6B-65-6E-3D-62-37-37-61-35-63-35-36-31-39-33-34-65-30-38-39-5D-5D-02-00-00-00-03-6B-65-79-05-76-61-6C-75-65-01-00-08-06-12-00-00-00-0A-56-61-6C-20-26-20-61-73-64-31-01-00-00-00-01-ED-FF-FF-FF-EF-FF-FF-FF-06-14-00-00-00-0B-56-61-6C-32-20-26-20-61-73-64-31-03-00-00-00-01-EB-FF-FF-FF-EF-FF-FF-FF-06-16-00-00-00-0B-56-61-6C-33-20-26-20-61-73-64-31-04-00-00-00-01-0E-00-00-00-04-00-00-00-06-17-00-00-00-0E-1F-61-72-72-61-79-3C-61-64-64-72-65-73-73-09-09-00-00-00-B6-FD-0B-45-F4-58-CC-88-01-0F-00-00-00-04-00-00-00-06-19-00-00-00-0F-61-72-72-61-79-20-32-20-61-64-64-72-65-73-73-09-09-00-00-00-B6-3D-A2-1A-2B-58-CC-88-0B"; + + private const string XmlText = + @"2010-01-21T11:12:16.0809174+13:00(503) 814-6335fff Street
2009-12-31T11:12:16.0809174+13:00(503) 814-6335array<address
2009-12-30T11:12:16.0809174+13:00(503) 814-6335array 2 address
34123123123.121Rick2010-01-01T12:12:16.0809174+13:00Val & asd11Val2 & asd13Val3 & asd14Markus egger ]><[, (2nd)
"; + + private const string JsonText = + @"{""strings"":[null,""Markus egger ]><[, (2nd)"",null],""dictionary"":{""Val & asd1"":1,""Val2 & asd1"":3,""Val3 & asd1"":4},""Name"":""Rick"",""Now"":""\/Date(1262301136080+1300)\/"",""BigNumber"":34123123123.121,""Address1"":{""Street"":""fff Street"",""Phone"":""(503) 814-6335"",""Entered"":""\/Date(1264025536080+1300)\/""},""Addresses"":[{""Street"":""\u001farray(SerializeMethod.DataContractSerializer, XmlText); + BenchmarkDeserializeMethod(SerializeMethod.BinaryFormatter, MiscellaneousUtils.HexToBytes(BinaryFormatterHex)); + BenchmarkDeserializeMethod(SerializeMethod.JavaScriptSerializer, JsonText); + BenchmarkDeserializeMethod(SerializeMethod.DataContractJsonSerializer, JsonText); + BenchmarkDeserializeMethod(SerializeMethod.JsonNet, JsonText); + BenchmarkDeserializeMethod(SerializeMethod.JsonNetBinary, MiscellaneousUtils.HexToBytes(BsonHex)); + } + + [Test] + public void SerializeSizeNormal() + { + SerializeSize(CreateSerializationObject()); + } + + [Test] + public void SerializeSizeData() + { + Image image = new Image(); + image.Data = System.IO.File.ReadAllBytes(@"bunny_pancake.jpg"); + image.FileName = "bunny_pancake.jpg"; + image.Author = "Hironori Akutagawa"; + image.Caption = "I have no idea what you are talking about so here's a bunny with a pancake on its head"; + + SerializeSize(image); + } + + private T TimeOperation(Func operation, string name) + { + // warm up + operation(); + + Stopwatch timed = new Stopwatch(); + timed.Start(); + + T result = operation(); + + Console.WriteLine(name); + Console.WriteLine("{0} ms", timed.ElapsedMilliseconds); + + timed.Stop(); + + return result; + } + + private void SerializeSize(object value) + { + // this is extremely slow with 5000 interations + int interations = 100; + + byte[] jsonBytes = TimeOperation(() => + { + string json = null; + for (int i = 0; i < interations; i++) + { + json = JsonConvert.SerializeObject(value, Formatting.None); + } + + return Encoding.UTF8.GetBytes(json); + }, "Json.NET"); + + byte[] bsonBytes = TimeOperation(() => + { + MemoryStream ms = null; + for (int i = 0; i < interations; i++) + { + ms = new MemoryStream(); + JsonSerializer serializer = new JsonSerializer(); + BsonWriter writer = new BsonWriter(ms); + + serializer.Serialize(writer, value); + writer.Flush(); + } + + return ms.ToArray(); + }, "Json.NET BSON"); + + byte[] xmlBytes = TimeOperation(() => + { + MemoryStream ms = null; + for (int i = 0; i < interations; i++) + { + ms = new MemoryStream(); + DataContractSerializer dataContractSerializer = new DataContractSerializer(value.GetType()); + dataContractSerializer.WriteObject(ms, value); + } + + return ms.ToArray(); + }, "DataContractSerializer"); + + byte[] wcfJsonBytes = TimeOperation(() => + { + MemoryStream ms = null; + for (int i = 0; i < interations; i++) + { + ms = new MemoryStream(); + DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(value.GetType()); + dataContractJsonSerializer.WriteObject(ms, value); + } + + return ms.ToArray(); + }, "DataContractJsonSerializer"); + + byte[] binaryFormatterBytes = TimeOperation(() => + { + MemoryStream ms = null; + for (int i = 0; i < interations; i++) + { + ms = new MemoryStream(); + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(ms, value); + } + + return ms.ToArray(); + }, "BinaryFormatter"); + + Console.WriteLine("Json.NET size: {0} bytes", jsonBytes.Length); + Console.WriteLine("BSON size: {0} bytes", bsonBytes.Length); + Console.WriteLine("WCF JSON size: {0} bytes", wcfJsonBytes.Length); + Console.WriteLine("WCF XML size: {0} bytes", xmlBytes.Length); + Console.WriteLine("BinaryFormatter size: {0} bytes", binaryFormatterBytes.Length); + } + + #region Serialize + private static readonly byte[] Buffer = new byte[4096]; + + public void BenchmarkSerializeMethod(SerializeMethod method, object value) + { + Serialize(method, value); + + Stopwatch timed = new Stopwatch(); + timed.Start(); + + string json = null; + for (int x = 0; x < Iterations; x++) + { + json = Serialize(method, value); + } + + timed.Stop(); + + Console.WriteLine("Serialize method: {0}", method); + Console.WriteLine("{0} ms", timed.ElapsedMilliseconds); + Console.WriteLine(json); + Console.WriteLine(); + } + + private TestClass CreateSerializationObject() + { + TestClass test = new TestClass(); + + test.dictionary = new Dictionary { { "Val & asd1", 1 }, { "Val2 & asd1", 3 }, { "Val3 & asd1", 4 } }; + + + test.Address1.Street = "fff Street"; + test.Address1.Entered = DateTime.Now.AddDays(20); + + test.BigNumber = 34123123123.121M; + test.Now = DateTime.Now.AddHours(1); + test.strings = new List() { null, "Markus egger ]><[, (2nd)", null }; + + Address address = new Address(); + address.Entered = DateTime.Now.AddDays(-1); + address.Street = "\u001farray\u003caddress"; + + test.Addresses.Add(address); + + address = new Address(); + address.Entered = DateTime.Now.AddDays(-2); + address.Street = "array 2 address"; + test.Addresses.Add(address); + return test; + } + + public string SerializeJsonNet(object value) + { + Type type = value.GetType(); + + Newtonsoft.Json.JsonSerializer json = new Newtonsoft.Json.JsonSerializer(); + + json.NullValueHandling = NullValueHandling.Ignore; + + json.ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Replace; + json.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore; + json.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + + + StringWriter sw = new StringWriter(); + Newtonsoft.Json.JsonTextWriter writer = new JsonTextWriter(sw); + + writer.Formatting = Formatting.None; + + writer.QuoteChar = '"'; + json.Serialize(writer, value); + + string output = sw.ToString(); + writer.Close(); + + return output; + } + + public string SerializeWebExtensions(object value) + { + JavaScriptSerializer ser = new JavaScriptSerializer(); + + return ser.Serialize(value); + } + + public string SerializeDataContractJson(object value) + { + DataContractJsonSerializer dataContractSerializer + = new DataContractJsonSerializer(value.GetType()); + + MemoryStream ms = new MemoryStream(); + dataContractSerializer.WriteObject(ms, value); + + ms.Seek(0, SeekOrigin.Begin); + + using (StreamReader sr = new StreamReader(ms)) + { + return sr.ReadToEnd(); + } + } + + public string SerializeDataContract(object value) + { + DataContractSerializer dataContractSerializer + = new DataContractSerializer(value.GetType()); + + MemoryStream ms = new MemoryStream(); + dataContractSerializer.WriteObject(ms, value); + + ms.Seek(0, SeekOrigin.Begin); + + using (StreamReader sr = new StreamReader(ms)) + { + return sr.ReadToEnd(); + } + } + + private string Serialize(SerializeMethod method, object value) + { + string json; + + switch (method) + { + case SerializeMethod.JsonNet: + json = JsonConvert.SerializeObject(value); + break; + case SerializeMethod.JsonNetBinary: + { + MemoryStream ms = new MemoryStream(Buffer); + JsonSerializer serializer = new JsonSerializer(); + BsonWriter writer = new BsonWriter(ms); + serializer.Serialize(writer, value); + + //json = BitConverter.ToString(ms.ToArray(), 0, (int)ms.Position); + json = "Bytes = " + ms.Position; + break; + } + case SerializeMethod.JavaScriptSerializer: + json = SerializeWebExtensions(value); + break; + case SerializeMethod.DataContractJsonSerializer: + json = SerializeDataContractJson(value); + break; + case SerializeMethod.DataContractSerializer: + json = SerializeDataContract(value); + break; + case SerializeMethod.BinaryFormatter: + json = SerializeBinaryFormatter(value); + break; + default: + throw new ArgumentOutOfRangeException("method"); + } + + return json; + } + + private string SerializeBinaryFormatter(object value) + { + string json; + MemoryStream ms = new MemoryStream(Buffer); + BinaryFormatter formatter = new BinaryFormatter(); + formatter.Serialize(ms, value); + + json = "Bytes = " + ms.Position; + //json = BitConverter.ToString(ms.ToArray(), 0, (int)ms.Position); + return json; + } + #endregion + + #region Deserialize + public void BenchmarkDeserializeMethod(SerializeMethod method, object json) + { + Deserialize(method, json); + + Stopwatch timed = new Stopwatch(); + timed.Start(); + + T value = default(T); + for (int x = 0; x < Iterations; x++) + { + value = Deserialize(method, json); + } + + timed.Stop(); + + Console.WriteLine("Deserialize method: {0}", method); + Console.WriteLine("{0} ms", timed.ElapsedMilliseconds); + Console.WriteLine(value); + Console.WriteLine(); + } + + public T DeserializeJsonNet(string json) + { + Type type = typeof(T); + + JsonSerializer serializer = new JsonSerializer(); + serializer.ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Replace; + serializer.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore; + serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + + return (T)serializer.Deserialize(new StringReader(json), type); + + //JsonTextReader reader = new JsonTextReader(new StringReader(JsonText)); + //while (reader.Read()) + //{ + + //} + //return default(T); + } + + public T DeserializeJsonNetBinary(byte[] bson) + { + Type type = typeof(T); + + JsonSerializer serializer = new JsonSerializer(); + serializer.ObjectCreationHandling = Newtonsoft.Json.ObjectCreationHandling.Replace; + serializer.MissingMemberHandling = Newtonsoft.Json.MissingMemberHandling.Ignore; + serializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + + return (T)serializer.Deserialize(new BsonReader(new MemoryStream(bson)), type); + } + + public T DeserializeWebExtensions(string json) + { + JavaScriptSerializer ser = new JavaScriptSerializer(); + + return ser.Deserialize(json); + } + + public T DeserializeDataContractJson(string json) + { + DataContractJsonSerializer dataContractSerializer + = new DataContractJsonSerializer(typeof(T)); + + MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)); + + return (T)dataContractSerializer.ReadObject(ms); + } + + private T Deserialize(SerializeMethod method, object json) + { + switch (method) + { + case SerializeMethod.JsonNet: + return DeserializeJsonNet((string)json); + case SerializeMethod.JsonNetBinary: + return DeserializeJsonNetBinary((byte[])json); + case SerializeMethod.BinaryFormatter: + return DeserializeBinaryFormatter((byte[])json); + case SerializeMethod.JavaScriptSerializer: + return DeserializeWebExtensions((string)json); + case SerializeMethod.DataContractSerializer: + return DeserializeDataContract((string)json); + case SerializeMethod.DataContractJsonSerializer: + return DeserializeDataContractJson((string)json); + default: + throw new ArgumentOutOfRangeException("method"); + } + } + + private T DeserializeDataContract(string xml) + { + MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(xml)); + + DataContractSerializer serializer = new DataContractSerializer(typeof(T)); + return (T)serializer.ReadObject(ms); + } + + private T DeserializeBinaryFormatter(byte[] bytes) + { + BinaryFormatter formatter = new BinaryFormatter(); + return (T)formatter.Deserialize(new MemoryStream(bytes)); + } + #endregion + + + + [Test] + public void SerializeLargeObject() + { + LargeRecursiveTestClass rootValue = null; + LargeRecursiveTestClass parentValue = null; + for (int i = 0; i < 20; i++) + { + LargeRecursiveTestClass currentValue = new LargeRecursiveTestClass() + { + Integer = int.MaxValue, + Text = "The quick red fox jumped over the lazy dog." + }; + + if (rootValue == null) + rootValue = currentValue; + if (parentValue != null) + parentValue.Child = currentValue; + + parentValue = currentValue; + } + + BenchmarkSerializeMethod(SerializeMethod.JsonNetBinary, rootValue); + } + + [Test] + public void JObjectToString() + { + JObject test = JObject.Parse(JsonText); + IsoDateTimeConverter isoDateTimeConverter = null;// = new IsoDateTimeConverter(); + + TimeOperation(() => + { + for (int i = 0; i < Iterations; i++) + { + test["dummy"] = new JValue(i); + Encoding.UTF8.GetBytes(test.ToString(Formatting.None)); + } + return null; + }, "JObject.ToString"); + } + + [Test] + public void JObjectToString2() + { + JObject test = JObject.Parse(JsonText); + IsoDateTimeConverter isoDateTimeConverter = null;// = new IsoDateTimeConverter(); + MemoryStream ms = new MemoryStream(); + + TimeOperation(() => + { + for (int i = 0; i < Iterations; i++) + { + test["dummy"] = new JValue(i); + ms.Seek(0, SeekOrigin.Begin); + JsonTextWriter jsonTextWriter = new JsonTextWriter(new StreamWriter(ms)); + test.WriteTo(jsonTextWriter); + jsonTextWriter.Flush(); + ms.ToArray(); + + //Encoding.UTF8.GetBytes(test.ToString(Formatting.None)); + } + return null; + }, "JObject.ToString"); + } + } + + public class LargeRecursiveTestClass + { + public LargeRecursiveTestClass Child { get; set; } + public string Text { get; set; } + public int Integer { get; set; } + } + + #region Classes + [Serializable] + [DataContract] + public class TestClass + { + [DataMember] + public string Name + { + get { return _Name; } + set { _Name = value; } + } + private string _Name = "Rick"; + + [DataMember] + public DateTime Now + { + get { return _Now; } + set { _Now = value; } + } + private DateTime _Now = DateTime.Now; + + [DataMember] + public decimal BigNumber + { + get { return _BigNumber; } + set { _BigNumber = value; } + } + private decimal _BigNumber = 1212121.22M; + + [DataMember] + public Address Address1 + { + get { return _Address1; } + set { _Address1 = value; } + } + private Address _Address1 = new Address(); + + + + [DataMember] + public List
Addresses + { + get { return _Addresses; } + set { _Addresses = value; } + } + private List
_Addresses = new List
(); + + [DataMember] + public List strings = new List(); + + [DataMember] + public Dictionary dictionary = new Dictionary(); + } + + [Serializable] + [DataContract] + public class Address + { + [DataMember] + public string Street + { + get { return _street; } + set { _street = value; } + } + private string _street = "32 Kaiea"; + + [DataMember] + public string Phone + { + get { return _Phone; } + set { _Phone = value; } + } + private string _Phone = "(503) 814-6335"; + + [DataMember] + public DateTime Entered + { + get { return _Entered; } + set { _Entered = value; } + } + private DateTime _Entered = DateTime.Parse("01/01/2007", CultureInfo.CurrentCulture.DateTimeFormat); + } + #endregion +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..312623e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,45 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. + +#if SILVERLIGHT +[assembly: AssemblyTitle("Newtonsoft Json.NET Tests Silverlight")] +#elif PocketPC +[assembly: AssemblyTitle("Newtonsoft Json.NET Tests Compact")] +#else +[assembly: AssemblyTitle("Newtonsoft Json.NET Tests")] +#endif + +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Newtonsoft")] +[assembly: AssemblyProduct("Newtonsoft Json.NET Tests")] +[assembly: AssemblyCopyright("Copyright © Newtonsoft 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0be3d72b-d2ef-409c-985c-d3ede89a25f1")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("4.0.2.0")] +#if !PocketPC +[assembly: AssemblyFileVersion("4.0.2.13623")] +#endif diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/ExtensionsTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/ExtensionsTests.cs new file mode 100644 index 0000000..3e315ea --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/ExtensionsTests.cs @@ -0,0 +1,280 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Schema; +using Newtonsoft.Json.Linq; +using System.IO; +using Newtonsoft.Json.Tests.TestObjects; +#if !SILVERLIGHT +using System.Data; +#endif + +namespace Newtonsoft.Json.Tests.Schema +{ + public class ExtensionsTests : TestFixtureBase + { + [Test] + public void IsValid() + { + JsonSchema schema = JsonSchema.Parse("{'type':'integer'}"); + JToken stringToken = JToken.FromObject("pie"); + JToken integerToken = JToken.FromObject(1); + + Assert.AreEqual(false, stringToken.IsValid(schema)); + Assert.AreEqual(true, integerToken.IsValid(schema)); + } + + [Test] + public void ValidateWithEventHandler() + { + JsonSchema schema = JsonSchema.Parse("{'pattern':'lol'}"); + JToken stringToken = JToken.FromObject("pie lol"); + + List errors = new List(); + stringToken.Validate(schema, (sender, args) => errors.Add(args.Message)); + Assert.AreEqual(0, errors.Count); + + stringToken = JToken.FromObject("pie"); + + stringToken.Validate(schema, (sender, args) => errors.Add(args.Message)); + Assert.AreEqual(1, errors.Count); + + Assert.AreEqual("String 'pie' does not match regex pattern 'lol'.", errors[0]); + } + + [Test] + [ExpectedException(typeof(JsonSchemaException), ExpectedMessage = @"String 'pie' does not match regex pattern 'lol'.")] + public void ValidateWithOutEventHandlerFailure() + { + JsonSchema schema = JsonSchema.Parse("{'pattern':'lol'}"); + JToken stringToken = JToken.FromObject("pie"); + stringToken.Validate(schema); + } + + [Test] + public void ValidateWithOutEventHandlerSuccess() + { + JsonSchema schema = JsonSchema.Parse("{'pattern':'lol'}"); + JToken stringToken = JToken.FromObject("pie lol"); + stringToken.Validate(schema); + } + + [Test] + public void ValidateFailureWithOutLineInfoBecauseOfEndToken() + { + JsonSchema schema = JsonSchema.Parse("{'properties':{'lol':{'required':true}}}"); + JObject o = JObject.Parse("{}"); + + List errors = new List(); + o.Validate(schema, (sender, args) => errors.Add(args.Message)); + + Assert.AreEqual("Required properties are missing from object: lol.", errors[0]); + Assert.AreEqual(1, errors.Count); + } + + [Test] + public void ValidateFailureWithLineInfo() + { + JsonSchema schema = JsonSchema.Parse("{'properties':{'lol':{'type':'string'}}}"); + JObject o = JObject.Parse("{'lol':1}"); + + List errors = new List(); + o.Validate(schema, (sender, args) => errors.Add(args.Message)); + + Assert.AreEqual("Invalid type. Expected String but got Integer. Line 1, position 9.", errors[0]); + Assert.AreEqual(1, errors.Count); + } + + [Test] + public void Blog() + { + string schemaJson = @" +{ + ""description"": ""A person schema"", + ""type"": ""object"", + ""properties"": + { + ""name"": {""type"":""string""}, + ""hobbies"": { + ""type"": ""array"", + ""items"": {""type"":""string""} + } + } +} +"; + + //JsonSchema schema; + + //using (JsonTextReader reader = new JsonTextReader(new StringReader(schemaJson))) + //{ + // JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + // schema = builder.Parse(reader); + //} + + JsonSchema schema = JsonSchema.Parse(schemaJson); + + JObject person = JObject.Parse(@"{ + ""name"": ""James"", + ""hobbies"": ["".NET"", ""Blogging"", ""Reading"", ""Xbox"", ""LOLCATS""] + }"); + + bool valid = person.IsValid(schema); + // true + } + + private void GenerateSchemaAndSerializeFromType(T value) + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseAssemblyQualifiedName; + JsonSchema typeSchema = generator.Generate(typeof (T)); + string schema = typeSchema.ToString(); + + string json = JsonConvert.SerializeObject(value, Formatting.Indented); + JToken token = JToken.ReadFrom(new JsonTextReader(new StringReader(json))); + + List errors = new List(); + + token.Validate(typeSchema, (sender, args) => + { + errors.Add(args.Message); + }); + + if (errors.Count > 0) + Assert.Fail("Schema generated for type '{0}' is not valid." + Environment.NewLine + string.Join(Environment.NewLine, errors.ToArray()), typeof(T)); + } + + [Test] + public void GenerateSchemaAndSerializeFromTypeTests() + { + GenerateSchemaAndSerializeFromType(new List { "1", "Two", "III" }); + GenerateSchemaAndSerializeFromType(new List { 1 }); + GenerateSchemaAndSerializeFromType(new Version("1.2.3.4")); + GenerateSchemaAndSerializeFromType(new Store()); + GenerateSchemaAndSerializeFromType(new Person()); + GenerateSchemaAndSerializeFromType(new PersonRaw()); + GenerateSchemaAndSerializeFromType(new CircularReferenceClass() { Name = "I'm required" }); + GenerateSchemaAndSerializeFromType(new CircularReferenceWithIdClass()); + GenerateSchemaAndSerializeFromType(new ClassWithArray()); + GenerateSchemaAndSerializeFromType(new ClassWithGuid()); +#if !NET20 && !PocketPC + GenerateSchemaAndSerializeFromType(new NullableDateTimeTestClass()); +#endif +#if !SILVERLIGHT + GenerateSchemaAndSerializeFromType(new DataSet()); +#endif + GenerateSchemaAndSerializeFromType(new object()); + GenerateSchemaAndSerializeFromType(1); + GenerateSchemaAndSerializeFromType("Hi"); + GenerateSchemaAndSerializeFromType(new DateTime(2000, 12, 29, 23, 59, 0, DateTimeKind.Utc)); + GenerateSchemaAndSerializeFromType(TimeSpan.FromTicks(1000000)); + GenerateSchemaAndSerializeFromType(DBNull.Value); + GenerateSchemaAndSerializeFromType(new JsonPropertyWithHandlingValues()); + } + + [Test] + public void UndefinedPropertyOnNoPropertySchema() + { + JsonSchema schema = JsonSchema.Parse(@"{ + ""description"": ""test"", + ""type"": ""object"", + ""additionalProperties"": false, + ""properties"": { + } +}"); + + JObject o = JObject.Parse("{'g':1}"); + + List errors = new List(); + o.Validate(schema, (sender, args) => errors.Add(args.Message)); + + Assert.AreEqual(1, errors.Count); + Assert.AreEqual("Property 'g' has not been defined and the schema does not allow additional properties. Line 1, position 5.", errors[0]); + } + + [Test] + [ExpectedException(typeof(JsonSchemaException), ExpectedMessage = "Integer 10 equals maximum value of 10 and exclusive maximum is true.")] + public void ExclusiveMaximum_Int() + { + JsonSchema schema = new JsonSchema(); + schema.Maximum = 10; + schema.ExclusiveMaximum = true; + + JValue v = new JValue(10); + v.Validate(schema); + } + + [Test] + [ExpectedException(typeof(JsonSchemaException), ExpectedMessage = "Float 10.1 equals maximum value of 10.1 and exclusive maximum is true.")] + public void ExclusiveMaximum_Float() + { + JsonSchema schema = new JsonSchema(); + schema.Maximum = 10.1; + schema.ExclusiveMaximum = true; + + JValue v = new JValue(10.1); + v.Validate(schema); + } + + [Test] + [ExpectedException(typeof(JsonSchemaException), ExpectedMessage = "Integer 10 equals minimum value of 10 and exclusive minimum is true.")] + public void ExclusiveMinimum_Int() + { + JsonSchema schema = new JsonSchema(); + schema.Minimum = 10; + schema.ExclusiveMinimum = true; + + JValue v = new JValue(10); + v.Validate(schema); + } + + [Test] + [ExpectedException(typeof(JsonSchemaException), ExpectedMessage = "Float 10.1 equals minimum value of 10.1 and exclusive minimum is true.")] + public void ExclusiveMinimum_Float() + { + JsonSchema schema = new JsonSchema(); + schema.Minimum = 10.1; + schema.ExclusiveMinimum = true; + + JValue v = new JValue(10.1); + v.Validate(schema); + } + + [Test] + [ExpectedException(typeof(JsonSchemaException), ExpectedMessage = "Integer 10 is not evenly divisible by 3.")] + public void DivisibleBy_Int() + { + JsonSchema schema = new JsonSchema(); + schema.DivisibleBy = 3; + + JValue v = new JValue(10); + v.Validate(schema); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaBuilderTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaBuilderTests.cs new file mode 100644 index 0000000..0c27c02 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaBuilderTests.cs @@ -0,0 +1,469 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Schema; +using System.IO; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Schema +{ + public class JsonSchemaBuilderTests : TestFixtureBase + { + [Test] + public void Simple() + { + string json = @" +{ + ""description"": ""A person"", + ""type"": ""object"", + ""properties"": + { + ""name"": {""type"":""string""}, + ""hobbies"": { + ""type"": ""array"", + ""items"": {""type"":""string""} + } + } +} +"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("A person", schema.Description); + Assert.AreEqual(JsonSchemaType.Object, schema.Type); + + Assert.AreEqual(2, schema.Properties.Count); + + Assert.AreEqual(JsonSchemaType.String, schema.Properties["name"].Type); + Assert.AreEqual(JsonSchemaType.Array, schema.Properties["hobbies"].Type); + Assert.AreEqual(JsonSchemaType.String, schema.Properties["hobbies"].Items[0].Type); + } + + [Test] + public void MultipleTypes() + { + string json = @"{ + ""description"":""Age"", + ""type"":[""string"", ""integer""] +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("Age", schema.Description); + Assert.AreEqual(JsonSchemaType.String | JsonSchemaType.Integer, schema.Type); + } + + [Test] + public void MultipleItems() + { + string json = @"{ + ""description"":""MultipleItems"", + ""type"":""array"", + ""items"": [{""type"":""string""},{""type"":""array""}] +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("MultipleItems", schema.Description); + Assert.AreEqual(JsonSchemaType.String, schema.Items[0].Type); + Assert.AreEqual(JsonSchemaType.Array, schema.Items[1].Type); + } + + [Test] + public void AdditionalProperties() + { + string json = @"{ + ""description"":""AdditionalProperties"", + ""type"":[""string"", ""integer""], + ""additionalProperties"":{""type"":[""object"", ""boolean""]} +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("AdditionalProperties", schema.Description); + Assert.AreEqual(JsonSchemaType.Object | JsonSchemaType.Boolean, schema.AdditionalProperties.Type); + } + + [Test] + public void Required() + { + string json = @"{ + ""description"":""Required"", + ""required"":true +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("Required", schema.Description); + Assert.AreEqual(true, schema.Required); + } + + [Test] + public void ExclusiveMinimum_ExclusiveMaximum() + { + string json = @"{ + ""exclusiveMinimum"":true, + ""exclusiveMaximum"":true +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual(true, schema.ExclusiveMinimum); + Assert.AreEqual(true, schema.ExclusiveMaximum); + } + + [Test] + public void ReadOnly() + { + string json = @"{ + ""description"":""ReadOnly"", + ""readonly"":true +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("ReadOnly", schema.Description); + Assert.AreEqual(true, schema.ReadOnly); + } + + [Test] + public void Hidden() + { + string json = @"{ + ""description"":""Hidden"", + ""hidden"":true +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("Hidden", schema.Description); + Assert.AreEqual(true, schema.Hidden); + } + + [Test] + public void Id() + { + string json = @"{ + ""description"":""Id"", + ""id"":""testid"" +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("Id", schema.Description); + Assert.AreEqual("testid", schema.Id); + } + + [Test] + public void Title() + { + string json = @"{ + ""description"":""Title"", + ""title"":""testtitle"" +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("Title", schema.Description); + Assert.AreEqual("testtitle", schema.Title); + } + + [Test] + public void Pattern() + { + string json = @"{ + ""description"":""Pattern"", + ""pattern"":""testpattern"" +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("Pattern", schema.Description); + Assert.AreEqual("testpattern", schema.Pattern); + } + + [Test] + public void Format() + { + string json = @"{ + ""description"":""Format"", + ""format"":""testformat"" +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("Format", schema.Description); + Assert.AreEqual("testformat", schema.Format); + } + + [Test] + public void Requires() + { + string json = @"{ + ""description"":""Requires"", + ""requires"":""PurpleMonkeyDishwasher"" +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("Requires", schema.Description); + Assert.AreEqual("PurpleMonkeyDishwasher", schema.Requires); + } + + [Test] + public void IdentitySingle() + { + string json = @"{ + ""description"":""Identity"", + ""identity"":""PurpleMonkeyDishwasher"" +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("Identity", schema.Description); + Assert.AreEqual(1, schema.Identity.Count); + Assert.AreEqual("PurpleMonkeyDishwasher", schema.Identity[0]); + } + + [Test] + public void IdentityMultiple() + { + string json = @"{ + ""description"":""Identity"", + ""identity"":[""PurpleMonkeyDishwasher"",""Antelope""] +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("Identity", schema.Description); + Assert.AreEqual(2, schema.Identity.Count); + Assert.AreEqual("PurpleMonkeyDishwasher", schema.Identity[0]); + Assert.AreEqual("Antelope", schema.Identity[1]); + } + + [Test] + public void MinimumMaximum() + { + string json = @"{ + ""description"":""MinimumMaximum"", + ""minimum"":1.1, + ""maximum"":1.2, + ""minItems"":1, + ""maxItems"":2, + ""minLength"":5, + ""maxLength"":50, + ""divisibleBy"":3, +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("MinimumMaximum", schema.Description); + Assert.AreEqual(1.1, schema.Minimum); + Assert.AreEqual(1.2, schema.Maximum); + Assert.AreEqual(1, schema.MinimumItems); + Assert.AreEqual(2, schema.MaximumItems); + Assert.AreEqual(5, schema.MinimumLength); + Assert.AreEqual(50, schema.MaximumLength); + Assert.AreEqual(3, schema.DivisibleBy); + } + + [Test] + public void DisallowSingleType() + { + string json = @"{ + ""description"":""DisallowSingleType"", + ""disallow"":""string"" +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("DisallowSingleType", schema.Description); + Assert.AreEqual(JsonSchemaType.String, schema.Disallow); + } + + [Test] + public void DisallowMultipleTypes() + { + string json = @"{ + ""description"":""DisallowMultipleTypes"", + ""disallow"":[""string"",""number""] +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("DisallowMultipleTypes", schema.Description); + Assert.AreEqual(JsonSchemaType.String | JsonSchemaType.Float, schema.Disallow); + } + + [Test] + public void DefaultPrimitiveType() + { + string json = @"{ + ""description"":""DefaultPrimitiveType"", + ""default"":1.1 +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("DefaultPrimitiveType", schema.Description); + Assert.AreEqual(1.1, (double)schema.Default); + } + + [Test] + public void DefaultComplexType() + { + string json = @"{ + ""description"":""DefaultComplexType"", + ""default"":{""pie"":true} +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("DefaultComplexType", schema.Description); + Assert.IsTrue(JToken.DeepEquals(JObject.Parse(@"{""pie"":true}"), schema.Default)); + } + + [Test] + public void Options() + { + string json = @"{ + ""description"":""NZ Island"", + ""type"":""string"", + ""options"": + [ + {""value"":""NI"",""label"":""North Island""}, + {""value"":""SI"",""label"":""South Island""} + ] +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("NZ Island", schema.Description); + Assert.AreEqual(JsonSchemaType.String, schema.Type); + + Assert.AreEqual(2, schema.Options.Count); + Assert.AreEqual("North Island", schema.Options[new JValue("NI")]); + Assert.AreEqual("South Island", schema.Options[new JValue("SI")]); + } + + [Test] + public void Enum() + { + string json = @"{ + ""description"":""Type"", + ""type"":[""string"",""array""], + ""enum"":[""string"",""object"",""array"",""boolean"",""number"",""integer"",""null"",""any""] +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("Type", schema.Description); + Assert.AreEqual(JsonSchemaType.String | JsonSchemaType.Array, schema.Type); + + Assert.AreEqual(8, schema.Enum.Count); + Assert.AreEqual("string", (string)schema.Enum[0]); + Assert.AreEqual("any", (string)schema.Enum[schema.Enum.Count - 1]); + } + + [Test] + public void CircularReference() + { + string json = @"{ + ""id"":""CircularReferenceArray"", + ""description"":""CircularReference"", + ""type"":[""array""], + ""items"":{""$ref"":""CircularReferenceArray""} +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.AreEqual("CircularReference", schema.Description); + Assert.AreEqual("CircularReferenceArray", schema.Id); + Assert.AreEqual(JsonSchemaType.Array, schema.Type); + + Assert.AreEqual(schema, schema.Items[0]); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = @"Could not resolve schema reference for Id 'MyUnresolvedReference'.")] + public void UnresolvedReference() + { + string json = @"{ + ""id"":""CircularReferenceArray"", + ""description"":""CircularReference"", + ""type"":[""array""], + ""items"":{""$ref"":""MyUnresolvedReference""} +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + } + + [Test] + public void PatternProperties() + { + string json = @"{ + ""patternProperties"": { + ""[abc]"": { ""id"":""Blah"" } + } +}"; + + JsonSchemaBuilder builder = new JsonSchemaBuilder(new JsonSchemaResolver()); + JsonSchema schema = builder.Parse(new JsonTextReader(new StringReader(json))); + + Assert.IsNotNull(schema.PatternProperties); + Assert.AreEqual(1, schema.PatternProperties.Count); + Assert.AreEqual("Blah", schema.PatternProperties["[abc]"].Id); + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs new file mode 100644 index 0000000..7365f9d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaGeneratorTests.cs @@ -0,0 +1,724 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Tests.TestObjects; +using Newtonsoft.Json.Utilities; +using NUnit.Framework; +using Newtonsoft.Json.Schema; +using System.IO; +using System.Linq; +using Newtonsoft.Json.Linq; +using System.Text; +using Extensions=Newtonsoft.Json.Schema.Extensions; + +namespace Newtonsoft.Json.Tests.Schema +{ + public class JsonSchemaGeneratorTests : TestFixtureBase + { + [Test] + public void Generate_GenericDictionary() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + JsonSchema schema = generator.Generate(typeof (Dictionary>)); + + string json = schema.ToString(); + + Assert.AreEqual(@"{ + ""type"": ""object"", + ""additionalProperties"": { + ""type"": [ + ""array"", + ""null"" + ], + ""items"": { + ""type"": [ + ""string"", + ""null"" + ] + } + } +}", json); + + Dictionary> value = new Dictionary> + { + {"HasValue", new List() { "first", "second", null }}, + {"NoValue", null} + }; + + string valueJson = JsonConvert.SerializeObject(value, Formatting.Indented); + JObject o = JObject.Parse(valueJson); + + Assert.IsTrue(o.IsValid(schema)); + } + +#if !PocketPC + [Test] + public void Generate_DefaultValueAttributeTestClass() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + JsonSchema schema = generator.Generate(typeof(DefaultValueAttributeTestClass)); + + string json = schema.ToString(); + + Assert.AreEqual(@"{ + ""description"": ""DefaultValueAttributeTestClass description!"", + ""type"": ""object"", + ""additionalProperties"": false, + ""properties"": { + ""TestField1"": { + ""required"": true, + ""type"": ""integer"", + ""default"": 21 + }, + ""TestProperty1"": { + ""required"": true, + ""type"": [ + ""string"", + ""null"" + ], + ""default"": ""TestProperty1Value"" + } + } +}", json); + } +#endif + + [Test] + public void Generate_Person() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + JsonSchema schema = generator.Generate(typeof(Person)); + + string json = schema.ToString(); + + Assert.AreEqual(@"{ + ""id"": ""Person"", + ""title"": ""Title!"", + ""description"": ""JsonObjectAttribute description!"", + ""type"": ""object"", + ""properties"": { + ""Name"": { + ""required"": true, + ""type"": [ + ""string"", + ""null"" + ] + }, + ""BirthDate"": { + ""required"": true, + ""type"": ""string"" + }, + ""LastModified"": { + ""required"": true, + ""type"": ""string"" + } + } +}", json); + } + + [Test] + public void Generate_UserNullable() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + JsonSchema schema = generator.Generate(typeof(UserNullable)); + + string json = schema.ToString(); + + Assert.AreEqual(@"{ + ""type"": ""object"", + ""properties"": { + ""Id"": { + ""required"": true, + ""type"": ""string"" + }, + ""FName"": { + ""required"": true, + ""type"": [ + ""string"", + ""null"" + ] + }, + ""LName"": { + ""required"": true, + ""type"": [ + ""string"", + ""null"" + ] + }, + ""RoleId"": { + ""required"": true, + ""type"": ""integer"" + }, + ""NullableRoleId"": { + ""required"": true, + ""type"": [ + ""integer"", + ""null"" + ] + }, + ""NullRoleId"": { + ""required"": true, + ""type"": [ + ""integer"", + ""null"" + ] + }, + ""Active"": { + ""required"": true, + ""type"": [ + ""boolean"", + ""null"" + ] + } + } +}", json); + } + + [Test] + public void Generate_RequiredMembersClass() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + JsonSchema schema = generator.Generate(typeof(RequiredMembersClass)); + + Assert.AreEqual(JsonSchemaType.String, schema.Properties["FirstName"].Type); + Assert.AreEqual(JsonSchemaType.String | JsonSchemaType.Null, schema.Properties["MiddleName"].Type); + Assert.AreEqual(JsonSchemaType.String | JsonSchemaType.Null, schema.Properties["LastName"].Type); + Assert.AreEqual(JsonSchemaType.String, schema.Properties["BirthDate"].Type); + } + + [Test] + public void Generate_Store() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + JsonSchema schema = generator.Generate(typeof(Store)); + + Assert.AreEqual(11, schema.Properties.Count); + + JsonSchema productArraySchema = schema.Properties["product"]; + JsonSchema productSchema = productArraySchema.Items[0]; + + Assert.AreEqual(4, productSchema.Properties.Count); + } + + [Test] + public void MissingSchemaIdHandlingTest() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + + JsonSchema schema = generator.Generate(typeof(Store)); + Assert.AreEqual(null, schema.Id); + + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + schema = generator.Generate(typeof (Store)); + Assert.AreEqual(typeof(Store).FullName, schema.Id); + + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseAssemblyQualifiedName; + schema = generator.Generate(typeof(Store)); + Assert.AreEqual(typeof(Store).AssemblyQualifiedName, schema.Id); + } + + [Test] + public void Generate_NumberFormatInfo() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + JsonSchema schema = generator.Generate(typeof(NumberFormatInfo)); + + string json = schema.ToString(); + + Console.WriteLine(json); + + // Assert.AreEqual(@"{ + // ""type"": ""object"", + // ""additionalProperties"": { + // ""type"": ""array"", + // ""items"": { + // ""type"": ""string"" + // } + // } + //}", json); + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = @"Unresolved circular reference for type 'Newtonsoft.Json.Tests.TestObjects.CircularReferenceClass'. Explicitly define an Id for the type using a JsonObject/JsonArray attribute or automatically generate a type Id using the UndefinedSchemaIdHandling property.")] + public void CircularReferenceError() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.Generate(typeof(CircularReferenceClass)); + } + + [Test] + public void CircularReferenceWithTypeNameId() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + + JsonSchema schema = generator.Generate(typeof(CircularReferenceClass), true); + + Assert.AreEqual(JsonSchemaType.String, schema.Properties["Name"].Type); + Assert.AreEqual(typeof(CircularReferenceClass).FullName, schema.Id); + Assert.AreEqual(JsonSchemaType.Object | JsonSchemaType.Null, schema.Properties["Child"].Type); + Assert.AreEqual(schema, schema.Properties["Child"]); + } + + [Test] + public void CircularReferenceWithExplicitId() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + + JsonSchema schema = generator.Generate(typeof(CircularReferenceWithIdClass)); + + Assert.AreEqual(JsonSchemaType.String | JsonSchemaType.Null, schema.Properties["Name"].Type); + Assert.AreEqual("MyExplicitId", schema.Id); + Assert.AreEqual(JsonSchemaType.Object | JsonSchemaType.Null, schema.Properties["Child"].Type); + Assert.AreEqual(schema, schema.Properties["Child"]); + } + + [Test] + public void GenerateSchemaForType() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + + JsonSchema schema = generator.Generate(typeof(Type)); + + Assert.AreEqual(JsonSchemaType.String, schema.Type); + + string json = JsonConvert.SerializeObject(typeof(Version), Formatting.Indented); + + JValue v = new JValue(json); + Assert.IsTrue(v.IsValid(schema)); + } + +#if !SILVERLIGHT && !PocketPC + [Test] + public void GenerateSchemaForISerializable() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + + JsonSchema schema = generator.Generate(typeof(Exception)); + + Assert.AreEqual(JsonSchemaType.Object, schema.Type); + Assert.AreEqual(true, schema.AllowAdditionalProperties); + Assert.AreEqual(null, schema.Properties); + } +#endif + + [Test] + public void GenerateSchemaForDBNull() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + + JsonSchema schema = generator.Generate(typeof(DBNull)); + + Assert.AreEqual(JsonSchemaType.Null, schema.Type); + } + + public class CustomDirectoryInfoMapper : DefaultContractResolver + { + public CustomDirectoryInfoMapper() + : base(true) + { + } + + protected override JsonContract CreateContract(Type objectType) + { + if (objectType == typeof(DirectoryInfo)) + return base.CreateObjectContract(objectType); + + return base.CreateContract(objectType); + } + + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) + { + IList properties = base.CreateProperties(type, memberSerialization); + + JsonPropertyCollection c = new JsonPropertyCollection(type); + CollectionUtils.AddRange(c, (IEnumerable)properties.Where(m => m.PropertyName != "Root")); + + return c; + } + } + + [Test] + public void GenerateSchemaForDirectoryInfo() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + generator.ContractResolver = new CustomDirectoryInfoMapper(); + + JsonSchema schema = generator.Generate(typeof(DirectoryInfo), true); + + string json = schema.ToString(); + + Assert.AreEqual(@"{ + ""id"": ""System.IO.DirectoryInfo"", + ""required"": true, + ""type"": [ + ""object"", + ""null"" + ], + ""additionalProperties"": false, + ""properties"": { + ""Name"": { + ""required"": true, + ""type"": [ + ""string"", + ""null"" + ] + }, + ""Parent"": { + ""$ref"": ""System.IO.DirectoryInfo"" + }, + ""Exists"": { + ""required"": true, + ""type"": ""boolean"" + }, + ""FullName"": { + ""required"": true, + ""type"": [ + ""string"", + ""null"" + ] + }, + ""Extension"": { + ""required"": true, + ""type"": [ + ""string"", + ""null"" + ] + }, + ""CreationTime"": { + ""required"": true, + ""type"": ""string"" + }, + ""CreationTimeUtc"": { + ""required"": true, + ""type"": ""string"" + }, + ""LastAccessTime"": { + ""required"": true, + ""type"": ""string"" + }, + ""LastAccessTimeUtc"": { + ""required"": true, + ""type"": ""string"" + }, + ""LastWriteTime"": { + ""required"": true, + ""type"": ""string"" + }, + ""LastWriteTimeUtc"": { + ""required"": true, + ""type"": ""string"" + }, + ""Attributes"": { + ""required"": true, + ""type"": ""integer"" + } + } +}", json); + + DirectoryInfo temp = new DirectoryInfo(@"c:\temp"); + + JTokenWriter jsonWriter = new JTokenWriter(); + JsonSerializer serializer = new JsonSerializer(); + serializer.Converters.Add(new IsoDateTimeConverter()); + serializer.ContractResolver = new CustomDirectoryInfoMapper(); + serializer.Serialize(jsonWriter, temp); + + List errors = new List(); + jsonWriter.Token.Validate(schema, (sender, args) => errors.Add(args.Message)); + + Assert.AreEqual(0, errors.Count); + } + + [Test] + public void GenerateSchemaCamelCase() + { + JsonSchemaGenerator generator = new JsonSchemaGenerator(); + generator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + generator.ContractResolver = new CamelCasePropertyNamesContractResolver(); + + JsonSchema schema = generator.Generate(typeof (Version), true); + + string json = schema.ToString(); + + Assert.AreEqual(@"{ + ""id"": ""System.Version"", + ""type"": [ + ""object"", + ""null"" + ], + ""additionalProperties"": false, + ""properties"": { + ""major"": { + ""required"": true, + ""type"": ""integer"" + }, + ""minor"": { + ""required"": true, + ""type"": ""integer"" + }, + ""build"": { + ""required"": true, + ""type"": ""integer"" + }, + ""revision"": { + ""required"": true, + ""type"": ""integer"" + }, + ""majorRevision"": { + ""required"": true, + ""type"": ""integer"" + }, + ""minorRevision"": { + ""required"": true, + ""type"": ""integer"" + } + } +}", json); + } + + public enum SortTypeFlag + { + No = 0, + Asc = 1, + Desc = -1 + } + + public class X + { + public SortTypeFlag x; + } + + [Test] + public void GenerateSchemaWithNegativeEnum() + { + JsonSchemaGenerator jsonSchemaGenerator = new JsonSchemaGenerator(); + JsonSchema schema = jsonSchemaGenerator.Generate(typeof(X)); + + string json = schema.ToString(); + + Assert.AreEqual(@"{ + ""type"": ""object"", + ""properties"": { + ""x"": { + ""required"": true, + ""type"": ""integer"", + ""enum"": [ + 0, + 1, + -1 + ], + ""options"": [ + { + ""value"": 0, + ""label"": ""No"" + }, + { + ""value"": 1, + ""label"": ""Asc"" + }, + { + ""value"": -1, + ""label"": ""Desc"" + } + ] + } + } +}", json); + } + + [Test] + public void CircularCollectionReferences() + { + Type type = typeof (Workspace); + JsonSchemaGenerator jsonSchemaGenerator = new JsonSchemaGenerator(); + + jsonSchemaGenerator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + JsonSchema jsonSchema = jsonSchemaGenerator.Generate(type); + + // should succeed + Assert.IsNotNull(jsonSchema); + } + + [Test] + public void CircularReferenceWithMixedRequires() + { + JsonSchemaGenerator jsonSchemaGenerator = new JsonSchemaGenerator(); + + jsonSchemaGenerator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + JsonSchema jsonSchema = jsonSchemaGenerator.Generate(typeof(CircularReferenceClass)); + string json = jsonSchema.ToString(); + + Assert.AreEqual(@"{ + ""id"": ""Newtonsoft.Json.Tests.TestObjects.CircularReferenceClass"", + ""type"": [ + ""object"", + ""null"" + ], + ""properties"": { + ""Name"": { + ""required"": true, + ""type"": ""string"" + }, + ""Child"": { + ""$ref"": ""Newtonsoft.Json.Tests.TestObjects.CircularReferenceClass"" + } + } +}", json); + } + + [Test] + public void JsonPropertyWithHandlingValues() + { + JsonSchemaGenerator jsonSchemaGenerator = new JsonSchemaGenerator(); + + jsonSchemaGenerator.UndefinedSchemaIdHandling = UndefinedSchemaIdHandling.UseTypeName; + JsonSchema jsonSchema = jsonSchemaGenerator.Generate(typeof(JsonPropertyWithHandlingValues)); + string json = jsonSchema.ToString(); + + Assert.AreEqual(@"{ + ""id"": ""Newtonsoft.Json.Tests.TestObjects.JsonPropertyWithHandlingValues"", + ""required"": true, + ""type"": [ + ""object"", + ""null"" + ], + ""properties"": { + ""DefaultValueHandlingIgnoreProperty"": { + ""type"": [ + ""string"", + ""null"" + ], + ""default"": ""Default!"" + }, + ""DefaultValueHandlingIncludeProperty"": { + ""required"": true, + ""type"": [ + ""string"", + ""null"" + ], + ""default"": ""Default!"" + }, + ""NullValueHandlingIgnoreProperty"": { + ""type"": [ + ""string"", + ""null"" + ] + }, + ""NullValueHandlingIncludeProperty"": { + ""required"": true, + ""type"": [ + ""string"", + ""null"" + ] + }, + ""ReferenceLoopHandlingErrorProperty"": { + ""$ref"": ""Newtonsoft.Json.Tests.TestObjects.JsonPropertyWithHandlingValues"" + }, + ""ReferenceLoopHandlingIgnoreProperty"": { + ""$ref"": ""Newtonsoft.Json.Tests.TestObjects.JsonPropertyWithHandlingValues"" + }, + ""ReferenceLoopHandlingSerializeProperty"": { + ""$ref"": ""Newtonsoft.Json.Tests.TestObjects.JsonPropertyWithHandlingValues"" + } + } +}", json); + } + } + + public class DMDSLBase + { + public String Comment; + } + + public class Workspace : DMDSLBase + { + public ControlFlowItemCollection Jobs = new ControlFlowItemCollection(); + } + + public class ControlFlowItemBase : DMDSLBase + { + public String Name; + } + + public class ControlFlowItem : ControlFlowItemBase//A Job + { + public TaskCollection Tasks = new TaskCollection(); + public ContainerCollection Containers = new ContainerCollection(); + } + + public class ControlFlowItemCollection : List + { + } + + public class Task : ControlFlowItemBase + { + public DataFlowTaskCollection DataFlowTasks = new DataFlowTaskCollection(); + public BulkInsertTaskCollection BulkInsertTask = new BulkInsertTaskCollection(); + } + + public class TaskCollection : List + { + } + + public class Container : ControlFlowItemBase + { + public ControlFlowItemCollection ContainerJobs = new ControlFlowItemCollection(); + } + + public class ContainerCollection : List + { + } + + public class DataFlowTask_DSL : ControlFlowItemBase + { + } + + public class DataFlowTaskCollection : List + { + } + + public class SequenceContainer_DSL : Container + { + } + + public class BulkInsertTaskCollection : List + { + } + + public class BulkInsertTask_DSL + { + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaModelBuilderTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaModelBuilderTests.cs new file mode 100644 index 0000000..0443dbd --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaModelBuilderTests.cs @@ -0,0 +1,166 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Schema; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Schema +{ + public class JsonSchemaModelBuilderTests : TestFixtureBase + { + [Test] + public void ExtendedComplex() + { + string first = @"{ + ""id"":""first"", + ""type"":""object"", + ""properties"": + { + ""firstproperty"":{""type"":""string""}, + ""secondproperty"":{""type"":""string"",""maxLength"":10}, + ""thirdproperty"":{ + ""type"":""object"", + ""properties"": + { + ""thirdproperty_firstproperty"":{""type"":""string"",""maxLength"":10,""minLength"":7} + } + } + }, + ""additionalProperties"":{} +}"; + + string second = @"{ + ""id"":""second"", + ""type"":""object"", + ""extends"":{""$ref"":""first""}, + ""properties"": + { + ""secondproperty"":{""type"":""any""}, + ""thirdproperty"":{ + ""extends"":{ + ""properties"": + { + ""thirdproperty_firstproperty"":{""maxLength"":9,""minLength"":6,""pattern"":""hi2u""} + }, + ""additionalProperties"":{""maxLength"":9,""minLength"":6,""enum"":[""one"",""two""]} + }, + ""type"":""object"", + ""properties"": + { + ""thirdproperty_firstproperty"":{""pattern"":""hi""} + }, + ""additionalProperties"":{""type"":""string"",""enum"":[""two"",""three""]} + }, + ""fourthproperty"":{""type"":""string""} + }, + ""additionalProperties"":false +}"; + + JsonSchemaResolver resolver = new JsonSchemaResolver(); + JsonSchema firstSchema = JsonSchema.Parse(first, resolver); + JsonSchema secondSchema = JsonSchema.Parse(second, resolver); + + JsonSchemaModelBuilder modelBuilder = new JsonSchemaModelBuilder(); + + JsonSchemaModel model = modelBuilder.Build(secondSchema); + + Assert.AreEqual(4, model.Properties.Count); + + Assert.AreEqual(JsonSchemaType.String, model.Properties["firstproperty"].Type); + + Assert.AreEqual(JsonSchemaType.String, model.Properties["secondproperty"].Type); + Assert.AreEqual(10, model.Properties["secondproperty"].MaximumLength); + Assert.AreEqual(null, model.Properties["secondproperty"].Enum); + Assert.AreEqual(null, model.Properties["secondproperty"].Patterns); + + Assert.AreEqual(JsonSchemaType.Object, model.Properties["thirdproperty"].Type); + Assert.AreEqual(3, model.Properties["thirdproperty"].AdditionalProperties.Enum.Count); + Assert.AreEqual("two", (string)model.Properties["thirdproperty"].AdditionalProperties.Enum[0]); + Assert.AreEqual("three", (string)model.Properties["thirdproperty"].AdditionalProperties.Enum[1]); + Assert.AreEqual("one", (string)model.Properties["thirdproperty"].AdditionalProperties.Enum[2]); + + Assert.AreEqual(JsonSchemaType.String, model.Properties["thirdproperty"].Properties["thirdproperty_firstproperty"].Type); + Assert.AreEqual(9, model.Properties["thirdproperty"].Properties["thirdproperty_firstproperty"].MaximumLength); + Assert.AreEqual(7, model.Properties["thirdproperty"].Properties["thirdproperty_firstproperty"].MinimumLength); + Assert.AreEqual(2, model.Properties["thirdproperty"].Properties["thirdproperty_firstproperty"].Patterns.Count); + Assert.AreEqual("hi", model.Properties["thirdproperty"].Properties["thirdproperty_firstproperty"].Patterns[0]); + Assert.AreEqual("hi2u", model.Properties["thirdproperty"].Properties["thirdproperty_firstproperty"].Patterns[1]); + Assert.AreEqual(null, model.Properties["thirdproperty"].Properties["thirdproperty_firstproperty"].Properties); + Assert.AreEqual(null, model.Properties["thirdproperty"].Properties["thirdproperty_firstproperty"].Items); + Assert.AreEqual(null, model.Properties["thirdproperty"].Properties["thirdproperty_firstproperty"].AdditionalProperties); + } + + [Test] + public void CircularReference() + { + string json = @"{ + ""id"":""CircularReferenceArray"", + ""description"":""CircularReference"", + ""type"":[""array""], + ""items"":{""$ref"":""CircularReferenceArray""} +}"; + + JsonSchema schema = JsonSchema.Parse(json); + + JsonSchemaModelBuilder modelBuilder = new JsonSchemaModelBuilder(); + + JsonSchemaModel model = modelBuilder.Build(schema); + + Assert.AreEqual(JsonSchemaType.Array, model.Type); + + Assert.AreEqual(model, model.Items[0]); + } + + [Test] + public void Required() + { + string schemaJson = @"{ + ""description"":""A person"", + ""type"":""object"", + ""properties"": + { + ""name"":{""type"":""string""}, + ""hobbies"":{""type"":""string"",required:true}, + ""age"":{""type"":""integer"",required:true} + } +}"; + + JsonSchema schema = JsonSchema.Parse(schemaJson); + JsonSchemaModelBuilder modelBuilder = new JsonSchemaModelBuilder(); + JsonSchemaModel model = modelBuilder.Build(schema); + + Assert.AreEqual(JsonSchemaType.Object, model.Type); + Assert.AreEqual(3, model.Properties.Count); + Assert.AreEqual(false, model.Properties["name"].Required); + Assert.AreEqual(true, model.Properties["hobbies"].Required); + Assert.AreEqual(true, model.Properties["age"].Required); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaNodeTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaNodeTests.cs new file mode 100644 index 0000000..fc26f09 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaNodeTests.cs @@ -0,0 +1,118 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Schema; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Schema +{ + public class JsonSchemaNodeTests : TestFixtureBase + { + [Test] + public void AddSchema() + { + string first = @"{ + ""id"":""first"", + ""type"":""object"", + ""properties"": + { + ""firstproperty"":{""type"":""string"",""maxLength"":10}, + ""secondproperty"":{ + ""type"":""object"", + ""properties"": + { + ""secondproperty_firstproperty"":{""type"":""string"",""maxLength"":10,""minLength"":7} + } + } + }, + ""additionalProperties"":{} +}"; + + string second = @"{ + ""id"":""second"", + ""type"":""object"", + ""extends"":{""$ref"":""first""}, + ""properties"": + { + ""firstproperty"":{""type"":""string""}, + ""secondproperty"":{ + ""extends"":{ + ""properties"": + { + ""secondproperty_firstproperty"":{""maxLength"":9,""minLength"":6} + } + }, + ""type"":""object"", + ""properties"": + { + ""secondproperty_firstproperty"":{} + } + }, + ""thirdproperty"":{""type"":""string""} + }, + ""additionalProperties"":false +}"; + + JsonSchemaResolver resolver = new JsonSchemaResolver(); + JsonSchema firstSchema = JsonSchema.Parse(first, resolver); + JsonSchema secondSchema = JsonSchema.Parse(second, resolver); + + JsonSchemaModelBuilder modelBuilder = new JsonSchemaModelBuilder(); + + JsonSchemaNode node = modelBuilder.AddSchema(null, secondSchema); + + Assert.AreEqual(2, node.Schemas.Count); + Assert.AreEqual(2, node.Properties["firstproperty"].Schemas.Count); + Assert.AreEqual(3, node.Properties["secondproperty"].Schemas.Count); + Assert.AreEqual(3, node.Properties["secondproperty"].Properties["secondproperty_firstproperty"].Schemas.Count); + } + + [Test] + public void CircularReference() + { + string json = @"{ + ""id"":""CircularReferenceArray"", + ""description"":""CircularReference"", + ""type"":[""array""], + ""items"":{""$ref"":""CircularReferenceArray""} +}"; + + JsonSchema schema = JsonSchema.Parse(json); + + JsonSchemaModelBuilder modelBuilder = new JsonSchemaModelBuilder(); + + JsonSchemaNode node = modelBuilder.AddSchema(null, schema); + + Assert.AreEqual(1, node.Schemas.Count); + + Assert.AreEqual(node, node.Items[0]); + } + + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaTests.cs new file mode 100644 index 0000000..ac242a2 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Schema/JsonSchemaTests.cs @@ -0,0 +1,426 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Schema; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Schema +{ + public class JsonSchemaTests : TestFixtureBase + { + [Test] + public void Extends() + { + string json; + JsonSchemaResolver resolver = new JsonSchemaResolver(); + + json = @"{ + ""id"":""first"", + ""type"":""object"", + ""additionalProperties"":{} +}"; + + JsonSchema first = JsonSchema.Parse(json, resolver); + + json = + @"{ + ""id"":""second"", + ""type"":""object"", + ""extends"":{""$ref"":""first""}, + ""additionalProperties"":{""type"":""string""} +}"; + + JsonSchema second = JsonSchema.Parse(json, resolver); + Assert.AreEqual(first, second.Extends); + + json = + @"{ + ""id"":""third"", + ""type"":""object"", + ""extends"":{""$ref"":""second""}, + ""additionalProperties"":false +}"; + + JsonSchema third = JsonSchema.Parse(json, resolver); + Assert.AreEqual(second, third.Extends); + Assert.AreEqual(first, third.Extends.Extends); + + StringWriter writer = new StringWriter(); + JsonTextWriter jsonWriter = new JsonTextWriter(writer); + jsonWriter.Formatting = Formatting.Indented; + + third.WriteTo(jsonWriter, resolver); + + string writtenJson = writer.ToString(); + Assert.AreEqual(@"{ + ""id"": ""third"", + ""type"": ""object"", + ""additionalProperties"": false, + ""extends"": { + ""$ref"": ""second"" + } +}", writtenJson); + + StringWriter writer1 = new StringWriter(); + JsonTextWriter jsonWriter1 = new JsonTextWriter(writer1); + jsonWriter1.Formatting = Formatting.Indented; + + third.WriteTo(jsonWriter1); + + writtenJson = writer1.ToString(); + Assert.AreEqual(@"{ + ""id"": ""third"", + ""type"": ""object"", + ""additionalProperties"": false, + ""extends"": { + ""id"": ""second"", + ""type"": ""object"", + ""additionalProperties"": { + ""type"": ""string"" + }, + ""extends"": { + ""id"": ""first"", + ""type"": ""object"", + ""additionalProperties"": {} + } + } +}", writtenJson); + } + [Test] + public void WriteTo_AdditionalProperties() + { + StringWriter writer = new StringWriter(); + JsonTextWriter jsonWriter = new JsonTextWriter(writer); + jsonWriter.Formatting = Formatting.Indented; + + JsonSchema schema = JsonSchema.Parse(@"{ + ""description"":""AdditionalProperties"", + ""type"":[""string"", ""integer""], + ""additionalProperties"":{""type"":[""object"", ""boolean""]} +}"); + + schema.WriteTo(jsonWriter); + + string json = writer.ToString(); + + Assert.AreEqual(@"{ + ""description"": ""AdditionalProperties"", + ""type"": [ + ""string"", + ""integer"" + ], + ""additionalProperties"": { + ""type"": [ + ""boolean"", + ""object"" + ] + } +}", json); + } + + [Test] + public void WriteTo_Properties() + { + JsonSchema schema = JsonSchema.Parse(@"{ + ""description"":""A person"", + ""type"":""object"", + ""properties"": + { + ""name"":{""type"":""string""}, + ""hobbies"": + { + ""type"":""array"", + ""items"": {""type"":""string""} + } + } +}"); + + StringWriter writer = new StringWriter(); + JsonTextWriter jsonWriter = new JsonTextWriter(writer); + jsonWriter.Formatting = Formatting.Indented; + + schema.WriteTo(jsonWriter); + + string json = writer.ToString(); + + Assert.AreEqual(@"{ + ""description"": ""A person"", + ""type"": ""object"", + ""properties"": { + ""name"": { + ""type"": ""string"" + }, + ""hobbies"": { + ""type"": ""array"", + ""items"": { + ""type"": ""string"" + } + } + } +}", json); + + } + + [Test] + public void WriteTo_Enum() + { + JsonSchema schema = JsonSchema.Parse(@"{ + ""description"":""Type"", + ""type"":[""string"",""array""], + ""items"":{}, + ""enum"":[""string"",""object"",""array"",""boolean"",""number"",""integer"",""null"",""any""] +}"); + + StringWriter writer = new StringWriter(); + JsonTextWriter jsonWriter = new JsonTextWriter(writer); + jsonWriter.Formatting = Formatting.Indented; + + schema.WriteTo(jsonWriter); + + string json = writer.ToString(); + + Assert.AreEqual(@"{ + ""description"": ""Type"", + ""type"": [ + ""string"", + ""array"" + ], + ""items"": {}, + ""enum"": [ + ""string"", + ""object"", + ""array"", + ""boolean"", + ""number"", + ""integer"", + ""null"", + ""any"" + ] +}", json); + } + + [Test] + public void WriteTo_CircularReference() + { + string json = @"{ + ""id"":""CircularReferenceArray"", + ""description"":""CircularReference"", + ""type"":[""array""], + ""items"":{""$ref"":""CircularReferenceArray""} +}"; + + JsonSchema schema = JsonSchema.Parse(json); + + StringWriter writer = new StringWriter(); + JsonTextWriter jsonWriter = new JsonTextWriter(writer); + jsonWriter.Formatting = Formatting.Indented; + + schema.WriteTo(jsonWriter); + + string writtenJson = writer.ToString(); + + Assert.AreEqual(@"{ + ""id"": ""CircularReferenceArray"", + ""description"": ""CircularReference"", + ""type"": ""array"", + ""items"": { + ""$ref"": ""CircularReferenceArray"" + } +}", writtenJson); + } + + [Test] + public void WriteTo_DisallowMultiple() + { + JsonSchema schema = JsonSchema.Parse(@"{ + ""description"":""Type"", + ""type"":[""string"",""array""], + ""items"":{}, + ""disallow"":[""string"",""object"",""array""] +}"); + + StringWriter writer = new StringWriter(); + JsonTextWriter jsonWriter = new JsonTextWriter(writer); + jsonWriter.Formatting = Formatting.Indented; + + schema.WriteTo(jsonWriter); + + string json = writer.ToString(); + + Assert.AreEqual(@"{ + ""description"": ""Type"", + ""type"": [ + ""string"", + ""array"" + ], + ""items"": {}, + ""disallow"": [ + ""string"", + ""object"", + ""array"" + ] +}", json); + } + + [Test] + public void WriteTo_DisallowSingle() + { + JsonSchema schema = JsonSchema.Parse(@"{ + ""description"":""Type"", + ""type"":[""string"",""array""], + ""items"":{}, + ""disallow"":""any"" +}"); + + StringWriter writer = new StringWriter(); + JsonTextWriter jsonWriter = new JsonTextWriter(writer); + jsonWriter.Formatting = Formatting.Indented; + + schema.WriteTo(jsonWriter); + + string json = writer.ToString(); + + Assert.AreEqual(@"{ + ""description"": ""Type"", + ""type"": [ + ""string"", + ""array"" + ], + ""items"": {}, + ""disallow"": ""any"" +}", json); + } + + [Test] + public void WriteTo_MultipleItems() + { + JsonSchema schema = JsonSchema.Parse(@"{ + ""items"":[{},{}] +}"); + + StringWriter writer = new StringWriter(); + JsonTextWriter jsonWriter = new JsonTextWriter(writer); + jsonWriter.Formatting = Formatting.Indented; + + schema.WriteTo(jsonWriter); + + string json = writer.ToString(); + + Assert.AreEqual(@"{ + ""items"": [ + {}, + {} + ] +}", json); + } + + [Test] + public void ReadOptions() + { + JsonSchema schema = JsonSchema.Parse(@"{ + ""type"": ""object"", + ""properties"": { + ""x"": { + ""type"": ""integer"", + ""enum"": [ + 0, + 1, + -1 + ], + ""options"": [ + { + ""value"": 0, + ""label"": ""No"" + }, + { + ""value"": 1, + ""label"": ""Asc"" + }, + { + ""value"": -1, + ""label"": ""Desc"" + } + ] + } + } +}"); + + Assert.AreEqual(schema.Properties["x"].Options.Count, 3); + Assert.AreEqual(schema.Properties["x"].Options[0], "No"); + Assert.AreEqual(schema.Properties["x"].Options[1], "Asc"); + Assert.AreEqual(schema.Properties["x"].Options[-1], "Desc"); + } + + [Test] + public void WriteTo_ExclusiveMinimum_ExclusiveMaximum() + { + JsonSchema schema = new JsonSchema(); + schema.ExclusiveMinimum = true; + schema.ExclusiveMaximum = true; + + StringWriter writer = new StringWriter(); + JsonTextWriter jsonWriter = new JsonTextWriter(writer); + jsonWriter.Formatting = Formatting.Indented; + + schema.WriteTo(jsonWriter); + + string json = writer.ToString(); + + Assert.AreEqual(@"{ + ""exclusiveMinimum"": true, + ""exclusiveMaximum"": true +}", json); + } + + [Test] + public void WriteTo_PatternProperties() + { + JsonSchema schema = new JsonSchema(); + schema.PatternProperties = new Dictionary + { + { "[abc]", new JsonSchema() } + }; + + StringWriter writer = new StringWriter(); + JsonTextWriter jsonWriter = new JsonTextWriter(writer); + jsonWriter.Formatting = Formatting.Indented; + + schema.WriteTo(jsonWriter); + + string json = writer.ToString(); + + Assert.AreEqual(@"{ + ""patternProperties"": { + ""[abc]"": {} + } +}", json); + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs new file mode 100644 index 0000000..b849def --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/CamelCasePropertyNamesContractResolverTests.cs @@ -0,0 +1,217 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Serialization; +using NUnit.Framework; +using Newtonsoft.Json.Tests.TestObjects; +using Newtonsoft.Json.Linq; +using System.Reflection; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class CamelCasePropertyNamesContractResolverTests : TestFixtureBase + { + [Test] + public void JsonConvertSerializerSettings() + { + Person person = new Person(); + person.BirthDate = new DateTime(2000, 11, 20, 23, 55, 44, DateTimeKind.Utc); + person.LastModified = new DateTime(2000, 11, 20, 23, 55, 44, DateTimeKind.Utc); + person.Name = "Name!"; + + string json = JsonConvert.SerializeObject(person, Formatting.Indented, new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }); + + Assert.AreEqual(@"{ + ""name"": ""Name!"", + ""birthDate"": ""\/Date(974764544000)\/"", + ""lastModified"": ""\/Date(974764544000)\/"" +}", json); + + Person deserializedPerson = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }); + + Assert.AreEqual(person.BirthDate, deserializedPerson.BirthDate); + Assert.AreEqual(person.LastModified, deserializedPerson.LastModified); + Assert.AreEqual(person.Name, deserializedPerson.Name); + + json = JsonConvert.SerializeObject(person, Formatting.Indented); + Assert.AreEqual(@"{ + ""Name"": ""Name!"", + ""BirthDate"": ""\/Date(974764544000)\/"", + ""LastModified"": ""\/Date(974764544000)\/"" +}", json); + + } + + [Test] + public void JTokenWriter() + { + JsonIgnoreAttributeOnClassTestClass ignoreAttributeOnClassTestClass = new JsonIgnoreAttributeOnClassTestClass(); + ignoreAttributeOnClassTestClass.Field = int.MinValue; + + JsonSerializer serializer = new JsonSerializer(); + serializer.ContractResolver = new CamelCasePropertyNamesContractResolver(); + + JTokenWriter writer = new JTokenWriter(); + + serializer.Serialize(writer, ignoreAttributeOnClassTestClass); + + JObject o = (JObject) writer.Token; + JProperty p = o.Property("theField"); + + Assert.IsNotNull(p); + Assert.AreEqual(int.MinValue, (int)p.Value); + + string json = o.ToString(); + } + + [Test] + public void MemberSearchFlags() + { + PrivateMembersClass privateMembersClass = new PrivateMembersClass("PrivateString!", "InternalString!"); + + string json = JsonConvert.SerializeObject(privateMembersClass, Formatting.Indented, new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver { DefaultMembersSearchFlags = BindingFlags.NonPublic | BindingFlags.Instance } + }); + + Assert.AreEqual(@"{ + ""_privateString"": ""PrivateString!"", + ""i"": 0, + ""_internalString"": ""InternalString!"" +}", json); + + PrivateMembersClass deserializedPrivateMembersClass = JsonConvert.DeserializeObject(@"{ + ""_privateString"": ""Private!"", + ""i"": -2, + ""_internalString"": ""Internal!"" +}", new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver { DefaultMembersSearchFlags = BindingFlags.NonPublic | BindingFlags.Instance } + }); + + Assert.AreEqual("Private!", ReflectionUtils.GetMemberValue(typeof(PrivateMembersClass).GetField("_privateString", BindingFlags.Instance | BindingFlags.NonPublic), deserializedPrivateMembersClass)); + Assert.AreEqual("Internal!", ReflectionUtils.GetMemberValue(typeof(PrivateMembersClass).GetField("_internalString", BindingFlags.Instance | BindingFlags.NonPublic), deserializedPrivateMembersClass)); + + // readonly + Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(PrivateMembersClass).GetField("i", BindingFlags.Instance | BindingFlags.NonPublic), deserializedPrivateMembersClass)); + } + + [Test] + public void BlogPostExample() + { + Product product = new Product + { + ExpiryDate = new DateTime(2010, 12, 20, 18, 1, 0, DateTimeKind.Utc), + Name = "Widget", + Price = 9.99m, + Sizes = new[] {"Small", "Medium", "Large"} + }; + + string json = + JsonConvert.SerializeObject( + product, + Formatting.Indented, + new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() } + ); + + //{ + // "name": "Widget", + // "expiryDate": "\/Date(1292868060000)\/", + // "price": 9.99, + // "sizes": [ + // "Small", + // "Medium", + // "Large" + // ] + //} + + Assert.AreEqual(@"{ + ""name"": ""Widget"", + ""expiryDate"": ""\/Date(1292868060000)\/"", + ""price"": 9.99, + ""sizes"": [ + ""Small"", + ""Medium"", + ""Large"" + ] +}", json); + } + +#if !(NET35 || NET20 || WINDOWS_PHONE) + [Test] + public void DynamicCamelCasePropertyNames() + { + dynamic o = new DynamicTests.TestDynamicObject(); + o.Text = "Text!"; + o.Integer = int.MaxValue; + + string json = JsonConvert.SerializeObject(o, Formatting.Indented, + new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }); + + Assert.AreEqual(@"{ + ""text"": ""Text!"", + ""integer"": 2147483647, + ""int"": 0, + ""childObject"": null +}", json); + } +#endif + + [Test] + public void DictionaryCamelCasePropertyNames() + { + Dictionary values = new Dictionary + { + {"First", "Value1!"}, + {"Second", "Value2!"} + }; + + string json = JsonConvert.SerializeObject(values, Formatting.Indented, + new JsonSerializerSettings + { + ContractResolver = new CamelCasePropertyNamesContractResolver() + }); + + Assert.AreEqual(@"{ + ""first"": ""Value1!"", + ""second"": ""Value2!"" +}", json); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs new file mode 100644 index 0000000..463d475 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/ConstructorHandlingTests.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class ConstructorHandlingTests : TestFixtureBase + { + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Unable to find a constructor to use for type Newtonsoft.Json.Tests.TestObjects.PrivateConstructorTestClass. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.")] + public void FailWithPrivateConstructorAndDefault() + { + string json = @"{Name:""Name!""}"; + + JsonConvert.DeserializeObject(json); + } + + [Test] + public void SuccessWithPrivateConstructorAndAllowNonPublic() + { + string json = @"{Name:""Name!""}"; + + PrivateConstructorTestClass c = JsonConvert.DeserializeObject(json, + new JsonSerializerSettings + { + ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor + }); + Assert.IsNotNull(c); + Assert.AreEqual("Name!", c.Name); + } + + [Test] + [ExpectedException(typeof(TargetInvocationException))] + public void FailWithPrivateConstructorPlusParametizedAndDefault() + { + string json = @"{Name:""Name!""}"; + + PrivateConstructorWithPublicParametizedConstructorTestClass c = JsonConvert.DeserializeObject(json); + } + + [Test] + public void SuccessWithPrivateConstructorPlusParametizedAndAllowNonPublic() + { + string json = @"{Name:""Name!""}"; + + PrivateConstructorWithPublicParametizedConstructorTestClass c = JsonConvert.DeserializeObject(json, + new JsonSerializerSettings + { + ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor + }); + Assert.IsNotNull(c); + Assert.AreEqual("Name!", c.Name); + Assert.AreEqual(1, c.Age); + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs new file mode 100644 index 0000000..50897dc --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/ContractResolverTests.cs @@ -0,0 +1,180 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Tests.TestObjects; +using System.Reflection; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class DynamicContractResolver : DefaultContractResolver + { + private readonly char _startingWithChar; + public DynamicContractResolver(char startingWithChar) + : base(false) + { + _startingWithChar = startingWithChar; + } + + protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) + { + IList properties = base.CreateProperties(type, memberSerialization); + + // only serializer properties that start with the specified character + properties = + properties.Where(p => p.PropertyName.StartsWith(_startingWithChar.ToString())).ToList(); + + return properties; + } + } + + public class Book + { + public string BookName { get; set; } + public decimal BookPrice { get; set; } + public string AuthorName { get; set; } + public int AuthorAge { get; set; } + public string AuthorCountry { get; set; } + } + + public interface IPerson + { + string FirstName { get; set; } + string LastName { get; set; } + DateTime BirthDate { get; set; } + } + + public class Employee : IPerson + { + public string FirstName { get; set; } + public string LastName { get; set; } + public DateTime BirthDate { get; set; } + + public string Department { get; set; } + public string JobTitle { get; set; } + } + + public class IPersonContractResolver : DefaultContractResolver + { + protected override JsonContract CreateContract(Type objectType) + { + if (objectType == typeof(Employee)) + objectType = typeof(IPerson); + + return base.CreateContract(objectType); + } + } + + public class ContractResolverTests : TestFixtureBase + { + [Test] + public void SerializeInterface() + { + Employee employee = new Employee + { + BirthDate = new DateTime(1977, 12, 30, 1, 1, 1, DateTimeKind.Utc), + FirstName = "Maurice", + LastName = "Moss", + Department = "IT", + JobTitle = "Support" + }; + + string iPersonJson = JsonConvert.SerializeObject(employee, Formatting.Indented, + new JsonSerializerSettings { ContractResolver = new IPersonContractResolver() }); + + Assert.AreEqual(@"{ + ""FirstName"": ""Maurice"", + ""LastName"": ""Moss"", + ""BirthDate"": ""\/Date(252291661000)\/"" +}", iPersonJson); + } + + [Test] + public void SingleTypeWithMultipleContractResolvers() + { + Book book = new Book + { + BookName = "The Gathering Storm", + BookPrice = 16.19m, + AuthorName = "Brandon Sanderson", + AuthorAge = 34, + AuthorCountry = "United States of America" + }; + + string startingWithA = JsonConvert.SerializeObject(book, Formatting.Indented, + new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('A') }); + + // { + // "AuthorName": "Brandon Sanderson", + // "AuthorAge": 34, + // "AuthorCountry": "United States of America" + // } + + string startingWithB = JsonConvert.SerializeObject(book, Formatting.Indented, + new JsonSerializerSettings { ContractResolver = new DynamicContractResolver('B') }); + + // { + // "BookName": "The Gathering Storm", + // "BookPrice": 16.19 + // } + + Assert.AreEqual(@"{ + ""AuthorName"": ""Brandon Sanderson"", + ""AuthorAge"": 34, + ""AuthorCountry"": ""United States of America"" +}", startingWithA); + + Assert.AreEqual(@"{ + ""BookName"": ""The Gathering Storm"", + ""BookPrice"": 16.19 +}", startingWithB); + } + + [Test] + public void SerializeCompilerGeneratedMembers() + { + StructTest structTest = new StructTest + { + IntField = 1, + IntProperty = 2, + StringField = "Field", + StringProperty = "Property" + }; + + DefaultContractResolver skipCompilerGeneratedResolver = new DefaultContractResolver + { + DefaultMembersSearchFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public + }; + + string skipCompilerGeneratedJson = JsonConvert.SerializeObject(structTest, Formatting.Indented, + new JsonSerializerSettings { ContractResolver = skipCompilerGeneratedResolver }); + + Assert.AreEqual(@"{ + ""StringField"": ""Field"", + ""IntField"": 1, + ""StringProperty"": ""Property"", + ""IntProperty"": 2 +}", skipCompilerGeneratedJson); + + DefaultContractResolver includeCompilerGeneratedResolver = new DefaultContractResolver + { + DefaultMembersSearchFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public, + SerializeCompilerGeneratedMembers = true + }; + + string includeCompilerGeneratedJson = JsonConvert.SerializeObject(structTest, Formatting.Indented, + new JsonSerializerSettings { ContractResolver = includeCompilerGeneratedResolver }); + + Assert.AreEqual(@"{ + ""StringField"": ""Field"", + ""IntField"": 1, + ""k__BackingField"": ""Property"", + ""k__BackingField"": 2, + ""StringProperty"": ""Property"", + ""IntProperty"": 2 +}", includeCompilerGeneratedJson); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs new file mode 100644 index 0000000..24aee8e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/DefaultValueHandlingTests.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class DefaultValueHandlingTests : TestFixtureBase + { + [Test] + public void SerializeInvoice() + { + Invoice invoice = new Invoice + { + Company = "Acme Ltd.", + Amount = 50.0m, + Paid = false, + FollowUpDays = 30, + FollowUpEmailAddress = string.Empty, + PaidDate = null + }; + + string included = JsonConvert.SerializeObject(invoice, + Formatting.Indented, + new JsonSerializerSettings { }); + + // { + // "Company": "Acme Ltd.", + // "Amount": 50.0, + // "Paid": false, + // "PaidDate": null, + // "FollowUpDays": 30, + // "FollowUpEmailAddress": "" + // } + + string ignored = JsonConvert.SerializeObject(invoice, + Formatting.Indented, + new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }); + + // { + // "Company": "Acme Ltd.", + // "Amount": 50.0 + // } + + Console.WriteLine(included); + Console.WriteLine(ignored); + } + + [Test] + public void DefaultValueAttributeTest() + { + string json = JsonConvert.SerializeObject(new DefaultValueAttributeTestClass(), + Formatting.None, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }); + Assert.AreEqual(@"{""TestField1"":0,""TestProperty1"":null}", json); + + json = JsonConvert.SerializeObject(new DefaultValueAttributeTestClass { TestField1 = int.MinValue, TestProperty1 = "NotDefault" }, + Formatting.None, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }); + Assert.AreEqual(@"{""TestField1"":-2147483648,""TestProperty1"":""NotDefault""}", json); + + json = JsonConvert.SerializeObject(new DefaultValueAttributeTestClass { TestField1 = 21, TestProperty1 = "NotDefault" }, + Formatting.None, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }); + Assert.AreEqual(@"{""TestProperty1"":""NotDefault""}", json); + + json = JsonConvert.SerializeObject(new DefaultValueAttributeTestClass { TestField1 = 21, TestProperty1 = "TestProperty1Value" }, + Formatting.None, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore }); + Assert.AreEqual(@"{}", json); + } + + [JsonObject] + public class NetworkUser + { + [JsonProperty(PropertyName = "userId")] + [DefaultValue(-1)] + public long GlobalId { get; set; } + + [JsonProperty(PropertyName = "floatUserId")] + [DefaultValue(-1.0d)] + public float FloatGlobalId { get; set; } + + [JsonProperty(PropertyName = "firstName")] + public string Firstname { get; set; } + [JsonProperty(PropertyName = "lastName")] + public string Lastname { get; set; } + + public NetworkUser() + { + GlobalId = -1; + FloatGlobalId = -1.0f; + } + } + + [Test] + public void IgnoreNumberTypeDifferencesWithDefaultValue() + { + NetworkUser user = new NetworkUser + { + Firstname = "blub" + }; + + string json = JsonConvert.SerializeObject(user, Formatting.None, new JsonSerializerSettings { DefaultValueHandling = DefaultValueHandling.Ignore, NullValueHandling = NullValueHandling.Ignore }); + + Assert.AreEqual(@"{""firstName"":""blub""}", json); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs new file mode 100644 index 0000000..3d93a23 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/DynamicTests.cs @@ -0,0 +1,285 @@ +#if !(NET35 || NET20) +using System; +using System.Collections; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization.Formatters; +using System.Text; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Tests.TestObjects; +using Newtonsoft.Json.Utilities; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class DynamicTests : TestFixtureBase + { + public class DynamicChildObject + { + public string Text { get; set; } + public int Integer { get; set; } + } + + public class TestDynamicObject : DynamicObject + { + private readonly Dictionary _members; + + public int Int; + public DynamicChildObject ChildObject { get; set; } + + internal Dictionary Members + { + get { return _members; } + } + + public TestDynamicObject() + { + _members = new Dictionary(); + } + + public override IEnumerable GetDynamicMemberNames() + { + return _members.Keys.Union(new[] { "Int", "ChildObject" }); + } + + public override bool TryConvert(ConvertBinder binder, out object result) + { + Type targetType = binder.Type; + + if (targetType == typeof(IDictionary) || + targetType == typeof(IDictionary)) + { + result = new Dictionary(_members); + return true; + } + else + { + return base.TryConvert(binder, out result); + } + } + + public override bool TryDeleteMember(DeleteMemberBinder binder) + { + return _members.Remove(binder.Name); + } + + public override bool TryGetMember(GetMemberBinder binder, out object result) + { + return _members.TryGetValue(binder.Name, out result); + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + _members[binder.Name] = value; + return true; + } + } + + public class ErrorSettingDynamicObject : DynamicObject + { + public override bool TrySetMember(SetMemberBinder binder, object value) + { + return false; + } + } + + [Test] + public void SerializeDynamicObject() + { + TestDynamicObject dynamicObject = new TestDynamicObject(); + + dynamic d = dynamicObject; + d.Int = 1; + d.Decimal = 99.9d; + d.ChildObject = new DynamicChildObject(); + + Dictionary values = new Dictionary(); + + foreach (string memberName in dynamicObject.GetDynamicMemberNames()) + { + object value; + dynamicObject.TryGetMember(memberName, out value); + + values.Add(memberName, value); + } + + Assert.AreEqual(d.Int, values["Int"]); + Assert.AreEqual(d.Decimal, values["Decimal"]); + Assert.AreEqual(d.ChildObject, values["ChildObject"]); + + string json = JsonConvert.SerializeObject(dynamicObject, Formatting.Indented); + Assert.AreEqual(@"{ + ""Decimal"": 99.9, + ""Int"": 1, + ""ChildObject"": { + ""Text"": null, + ""Integer"": 0 + } +}", json); + + TestDynamicObject newDynamicObject = JsonConvert.DeserializeObject(json); + d = newDynamicObject; + + Assert.AreEqual(99.9, d.Decimal); + Assert.AreEqual(1, d.Int); + Assert.AreEqual(dynamicObject.ChildObject.Integer, d.ChildObject.Integer); + Assert.AreEqual(dynamicObject.ChildObject.Text, d.ChildObject.Text); + } + + [Test] + public void sdfsdf() + { + ErrorSettingDynamicObject d = JsonConvert.DeserializeObject("{'hi':5}"); + } + + [Test] + public void SerializeDynamicObjectWithObjectTracking() + { + dynamic o = new ExpandoObject(); + o.Text = "Text!"; + o.Integer = int.MaxValue; + o.DynamicChildObject = new DynamicChildObject + { + Integer = int.MinValue, + Text = "Child text!" + }; + + string json = JsonConvert.SerializeObject(o, Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Full + }); + + Console.WriteLine(json); + + string dynamicChildObjectTypeName = ReflectionUtils.GetTypeName(typeof(DynamicChildObject), FormatterAssemblyStyle.Full); + string expandoObjectTypeName = ReflectionUtils.GetTypeName(typeof(ExpandoObject), FormatterAssemblyStyle.Full); + + Assert.AreEqual(@"{ + ""$type"": """ + expandoObjectTypeName + @""", + ""Text"": ""Text!"", + ""Integer"": 2147483647, + ""DynamicChildObject"": { + ""$type"": """ + dynamicChildObjectTypeName + @""", + ""Text"": ""Child text!"", + ""Integer"": -2147483648 + } +}", json); + + dynamic n = JsonConvert.DeserializeObject(json, null, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Full + }); + + Assert.IsInstanceOfType(typeof(ExpandoObject), n); + Assert.AreEqual("Text!", n.Text); + Assert.AreEqual(int.MaxValue, n.Integer); + + Assert.IsInstanceOfType(typeof(DynamicChildObject), n.DynamicChildObject); + Assert.AreEqual("Child text!", n.DynamicChildObject.Text); + Assert.AreEqual(int.MinValue, n.DynamicChildObject.Integer); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Unable to find a default constructor to use for type System.Dynamic.DynamicObject.")] + public void NoPublicDefaultConstructor() + { + var settings = new JsonSerializerSettings(); + settings.NullValueHandling = NullValueHandling.Ignore; + var json = @"{ + ""contributors"": null +}"; + + JsonConvert.DeserializeObject(json, settings); + } + + public class DictionaryDynamicObject : DynamicObject + { + public IDictionary Values { get; private set; } + + protected DictionaryDynamicObject() + { + Values = new Dictionary(); + } + + public override bool TrySetMember(SetMemberBinder binder, object value) + { + Values[binder.Name] = value; + return true; + } + } + + [Test] + public void AllowNonPublicDefaultConstructor() + { + var settings = new JsonSerializerSettings(); + settings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor; + + var json = @"{ + ""contributors"": null, + ""retweeted"": false, + ""text"": ""Guys SX4 diesel is launched.what are your plans?catch us at #facebook http://bit.ly/dV3H1a #auto #car #maruti #india #delhi"", + ""in_reply_to_user_id_str"": null, + ""retweet_count"": 0, + ""geo"": null, + ""id_str"": ""40678260320768000"", + ""in_reply_to_status_id"": null, + ""source"": ""TweetDeck"", + ""created_at"": ""Thu Feb 24 07:43:47 +0000 2011"", + ""place"": null, + ""coordinates"": null, + ""truncated"": false, + ""favorited"": false, + ""user"": { + ""profile_background_image_url"": ""http://a1.twimg.com/profile_background_images/206944715/twitter_bg.jpg"", + ""url"": ""http://bit.ly/dcFwWC"", + ""screen_name"": ""marutisuzukisx4"", + ""verified"": false, + ""friends_count"": 45, + ""description"": ""This is the Official Maruti Suzuki SX4 Twitter ID! Men are Back - mail us on social (at) sx4bymaruti (dot) com"", + ""follow_request_sent"": null, + ""time_zone"": ""Chennai"", + ""profile_text_color"": ""333333"", + ""location"": ""India"", + ""notifications"": null, + ""profile_sidebar_fill_color"": ""efefef"", + ""id_str"": ""196143889"", + ""contributors_enabled"": false, + ""lang"": ""en"", + ""profile_background_tile"": false, + ""created_at"": ""Tue Sep 28 12:55:15 +0000 2010"", + ""followers_count"": 117, + ""show_all_inline_media"": true, + ""listed_count"": 1, + ""geo_enabled"": true, + ""profile_link_color"": ""009999"", + ""profile_sidebar_border_color"": ""eeeeee"", + ""protected"": false, + ""name"": ""Maruti Suzuki SX4"", + ""statuses_count"": 637, + ""following"": null, + ""profile_use_background_image"": true, + ""profile_image_url"": ""http://a3.twimg.com/profile_images/1170694644/Slide1_normal.JPG"", + ""id"": 196143889, + ""is_translator"": false, + ""utc_offset"": 19800, + ""favourites_count"": 0, + ""profile_background_color"": ""131516"" + }, + ""in_reply_to_screen_name"": null, + ""id"": 40678260320768000, + ""in_reply_to_status_id_str"": null, + ""in_reply_to_user_id"": null +}"; + + DictionaryDynamicObject foo = JsonConvert.DeserializeObject(json, settings); + + Assert.AreEqual(false, foo.Values["retweeted"]); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/EntitiesSerializationTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/EntitiesSerializationTests.cs new file mode 100644 index 0000000..7731482 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/EntitiesSerializationTests.cs @@ -0,0 +1,314 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !(NET35 || NET20 || SILVERLIGHT) +using System; +using System.Collections.Generic; +using System.Data; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class EntitiesSerializationTests : TestFixtureBase + { + [Test] + public void SerializeEntity() + { + Folder rootFolder = CreateEntitiesTestData(); + + string json = JsonConvert.SerializeObject(rootFolder, Formatting.Indented, new IsoDateTimeConverter()); + + string expected = @"{ + ""$id"": ""1"", + ""FolderId"": ""a4e8ba80-eb24-4591-bb1c-62d3ad83701e"", + ""Name"": ""Root folder"", + ""Description"": ""Description!"", + ""CreatedDate"": ""2000-12-10T10:50:00Z"", + ""Files"": [], + ""ChildFolders"": [ + { + ""$id"": ""2"", + ""FolderId"": ""484936e2-7cbb-4592-93ff-b2103e5705e4"", + ""Name"": ""Child folder"", + ""Description"": ""Description!"", + ""CreatedDate"": ""2001-11-20T10:50:00Z"", + ""Files"": [ + { + ""$id"": ""3"", + ""FileId"": ""cc76d734-49f1-4616-bb38-41514228ac6c"", + ""Name"": ""File 1"", + ""Description"": ""Description!"", + ""CreatedDate"": ""2002-10-30T10:50:00Z"", + ""Folder"": { + ""$ref"": ""2"" + }, + ""EntityKey"": { + ""$id"": ""4"", + ""EntitySetName"": ""File"", + ""EntityContainerName"": ""DataServicesTestDatabaseEntities"", + ""EntityKeyValues"": [ + { + ""Key"": ""FileId"", + ""Type"": ""System.Guid"", + ""Value"": ""cc76d734-49f1-4616-bb38-41514228ac6c"" + } + ] + } + } + ], + ""ChildFolders"": [], + ""ParentFolder"": { + ""$ref"": ""1"" + }, + ""EntityKey"": { + ""$id"": ""5"", + ""EntitySetName"": ""Folder"", + ""EntityContainerName"": ""DataServicesTestDatabaseEntities"", + ""EntityKeyValues"": [ + { + ""Key"": ""FolderId"", + ""Type"": ""System.Guid"", + ""Value"": ""484936e2-7cbb-4592-93ff-b2103e5705e4"" + } + ] + } + } + ], + ""ParentFolder"": null, + ""EntityKey"": { + ""$id"": ""6"", + ""EntitySetName"": ""Folder"", + ""EntityContainerName"": ""DataServicesTestDatabaseEntities"", + ""EntityKeyValues"": [ + { + ""Key"": ""FolderId"", + ""Type"": ""System.Guid"", + ""Value"": ""a4e8ba80-eb24-4591-bb1c-62d3ad83701e"" + } + ] + } +}"; + + Assert.AreEqual(expected, json); + } + + [Test] + public void DeserializeEntity() + { + string json = @"{ + ""$id"": ""1"", + ""FolderId"": ""a4e8ba80-eb24-4591-bb1c-62d3ad83701e"", + ""Name"": ""Root folder"", + ""Description"": ""Description!"", + ""CreatedDate"": ""2000-12-10T10:50:00Z"", + ""Files"": [], + ""ChildFolders"": [ + { + ""$id"": ""2"", + ""FolderId"": ""484936e2-7cbb-4592-93ff-b2103e5705e4"", + ""Name"": ""Child folder"", + ""Description"": ""Description!"", + ""CreatedDate"": ""2001-11-20T10:50:00Z"", + ""Files"": [ + { + ""$id"": ""3"", + ""FileId"": ""cc76d734-49f1-4616-bb38-41514228ac6c"", + ""Name"": ""File 1"", + ""Description"": ""Description!"", + ""CreatedDate"": ""2002-10-30T10:50:00Z"", + ""Folder"": { + ""$ref"": ""2"" + }, + ""EntityKey"": { + ""$id"": ""4"", + ""EntitySetName"": ""File"", + ""EntityContainerName"": ""DataServicesTestDatabaseEntities"", + ""EntityKeyValues"": [ + { + ""Key"": ""FileId"", + ""Type"": ""System.Guid"", + ""Value"": ""cc76d734-49f1-4616-bb38-41514228ac6c"" + } + ] + } + } + ], + ""ChildFolders"": [], + ""ParentFolder"": { + ""$ref"": ""1"" + }, + ""EntityKey"": { + ""$id"": ""5"", + ""EntitySetName"": ""Folder"", + ""EntityContainerName"": ""DataServicesTestDatabaseEntities"", + ""EntityKeyValues"": [ + { + ""Key"": ""FolderId"", + ""Type"": ""System.Guid"", + ""Value"": ""484936e2-7cbb-4592-93ff-b2103e5705e4"" + } + ] + } + } + ], + ""ParentFolder"": null, + ""EntityKey"": { + ""$id"": ""6"", + ""EntitySetName"": ""Folder"", + ""EntityContainerName"": ""DataServicesTestDatabaseEntities"", + ""EntityKeyValues"": [ + { + ""Key"": ""FolderId"", + ""Type"": ""System.Guid"", + ""Value"": ""a4e8ba80-eb24-4591-bb1c-62d3ad83701e"" + } + ] + } +}"; + + Folder f = JsonConvert.DeserializeObject(json, new IsoDateTimeConverter()); + + Assert.IsNotNull(f); + Assert.AreEqual(new Guid("A4E8BA80-EB24-4591-BB1C-62D3AD83701E"), f.FolderId); + Assert.AreEqual("Folder", f.EntityKey.EntitySetName); + Assert.AreEqual("DataServicesTestDatabaseEntities", f.EntityKey.EntityContainerName); + Assert.AreEqual("Folder", f.EntityKey.EntitySetName); + Assert.AreEqual(false, f.EntityKey.IsTemporary); + Assert.AreEqual(1, f.EntityKey.EntityKeyValues.Length); + Assert.AreEqual("FolderId", f.EntityKey.EntityKeyValues[0].Key); + Assert.AreEqual(new Guid("A4E8BA80-EB24-4591-BB1C-62D3AD83701E"), f.EntityKey.EntityKeyValues[0].Value); + Assert.AreEqual("Root folder", f.Name); + Assert.AreEqual(new DateTime(2000, 12, 10, 10, 50, 0, DateTimeKind.Utc), f.CreatedDate); + Assert.AreEqual(null, f.ParentFolder); + Assert.AreEqual(1, f.ChildFolders.Count); + + Folder childFolder = f.ChildFolders.ElementAt(0); + + Assert.AreEqual("Child folder", childFolder.Name); + Assert.AreEqual("Description!", childFolder.Description); + Assert.AreEqual(f, childFolder.ParentFolder); + Assert.AreEqual(f, childFolder.ParentFolderReference.Value); + // is this a problem? + Assert.AreEqual(null, childFolder.ParentFolderReference.EntityKey); + } + + [Test] + public void SerializeMultiValueEntityKey() + { + EntityKey e = new EntityKey("DataServicesTestDatabaseEntities.Folder", + new List + { + new EntityKeyMember("GuidId", new Guid("A4E8BA80-EB24-4591-BB1C-62D3AD83701E")), + new EntityKeyMember("IntId", int.MaxValue), + new EntityKeyMember("LongId", long.MaxValue), + new EntityKeyMember("StringId", "String!"), + new EntityKeyMember("DateTimeId", new DateTime(2000, 12, 10, 10, 50, 0, DateTimeKind.Utc)) + }); + + string json = JsonConvert.SerializeObject(e, Formatting.Indented); + + Assert.AreEqual(@"{ + ""$id"": ""1"", + ""EntitySetName"": ""Folder"", + ""EntityContainerName"": ""DataServicesTestDatabaseEntities"", + ""EntityKeyValues"": [ + { + ""Key"": ""GuidId"", + ""Type"": ""System.Guid"", + ""Value"": ""a4e8ba80-eb24-4591-bb1c-62d3ad83701e"" + }, + { + ""Key"": ""IntId"", + ""Type"": ""System.Int32"", + ""Value"": ""2147483647"" + }, + { + ""Key"": ""LongId"", + ""Type"": ""System.Int64"", + ""Value"": ""9223372036854775807"" + }, + { + ""Key"": ""StringId"", + ""Type"": ""System.String"", + ""Value"": ""String!"" + }, + { + ""Key"": ""DateTimeId"", + ""Type"": ""System.DateTime"", + ""Value"": ""12/10/2000 10:50:00"" + } + ] +}", json); + + EntityKey newKey = JsonConvert.DeserializeObject(json); + Assert.IsFalse(ReferenceEquals(e, newKey)); + + Assert.AreEqual(5, newKey.EntityKeyValues.Length); + Assert.AreEqual("GuidId", newKey.EntityKeyValues[0].Key); + Assert.AreEqual(new Guid("A4E8BA80-EB24-4591-BB1C-62D3AD83701E"), newKey.EntityKeyValues[0].Value); + Assert.AreEqual("IntId", newKey.EntityKeyValues[1].Key); + Assert.AreEqual(int.MaxValue, newKey.EntityKeyValues[1].Value); + Assert.AreEqual("LongId", newKey.EntityKeyValues[2].Key); + Assert.AreEqual(long.MaxValue, newKey.EntityKeyValues[2].Value); + Assert.AreEqual("StringId", newKey.EntityKeyValues[3].Key); + Assert.AreEqual("String!", newKey.EntityKeyValues[3].Value); + Assert.AreEqual("DateTimeId", newKey.EntityKeyValues[4].Key); + Assert.AreEqual(new DateTime(2000, 12, 10, 10, 50, 0, DateTimeKind.Utc), newKey.EntityKeyValues[4].Value); + } + + private Folder CreateEntitiesTestData() + { + Folder folder = new Folder(); + folder.FolderId = new Guid("A4E8BA80-EB24-4591-BB1C-62D3AD83701E"); + folder.EntityKey = new EntityKey("DataServicesTestDatabaseEntities.Folder", "FolderId", folder.FolderId); + folder.Name = "Root folder"; + folder.Description = "Description!"; + folder.CreatedDate = new DateTime(2000, 12, 10, 10, 50, 0, DateTimeKind.Utc); + + Folder childFolder = new Folder(); + childFolder.FolderId = new Guid("484936E2-7CBB-4592-93FF-B2103E5705E4"); + childFolder.EntityKey = new EntityKey("DataServicesTestDatabaseEntities.Folder", "FolderId", childFolder.FolderId); + childFolder.Name = "Child folder"; + childFolder.Description = "Description!"; + childFolder.CreatedDate = new DateTime(2001, 11, 20, 10, 50, 0, DateTimeKind.Utc); + + folder.ChildFolders.Add(childFolder); + + File file1 = new File(); + file1.FileId = new Guid("CC76D734-49F1-4616-BB38-41514228AC6C"); + file1.EntityKey = new EntityKey("DataServicesTestDatabaseEntities.File", "FileId", file1.FileId); + file1.Name = "File 1"; + file1.Description = "Description!"; + file1.CreatedDate = new DateTime(2002, 10, 30, 10, 50, 0, DateTimeKind.Utc); + + childFolder.Files.Add(file1); + return folder; + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs new file mode 100644 index 0000000..25d05eb --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/JsonSerializerTest.cs @@ -0,0 +1,4314 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +#if !SILVERLIGHT && !PocketPC && !NET20 +using System.ComponentModel.DataAnnotations; +using System.Configuration; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Web.Script.Serialization; +#endif +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json; +using System.IO; +using System.Collections; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Linq; +using System.Linq; +using Newtonsoft.Json.Converters; +#if !PocketPC && !NET20 && !WINDOWS_PHONE +using System.Runtime.Serialization.Json; +#endif +using Newtonsoft.Json.Tests.TestObjects; +using System.Runtime.Serialization; +using System.Globalization; +using Newtonsoft.Json.Utilities; +using System.Reflection; +#if !NET20 && !SILVERLIGHT +using System.Xml.Linq; +using System.Text.RegularExpressions; +using System.Collections.Specialized; +using System.Linq.Expressions; +#endif +#if !(NET35 || NET20 || WINDOWS_PHONE) +using System.Dynamic; +using System.ComponentModel; +#endif + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class JsonSerializerTest : TestFixtureBase + { + [Test] + public void PersonTypedObjectDeserialization() + { + Store store = new Store(); + + string jsonText = JsonConvert.SerializeObject(store); + + Store deserializedStore = (Store)JsonConvert.DeserializeObject(jsonText, typeof(Store)); + + Assert.AreEqual(store.Establised, deserializedStore.Establised); + Assert.AreEqual(store.product.Count, deserializedStore.product.Count); + + Console.WriteLine(jsonText); + } + + [Test] + public void TypedObjectDeserialization() + { + Product product = new Product(); + + product.Name = "Apple"; + product.ExpiryDate = new DateTime(2008, 12, 28); + product.Price = 3.99M; + product.Sizes = new string[] { "Small", "Medium", "Large" }; + + string output = JsonConvert.SerializeObject(product); + //{ + // "Name": "Apple", + // "ExpiryDate": "\/Date(1230375600000+1300)\/", + // "Price": 3.99, + // "Sizes": [ + // "Small", + // "Medium", + // "Large" + // ] + //} + + Product deserializedProduct = (Product)JsonConvert.DeserializeObject(output, typeof(Product)); + + Assert.AreEqual("Apple", deserializedProduct.Name); + Assert.AreEqual(new DateTime(2008, 12, 28), deserializedProduct.ExpiryDate); + Assert.AreEqual(3.99, deserializedProduct.Price); + Assert.AreEqual("Small", deserializedProduct.Sizes[0]); + Assert.AreEqual("Medium", deserializedProduct.Sizes[1]); + Assert.AreEqual("Large", deserializedProduct.Sizes[2]); + } + + //[Test] + //public void Advanced() + //{ + // Product product = new Product(); + // product.ExpiryDate = new DateTime(2008, 12, 28); + + // JsonSerializer serializer = new JsonSerializer(); + // serializer.Converters.Add(new JavaScriptDateTimeConverter()); + // serializer.NullValueHandling = NullValueHandling.Ignore; + + // using (StreamWriter sw = new StreamWriter(@"c:\json.txt")) + // using (JsonWriter writer = new JsonTextWriter(sw)) + // { + // serializer.Serialize(writer, product); + // // {"ExpiryDate":new Date(1230375600000),"Price":0} + // } + //} + + [Test] + public void JsonConvertSerializer() + { + string value = @"{""Name"":""Orange"", ""Price"":3.99, ""ExpiryDate"":""01/24/2010 12:00:00""}"; + + Product p = JsonConvert.DeserializeObject(value, typeof(Product)) as Product; + + Assert.AreEqual("Orange", p.Name); + Assert.AreEqual(new DateTime(2010, 1, 24, 12, 0, 0), p.ExpiryDate); + Assert.AreEqual(3.99, p.Price); + } + + [Test] + public void DeserializeJavaScriptDate() + { + DateTime dateValue = new DateTime(2010, 3, 30); + Dictionary testDictionary = new Dictionary(); + testDictionary["date"] = dateValue; + + string jsonText = JsonConvert.SerializeObject(testDictionary); + +#if !PocketPC && !NET20 && !WINDOWS_PHONE + MemoryStream ms = new MemoryStream(); + DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(Dictionary)); + serializer.WriteObject(ms, testDictionary); + + byte[] data = ms.ToArray(); + string output = Encoding.UTF8.GetString(data, 0, data.Length); +#endif + + Dictionary deserializedDictionary = (Dictionary)JsonConvert.DeserializeObject(jsonText, typeof(Dictionary)); + DateTime deserializedDate = (DateTime)deserializedDictionary["date"]; + + Assert.AreEqual(dateValue, deserializedDate); + + Console.WriteLine("DeserializeJavaScriptDate"); + Console.WriteLine(jsonText); + Console.WriteLine(); + Console.WriteLine(jsonText); + } + + [Test] + public void TestMethodExecutorObject() + { + MethodExecutorObject executorObject = new MethodExecutorObject(); + executorObject.serverClassName = "BanSubs"; + executorObject.serverMethodParams = new object[] { "21321546", "101", "1236", "D:\\1.txt" }; + executorObject.clientGetResultFunction = "ClientBanSubsCB"; + + string output = JsonConvert.SerializeObject(executorObject); + + MethodExecutorObject executorObject2 = JsonConvert.DeserializeObject(output, typeof(MethodExecutorObject)) as MethodExecutorObject; + + Assert.AreNotSame(executorObject, executorObject2); + Assert.AreEqual(executorObject2.serverClassName, "BanSubs"); + Assert.AreEqual(executorObject2.serverMethodParams.Length, 4); + Assert.Contains("101", executorObject2.serverMethodParams); + Assert.AreEqual(executorObject2.clientGetResultFunction, "ClientBanSubsCB"); + } + +#if !SILVERLIGHT + [Test] + public void HashtableDeserialization() + { + string value = @"{""Name"":""Orange"", ""Price"":3.99, ""ExpiryDate"":""01/24/2010 12:00:00""}"; + + Hashtable p = JsonConvert.DeserializeObject(value, typeof(Hashtable)) as Hashtable; + + Assert.AreEqual("Orange", p["Name"].ToString()); + } + + [Test] + public void TypedHashtableDeserialization() + { + string value = @"{""Name"":""Orange"", ""Hash"":{""ExpiryDate"":""01/24/2010 12:00:00"",""UntypedArray"":[""01/24/2010 12:00:00""]}}"; + + TypedSubHashtable p = JsonConvert.DeserializeObject(value, typeof(TypedSubHashtable)) as TypedSubHashtable; + + Assert.AreEqual("01/24/2010 12:00:00", p.Hash["ExpiryDate"].ToString()); + Assert.AreEqual(@"[ + ""01/24/2010 12:00:00"" +]", p.Hash["UntypedArray"].ToString()); + } +#endif + + [Test] + public void SerializeDeserializeGetOnlyProperty() + { + string value = JsonConvert.SerializeObject(new GetOnlyPropertyClass()); + + GetOnlyPropertyClass c = JsonConvert.DeserializeObject(value); + + Assert.AreEqual(c.Field, "Field"); + Assert.AreEqual(c.GetOnlyProperty, "GetOnlyProperty"); + } + + [Test] + public void SerializeDeserializeSetOnlyProperty() + { + string value = JsonConvert.SerializeObject(new SetOnlyPropertyClass()); + + SetOnlyPropertyClass c = JsonConvert.DeserializeObject(value); + + Assert.AreEqual(c.Field, "Field"); + } + + [Test] + public void JsonIgnoreAttributeTest() + { + string json = JsonConvert.SerializeObject(new JsonIgnoreAttributeTestClass()); + + Assert.AreEqual(@"{""Field"":0,""Property"":21}", json); + + JsonIgnoreAttributeTestClass c = JsonConvert.DeserializeObject(@"{""Field"":99,""Property"":-1,""IgnoredField"":-1,""IgnoredObject"":[1,2,3,4,5]}"); + + Assert.AreEqual(0, c.IgnoredField); + Assert.AreEqual(99, c.Field); + } + + [Test] + public void GoogleSearchAPI() + { + string json = @"{ + results: + [ + { + GsearchResultClass:""GwebSearch"", + unescapedUrl : ""http://www.google.com/"", + url : ""http://www.google.com/"", + visibleUrl : ""www.google.com"", + cacheUrl : +""http://www.google.com/search?q=cache:zhool8dxBV4J:www.google.com"", + title : ""Google"", + titleNoFormatting : ""Google"", + content : ""Enables users to search the Web, Usenet, and +images. Features include PageRank, caching and translation of +results, and an option to find similar pages."" + }, + { + GsearchResultClass:""GwebSearch"", + unescapedUrl : ""http://news.google.com/"", + url : ""http://news.google.com/"", + visibleUrl : ""news.google.com"", + cacheUrl : +""http://www.google.com/search?q=cache:Va_XShOz_twJ:news.google.com"", + title : ""Google News"", + titleNoFormatting : ""Google News"", + content : ""Aggregated headlines and a search engine of many of the world's news sources."" + }, + + { + GsearchResultClass:""GwebSearch"", + unescapedUrl : ""http://groups.google.com/"", + url : ""http://groups.google.com/"", + visibleUrl : ""groups.google.com"", + cacheUrl : +""http://www.google.com/search?q=cache:x2uPD3hfkn0J:groups.google.com"", + title : ""Google Groups"", + titleNoFormatting : ""Google Groups"", + content : ""Enables users to search and browse the Usenet +archives which consist of over 700 million messages, and post new +comments."" + }, + + { + GsearchResultClass:""GwebSearch"", + unescapedUrl : ""http://maps.google.com/"", + url : ""http://maps.google.com/"", + visibleUrl : ""maps.google.com"", + cacheUrl : +""http://www.google.com/search?q=cache:dkf5u2twBXIJ:maps.google.com"", + title : ""Google Maps"", + titleNoFormatting : ""Google Maps"", + content : ""Provides directions, interactive maps, and +satellite/aerial imagery of the United States. Can also search by +keyword such as type of business."" + } + ], + + adResults: + [ + { + GsearchResultClass:""GwebSearch.ad"", + title : ""Gartner Symposium/ITxpo"", + content1 : ""Meet brilliant Gartner IT analysts"", + content2 : ""20-23 May 2007- Barcelona, Spain"", + url : +""http://www.google.com/url?sa=L&ai=BVualExYGRo3hD5ianAPJvejjD8-s6ye7kdTwArbI4gTAlrECEAEYASDXtMMFOAFQubWAjvr_____AWDXw_4EiAEBmAEAyAEBgAIB&num=1&q=http://www.gartner.com/it/sym/2007/spr8/spr8.jsp%3Fsrc%3D_spain_07_%26WT.srch%3D1&usg=__CxRH06E4Xvm9Muq13S4MgMtnziY="", + + impressionUrl : +""http://www.google.com/uds/css/ad-indicator-on.gif?ai=BVualExYGRo3hD5ianAPJvejjD8-s6ye7kdTwArbI4gTAlrECEAEYASDXtMMFOAFQubWAjvr_____AWDXw_4EiAEBmAEAyAEBgAIB"", + + unescapedUrl : +""http://www.google.com/url?sa=L&ai=BVualExYGRo3hD5ianAPJvejjD8-s6ye7kdTwArbI4gTAlrECEAEYASDXtMMFOAFQubWAjvr_____AWDXw_4EiAEBmAEAyAEBgAIB&num=1&q=http://www.gartner.com/it/sym/2007/spr8/spr8.jsp%3Fsrc%3D_spain_07_%26WT.srch%3D1&usg=__CxRH06E4Xvm9Muq13S4MgMtnziY="", + + visibleUrl : ""www.gartner.com"" + } + ] +} +"; + object o = JsonConvert.DeserializeObject(json); + string s = string.Empty; + s += s; + } + + [Test] + public void TorrentDeserializeTest() + { + string jsonText = @"{ +"""":"""", +""label"": [ + [""SomeName"",6] +], +""torrents"": [ + [""192D99A5C943555CB7F00A852821CF6D6DB3008A"",201,""filename.avi"",178311826,1000,178311826,72815250,408,1603,7,121430,""NameOfLabelPrevioslyDefined"",3,6,0,8,128954,-1,0], +], +""torrentc"": ""1816000723"" +}"; + + JObject o = (JObject)JsonConvert.DeserializeObject(jsonText); + Assert.AreEqual(4, o.Children().Count()); + + JToken torrentsArray = (JToken)o["torrents"]; + JToken nestedTorrentsArray = (JToken)torrentsArray[0]; + Assert.AreEqual(nestedTorrentsArray.Children().Count(), 19); + } + + [Test] + public void JsonPropertyClassSerialize() + { + JsonPropertyClass test = new JsonPropertyClass(); + test.Pie = "Delicious"; + test.SweetCakesCount = int.MaxValue; + + string jsonText = JsonConvert.SerializeObject(test); + + Assert.AreEqual(@"{""pie"":""Delicious"",""pie1"":""PieChart!"",""sweet_cakes_count"":2147483647}", jsonText); + + JsonPropertyClass test2 = JsonConvert.DeserializeObject(jsonText); + + Assert.AreEqual(test.Pie, test2.Pie); + Assert.AreEqual(test.SweetCakesCount, test2.SweetCakesCount); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"A member with the name 'pie' already exists on 'Newtonsoft.Json.Tests.TestObjects.BadJsonPropertyClass'. Use the JsonPropertyAttribute to specify another name.")] + public void BadJsonPropertyClassSerialize() + { + JsonConvert.SerializeObject(new BadJsonPropertyClass()); + } + + [Test] + public void InheritedListSerialize() + { + Article a1 = new Article("a1"); + Article a2 = new Article("a2"); + + ArticleCollection articles1 = new ArticleCollection(); + articles1.Add(a1); + articles1.Add(a2); + + string jsonText = JsonConvert.SerializeObject(articles1); + + ArticleCollection articles2 = JsonConvert.DeserializeObject(jsonText); + + Assert.AreEqual(articles1.Count, articles2.Count); + Assert.AreEqual(articles1[0].Name, articles2[0].Name); + } + + [Test] + public void ReadOnlyCollectionSerialize() + { + ReadOnlyCollection r1 = new ReadOnlyCollection(new int[] { 0, 1, 2, 3, 4 }); + + string jsonText = JsonConvert.SerializeObject(r1); + + ReadOnlyCollection r2 = JsonConvert.DeserializeObject>(jsonText); + + CollectionAssert.AreEqual(r1, r2); + } + +#if !PocketPC && !NET20 && !WINDOWS_PHONE + [Test] + public void Unicode() + { + string json = @"[""PRE\u003cPOST""]"; + + DataContractJsonSerializer s = new DataContractJsonSerializer(typeof(List)); + List dataContractResult = (List)s.ReadObject(new MemoryStream(Encoding.UTF8.GetBytes(json))); + + List jsonNetResult = JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(1, jsonNetResult.Count); + Assert.AreEqual(dataContractResult[0], jsonNetResult[0]); + } + + [Test] + public void BackslashEqivilence() + { + string json = @"[""vvv\/vvv\tvvv\""vvv\bvvv\nvvv\rvvv\\vvv\fvvv""]"; + +#if !SILVERLIGHT + JavaScriptSerializer javaScriptSerializer = new JavaScriptSerializer(); + List javaScriptSerializerResult = javaScriptSerializer.Deserialize>(json); +#endif + + DataContractJsonSerializer s = new DataContractJsonSerializer(typeof(List)); + List dataContractResult = (List)s.ReadObject(new MemoryStream(Encoding.UTF8.GetBytes(json))); + + List jsonNetResult = JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(1, jsonNetResult.Count); + Assert.AreEqual(dataContractResult[0], jsonNetResult[0]); +#if !SILVERLIGHT + Assert.AreEqual(javaScriptSerializerResult[0], jsonNetResult[0]); +#endif + } + + [Test] + [ExpectedException(typeof(JsonReaderException), ExpectedMessage = @"Bad JSON escape sequence: \j. Line 1, position 7.")] + public void InvalidBackslash() + { + string json = @"[""vvv\jvvv""]"; + + JsonConvert.DeserializeObject>(json); + } + + [Test] + public void DateTimeTest() + { + List testDates = new List { + new DateTime(100, 1, 1, 1, 1, 1, DateTimeKind.Local), + new DateTime(100, 1, 1, 1, 1, 1, DateTimeKind.Unspecified), + new DateTime(100, 1, 1, 1, 1, 1, DateTimeKind.Utc), + new DateTime(2000, 1, 1, 1, 1, 1, DateTimeKind.Local), + new DateTime(2000, 1, 1, 1, 1, 1, DateTimeKind.Unspecified), + new DateTime(2000, 1, 1, 1, 1, 1, DateTimeKind.Utc), + }; + string result; + + + MemoryStream ms = new MemoryStream(); + DataContractJsonSerializer s = new DataContractJsonSerializer(typeof(List)); + s.WriteObject(ms, testDates); + ms.Seek(0, SeekOrigin.Begin); + StreamReader sr = new StreamReader(ms); + + string expected = sr.ReadToEnd(); + + result = JsonConvert.SerializeObject(testDates); + Assert.AreEqual(expected, result); + } + + [Test] + public void DateTimeOffset() + { + List testDates = new List { + new DateTimeOffset(new DateTime(100, 1, 1, 1, 1, 1, DateTimeKind.Utc)), + new DateTimeOffset(2000, 1, 1, 1, 1, 1, TimeSpan.Zero), + new DateTimeOffset(2000, 1, 1, 1, 1, 1, TimeSpan.FromHours(13)), + new DateTimeOffset(2000, 1, 1, 1, 1, 1, TimeSpan.FromHours(-3.5)), + }; + + string result = JsonConvert.SerializeObject(testDates); + Assert.AreEqual(@"[""\/Date(-59011455539000+0000)\/"",""\/Date(946688461000+0000)\/"",""\/Date(946641661000+1300)\/"",""\/Date(946701061000-0330)\/""]", result); + } +#endif + + [Test] + public void NonStringKeyDictionary() + { + Dictionary values = new Dictionary(); + values.Add(-5, 6); + values.Add(int.MinValue, int.MaxValue); + + string json = JsonConvert.SerializeObject(values); + + Assert.AreEqual(@"{""-5"":6,""-2147483648"":2147483647}", json); + + Dictionary newValues = JsonConvert.DeserializeObject>(json); + + CollectionAssert.AreEqual(values, newValues); + } + + [Test] + public void AnonymousObjectSerialization() + { + var anonymous = + new + { + StringValue = "I am a string", + IntValue = int.MaxValue, + NestedAnonymous = new { NestedValue = byte.MaxValue }, + NestedArray = new[] { 1, 2 }, + Product = new Product() { Name = "TestProduct" } + }; + + string json = JsonConvert.SerializeObject(anonymous); + Assert.AreEqual(@"{""StringValue"":""I am a string"",""IntValue"":2147483647,""NestedAnonymous"":{""NestedValue"":255},""NestedArray"":[1,2],""Product"":{""Name"":""TestProduct"",""ExpiryDate"":""\/Date(946684800000)\/"",""Price"":0.0,""Sizes"":null}}", json); + + anonymous = JsonConvert.DeserializeAnonymousType(json, anonymous); + Assert.AreEqual("I am a string", anonymous.StringValue); + Assert.AreEqual(int.MaxValue, anonymous.IntValue); + Assert.AreEqual(255, anonymous.NestedAnonymous.NestedValue); + Assert.AreEqual(2, anonymous.NestedArray.Length); + Assert.AreEqual(1, anonymous.NestedArray[0]); + Assert.AreEqual(2, anonymous.NestedArray[1]); + Assert.AreEqual("TestProduct", anonymous.Product.Name); + } + + [Test] + public void CustomCollectionSerialization() + { + ProductCollection collection = new ProductCollection() + { + new Product() { Name = "Test1" }, + new Product() { Name = "Test2" }, + new Product() { Name = "Test3" } + }; + + JsonSerializer jsonSerializer = new JsonSerializer(); + jsonSerializer.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + + StringWriter sw = new StringWriter(); + + jsonSerializer.Serialize(sw, collection); + + Assert.AreEqual(@"[{""Name"":""Test1"",""ExpiryDate"":""\/Date(946684800000)\/"",""Price"":0.0,""Sizes"":null},{""Name"":""Test2"",""ExpiryDate"":""\/Date(946684800000)\/"",""Price"":0.0,""Sizes"":null},{""Name"":""Test3"",""ExpiryDate"":""\/Date(946684800000)\/"",""Price"":0.0,""Sizes"":null}]", + sw.GetStringBuilder().ToString()); + + ProductCollection collectionNew = (ProductCollection)jsonSerializer.Deserialize(new JsonTextReader(new StringReader(sw.GetStringBuilder().ToString())), typeof(ProductCollection)); + + CollectionAssert.AreEqual(collection, collectionNew); + } + + [Test] + public void SerializeObject() + { + string json = JsonConvert.SerializeObject(new object()); + Assert.AreEqual("{}", json); + } + + [Test] + public void SerializeNull() + { + string json = JsonConvert.SerializeObject(null); + Assert.AreEqual("null", json); + } + + [Test] + public void CanDeserializeIntArrayWhenNotFirstPropertyInJson() + { + string json = "{foo:'hello',bar:[1,2,3]}"; + ClassWithArray wibble = JsonConvert.DeserializeObject(json); + Assert.AreEqual("hello", wibble.Foo); + + Assert.AreEqual(4, wibble.Bar.Count); + Assert.AreEqual(int.MaxValue, wibble.Bar[0]); + Assert.AreEqual(1, wibble.Bar[1]); + Assert.AreEqual(2, wibble.Bar[2]); + Assert.AreEqual(3, wibble.Bar[3]); + } + + [Test] + public void CanDeserializeIntArray_WhenArrayIsFirstPropertyInJson() + { + string json = "{bar:[1,2,3], foo:'hello'}"; + ClassWithArray wibble = JsonConvert.DeserializeObject(json); + Assert.AreEqual("hello", wibble.Foo); + + Assert.AreEqual(4, wibble.Bar.Count); + Assert.AreEqual(int.MaxValue, wibble.Bar[0]); + Assert.AreEqual(1, wibble.Bar[1]); + Assert.AreEqual(2, wibble.Bar[2]); + Assert.AreEqual(3, wibble.Bar[3]); + } + + [Test] + public void ObjectCreationHandlingReplace() + { + string json = "{bar:[1,2,3], foo:'hello'}"; + + JsonSerializer s = new JsonSerializer(); + s.ObjectCreationHandling = ObjectCreationHandling.Replace; + + ClassWithArray wibble = (ClassWithArray)s.Deserialize(new StringReader(json), typeof(ClassWithArray)); + + Assert.AreEqual("hello", wibble.Foo); + + Assert.AreEqual(1, wibble.Bar.Count); + } + + [Test] + public void CanDeserializeSerializedJson() + { + ClassWithArray wibble = new ClassWithArray(); + wibble.Foo = "hello"; + wibble.Bar.Add(1); + wibble.Bar.Add(2); + wibble.Bar.Add(3); + string json = JsonConvert.SerializeObject(wibble); + + ClassWithArray wibbleOut = JsonConvert.DeserializeObject(json); + Assert.AreEqual("hello", wibbleOut.Foo); + + Assert.AreEqual(5, wibbleOut.Bar.Count); + Assert.AreEqual(int.MaxValue, wibbleOut.Bar[0]); + Assert.AreEqual(int.MaxValue, wibbleOut.Bar[1]); + Assert.AreEqual(1, wibbleOut.Bar[2]); + Assert.AreEqual(2, wibbleOut.Bar[3]); + Assert.AreEqual(3, wibbleOut.Bar[4]); + } + + [Test] + public void SerializeConverableObjects() + { + string json = JsonConvert.SerializeObject(new ConverableMembers()); + + Assert.AreEqual(@"{""String"":""string"",""Int32"":2147483647,""UInt32"":4294967295,""Byte"":255,""SByte"":127,""Short"":32767,""UShort"":65535,""Long"":9223372036854775807,""ULong"":9223372036854775807,""Double"":1.7976931348623157E+308,""Float"":3.40282347E+38,""DBNull"":null,""Bool"":true,""Char"":""\u0000""}", json); + + ConverableMembers c = JsonConvert.DeserializeObject(json); + Assert.AreEqual("string", c.String); + Assert.AreEqual(double.MaxValue, c.Double); + Assert.AreEqual(DBNull.Value, c.DBNull); + } + + [Test] + public void SerializeStack() + { + Stack s = new Stack(); + s.Push(1); + s.Push(2); + s.Push(3); + + string json = JsonConvert.SerializeObject(s); + Assert.AreEqual("[3,2,1]", json); + } + + [Test] + public void GuidTest() + { + Guid guid = new Guid("BED7F4EA-1A96-11d2-8F08-00A0C9A6186D"); + + string json = JsonConvert.SerializeObject(new ClassWithGuid { GuidField = guid }); + Assert.AreEqual(@"{""GuidField"":""bed7f4ea-1a96-11d2-8f08-00a0c9a6186d""}", json); + + ClassWithGuid c = JsonConvert.DeserializeObject(json); + Assert.AreEqual(guid, c.GuidField); + } + + [Test] + public void EnumTest() + { + string json = JsonConvert.SerializeObject(StringComparison.CurrentCultureIgnoreCase); + Assert.AreEqual(@"1", json); + + StringComparison s = JsonConvert.DeserializeObject(json); + Assert.AreEqual(StringComparison.CurrentCultureIgnoreCase, s); + } + + public class ClassWithTimeSpan + { + public TimeSpan TimeSpanField; + } + + [Test] + public void TimeSpanTest() + { + TimeSpan ts = new TimeSpan(00, 23, 59, 1); + + string json = JsonConvert.SerializeObject(new ClassWithTimeSpan { TimeSpanField = ts }, Formatting.Indented); + Assert.AreEqual(@"{ + ""TimeSpanField"": ""23:59:01"" +}", json); + + ClassWithTimeSpan c = JsonConvert.DeserializeObject(json); + Assert.AreEqual(ts, c.TimeSpanField); + } + + [Test] + public void JsonIgnoreAttributeOnClassTest() + { + string json = JsonConvert.SerializeObject(new JsonIgnoreAttributeOnClassTestClass()); + + Assert.AreEqual(@"{""TheField"":0,""Property"":21}", json); + + JsonIgnoreAttributeOnClassTestClass c = JsonConvert.DeserializeObject(@"{""TheField"":99,""Property"":-1,""IgnoredField"":-1}"); + + Assert.AreEqual(0, c.IgnoredField); + Assert.AreEqual(99, c.Field); + } + +#if !SILVERLIGHT + [Test] + public void SerializeArrayAsArrayList() + { + string jsonText = @"[3, ""somestring"",[1,2,3],{}]"; + ArrayList o = JsonConvert.DeserializeObject(jsonText); + + Assert.AreEqual(4, o.Count); + Assert.AreEqual(3, ((JArray)o[2]).Count); + Assert.AreEqual(0, ((JObject)o[3]).Count); + } +#endif + + [Test] + public void SerializeMemberGenericList() + { + Name name = new Name("The Idiot in Next To Me"); + + PhoneNumber p1 = new PhoneNumber("555-1212"); + PhoneNumber p2 = new PhoneNumber("444-1212"); + + name.pNumbers.Add(p1); + name.pNumbers.Add(p2); + + string json = JsonConvert.SerializeObject(name, Formatting.Indented); + + Assert.AreEqual(@"{ + ""personsName"": ""The Idiot in Next To Me"", + ""pNumbers"": [ + { + ""phoneNumber"": ""555-1212"" + }, + { + ""phoneNumber"": ""444-1212"" + } + ] +}", json); + + Name newName = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("The Idiot in Next To Me", newName.personsName); + + // not passed in as part of the constructor but assigned to pNumbers property + Assert.AreEqual(2, newName.pNumbers.Count); + Assert.AreEqual("555-1212", newName.pNumbers[0].phoneNumber); + Assert.AreEqual("444-1212", newName.pNumbers[1].phoneNumber); + } + + [Test] + public void ConstructorCaseSensitivity() + { + ConstructorCaseSensitivityClass c = new ConstructorCaseSensitivityClass("param1", "Param1", "Param2"); + + string json = JsonConvert.SerializeObject(c); + + ConstructorCaseSensitivityClass deserialized = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("param1", deserialized.param1); + Assert.AreEqual("Param1", deserialized.Param1); + Assert.AreEqual("Param2", deserialized.Param2); + } + + [Test] + public void SerializerShouldUseClassConverter() + { + ConverterPrecedenceClass c1 = new ConverterPrecedenceClass("!Test!"); + + string json = JsonConvert.SerializeObject(c1); + Assert.AreEqual(@"[""Class"",""!Test!""]", json); + + ConverterPrecedenceClass c2 = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("!Test!", c2.TestValue); + } + + [Test] + public void SerializerShouldUseClassConverterOverArgumentConverter() + { + ConverterPrecedenceClass c1 = new ConverterPrecedenceClass("!Test!"); + + string json = JsonConvert.SerializeObject(c1, new ArgumentConverterPrecedenceClassConverter()); + Assert.AreEqual(@"[""Class"",""!Test!""]", json); + + ConverterPrecedenceClass c2 = JsonConvert.DeserializeObject(json, new ArgumentConverterPrecedenceClassConverter()); + + Assert.AreEqual("!Test!", c2.TestValue); + } + + [Test] + public void SerializerShouldUseMemberConverter() + { + DateTime testDate = new DateTime(JsonConvert.InitialJavaScriptDateTicks, DateTimeKind.Utc); + MemberConverterClass m1 = new MemberConverterClass { DefaultConverter = testDate, MemberConverter = testDate }; + + string json = JsonConvert.SerializeObject(m1); + Assert.AreEqual(@"{""DefaultConverter"":""\/Date(0)\/"",""MemberConverter"":""1970-01-01T00:00:00Z""}", json); + + MemberConverterClass m2 = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(testDate, m2.DefaultConverter); + Assert.AreEqual(testDate, m2.MemberConverter); + } + + [Test] + public void SerializerShouldUseMemberConverterOverArgumentConverter() + { + DateTime testDate = new DateTime(JsonConvert.InitialJavaScriptDateTicks, DateTimeKind.Utc); + MemberConverterClass m1 = new MemberConverterClass { DefaultConverter = testDate, MemberConverter = testDate }; + + string json = JsonConvert.SerializeObject(m1, new JavaScriptDateTimeConverter()); + Assert.AreEqual(@"{""DefaultConverter"":new Date(0),""MemberConverter"":""1970-01-01T00:00:00Z""}", json); + + MemberConverterClass m2 = JsonConvert.DeserializeObject(json, new JavaScriptDateTimeConverter()); + + Assert.AreEqual(testDate, m2.DefaultConverter); + Assert.AreEqual(testDate, m2.MemberConverter); + } + + [Test] + public void ConverterAttributeExample() + { + DateTime date = Convert.ToDateTime("1970-01-01T00:00:00Z").ToUniversalTime(); + + MemberConverterClass c = new MemberConverterClass + { + DefaultConverter = date, + MemberConverter = date + }; + + string json = JsonConvert.SerializeObject(c, Formatting.Indented); + + Console.WriteLine(json); + //{ + // "DefaultConverter": "\/Date(0)\/", + // "MemberConverter": "1970-01-01T00:00:00Z" + //} + } + + [Test] + public void SerializerShouldUseMemberConverterOverClassAndArgumentConverter() + { + ClassAndMemberConverterClass c1 = new ClassAndMemberConverterClass(); + c1.DefaultConverter = new ConverterPrecedenceClass("DefaultConverterValue"); + c1.MemberConverter = new ConverterPrecedenceClass("MemberConverterValue"); + + string json = JsonConvert.SerializeObject(c1, new ArgumentConverterPrecedenceClassConverter()); + Assert.AreEqual(@"{""DefaultConverter"":[""Class"",""DefaultConverterValue""],""MemberConverter"":[""Member"",""MemberConverterValue""]}", json); + + ClassAndMemberConverterClass c2 = JsonConvert.DeserializeObject(json, new ArgumentConverterPrecedenceClassConverter()); + + Assert.AreEqual("DefaultConverterValue", c2.DefaultConverter.TestValue); + Assert.AreEqual("MemberConverterValue", c2.MemberConverter.TestValue); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "JsonConverter IsoDateTimeConverter on Newtonsoft.Json.Tests.TestObjects.IncompatibleJsonAttributeClass is not compatible with member type IncompatibleJsonAttributeClass.")] + public void IncompatibleJsonAttributeShouldThrow() + { + IncompatibleJsonAttributeClass c = new IncompatibleJsonAttributeClass(); + JsonConvert.SerializeObject(c); + } + + [Test] + public void GenericAbstractProperty() + { + string json = JsonConvert.SerializeObject(new GenericImpl()); + Assert.AreEqual(@"{""Id"":0}", json); + } + + [Test] + public void DeserializeNullable() + { + string json; + + json = JsonConvert.SerializeObject((int?)null); + Assert.AreEqual("null", json); + + json = JsonConvert.SerializeObject((int?)1); + Assert.AreEqual("1", json); + } + + [Test] + public void SerializeJsonRaw() + { + PersonRaw personRaw = new PersonRaw + { + FirstName = "FirstNameValue", + RawContent = new JRaw("[1,2,3,4,5]"), + LastName = "LastNameValue" + }; + + string json; + + json = JsonConvert.SerializeObject(personRaw); + Assert.AreEqual(@"{""first_name"":""FirstNameValue"",""RawContent"":[1,2,3,4,5],""last_name"":""LastNameValue""}", json); + } + + [Test] + public void DeserializeJsonRaw() + { + string json = @"{""first_name"":""FirstNameValue"",""RawContent"":[1,2,3,4,5],""last_name"":""LastNameValue""}"; + + PersonRaw personRaw = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("FirstNameValue", personRaw.FirstName); + Assert.AreEqual("[1,2,3,4,5]", personRaw.RawContent.ToString()); + Assert.AreEqual("LastNameValue", personRaw.LastName); + } + + + [Test] + public void DeserializeNullableMember() + { + UserNullable userNullablle = new UserNullable + { + Id = new Guid("AD6205E8-0DF4-465d-AEA6-8BA18E93A7E7"), + FName = "FirstValue", + LName = "LastValue", + RoleId = 5, + NullableRoleId = 6, + NullRoleId = null, + Active = true + }; + + string json = JsonConvert.SerializeObject(userNullablle); + + Assert.AreEqual(@"{""Id"":""ad6205e8-0df4-465d-aea6-8ba18e93a7e7"",""FName"":""FirstValue"",""LName"":""LastValue"",""RoleId"":5,""NullableRoleId"":6,""NullRoleId"":null,""Active"":true}", json); + + UserNullable userNullablleDeserialized = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(new Guid("AD6205E8-0DF4-465d-AEA6-8BA18E93A7E7"), userNullablleDeserialized.Id); + Assert.AreEqual("FirstValue", userNullablleDeserialized.FName); + Assert.AreEqual("LastValue", userNullablleDeserialized.LName); + Assert.AreEqual(5, userNullablleDeserialized.RoleId); + Assert.AreEqual(6, userNullablleDeserialized.NullableRoleId); + Assert.AreEqual(null, userNullablleDeserialized.NullRoleId); + Assert.AreEqual(true, userNullablleDeserialized.Active); + } + + [Test] + public void DeserializeInt64ToNullableDouble() + { + string json = @"{""Height"":1}"; + + DoubleClass c = JsonConvert.DeserializeObject(json); + Assert.AreEqual(1, c.Height); + } + + [Test] + public void SerializeTypeProperty() + { + string boolRef = typeof(bool).AssemblyQualifiedName; + TypeClass typeClass = new TypeClass { TypeProperty = typeof(bool) }; + + string json = JsonConvert.SerializeObject(typeClass); + Assert.AreEqual(@"{""TypeProperty"":""" + boolRef + @"""}", json); + + TypeClass typeClass2 = JsonConvert.DeserializeObject(json); + Assert.AreEqual(typeof(bool), typeClass2.TypeProperty); + + string jsonSerializerTestRef = typeof(JsonSerializerTest).AssemblyQualifiedName; + typeClass = new TypeClass { TypeProperty = typeof(JsonSerializerTest) }; + + json = JsonConvert.SerializeObject(typeClass); + Assert.AreEqual(@"{""TypeProperty"":""" + jsonSerializerTestRef + @"""}", json); + + typeClass2 = JsonConvert.DeserializeObject(json); + Assert.AreEqual(typeof(JsonSerializerTest), typeClass2.TypeProperty); + } + + [Test] + public void RequiredMembersClass() + { + RequiredMembersClass c = new RequiredMembersClass() + { + BirthDate = new DateTime(2000, 12, 20, 10, 55, 55, DateTimeKind.Utc), + FirstName = "Bob", + LastName = "Smith", + MiddleName = "Cosmo" + }; + + string json = JsonConvert.SerializeObject(c, Formatting.Indented); + + Assert.AreEqual(@"{ + ""FirstName"": ""Bob"", + ""MiddleName"": ""Cosmo"", + ""LastName"": ""Smith"", + ""BirthDate"": ""\/Date(977309755000)\/"" +}", json); + + RequiredMembersClass c2 = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("Bob", c2.FirstName); + Assert.AreEqual(new DateTime(2000, 12, 20, 10, 55, 55, DateTimeKind.Utc), c2.BirthDate); + } + + [Test] + public void DeserializeRequiredMembersClassWithNullValues() + { + string json = @"{ + ""FirstName"": ""I can't be null bro!"", + ""MiddleName"": null, + ""LastName"": null, + ""BirthDate"": ""\/Date(977309755000)\/"" +}"; + + RequiredMembersClass c = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("I can't be null bro!", c.FirstName); + Assert.AreEqual(null, c.MiddleName); + Assert.AreEqual(null, c.LastName); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Required property 'FirstName' expects a value but got null.")] + public void DeserializeRequiredMembersClassNullRequiredValueProperty() + { + string json = @"{ + ""FirstName"": null, + ""MiddleName"": null, + ""LastName"": null, + ""BirthDate"": ""\/Date(977309755000)\/"" +}"; + + JsonConvert.DeserializeObject(json); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Cannot write a null value for property 'FirstName'. Property requires a value.")] + public void SerializeRequiredMembersClassNullRequiredValueProperty() + { + RequiredMembersClass requiredMembersClass = new RequiredMembersClass + { + FirstName = null, + BirthDate = new DateTime(2000, 10, 10, 10, 10, 10, DateTimeKind.Utc), + LastName = null, + MiddleName = null + }; + + string json = JsonConvert.SerializeObject(requiredMembersClass); + Console.WriteLine(json); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Required property 'LastName' not found in JSON.")] + public void RequiredMembersClassMissingRequiredProperty() + { + string json = @"{ + ""FirstName"": ""Bob"" +}"; + + JsonConvert.DeserializeObject(json); + } + + [Test] + public void SerializeJaggedArray() + { + JaggedArray aa = new JaggedArray(); + aa.Before = "Before!"; + aa.After = "After!"; + aa.Coordinates = new[] { new[] { 1, 1 }, new[] { 1, 2 }, new[] { 2, 1 }, new[] { 2, 2 } }; + + string json = JsonConvert.SerializeObject(aa); + + Assert.AreEqual(@"{""Before"":""Before!"",""Coordinates"":[[1,1],[1,2],[2,1],[2,2]],""After"":""After!""}", json); + } + + [Test] + public void DeserializeJaggedArray() + { + string json = @"{""Before"":""Before!"",""Coordinates"":[[1,1],[1,2],[2,1],[2,2]],""After"":""After!""}"; + + JaggedArray aa = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("Before!", aa.Before); + Assert.AreEqual("After!", aa.After); + Assert.AreEqual(4, aa.Coordinates.Length); + Assert.AreEqual(2, aa.Coordinates[0].Length); + Assert.AreEqual(1, aa.Coordinates[0][0]); + Assert.AreEqual(2, aa.Coordinates[1][1]); + + string after = JsonConvert.SerializeObject(aa); + + Assert.AreEqual(json, after); + } + + [Test] + public void DeserializeGoogleGeoCode() + { + string json = @"{ + ""name"": ""1600 Amphitheatre Parkway, Mountain View, CA, USA"", + ""Status"": { + ""code"": 200, + ""request"": ""geocode"" + }, + ""Placemark"": [ + { + ""address"": ""1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA"", + ""AddressDetails"": { + ""Country"": { + ""CountryNameCode"": ""US"", + ""AdministrativeArea"": { + ""AdministrativeAreaName"": ""CA"", + ""SubAdministrativeArea"": { + ""SubAdministrativeAreaName"": ""Santa Clara"", + ""Locality"": { + ""LocalityName"": ""Mountain View"", + ""Thoroughfare"": { + ""ThoroughfareName"": ""1600 Amphitheatre Pkwy"" + }, + ""PostalCode"": { + ""PostalCodeNumber"": ""94043"" + } + } + } + } + }, + ""Accuracy"": 8 + }, + ""Point"": { + ""coordinates"": [-122.083739, 37.423021, 0] + } + } + ] +}"; + + GoogleMapGeocoderStructure jsonGoogleMapGeocoder = JsonConvert.DeserializeObject(json); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Could not create an instance of type Newtonsoft.Json.Tests.TestObjects.ICo. Type is an interface or abstract class and cannot be instantated.")] + public void DeserializeInterfaceProperty() + { + InterfacePropertyTestClass testClass = new InterfacePropertyTestClass(); + testClass.co = new Co(); + String strFromTest = JsonConvert.SerializeObject(testClass); + InterfacePropertyTestClass testFromDe = (InterfacePropertyTestClass)JsonConvert.DeserializeObject(strFromTest, typeof(InterfacePropertyTestClass)); + } + + private Person GetPerson() + { + Person person = new Person + { + Name = "Mike Manager", + BirthDate = new DateTime(1983, 8, 3, 0, 0, 0, DateTimeKind.Utc), + Department = "IT", + LastModified = new DateTime(2009, 2, 15, 0, 0, 0, DateTimeKind.Utc) + }; + return person; + } + + //[Test] + public void WriteJsonToFile() + { + //Person person = GetPerson(); + + //string json = JsonConvert.SerializeObject(person, Formatting.Indented); + + //File.WriteAllText(@"c:\person.json", json); + + Person person = GetPerson(); + + using (FileStream fs = System.IO.File.Open(@"c:\person.json", FileMode.CreateNew)) + using (StreamWriter sw = new StreamWriter(fs)) + using (JsonWriter jw = new JsonTextWriter(sw)) + { + jw.Formatting = Formatting.Indented; + + JsonSerializer serializer = new JsonSerializer(); + serializer.Serialize(jw, person); + } + } + + [Test] + public void WriteJsonDates() + { + LogEntry entry = new LogEntry + { + LogDate = new DateTime(2009, 2, 15, 0, 0, 0, DateTimeKind.Utc), + Details = "Application started." + }; + + string defaultJson = JsonConvert.SerializeObject(entry); + // {"Details":"Application started.","LogDate":"\/Date(1234656000000)\/"} + + string isoJson = JsonConvert.SerializeObject(entry, new IsoDateTimeConverter()); + // {"Details":"Application started.","LogDate":"2009-02-15T00:00:00.0000000Z"} + + string javascriptJson = JsonConvert.SerializeObject(entry, new JavaScriptDateTimeConverter()); + // {"Details":"Application started.","LogDate":new Date(1234656000000)} + + Console.WriteLine(defaultJson); + Console.WriteLine(isoJson); + Console.WriteLine(javascriptJson); + } + + public void GenericListAndDictionaryInterfaceProperties() + { + GenericListAndDictionaryInterfaceProperties o = new GenericListAndDictionaryInterfaceProperties(); + o.IDictionaryProperty = new Dictionary + { + {"one", 1}, + {"two", 2}, + {"three", 3} + }; + o.IListProperty = new List + { + 1, 2, 3 + }; + o.IEnumerableProperty = new List + { + 4, 5, 6 + }; + + string json = JsonConvert.SerializeObject(o, Formatting.Indented); + + Assert.AreEqual(@"{ + ""IEnumerableProperty"": [ + 4, + 5, + 6 + ], + ""IListProperty"": [ + 1, + 2, + 3 + ], + ""IDictionaryProperty"": { + ""one"": 1, + ""two"": 2, + ""three"": 3 + } +}", json); + + GenericListAndDictionaryInterfaceProperties deserializedObject = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(deserializedObject); + + CollectionAssert.AreEqual(o.IListProperty.ToArray(), deserializedObject.IListProperty.ToArray()); + CollectionAssert.AreEqual(o.IEnumerableProperty.ToArray(), deserializedObject.IEnumerableProperty.ToArray()); + CollectionAssert.AreEqual(o.IDictionaryProperty.ToArray(), deserializedObject.IDictionaryProperty.ToArray()); + } + + [Test] + public void DeserializeBestMatchPropertyCase() + { + string json = @"{ + ""firstName"": ""firstName"", + ""FirstName"": ""FirstName"", + ""LastName"": ""LastName"", + ""lastName"": ""lastName"", +}"; + + PropertyCase o = JsonConvert.DeserializeObject(json); + Assert.IsNotNull(o); + + Assert.AreEqual("firstName", o.firstName); + Assert.AreEqual("FirstName", o.FirstName); + Assert.AreEqual("LastName", o.LastName); + Assert.AreEqual("lastName", o.lastName); + } + + [Test] + public void DeserializePropertiesOnToNonDefaultConstructor() + { + SubKlass i = new SubKlass("my subprop"); + i.SuperProp = "overrided superprop"; + + string json = JsonConvert.SerializeObject(i); + Assert.AreEqual(@"{""SubProp"":""my subprop"",""SuperProp"":""overrided superprop""}", json); + + SubKlass ii = JsonConvert.DeserializeObject(json); + + string newJson = JsonConvert.SerializeObject(ii); + Assert.AreEqual(@"{""SubProp"":""my subprop"",""SuperProp"":""overrided superprop""}", newJson); + } + + [Test] + public void JsonPropertyWithHandlingValues() + { + JsonPropertyWithHandlingValues o = new JsonPropertyWithHandlingValues(); + o.DefaultValueHandlingIgnoreProperty = "Default!"; + o.DefaultValueHandlingIncludeProperty = "Default!"; + + string json = JsonConvert.SerializeObject(o, Formatting.Indented); + + Assert.AreEqual(@"{ + ""DefaultValueHandlingIncludeProperty"": ""Default!"", + ""NullValueHandlingIncludeProperty"": null, + ""ReferenceLoopHandlingErrorProperty"": null, + ""ReferenceLoopHandlingIgnoreProperty"": null, + ""ReferenceLoopHandlingSerializeProperty"": null +}", json); + + json = JsonConvert.SerializeObject(o, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + + Assert.AreEqual(@"{ + ""DefaultValueHandlingIncludeProperty"": ""Default!"", + ""NullValueHandlingIncludeProperty"": null +}", json); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException))] + public void JsonPropertyWithHandlingValues_ReferenceLoopError() + { + JsonPropertyWithHandlingValues o = new JsonPropertyWithHandlingValues(); + o.ReferenceLoopHandlingErrorProperty = o; + + JsonConvert.SerializeObject(o, Formatting.Indented, new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); + } + + [Test] + public void PartialClassDeserialize() + { + string json = @"{ + ""request"": ""ux.settings.update"", + ""sid"": ""14c561bd-32a8-457e-b4e5-4bba0832897f"", + ""uid"": ""30c39065-0f31-de11-9442-001e3786a8ec"", + ""fidOrder"": [ + ""id"", + ""andytest_name"", + ""andytest_age"", + ""andytest_address"", + ""andytest_phone"", + ""date"", + ""title"", + ""titleId"" + ], + ""entityName"": ""Andy Test"", + ""setting"": ""entity.field.order"" +}"; + + RequestOnly r = JsonConvert.DeserializeObject(json); + Assert.AreEqual("ux.settings.update", r.Request); + + NonRequest n = JsonConvert.DeserializeObject(json); + Assert.AreEqual(new Guid("14c561bd-32a8-457e-b4e5-4bba0832897f"), n.Sid); + Assert.AreEqual(new Guid("30c39065-0f31-de11-9442-001e3786a8ec"), n.Uid); + Assert.AreEqual(8, n.FidOrder.Count); + Assert.AreEqual("id", n.FidOrder[0]); + Assert.AreEqual("titleId", n.FidOrder[n.FidOrder.Count - 1]); + } + +#if !SILVERLIGHT && !PocketPC && !NET20 + [MetadataType(typeof(OptInClassMetadata))] + public class OptInClass + { + [DataContract] + public class OptInClassMetadata + { + [DataMember] + public string Name { get; set; } + [DataMember] + public int Age { get; set; } + public string NotIncluded { get; set; } + } + + public string Name { get; set; } + public int Age { get; set; } + public string NotIncluded { get; set; } + } + + [Test] + public void OptInClassMetadataSerialization() + { + OptInClass optInClass = new OptInClass(); + optInClass.Age = 26; + optInClass.Name = "James NK"; + optInClass.NotIncluded = "Poor me :("; + + string json = JsonConvert.SerializeObject(optInClass, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Name"": ""James NK"", + ""Age"": 26 +}", json); + + OptInClass newOptInClass = JsonConvert.DeserializeObject(@"{ + ""Name"": ""James NK"", + ""NotIncluded"": ""Ignore me!"", + ""Age"": 26 +}"); + Assert.AreEqual(26, newOptInClass.Age); + Assert.AreEqual("James NK", newOptInClass.Name); + Assert.AreEqual(null, newOptInClass.NotIncluded); + } +#endif + +#if !PocketPC && !NET20 + [DataContract] + public class DataContractPrivateMembers + { + public DataContractPrivateMembers() + { + } + + public DataContractPrivateMembers(string name, int age, int rank, string title) + { + _name = name; + Age = age; + Rank = rank; + Title = title; + } + + [DataMember] + private string _name; + [DataMember(Name = "_age")] + private int Age { get; set; } + [JsonProperty] + private int Rank { get; set; } + [JsonProperty(PropertyName = "JsonTitle")] + [DataMember(Name = "DataTitle")] + private string Title { get; set; } + + public string NotIncluded { get; set; } + + public override string ToString() + { + return "_name: " + _name + ", _age: " + Age + ", Rank: " + Rank + ", JsonTitle: " + Title; + } + } + + [Test] + public void SerializeDataContractPrivateMembers() + { + DataContractPrivateMembers c = new DataContractPrivateMembers("Jeff", 26, 10, "Dr"); + c.NotIncluded = "Hi"; + string json = JsonConvert.SerializeObject(c, Formatting.Indented); + + Assert.AreEqual(@"{ + ""_name"": ""Jeff"", + ""_age"": 26, + ""Rank"": 10, + ""JsonTitle"": ""Dr"" +}", json); + + DataContractPrivateMembers cc = JsonConvert.DeserializeObject(json); + Assert.AreEqual("_name: Jeff, _age: 26, Rank: 10, JsonTitle: Dr", cc.ToString()); + } +#endif + + [Test] + public void DeserializeDictionaryInterface() + { + string json = @"{ + ""Name"": ""Name!"", + ""Dictionary"": { + ""Item"": 11 + } +}"; + + DictionaryInterfaceClass c = JsonConvert.DeserializeObject(json, + new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace }); + Assert.AreEqual("Name!", c.Name); + Assert.AreEqual(1, c.Dictionary.Count); + Assert.AreEqual(11, c.Dictionary["Item"]); + } + + [Test] + public void DeserializeDictionaryInterfaceWithExistingValues() + { + string json = @"{ + ""Random"": { + ""blah"": 1 + }, + ""Name"": ""Name!"", + ""Dictionary"": { + ""Item"": 11, + ""Item1"": 12 + }, + ""Collection"": [ + 999 + ], + ""Employee"": { + ""Manager"": { + ""Name"": ""ManagerName!"" + } + } +}"; + + DictionaryInterfaceClass c = JsonConvert.DeserializeObject(json, + new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Reuse }); + + Assert.AreEqual("Name!", c.Name); + Assert.AreEqual(3, c.Dictionary.Count); + Assert.AreEqual(11, c.Dictionary["Item"]); + Assert.AreEqual(1, c.Dictionary["existing"]); + Assert.AreEqual(4, c.Collection.Count); + Assert.AreEqual(1, c.Collection.ElementAt(0)); + Assert.AreEqual(999, c.Collection.ElementAt(3)); + Assert.AreEqual("EmployeeName!", c.Employee.Name); + Assert.AreEqual("ManagerName!", c.Employee.Manager.Name); + Assert.IsNotNull(c.Random); + } + + [Test] + public void TypedObjectDeserializationWithComments() + { + string json = @"/*comment*/ { /*comment*/ + ""Name"": /*comment*/ ""Apple"" /*comment*/, /*comment*/ + ""ExpiryDate"": ""\/Date(1230422400000)\/"", + ""Price"": 3.99, + ""Sizes"": /*comment*/ [ /*comment*/ + ""Small"", /*comment*/ + ""Medium"" /*comment*/, + /*comment*/ ""Large"" + /*comment*/ ] /*comment*/ + } /*comment*/"; + + Product deserializedProduct = (Product)JsonConvert.DeserializeObject(json, typeof(Product)); + + Assert.AreEqual("Apple", deserializedProduct.Name); + Assert.AreEqual(new DateTime(2008, 12, 28, 0, 0, 0, DateTimeKind.Utc), deserializedProduct.ExpiryDate); + Assert.AreEqual(3.99, deserializedProduct.Price); + Assert.AreEqual("Small", deserializedProduct.Sizes[0]); + Assert.AreEqual("Medium", deserializedProduct.Sizes[1]); + Assert.AreEqual("Large", deserializedProduct.Sizes[2]); + } + + [Test] + public void NestedInsideOuterObject() + { + string json = @"{ + ""short"": { + ""original"": ""http://www.contrast.ie/blog/online-marketing-2009/"", + ""short"": ""m2sqc6"", + ""shortened"": ""http://short.ie/m2sqc6"", + ""error"": { + ""code"": 0, + ""msg"": ""No action taken"" + } + } +}"; + + JObject o = JObject.Parse(json); + + Shortie s = JsonConvert.DeserializeObject(o["short"].ToString()); + Assert.IsNotNull(s); + + Assert.AreEqual(s.Original, "http://www.contrast.ie/blog/online-marketing-2009/"); + Assert.AreEqual(s.Short, "m2sqc6"); + Assert.AreEqual(s.Shortened, "http://short.ie/m2sqc6"); + } + + [Test] + public void UriSerialization() + { + Uri uri = new Uri("http://codeplex.com"); + string json = JsonConvert.SerializeObject(uri); + + Assert.AreEqual("http://codeplex.com/", uri.ToString()); + + Uri newUri = JsonConvert.DeserializeObject(json); + Assert.AreEqual(uri, newUri); + } + + [Test] + public void AnonymousPlusLinqToSql() + { + var value = new + { + bar = new JObject(new JProperty("baz", 13)) + }; + + string json = JsonConvert.SerializeObject(value); + + Assert.AreEqual(@"{""bar"":{""baz"":13}}", json); + } + + [Test] + public void SerializeEnumerableAsObject() + { + Content content = new Content + { + Text = "Blah, blah, blah", + Children = new List + { + new Content { Text = "First" }, + new Content { Text = "Second" } + } + }; + + string json = JsonConvert.SerializeObject(content, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Children"": [ + { + ""Children"": null, + ""Text"": ""First"" + }, + { + ""Children"": null, + ""Text"": ""Second"" + } + ], + ""Text"": ""Blah, blah, blah"" +}", json); + } + + [Test] + public void DeserializeEnumerableAsObject() + { + string json = @"{ + ""Children"": [ + { + ""Children"": null, + ""Text"": ""First"" + }, + { + ""Children"": null, + ""Text"": ""Second"" + } + ], + ""Text"": ""Blah, blah, blah"" +}"; + + Content content = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("Blah, blah, blah", content.Text); + Assert.AreEqual(2, content.Children.Count); + Assert.AreEqual("First", content.Children[0].Text); + Assert.AreEqual("Second", content.Children[1].Text); + } + + [Test] + public void RoleTransferTest() + { + string json = @"{""Operation"":""1"",""RoleName"":""Admin"",""Direction"":""0""}"; + + RoleTransfer r = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(RoleTransferOperation.Second, r.Operation); + Assert.AreEqual("Admin", r.RoleName); + Assert.AreEqual(RoleTransferDirection.First, r.Direction); + } + + [Test] + public void PrimitiveValuesInObjectArray() + { + string json = @"{""action"":""Router"",""method"":""Navigate"",""data"":[""dashboard"",null],""type"":""rpc"",""tid"":2}"; + + ObjectArrayPropertyTest o = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("Router", o.Action); + Assert.AreEqual("Navigate", o.Method); + Assert.AreEqual(2, o.Data.Length); + Assert.AreEqual("dashboard", o.Data[0]); + Assert.AreEqual(null, o.Data[1]); + } + + [Test] + public void ComplexValuesInObjectArray() + { + string json = @"{""action"":""Router"",""method"":""Navigate"",""data"":[""dashboard"",[""id"", 1, ""teststring"", ""test""],{""one"":1}],""type"":""rpc"",""tid"":2}"; + + ObjectArrayPropertyTest o = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("Router", o.Action); + Assert.AreEqual("Navigate", o.Method); + Assert.AreEqual(3, o.Data.Length); + Assert.AreEqual("dashboard", o.Data[0]); + Assert.IsInstanceOfType(typeof(JArray), o.Data[1]); + Assert.AreEqual(4, ((JArray)o.Data[1]).Count); + Assert.IsInstanceOfType(typeof(JObject), o.Data[2]); + Assert.AreEqual(1, ((JObject)o.Data[2]).Count); + Assert.AreEqual(1, (int)((JObject)o.Data[2])["one"]); + } + + [Test] + public void DeserializeGenericDictionary() + { + string json = @"{""key1"":""value1"",""key2"":""value2""}"; + + Dictionary values = JsonConvert.DeserializeObject>(json); + + Console.WriteLine(values.Count); + // 2 + + Console.WriteLine(values["key1"]); + // value1 + + Assert.AreEqual(2, values.Count); + Assert.AreEqual("value1", values["key1"]); + Assert.AreEqual("value2", values["key2"]); + } + + [Test] + public void SerializeGenericList() + { + Product p1 = new Product + { + Name = "Product 1", + Price = 99.95m, + ExpiryDate = new DateTime(2000, 12, 29, 0, 0, 0, DateTimeKind.Utc), + }; + Product p2 = new Product + { + Name = "Product 2", + Price = 12.50m, + ExpiryDate = new DateTime(2009, 7, 31, 0, 0, 0, DateTimeKind.Utc), + }; + + List products = new List(); + products.Add(p1); + products.Add(p2); + + string json = JsonConvert.SerializeObject(products, Formatting.Indented); + //[ + // { + // "Name": "Product 1", + // "ExpiryDate": "\/Date(978048000000)\/", + // "Price": 99.95, + // "Sizes": null + // }, + // { + // "Name": "Product 2", + // "ExpiryDate": "\/Date(1248998400000)\/", + // "Price": 12.50, + // "Sizes": null + // } + //] + + Assert.AreEqual(@"[ + { + ""Name"": ""Product 1"", + ""ExpiryDate"": ""\/Date(978048000000)\/"", + ""Price"": 99.95, + ""Sizes"": null + }, + { + ""Name"": ""Product 2"", + ""ExpiryDate"": ""\/Date(1248998400000)\/"", + ""Price"": 12.50, + ""Sizes"": null + } +]", json); + } + + [Test] + public void DeserializeGenericList() + { + string json = @"[ + { + ""Name"": ""Product 1"", + ""ExpiryDate"": ""\/Date(978048000000)\/"", + ""Price"": 99.95, + ""Sizes"": null + }, + { + ""Name"": ""Product 2"", + ""ExpiryDate"": ""\/Date(1248998400000)\/"", + ""Price"": 12.50, + ""Sizes"": null + } + ]"; + + List products = JsonConvert.DeserializeObject>(json); + + Console.WriteLine(products.Count); + // 2 + + Product p1 = products[0]; + + Console.WriteLine(p1.Name); + // Product 1 + + Assert.AreEqual(2, products.Count); + Assert.AreEqual("Product 1", products[0].Name); + } + +#if !PocketPC && !NET20 + [Test] + public void DeserializeEmptyStringToNullableDateTime() + { + string json = @"{""DateTimeField"":""""}"; + + NullableDateTimeTestClass c = JsonConvert.DeserializeObject(json); + Assert.AreEqual(null, c.DateTimeField); + } +#endif + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Unable to find a constructor to use for type Newtonsoft.Json.Tests.TestObjects.Event. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.")] + public void FailWhenClassWithNoDefaultConstructorHasMultipleConstructorsWithArguments() + { + string json = @"{""sublocation"":""AlertEmailSender.Program.Main"",""userId"":0,""type"":0,""summary"":""Loading settings variables"",""details"":null,""stackTrace"":"" at System.Environment.GetStackTrace(Exception e, Boolean needFileInfo)\r\n at System.Environment.get_StackTrace()\r\n at mr.Logging.Event..ctor(String summary) in C:\\Projects\\MRUtils\\Logging\\Event.vb:line 71\r\n at AlertEmailSender.Program.Main(String[] args) in C:\\Projects\\AlertEmailSender\\AlertEmailSender\\Program.cs:line 25"",""tag"":null,""time"":""\/Date(1249591032026-0400)\/""}"; + + Event e = JsonConvert.DeserializeObject(json); + } + + [Test] + public void DeserializeObjectSetOnlyProperty() + { + string json = @"{'SetOnlyProperty':[1,2,3,4,5]}"; + + SetOnlyPropertyClass2 setOnly = JsonConvert.DeserializeObject(json); + JArray a = (JArray)setOnly.GetValue(); + Assert.AreEqual(5, a.Count); + Assert.AreEqual(1, (int)a[0]); + Assert.AreEqual(5, (int)a[a.Count - 1]); + } + + [Test] + public void DeserializeOptInClasses() + { + string json = @"{id: ""12"", name: ""test"", items: [{id: ""112"", name: ""testing""}]}"; + + ListTestClass l = JsonConvert.DeserializeObject(json); + } + + [Test] + public void DeserializeNullableListWithNulls() + { + List l = JsonConvert.DeserializeObject>("[ 3.3, null, 1.1 ] "); + Assert.AreEqual(3, l.Count); + + Assert.AreEqual(3.3m, l[0]); + Assert.AreEqual(null, l[1]); + Assert.AreEqual(1.1m, l[2]); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Cannot deserialize JSON array into type 'Newtonsoft.Json.Tests.TestObjects.Person'.")] + public void CannotDeserializeArrayIntoObject() + { + string json = @"[]"; + + JsonConvert.DeserializeObject(json); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Cannot deserialize JSON object into type 'System.Collections.Generic.List`1[Newtonsoft.Json.Tests.TestObjects.Person]'.")] + public void CannotDeserializeObjectIntoArray() + { + string json = @"{}"; + + JsonConvert.DeserializeObject>(json); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Cannot populate JSON array onto type 'Newtonsoft.Json.Tests.TestObjects.Person'.")] + public void CannotPopulateArrayIntoObject() + { + string json = @"[]"; + + JsonConvert.PopulateObject(json, new Person()); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Cannot populate JSON object onto type 'System.Collections.Generic.List`1[Newtonsoft.Json.Tests.TestObjects.Person]'.")] + public void CannotPopulateObjectIntoArray() + { + string json = @"{}"; + + JsonConvert.PopulateObject(json, new List()); + } + + [Test] + public void DeserializeEmptyString() + { + string json = @"{""Name"":""""}"; + + Person p = JsonConvert.DeserializeObject(json); + Assert.AreEqual("", p.Name); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Error getting value from 'ReadTimeout' on 'System.IO.MemoryStream'.")] + public void SerializePropertyGetError() + { + JsonConvert.SerializeObject(new MemoryStream()); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Error setting value to 'ReadTimeout' on 'System.IO.MemoryStream'.")] + public void DeserializePropertySetError() + { + JsonConvert.DeserializeObject("{ReadTimeout:0}"); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Error converting value """" to type 'System.Int32'.")] + public void DeserializeEnsureTypeEmptyStringToIntError() + { + JsonConvert.DeserializeObject("{ReadTimeout:''}"); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Error converting value {null} to type 'System.Int32'.")] + public void DeserializeEnsureTypeNullToIntError() + { + JsonConvert.DeserializeObject("{ReadTimeout:null}"); + } + + [Test] + public void SerializeGenericListOfStrings() + { + List strings = new List(); + + strings.Add("str_1"); + strings.Add("str_2"); + strings.Add("str_3"); + + string json = JsonConvert.SerializeObject(strings); + Assert.AreEqual(@"[""str_1"",""str_2"",""str_3""]", json); + } + + [Test] + public void ConstructorReadonlyFieldsTest() + { + ConstructorReadonlyFields c1 = new ConstructorReadonlyFields("String!", int.MaxValue); + string json = JsonConvert.SerializeObject(c1, Formatting.Indented); + Assert.AreEqual(@"{ + ""A"": ""String!"", + ""B"": 2147483647 +}", json); + + ConstructorReadonlyFields c2 = JsonConvert.DeserializeObject(json); + Assert.AreEqual("String!", c2.A); + Assert.AreEqual(int.MaxValue, c2.B); + } + + [Test] + public void SerializeStruct() + { + StructTest structTest = new StructTest + { + StringProperty = "StringProperty!", + StringField = "StringField", + IntProperty = 5, + IntField = 10 + }; + + string json = JsonConvert.SerializeObject(structTest, Formatting.Indented); + Console.WriteLine(json); + Assert.AreEqual(@"{ + ""StringField"": ""StringField"", + ""IntField"": 10, + ""StringProperty"": ""StringProperty!"", + ""IntProperty"": 5 +}", json); + + StructTest deserialized = JsonConvert.DeserializeObject(json); + Assert.AreEqual(structTest.StringProperty, deserialized.StringProperty); + Assert.AreEqual(structTest.StringField, deserialized.StringField); + Assert.AreEqual(structTest.IntProperty, deserialized.IntProperty); + Assert.AreEqual(structTest.IntField, deserialized.IntField); + } + + [Test] + public void SerializeListWithJsonConverter() + { + Foo f = new Foo(); + f.Bars.Add(new Bar { Id = 0 }); + f.Bars.Add(new Bar { Id = 1 }); + f.Bars.Add(new Bar { Id = 2 }); + + string json = JsonConvert.SerializeObject(f, Formatting.Indented); + Assert.AreEqual(@"{ + ""Bars"": [ + 0, + 1, + 2 + ] +}", json); + + Foo newFoo = JsonConvert.DeserializeObject(json); + Assert.AreEqual(3, newFoo.Bars.Count); + Assert.AreEqual(0, newFoo.Bars[0].Id); + Assert.AreEqual(1, newFoo.Bars[1].Id); + Assert.AreEqual(2, newFoo.Bars[2].Id); + } + + [Test] + public void SerializeGuidKeyedDictionary() + { + Dictionary dictionary = new Dictionary(); + dictionary.Add(new Guid("F60EAEE0-AE47-488E-B330-59527B742D77"), 1); + dictionary.Add(new Guid("C2594C02-EBA1-426A-AA87-8DD8871350B0"), 2); + + string json = JsonConvert.SerializeObject(dictionary, Formatting.Indented); + Assert.AreEqual(@"{ + ""f60eaee0-ae47-488e-b330-59527b742d77"": 1, + ""c2594c02-eba1-426a-aa87-8dd8871350b0"": 2 +}", json); + } + + [Test] + public void SerializePersonKeyedDictionary() + { + Dictionary dictionary = new Dictionary(); + dictionary.Add(new Person { Name = "p1" }, 1); + dictionary.Add(new Person { Name = "p2" }, 2); + + string json = JsonConvert.SerializeObject(dictionary, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Newtonsoft.Json.Tests.TestObjects.Person"": 1, + ""Newtonsoft.Json.Tests.TestObjects.Person"": 2 +}", json); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Could not convert string 'Newtonsoft.Json.Tests.TestObjects.Person' to dictionary key type 'Newtonsoft.Json.Tests.TestObjects.Person'. Create a TypeConverter to convert from the string to the key type object.")] + public void DeserializePersonKeyedDictionary() + { + string json = + @"{ + ""Newtonsoft.Json.Tests.TestObjects.Person"": 1, + ""Newtonsoft.Json.Tests.TestObjects.Person"": 2 +}"; + + JsonConvert.DeserializeObject>(json); + } + + [Test] + public void SerializeFragment() + { + string googleSearchText = @"{ + ""responseData"": { + ""results"": [ + { + ""GsearchResultClass"": ""GwebSearch"", + ""unescapedUrl"": ""http://en.wikipedia.org/wiki/Paris_Hilton"", + ""url"": ""http://en.wikipedia.org/wiki/Paris_Hilton"", + ""visibleUrl"": ""en.wikipedia.org"", + ""cacheUrl"": ""http://www.google.com/search?q=cache:TwrPfhd22hYJ:en.wikipedia.org"", + ""title"": ""Paris Hilton - Wikipedia, the free encyclopedia"", + ""titleNoFormatting"": ""Paris Hilton - Wikipedia, the free encyclopedia"", + ""content"": ""[1] In 2006, she released her debut album..."" + }, + { + ""GsearchResultClass"": ""GwebSearch"", + ""unescapedUrl"": ""http://www.imdb.com/name/nm0385296/"", + ""url"": ""http://www.imdb.com/name/nm0385296/"", + ""visibleUrl"": ""www.imdb.com"", + ""cacheUrl"": ""http://www.google.com/search?q=cache:1i34KkqnsooJ:www.imdb.com"", + ""title"": ""Paris Hilton"", + ""titleNoFormatting"": ""Paris Hilton"", + ""content"": ""Self: Zoolander. Socialite Paris Hilton..."" + } + ], + ""cursor"": { + ""pages"": [ + { + ""start"": ""0"", + ""label"": 1 + }, + { + ""start"": ""4"", + ""label"": 2 + }, + { + ""start"": ""8"", + ""label"": 3 + }, + { + ""start"": ""12"", + ""label"": 4 + } + ], + ""estimatedResultCount"": ""59600000"", + ""currentPageIndex"": 0, + ""moreResultsUrl"": ""http://www.google.com/search?oe=utf8&ie=utf8..."" + } + }, + ""responseDetails"": null, + ""responseStatus"": 200 + }"; + + JObject googleSearch = JObject.Parse(googleSearchText); + + // get JSON result objects into a list + IList results = googleSearch["responseData"]["results"].Children().ToList(); + + // serialize JSON results into .NET objects + IList searchResults = new List(); + foreach (JToken result in results) + { + SearchResult searchResult = JsonConvert.DeserializeObject(result.ToString()); + searchResults.Add(searchResult); + } + + // Title = Paris Hilton - Wikipedia, the free encyclopedia + // Content = [1] In 2006, she released her debut album... + // Url = http://en.wikipedia.org/wiki/Paris_Hilton + + // Title = Paris Hilton + // Content = Self: Zoolander. Socialite Paris Hilton... + // Url = http://www.imdb.com/name/nm0385296/ + + Assert.AreEqual(2, searchResults.Count); + Assert.AreEqual("Paris Hilton - Wikipedia, the free encyclopedia", searchResults[0].Title); + Assert.AreEqual("Paris Hilton", searchResults[1].Title); + } + + [Test] + public void DeserializeBaseReferenceWithDerivedValue() + { + PersonPropertyClass personPropertyClass = new PersonPropertyClass(); + WagePerson wagePerson = (WagePerson)personPropertyClass.Person; + + wagePerson.BirthDate = new DateTime(2000, 11, 29, 23, 59, 59, DateTimeKind.Utc); + wagePerson.Department = "McDees"; + wagePerson.HourlyWage = 12.50m; + wagePerson.LastModified = new DateTime(2000, 11, 29, 23, 59, 59, DateTimeKind.Utc); + wagePerson.Name = "Jim Bob"; + + string json = JsonConvert.SerializeObject(personPropertyClass, Formatting.Indented); + Assert.AreEqual( + @"{ + ""Person"": { + ""HourlyWage"": 12.50, + ""Name"": ""Jim Bob"", + ""BirthDate"": ""\/Date(975542399000)\/"", + ""LastModified"": ""\/Date(975542399000)\/"" + } +}", + json); + + PersonPropertyClass newPersonPropertyClass = JsonConvert.DeserializeObject(json); + Assert.AreEqual(wagePerson.HourlyWage, ((WagePerson)newPersonPropertyClass.Person).HourlyWage); + } + + public class ExistingValueClass + { + public Dictionary Dictionary { get; set; } + public List List { get; set; } + + public ExistingValueClass() + { + Dictionary = new Dictionary + { + {"existing", "yup"} + }; + List = new List + { + "existing" + }; + } + } + + [Test] + public void DeserializePopulateDictionaryAndList() + { + ExistingValueClass d = JsonConvert.DeserializeObject(@"{'Dictionary':{appended:'appended',existing:'new'}}"); + + Assert.IsNotNull(d); + Assert.IsNotNull(d.Dictionary); + Assert.AreEqual(typeof(Dictionary), d.Dictionary.GetType()); + Assert.AreEqual(typeof(List), d.List.GetType()); + Assert.AreEqual(2, d.Dictionary.Count); + Assert.AreEqual("new", d.Dictionary["existing"]); + Assert.AreEqual("appended", d.Dictionary["appended"]); + Assert.AreEqual(1, d.List.Count); + Assert.AreEqual("existing", d.List[0]); + } + + public interface IKeyValueId + { + int Id { get; set; } + string Key { get; set; } + string Value { get; set; } + } + + + public class KeyValueId : IKeyValueId + { + public int Id { get; set; } + public string Key { get; set; } + public string Value { get; set; } + } + + public class ThisGenericTest where T : IKeyValueId + { + private Dictionary _dict1 = new Dictionary(); + + public string MyProperty { get; set; } + + public void Add(T item) + { + this._dict1.Add(item.Key, item); + } + + public T this[string key] + { + get { return this._dict1[key]; } + set { this._dict1[key] = value; } + } + + public T this[int id] + { + get { return this._dict1.Values.FirstOrDefault(x => x.Id == id); } + set + { + var item = this[id]; + + if (item == null) + this.Add(value); + else + this._dict1[item.Key] = value; + } + } + + public string ToJson() + { + return JsonConvert.SerializeObject(this, Formatting.Indented); + } + + public T[] TheItems + { + get { return this._dict1.Values.ToArray(); } + set + { + foreach (var item in value) + this.Add(item); + } + } + } + + [Test] + public void IgnoreIndexedProperties() + { + ThisGenericTest g = new ThisGenericTest(); + + g.Add(new KeyValueId { Id = 1, Key = "key1", Value = "value1" }); + g.Add(new KeyValueId { Id = 2, Key = "key2", Value = "value2" }); + + g.MyProperty = "some value"; + + string json = g.ToJson(); + + Assert.AreEqual(@"{ + ""MyProperty"": ""some value"", + ""TheItems"": [ + { + ""Id"": 1, + ""Key"": ""key1"", + ""Value"": ""value1"" + }, + { + ""Id"": 2, + ""Key"": ""key2"", + ""Value"": ""value2"" + } + ] +}", json); + + ThisGenericTest gen = JsonConvert.DeserializeObject>(json); + Assert.AreEqual("some value", gen.MyProperty); + } + + public class JRawValueTestObject + { + public JRaw Value { get; set; } + } + + [Test] + public void JRawValue() + { + JRawValueTestObject deserialized = JsonConvert.DeserializeObject("{value:3}"); + Assert.AreEqual("3", deserialized.Value.ToString()); + + deserialized = JsonConvert.DeserializeObject("{value:'3'}"); + Assert.AreEqual(@"""3""", deserialized.Value.ToString()); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Unable to find a default constructor to use for type Newtonsoft.Json.Tests.Serialization.JsonSerializerTest+DictionaryWithNoDefaultConstructor.")] + public void DeserializeDictionaryWithNoDefaultConstructor() + { + string json = "{key1:'value',key2:'value',key3:'value'}"; + JsonConvert.DeserializeObject(json); + } + + public class DictionaryWithNoDefaultConstructor : Dictionary + { + public DictionaryWithNoDefaultConstructor(IEnumerable> initial) + { + foreach (KeyValuePair pair in initial) + { + Add(pair.Key, pair.Value); + } + } + } + + [JsonObject(MemberSerialization.OptIn)] + public class A + { + [JsonProperty("A1")] + private string _A1; + public string A1 { get { return _A1; } set { _A1 = value; } } + + [JsonProperty("A2")] + private string A2 { get; set; } + } + + [JsonObject(MemberSerialization.OptIn)] + public class B : A + { + public string B1 { get; set; } + + [JsonProperty("B2")] + string _B2; + public string B2 { get { return _B2; } set { _B2 = value; } } + + [JsonProperty("B3")] + private string B3 { get; set; } + } + + [Test] + public void SerializeNonPublicBaseJsonProperties() + { + B value = new B(); + string json = JsonConvert.SerializeObject(value, Formatting.Indented); + + Assert.AreEqual(@"{ + ""B2"": null, + ""A1"": null, + ""B3"": null, + ""A2"": null +}", json); + } + + public class TestClass + { + public string Key { get; set; } + public object Value { get; set; } + } + + [Test] + public void DeserializeToObjectProperty() + { + var json = "{ Key: 'abc', Value: 123 }"; + var item = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(123, item.Value); + } + + public abstract class Animal + { + public abstract string Name { get; } + } + + public class Human : Animal + { + public override string Name + { + get { return typeof(Human).Name; } + } + + public string Ethnicity { get; set; } + } + +#if !NET20 && !PocketPC && !WINDOWS_PHONE + public class DataContractJsonSerializerTestClass + { + public TimeSpan TimeSpanProperty { get; set; } + public Guid GuidProperty { get; set; } + public Animal AnimalProperty { get; set; } + public Exception ExceptionProperty { get; set; } + } + + [Test] + public void DataContractJsonSerializerTest() + { + Exception ex = new Exception("Blah blah blah"); + + DataContractJsonSerializerTestClass c = new DataContractJsonSerializerTestClass(); + c.TimeSpanProperty = new TimeSpan(200, 20, 59, 30, 900); + c.GuidProperty = new Guid("66143115-BE2A-4a59-AF0A-348E1EA15B1E"); + c.AnimalProperty = new Human() { Ethnicity = "European" }; + c.ExceptionProperty = ex; + + MemoryStream ms = new MemoryStream(); + DataContractJsonSerializer serializer = new DataContractJsonSerializer( + typeof(DataContractJsonSerializerTestClass), + new Type[] { typeof(Human) }); + serializer.WriteObject(ms, c); + + byte[] jsonBytes = ms.ToArray(); + string json = Encoding.UTF8.GetString(jsonBytes, 0, jsonBytes.Length); + + Console.WriteLine(JObject.Parse(json).ToString()); + Console.WriteLine(); + + Console.WriteLine(JsonConvert.SerializeObject(c, Formatting.Indented, new JsonSerializerSettings + { + // TypeNameHandling = TypeNameHandling.Objects + })); + } +#endif + + public class ModelStateDictionary : IDictionary + { + + private readonly Dictionary _innerDictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + + public ModelStateDictionary() + { + } + + public ModelStateDictionary(ModelStateDictionary dictionary) + { + if (dictionary == null) + { + throw new ArgumentNullException("dictionary"); + } + + foreach (var entry in dictionary) + { + _innerDictionary.Add(entry.Key, entry.Value); + } + } + + public int Count + { + get + { + return _innerDictionary.Count; + } + } + + public bool IsReadOnly + { + get + { + return ((IDictionary)_innerDictionary).IsReadOnly; + } + } + + public ICollection Keys + { + get + { + return _innerDictionary.Keys; + } + } + + public T this[string key] + { + get + { + T value; + _innerDictionary.TryGetValue(key, out value); + return value; + } + set + { + _innerDictionary[key] = value; + } + } + + public ICollection Values + { + get + { + return _innerDictionary.Values; + } + } + + public void Add(KeyValuePair item) + { + ((IDictionary)_innerDictionary).Add(item); + } + + public void Add(string key, T value) + { + _innerDictionary.Add(key, value); + } + + public void Clear() + { + _innerDictionary.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return ((IDictionary)_innerDictionary).Contains(item); + } + + public bool ContainsKey(string key) + { + return _innerDictionary.ContainsKey(key); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((IDictionary)_innerDictionary).CopyTo(array, arrayIndex); + } + + public IEnumerator> GetEnumerator() + { + return _innerDictionary.GetEnumerator(); + } + + public void Merge(ModelStateDictionary dictionary) + { + if (dictionary == null) + { + return; + } + + foreach (var entry in dictionary) + { + this[entry.Key] = entry.Value; + } + } + + public bool Remove(KeyValuePair item) + { + return ((IDictionary)_innerDictionary).Remove(item); + } + + public bool Remove(string key) + { + return _innerDictionary.Remove(key); + } + + public bool TryGetValue(string key, out T value) + { + return _innerDictionary.TryGetValue(key, out value); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)_innerDictionary).GetEnumerator(); + } + } + + [Test] + public void SerializeNonIDictionary() + { + ModelStateDictionary modelStateDictionary = new ModelStateDictionary(); + modelStateDictionary.Add("key", "value"); + + string json = JsonConvert.SerializeObject(modelStateDictionary); + + Assert.AreEqual(@"{""key"":""value""}", json); + + ModelStateDictionary newModelStateDictionary = JsonConvert.DeserializeObject>(json); + Assert.AreEqual(1, newModelStateDictionary.Count); + Assert.AreEqual("value", newModelStateDictionary["key"]); + } + +#if !SILVERLIGHT && !PocketPC + public class ISerializableTestObject : ISerializable + { + internal string _stringValue; + internal int _intValue; + internal DateTimeOffset _dateTimeOffsetValue; + internal Person _personValue; + internal Person _nullPersonValue; + internal int? _nullableInt; + internal bool _booleanValue; + internal byte _byteValue; + internal char _charValue; + internal DateTime _dateTimeValue; + internal decimal _decimalValue; + internal short _shortValue; + internal long _longValue; + internal sbyte _sbyteValue; + internal float _floatValue; + internal ushort _ushortValue; + internal uint _uintValue; + internal ulong _ulongValue; + + public ISerializableTestObject(string stringValue, int intValue, DateTimeOffset dateTimeOffset, Person personValue) + { + _stringValue = stringValue; + _intValue = intValue; + _dateTimeOffsetValue = dateTimeOffset; + _personValue = personValue; + _dateTimeValue = new DateTime(0, DateTimeKind.Utc); + } + + protected ISerializableTestObject(SerializationInfo info, StreamingContext context) + { + _stringValue = info.GetString("stringValue"); + _intValue = info.GetInt32("intValue"); + _dateTimeOffsetValue = (DateTimeOffset)info.GetValue("dateTimeOffsetValue", typeof(DateTimeOffset)); + _personValue = (Person)info.GetValue("personValue", typeof(Person)); + _nullPersonValue = (Person)info.GetValue("nullPersonValue", typeof(Person)); + _nullableInt = (int?)info.GetValue("nullableInt", typeof(int?)); + + _booleanValue = info.GetBoolean("booleanValue"); + _byteValue = info.GetByte("byteValue"); + _charValue = info.GetChar("charValue"); + _dateTimeValue = info.GetDateTime("dateTimeValue"); + _decimalValue = info.GetDecimal("decimalValue"); + _shortValue = info.GetInt16("shortValue"); + _longValue = info.GetInt64("longValue"); + _sbyteValue = info.GetSByte("sbyteValue"); + _floatValue = info.GetSingle("floatValue"); + _ushortValue = info.GetUInt16("ushortValue"); + _uintValue = info.GetUInt32("uintValue"); + _ulongValue = info.GetUInt64("ulongValue"); + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("stringValue", _stringValue); + info.AddValue("intValue", _intValue); + info.AddValue("dateTimeOffsetValue", _dateTimeOffsetValue); + info.AddValue("personValue", _personValue); + info.AddValue("nullPersonValue", _nullPersonValue); + info.AddValue("nullableInt", null); + + info.AddValue("booleanValue", _booleanValue); + info.AddValue("byteValue", _byteValue); + info.AddValue("charValue", _charValue); + info.AddValue("dateTimeValue", _dateTimeValue); + info.AddValue("decimalValue", _decimalValue); + info.AddValue("shortValue", _shortValue); + info.AddValue("longValue", _longValue); + info.AddValue("sbyteValue", _sbyteValue); + info.AddValue("floatValue", _floatValue); + info.AddValue("ushortValue", _ushortValue); + info.AddValue("uintValue", _uintValue); + info.AddValue("ulongValue", _ulongValue); + } + } + + [Test] + public void SerializeISerializableTestObject() + { + Person person = new Person(); + person.BirthDate = new DateTime(2000, 1, 1, 1, 1, 1, DateTimeKind.Utc); + person.LastModified = person.BirthDate; + person.Department = "Department!"; + person.Name = "Name!"; + + DateTimeOffset dateTimeOffset = new DateTimeOffset(2000, 12, 20, 22, 59, 59, TimeSpan.FromHours(2)); + string dateTimeOffsetText; +#if !NET20 + dateTimeOffsetText = @"\/Date(977345999000+0200)\/"; +#else + dateTimeOffsetText = @"12/20/2000 22:59:59 +02:00"; +#endif + + ISerializableTestObject o = new ISerializableTestObject("String!", int.MinValue, dateTimeOffset, person); + + string json = JsonConvert.SerializeObject(o, Formatting.Indented); + Assert.AreEqual(@"{ + ""stringValue"": ""String!"", + ""intValue"": -2147483648, + ""dateTimeOffsetValue"": """ + dateTimeOffsetText + @""", + ""personValue"": { + ""Name"": ""Name!"", + ""BirthDate"": ""\/Date(946688461000)\/"", + ""LastModified"": ""\/Date(946688461000)\/"" + }, + ""nullPersonValue"": null, + ""nullableInt"": null, + ""booleanValue"": false, + ""byteValue"": 0, + ""charValue"": ""\u0000"", + ""dateTimeValue"": ""\/Date(-62135596800000)\/"", + ""decimalValue"": 0.0, + ""shortValue"": 0, + ""longValue"": 0, + ""sbyteValue"": 0, + ""floatValue"": 0.0, + ""ushortValue"": 0, + ""uintValue"": 0, + ""ulongValue"": 0 +}", json); + + ISerializableTestObject o2 = JsonConvert.DeserializeObject(json); + Assert.AreEqual("String!", o2._stringValue); + Assert.AreEqual(int.MinValue, o2._intValue); + Assert.AreEqual(dateTimeOffset, o2._dateTimeOffsetValue); + Assert.AreEqual("Name!", o2._personValue.Name); + Assert.AreEqual(null, o2._nullPersonValue); + Assert.AreEqual(null, o2._nullableInt); + } +#endif + + public class KVPair + { + public TKey Key { get; set; } + public TValue Value { get; set; } + + public KVPair(TKey k, TValue v) + { + Key = k; + Value = v; + } + } + + [Test] + public void DeserializeUsingNonDefaultConstructorWithLeftOverValues() + { + List> kvPairs = + JsonConvert.DeserializeObject>>( + "[{\"Key\":\"Two\",\"Value\":\"2\"},{\"Key\":\"One\",\"Value\":\"1\"}]"); + + Assert.AreEqual(2, kvPairs.Count); + Assert.AreEqual("Two", kvPairs[0].Key); + Assert.AreEqual("2", kvPairs[0].Value); + Assert.AreEqual("One", kvPairs[1].Key); + Assert.AreEqual("1", kvPairs[1].Value); + } + + [Test] + public void SerializeClassWithInheritedProtectedMember() + { + AA myA = new AA(2); + string json = JsonConvert.SerializeObject(myA, Formatting.Indented); + Assert.AreEqual(@"{ + ""AA_field1"": 2, + ""AA_property1"": 2, + ""AA_property2"": 2, + ""AA_property3"": 2, + ""AA_property4"": 2 +}", json); + + BB myB = new BB(3, 4); + json = JsonConvert.SerializeObject(myB, Formatting.Indented); + Assert.AreEqual(@"{ + ""BB_field1"": 4, + ""BB_field2"": 4, + ""AA_field1"": 3, + ""BB_property1"": 4, + ""BB_property2"": 4, + ""BB_property3"": 4, + ""BB_property4"": 4, + ""BB_property5"": 4, + ""BB_property7"": 4, + ""AA_property1"": 3, + ""AA_property2"": 3, + ""AA_property3"": 3, + ""AA_property4"": 3 +}", json); + } + + [Test] + public void DeserializeClassWithInheritedProtectedMember() + { + AA myA = JsonConvert.DeserializeObject( + @"{ + ""AA_field1"": 2, + ""AA_field2"": 2, + ""AA_property1"": 2, + ""AA_property2"": 2, + ""AA_property3"": 2, + ""AA_property4"": 2, + ""AA_property5"": 2, + ""AA_property6"": 2 +}"); + + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetField("AA_field1", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(AA).GetField("AA_field2", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property1", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property2", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property3", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property4", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property5", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property6", BindingFlags.Instance | BindingFlags.NonPublic), myA)); + + BB myB = JsonConvert.DeserializeObject( + @"{ + ""BB_field1"": 4, + ""BB_field2"": 4, + ""AA_field1"": 3, + ""AA_field2"": 3, + ""AA_property1"": 2, + ""AA_property2"": 2, + ""AA_property3"": 2, + ""AA_property4"": 2, + ""AA_property5"": 2, + ""AA_property6"": 2, + ""BB_property1"": 3, + ""BB_property2"": 3, + ""BB_property3"": 3, + ""BB_property4"": 3, + ""BB_property5"": 3, + ""BB_property6"": 3, + ""BB_property7"": 3, + ""BB_property8"": 3 +}"); + + Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(AA).GetField("AA_field1", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(AA).GetField("AA_field2", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property1", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property2", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property3", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(2, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property4", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property5", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(0, ReflectionUtils.GetMemberValue(typeof(AA).GetProperty("AA_property6", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + + Assert.AreEqual(4, myB.BB_field1); + Assert.AreEqual(4, myB.BB_field2); + Assert.AreEqual(3, myB.BB_property1); + Assert.AreEqual(3, myB.BB_property2); + Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(BB).GetProperty("BB_property3", BindingFlags.Instance | BindingFlags.Public), myB)); + Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(BB).GetProperty("BB_property4", BindingFlags.Instance | BindingFlags.NonPublic), myB)); + Assert.AreEqual(0, myB.BB_property5); + Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(BB).GetProperty("BB_property6", BindingFlags.Instance | BindingFlags.Public), myB)); + Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(BB).GetProperty("BB_property7", BindingFlags.Instance | BindingFlags.Public), myB)); + Assert.AreEqual(3, ReflectionUtils.GetMemberValue(typeof(BB).GetProperty("BB_property8", BindingFlags.Instance | BindingFlags.Public), myB)); + } + + public class AA + { + [JsonProperty] + protected int AA_field1; + protected int AA_field2; + [JsonProperty] + protected int AA_property1 { get; set; } + [JsonProperty] + protected int AA_property2 { get; private set; } + [JsonProperty] + protected int AA_property3 { private get; set; } + [JsonProperty] + private int AA_property4 { get; set; } + protected int AA_property5 { get; private set; } + protected int AA_property6 { private get; set; } + + public AA() + { + } + + public AA(int f) + { + AA_field1 = f; + AA_field2 = f; + AA_property1 = f; + AA_property2 = f; + AA_property3 = f; + AA_property4 = f; + AA_property5 = f; + AA_property6 = f; + } + } + + public class BB : AA + { + [JsonProperty] + public int BB_field1; + public int BB_field2; + [JsonProperty] + public int BB_property1 { get; set; } + [JsonProperty] + public int BB_property2 { get; private set; } + [JsonProperty] + public int BB_property3 { private get; set; } + [JsonProperty] + private int BB_property4 { get; set; } + public int BB_property5 { get; private set; } + public int BB_property6 { private get; set; } + [JsonProperty] + public int BB_property7 { protected get; set; } + public int BB_property8 { protected get; set; } + + public BB() + { + } + + public BB(int f, int g) + : base(f) + { + BB_field1 = g; + BB_field2 = g; + BB_property1 = g; + BB_property2 = g; + BB_property3 = g; + BB_property4 = g; + BB_property5 = g; + BB_property6 = g; + BB_property7 = g; + BB_property8 = g; + } + } + +#if !NET20 && !SILVERLIGHT + public class XNodeTestObject + { + public XDocument Document { get; set; } + public XElement Element { get; set; } + } +#endif + +#if !SILVERLIGHT + public class XmlNodeTestObject + { + public XmlDocument Document { get; set; } + } +#endif + +#if !NET20 && !SILVERLIGHT + [Test] + public void SerializeDeserializeXNodeProperties() + { + XNodeTestObject testObject = new XNodeTestObject(); + testObject.Document = XDocument.Parse("hehe, root"); + testObject.Element = XElement.Parse(@"element"); + + string json = JsonConvert.SerializeObject(testObject, Formatting.Indented); + string expected = @"{ + ""Document"": { + ""root"": ""hehe, root"" + }, + ""Element"": { + ""fifth"": { + ""@xmlns:json"": ""http://json.org"", + ""@json:Awesome"": ""true"", + ""#text"": ""element"" + } + } +}"; + Assert.AreEqual(expected, json); + + XNodeTestObject newTestObject = JsonConvert.DeserializeObject(json); + Assert.AreEqual(testObject.Document.ToString(), newTestObject.Document.ToString()); + Assert.AreEqual(testObject.Element.ToString(), newTestObject.Element.ToString()); + + Assert.IsNull(newTestObject.Element.Parent); + } +#endif + +#if !SILVERLIGHT + [Test] + public void SerializeDeserializeXmlNodeProperties() + { + XmlNodeTestObject testObject = new XmlNodeTestObject(); + XmlDocument document = new XmlDocument(); + document.LoadXml("hehe, root"); + testObject.Document = document; + + string json = JsonConvert.SerializeObject(testObject, Formatting.Indented); + string expected = @"{ + ""Document"": { + ""root"": ""hehe, root"" + } +}"; + Assert.AreEqual(expected, json); + + XmlNodeTestObject newTestObject = JsonConvert.DeserializeObject(json); + Assert.AreEqual(testObject.Document.InnerXml, newTestObject.Document.InnerXml); + } +#endif + + [Test] + public void FullClientMapSerialization() + { + ClientMap source = new ClientMap() + { + position = new Pos() { X = 100, Y = 200 }, + center = new PosDouble() { X = 251.6, Y = 361.3 } + }; + + string json = JsonConvert.SerializeObject(source, new PosConverter(), new PosDoubleConverter()); + Assert.AreEqual("{\"position\":new Pos(100,200),\"center\":new PosD(251.6,361.3)}", json); + } + + public class ClientMap + { + public Pos position { get; set; } + public PosDouble center { get; set; } + } + + public class Pos + { + public int X { get; set; } + public int Y { get; set; } + } + + public class PosDouble + { + public double X { get; set; } + public double Y { get; set; } + } + + public class PosConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + Pos p = (Pos)value; + + if (p != null) + writer.WriteRawValue(String.Format("new Pos({0},{1})", p.X, p.Y)); + else + writer.WriteNull(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override bool CanConvert(Type objectType) + { + return objectType.IsAssignableFrom(typeof(Pos)); + } + } + + public class PosDoubleConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + PosDouble p = (PosDouble)value; + + if (p != null) + writer.WriteRawValue(String.Format(CultureInfo.InvariantCulture, "new PosD({0},{1})", p.X, p.Y)); + else + writer.WriteNull(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + throw new NotImplementedException(); + } + + public override bool CanConvert(Type objectType) + { + return objectType.IsAssignableFrom(typeof(PosDouble)); + } + } + + [Test] + public void TestEscapeDictionaryStrings() + { + const string s = @"host\user"; + string serialized = JsonConvert.SerializeObject(s); + Assert.AreEqual(@"""host\\user""", serialized); + + Dictionary d1 = new Dictionary(); + d1.Add(5, s); + Assert.AreEqual(@"{""5"":""host\\user""}", JsonConvert.SerializeObject(d1)); + + Dictionary d2 = new Dictionary(); + d2.Add(s, 5); + Assert.AreEqual(@"{""host\\user"":5}", JsonConvert.SerializeObject(d2)); + } + + public class GenericListTestClass + { + public List GenericList { get; set; } + + public GenericListTestClass() + { + GenericList = new List(); + } + } + + [Test] + public void DeserializeExistingGenericList() + { + GenericListTestClass c = new GenericListTestClass(); + c.GenericList.Add("1"); + c.GenericList.Add("2"); + + string json = JsonConvert.SerializeObject(c, Formatting.Indented); + + GenericListTestClass newValue = JsonConvert.DeserializeObject(json); + Assert.AreEqual(2, newValue.GenericList.Count); + Assert.AreEqual(typeof(List), newValue.GenericList.GetType()); + } + + [Test] + public void DeserializeSimpleKeyValuePair() + { + List> list = new List>(); + list.Add(new KeyValuePair("key1", "value1")); + list.Add(new KeyValuePair("key2", "value2")); + + string json = JsonConvert.SerializeObject(list); + + Assert.AreEqual(@"[{""Key"":""key1"",""Value"":""value1""},{""Key"":""key2"",""Value"":""value2""}]", json); + + List> result = JsonConvert.DeserializeObject>>(json); + Assert.AreEqual(2, result.Count); + Assert.AreEqual("key1", result[0].Key); + Assert.AreEqual("value1", result[0].Value); + Assert.AreEqual("key2", result[1].Key); + Assert.AreEqual("value2", result[1].Value); + } + + [Test] + public void DeserializeComplexKeyValuePair() + { + DateTime dateTime = new DateTime(2000, 12, 1, 23, 1, 1, DateTimeKind.Utc); + + List> list = new List>(); + list.Add(new KeyValuePair("key1", new WagePerson + { + BirthDate = dateTime, + Department = "Department1", + LastModified = dateTime, + HourlyWage = 1 + })); + list.Add(new KeyValuePair("key2", new WagePerson + { + BirthDate = dateTime, + Department = "Department2", + LastModified = dateTime, + HourlyWage = 2 + })); + + string json = JsonConvert.SerializeObject(list, Formatting.Indented); + + Assert.AreEqual(@"[ + { + ""Key"": ""key1"", + ""Value"": { + ""HourlyWage"": 1.0, + ""Name"": null, + ""BirthDate"": ""\/Date(975711661000)\/"", + ""LastModified"": ""\/Date(975711661000)\/"" + } + }, + { + ""Key"": ""key2"", + ""Value"": { + ""HourlyWage"": 2.0, + ""Name"": null, + ""BirthDate"": ""\/Date(975711661000)\/"", + ""LastModified"": ""\/Date(975711661000)\/"" + } + } +]", json); + + List> result = JsonConvert.DeserializeObject>>(json); + Assert.AreEqual(2, result.Count); + Assert.AreEqual("key1", result[0].Key); + Assert.AreEqual(1, result[0].Value.HourlyWage); + Assert.AreEqual("key2", result[1].Key); + Assert.AreEqual(2, result[1].Value.HourlyWage); + } + + public class StringListAppenderConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + List existingStrings = (List)existingValue; + List newStrings = new List(existingStrings); + + reader.Read(); + + while (reader.TokenType != JsonToken.EndArray) + { + string s = (string)reader.Value; + newStrings.Add(s); + + reader.Read(); + } + + return newStrings; + } + + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(List)); + } + } + + [Test] + public void StringListAppenderConverterTest() + { + Movie p = new Movie(); + p.ReleaseCountries = new List { "Existing" }; + + JsonConvert.PopulateObject("{'ReleaseCountries':['Appended']}", p, new JsonSerializerSettings + { + Converters = new List { new StringListAppenderConverter() } + }); + + Assert.AreEqual(2, p.ReleaseCountries.Count); + Assert.AreEqual("Existing", p.ReleaseCountries[0]); + Assert.AreEqual("Appended", p.ReleaseCountries[1]); + } + + public class StringAppenderConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + writer.WriteValue(value); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + string existingString = (string)existingValue; + string newString = existingString + (string)reader.Value; + + return newString; + } + + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(string)); + } + } + + [Test] + public void StringAppenderConverterTest() + { + Movie p = new Movie(); + p.Name = "Existing,"; + + JsonConvert.PopulateObject("{'Name':'Appended'}", p, new JsonSerializerSettings + { + Converters = new List { new StringAppenderConverter() } + }); + + Assert.AreEqual(p.Name, "Existing,Appended"); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Additional content found in JSON reference object. A JSON reference object should only have a $ref property.")] + public void SerializeRefAdditionalContent() + { + //Additional text found in JSON string after finishing deserializing object. + //Test 1 + var reference = new Dictionary(); + reference.Add("$ref", "Persons"); + reference.Add("$id", 1); + + var child = new Dictionary(); + child.Add("_id", 2); + child.Add("Name", "Isabell"); + child.Add("Father", reference); + + var json = JsonConvert.SerializeObject(child); + JsonConvert.DeserializeObject>(json); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "JSON reference $ref property must have a string value.")] + public void SerializeRefBadType() + { + //Additional text found in JSON string after finishing deserializing object. + //Test 1 + var reference = new Dictionary(); + reference.Add("$ref", 1); + reference.Add("$id", 1); + + var child = new Dictionary(); + child.Add("_id", 2); + child.Add("Name", "Isabell"); + child.Add("Father", reference); + + var json = JsonConvert.SerializeObject(child); + JsonConvert.DeserializeObject>(json); + } + + public class ConstructorCompexIgnoredProperty + { + [JsonIgnore] + public Product Ignored { get; set; } + public string First { get; set; } + public int Second { get; set; } + + public ConstructorCompexIgnoredProperty(string first, int second) + { + First = first; + Second = second; + } + } + + [Test] + public void DeserializeIgnoredPropertyInConstructor() + { + string json = @"{""First"":""First"",""Second"":2,""Ignored"":{""Name"":""James""},""AdditionalContent"":{""LOL"":true}}"; + + ConstructorCompexIgnoredProperty cc = JsonConvert.DeserializeObject(json); + Assert.AreEqual("First", cc.First); + Assert.AreEqual(2, cc.Second); + Assert.AreEqual(null, cc.Ignored); + } + + public class ShouldSerializeTestClass + { + internal bool _shouldSerializeName; + + public string Name { get; set; } + public int Age { get; set; } + + public void ShouldSerializeAge() + { + // dummy. should never be used because it doesn't return bool + } + + public bool ShouldSerializeName() + { + return _shouldSerializeName; + } + } + + [Test] + public void ShouldSerializeTest() + { + ShouldSerializeTestClass c = new ShouldSerializeTestClass(); + c.Name = "James"; + c.Age = 27; + + string json = JsonConvert.SerializeObject(c, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Age"": 27 +}", json); + + c._shouldSerializeName = true; + json = JsonConvert.SerializeObject(c, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Name"": ""James"", + ""Age"": 27 +}", json); + + ShouldSerializeTestClass deserialized = JsonConvert.DeserializeObject(json); + Assert.AreEqual("James", deserialized.Name); + Assert.AreEqual(27, deserialized.Age); + } + + public class Employee + { + public string Name { get; set; } + public Employee Manager { get; set; } + + public bool ShouldSerializeManager() + { + return (Manager != this); + } + } + + [Test] + public void ShouldSerializeExample() + { + Employee joe = new Employee(); + joe.Name = "Joe Employee"; + Employee mike = new Employee(); + mike.Name = "Mike Manager"; + + joe.Manager = mike; + mike.Manager = mike; + + string json = JsonConvert.SerializeObject(new[] { joe, mike }, Formatting.Indented); + // [ + // { + // "Name": "Joe Employee", + // "Manager": { + // "Name": "Mike Manager" + // } + // }, + // { + // "Name": "Mike Manager" + // } + // ] + + Console.WriteLine(json); + } + + public class SpecifiedTestClass + { + private bool _nameSpecified; + + public string Name { get; set; } + public int Age { get; set; } + public int Weight { get; set; } + public int Height { get; set; } + + // dummy. should never be used because it isn't of type bool + [JsonIgnore] + public long AgeSpecified { get; set; } + + [JsonIgnore] + public bool NameSpecified + { + get { return _nameSpecified; } + set { _nameSpecified = value; } + } + + [JsonIgnore] + public bool WeightSpecified; + + [JsonIgnore] + [System.Xml.Serialization.XmlIgnoreAttribute] + public bool HeightSpecified; + } + + [Test] + public void SpecifiedTest() + { + SpecifiedTestClass c = new SpecifiedTestClass(); + c.Name = "James"; + c.Age = 27; + c.NameSpecified = false; + + string json = JsonConvert.SerializeObject(c, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Age"": 27 +}", json); + + SpecifiedTestClass deserialized = JsonConvert.DeserializeObject(json); + Assert.IsNull(deserialized.Name); + Assert.IsFalse(deserialized.NameSpecified); + Assert.IsFalse(deserialized.WeightSpecified); + Assert.IsFalse(deserialized.HeightSpecified); + Assert.AreEqual(27, deserialized.Age); + + c.NameSpecified = true; + c.WeightSpecified = true; + c.HeightSpecified = true; + json = JsonConvert.SerializeObject(c, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Name"": ""James"", + ""Age"": 27, + ""Weight"": 0, + ""Height"": 0 +}", json); + + deserialized = JsonConvert.DeserializeObject(json); + Assert.AreEqual("James", deserialized.Name); + Assert.IsTrue(deserialized.NameSpecified); + Assert.IsTrue(deserialized.WeightSpecified); + Assert.IsTrue(deserialized.HeightSpecified); + Assert.AreEqual(27, deserialized.Age); + } + + // [Test] + // public void XmlSerializerSpecifiedTrueTest() + // { + // XmlSerializer s = new XmlSerializer(typeof(OptionalOrder)); + + // StringWriter sw = new StringWriter(); + // s.Serialize(sw, new OptionalOrder() { FirstOrder = "First", FirstOrderSpecified = true }); + + // Console.WriteLine(sw.ToString()); + + // string xml = @" + // + // First + //"; + + // OptionalOrder o = (OptionalOrder)s.Deserialize(new StringReader(xml)); + // Console.WriteLine(o.FirstOrder); + // Console.WriteLine(o.FirstOrderSpecified); + // } + + // [Test] + // public void XmlSerializerSpecifiedFalseTest() + // { + // XmlSerializer s = new XmlSerializer(typeof(OptionalOrder)); + + // StringWriter sw = new StringWriter(); + // s.Serialize(sw, new OptionalOrder() { FirstOrder = "First", FirstOrderSpecified = false }); + + // Console.WriteLine(sw.ToString()); + + // // string xml = @" + // // + // // First + // //"; + + // // OptionalOrder o = (OptionalOrder)s.Deserialize(new StringReader(xml)); + // // Console.WriteLine(o.FirstOrder); + // // Console.WriteLine(o.FirstOrderSpecified); + // } + + public class OptionalOrder + { + // This field shouldn't be serialized + // if it is uninitialized. + public string FirstOrder; + // Use the XmlIgnoreAttribute to ignore the + // special field named "FirstOrderSpecified". + [System.Xml.Serialization.XmlIgnoreAttribute] + public bool FirstOrderSpecified; + } + + public class FamilyDetails + { + public string Name { get; set; } + public int NumberOfChildren { get; set; } + + [JsonIgnore] + public bool NumberOfChildrenSpecified { get; set; } + } + + [Test] + public void SpecifiedExample() + { + FamilyDetails joe = new FamilyDetails(); + joe.Name = "Joe Family Details"; + joe.NumberOfChildren = 4; + joe.NumberOfChildrenSpecified = true; + + FamilyDetails martha = new FamilyDetails(); + martha.Name = "Martha Family Details"; + martha.NumberOfChildren = 3; + martha.NumberOfChildrenSpecified = false; + + string json = JsonConvert.SerializeObject(new[] { joe, martha }, Formatting.Indented); + //[ + // { + // "Name": "Joe Family Details", + // "NumberOfChildren": 4 + // }, + // { + // "Name": "Martha Family Details" + // } + //] + Console.WriteLine(json); + + string mikeString = "{\"Name\": \"Mike Person\"}"; + FamilyDetails mike = JsonConvert.DeserializeObject(mikeString); + + Console.WriteLine("mikeString specifies number of children: {0}", mike.NumberOfChildrenSpecified); + + string mikeFullDisclosureString = "{\"Name\": \"Mike Person\", \"NumberOfChildren\": \"0\"}"; + mike = JsonConvert.DeserializeObject(mikeFullDisclosureString); + + Console.WriteLine("mikeString specifies number of children: {0}", mike.NumberOfChildrenSpecified); + } + + public class DictionaryKey + { + public string Value { get; set; } + + public override string ToString() + { + return Value; + } + + public static implicit operator DictionaryKey(string value) + { + return new DictionaryKey() { Value = value }; + } + } + + [Test] + public void SerializeDeserializeDictionaryKey() + { + Dictionary dictionary = new Dictionary(); + + dictionary.Add(new DictionaryKey() { Value = "First!" }, "First"); + dictionary.Add(new DictionaryKey() { Value = "Second!" }, "Second"); + + string json = JsonConvert.SerializeObject(dictionary, Formatting.Indented); + + Assert.AreEqual(@"{ + ""First!"": ""First"", + ""Second!"": ""Second"" +}", json); + + Dictionary newDictionary = + JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(2, newDictionary.Count); + } + + [Test] + public void SerializeNullableArray() + { + string jsonText = JsonConvert.SerializeObject(new double?[] { 2.4, 4.3, null }, Formatting.Indented); + + Assert.AreEqual(@"[ + 2.4, + 4.3, + null +]", jsonText); + + double?[] d = (double?[])JsonConvert.DeserializeObject(jsonText, typeof(double?[])); + + Assert.AreEqual(3, d.Length); + Assert.AreEqual(2.4, d[0]); + Assert.AreEqual(4.3, d[1]); + Assert.AreEqual(null, d[2]); + } + +#if !SILVERLIGHT && !NET20 && !PocketPC + [Test] + public void SerializeHashSet() + { + string jsonText = JsonConvert.SerializeObject(new HashSet() + { + "One", + "2", + "III" + }, Formatting.Indented); + + Assert.AreEqual(@"[ + ""One"", + ""2"", + ""III"" +]", jsonText); + + HashSet d = JsonConvert.DeserializeObject>(jsonText); + + Assert.AreEqual(3, d.Count); + Assert.IsTrue(d.Contains("One")); + Assert.IsTrue(d.Contains("2")); + Assert.IsTrue(d.Contains("III")); + } +#endif + + private class MyClass + { + public byte[] Prop1 { get; set; } + + public MyClass() + { + Prop1 = new byte[0]; + } + } + + [Test] + public void DeserializeByteArray() + { + JsonSerializer serializer1 = new JsonSerializer(); + serializer1.Converters.Add(new IsoDateTimeConverter()); + serializer1.NullValueHandling = NullValueHandling.Ignore; + + string json = @"[{""Prop1"":""""},{""Prop1"":""""}]"; + + JsonTextReader reader = new JsonTextReader(new StringReader(json)); + + MyClass[] z = (MyClass[])serializer1.Deserialize(reader, typeof(MyClass[])); + Assert.AreEqual(2, z.Length); + Assert.AreEqual(0, z[0].Prop1.Length); + Assert.AreEqual(0, z[1].Prop1.Length); + } + +#if !NET20 && !PocketPC && !SILVERLIGHT + public class StringDictionaryTestClass + { + public StringDictionary StringDictionaryProperty { get; set; } + } + + [Test] + [ExpectedException(typeof(Exception), ExpectedMessage = "Cannot create and populate list type System.Collections.Specialized.StringDictionary.")] + public void StringDictionaryTest() + { + StringDictionaryTestClass s1 = new StringDictionaryTestClass() + { + StringDictionaryProperty = new StringDictionary() + { + {"1", "One"}, + {"2", "II"}, + {"3", "3"} + } + }; + + string json = JsonConvert.SerializeObject(s1, Formatting.Indented); + + JsonConvert.DeserializeObject(json); + } +#endif + + [JsonObject(MemberSerialization.OptIn)] + public struct StructWithAttribute + { + public string MyString { get; set; } + [JsonProperty] + public int MyInt { get; set; } + } + + [Test] + public void SerializeStructWithJsonObjectAttribute() + { + StructWithAttribute testStruct = new StructWithAttribute + { + MyInt = int.MaxValue + }; + + string json = JsonConvert.SerializeObject(testStruct, Formatting.Indented); + + Assert.AreEqual(@"{ + ""MyInt"": 2147483647 +}", json); + + StructWithAttribute newStruct = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(int.MaxValue, newStruct.MyInt); + } + + public class TimeZoneOffsetObject + { + public DateTimeOffset Offset { get; set; } + } + +#if !NET20 + [Test] + public void ReadWriteTimeZoneOffset() + { + var serializeObject = JsonConvert.SerializeObject(new TimeZoneOffsetObject + { + Offset = new DateTimeOffset(new DateTime(2000, 1, 1), TimeSpan.FromHours(6)) + }); + + Assert.AreEqual("{\"Offset\":\"\\/Date(946663200000+0600)\\/\"}", serializeObject); + var deserializeObject = JsonConvert.DeserializeObject(serializeObject); + Assert.AreEqual(TimeSpan.FromHours(6), deserializeObject.Offset.Offset); + Assert.AreEqual(new DateTime(2000, 1, 1), deserializeObject.Offset.Date); + } + + [Test] + public void DeserializePropertyNullableDateTimeOffsetExact() + { + NullableDateTimeTestClass d = JsonConvert.DeserializeObject("{\"DateTimeOffsetField\":\"\\/Date(946663200000+0600)\\/\"}"); + Assert.AreEqual(new DateTimeOffset(new DateTime(2000, 1, 1), TimeSpan.FromHours(6)), d.DateTimeOffsetField); + } +#endif + + public abstract class LogEvent + { + [JsonProperty("event")] + public abstract string EventName { get; } + } + + public class DerivedEvent : LogEvent + { + public override string EventName { get { return "derived"; } } + } + + [Test] + public void OverridenPropertyMembers() + { + string json = JsonConvert.SerializeObject(new DerivedEvent(), Formatting.Indented); + + Assert.AreEqual(@"{ + ""event"": ""derived"" +}", json); + } + +#if !(NET35 || NET20 || WINDOWS_PHONE) + [Test] + public void SerializeExpandoObject() + { + dynamic expando = new ExpandoObject(); + expando.Int = 1; + expando.Decimal = 99.9d; + expando.Complex = new ExpandoObject(); + expando.Complex.String = "I am a string"; + expando.Complex.DateTime = new DateTime(2000, 12, 20, 18, 55, 0, DateTimeKind.Utc); + + string json = JsonConvert.SerializeObject(expando, Formatting.Indented); + Assert.AreEqual(@"{ + ""Int"": 1, + ""Decimal"": 99.9, + ""Complex"": { + ""String"": ""I am a string"", + ""DateTime"": ""\/Date(977338500000)\/"" + } +}", json); + + IDictionary newExpando = JsonConvert.DeserializeObject(json); + + Assert.IsInstanceOfType(typeof(long), newExpando["Int"]); + Assert.AreEqual(expando.Int, newExpando["Int"]); + + Assert.IsInstanceOfType(typeof(double), newExpando["Decimal"]); + Assert.AreEqual(expando.Decimal, newExpando["Decimal"]); + + Assert.IsInstanceOfType(typeof(ExpandoObject), newExpando["Complex"]); + IDictionary o = (ExpandoObject)newExpando["Complex"]; + + Assert.IsInstanceOfType(typeof(string), o["String"]); + Assert.AreEqual(expando.Complex.String, o["String"]); + + Assert.IsInstanceOfType(typeof(DateTime), o["DateTime"]); + Assert.AreEqual(expando.Complex.DateTime, o["DateTime"]); + } +#endif + + [Test] + public void DeserializeDecimalExact() + { + decimal d = JsonConvert.DeserializeObject("123456789876543.21"); + Assert.AreEqual(123456789876543.21m, d); + } + + [Test] + public void DeserializeNullableDecimalExact() + { + decimal? d = JsonConvert.DeserializeObject("123456789876543.21"); + Assert.AreEqual(123456789876543.21m, d); + } + + [Test] + public void DeserializeDecimalPropertyExact() + { + string json = "{Amount:123456789876543.21}"; + Invoice i = JsonConvert.DeserializeObject(json); + Assert.AreEqual(123456789876543.21m, i.Amount); + } + + [Test] + public void DeserializeDecimalArrayExact() + { + string json = "[123456789876543.21]"; + IList a = JsonConvert.DeserializeObject>(json); + Assert.AreEqual(123456789876543.21m, a[0]); + } + + [Test] + public void DeserializeDecimalDictionaryExact() + { + string json = "{'Value':123456789876543.21}"; + IDictionary d = JsonConvert.DeserializeObject>(json); + Assert.AreEqual(123456789876543.21m, d["Value"]); + } + + public struct Vector + { + public float X; + public float Y; + public float Z; + + public override string ToString() + { + return string.Format("({0},{1},{2})", X, Y, Z); + } + } + + public class VectorParent + { + public Vector Position; + } + + [Test] + public void DeserializeStructProperty() + { + VectorParent obj = new VectorParent(); + obj.Position = new Vector { X = 1, Y = 2, Z = 3 }; + + string str = JsonConvert.SerializeObject(obj); + + obj = JsonConvert.DeserializeObject(str); + + Assert.AreEqual(1, obj.Position.X); + Assert.AreEqual(2, obj.Position.Y); + Assert.AreEqual(3, obj.Position.Z); + } + + [JsonObject(MemberSerialization.OptIn)] + public class Derived : Base + { + [JsonProperty] + public string IDoWork { get; private set; } + + private Derived() { } + + internal Derived(string dontWork, string doWork) + : base(dontWork) + { + IDoWork = doWork; + } + } + + [JsonObject(MemberSerialization.OptIn)] + public class Base + { + [JsonProperty] + public string IDontWork { get; private set; } + + protected Base() { } + + internal Base(string dontWork) + { + IDontWork = dontWork; + } + } + + [Test] + public void PrivateSetterOnBaseClassProperty() + { + var derived = new Derived("meh", "woo"); + + var settings = new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects, + ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor + }; + + string json = JsonConvert.SerializeObject(derived, Formatting.Indented, settings); + + var meh = JsonConvert.DeserializeObject(json, settings); + + Assert.AreEqual(((Derived)meh).IDoWork, "woo"); + Assert.AreEqual(meh.IDontWork, "meh"); + } + +#if !(SILVERLIGHT || PocketPC || NET20) + [DataContract] + public struct StructISerializable : ISerializable + { + private string _name; + + public StructISerializable(SerializationInfo info, StreamingContext context) + { + _name = info.GetString("Name"); + } + + [DataMember] + public string Name + { + get { return _name; } + set { _name = value; } + } + + public void GetObjectData(SerializationInfo info, StreamingContext context) + { + info.AddValue("Name", _name); + } + } + + [DataContract] + public class NullableStructPropertyClass + { + private StructISerializable _foo1; + private StructISerializable? _foo2; + + [DataMember] + public StructISerializable Foo1 + { + get { return _foo1; } + set { _foo1 = value; } + } + + [DataMember] + public StructISerializable? Foo2 + { + get { return _foo2; } + set { _foo2 = value; } + } + } + + [Test] + public void DeserializeNullableStruct() + { + NullableStructPropertyClass nullableStructPropertyClass = new NullableStructPropertyClass(); + nullableStructPropertyClass.Foo1 = new StructISerializable() { Name = "foo 1" }; + nullableStructPropertyClass.Foo2 = new StructISerializable() { Name = "foo 2" }; + + NullableStructPropertyClass barWithNull = new NullableStructPropertyClass(); + barWithNull.Foo1 = new StructISerializable() { Name = "foo 1" }; + barWithNull.Foo2 = null; + + //throws error on deserialization because bar1.Foo2 is of type Foo? + string s = JsonConvert.SerializeObject(nullableStructPropertyClass); + NullableStructPropertyClass deserialized = deserialize(s); + Assert.AreEqual(deserialized.Foo1.Name, "foo 1"); + Assert.AreEqual(deserialized.Foo2.Value.Name, "foo 2"); + + //no error Foo2 is null + s = JsonConvert.SerializeObject(barWithNull); + deserialized = deserialize(s); + Assert.AreEqual(deserialized.Foo1.Name, "foo 1"); + Assert.AreEqual(deserialized.Foo2, null); + } + + + static NullableStructPropertyClass deserialize(string serStr) + { + return JsonConvert.DeserializeObject( + serStr, + new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + MissingMemberHandling = MissingMemberHandling.Ignore + }); + } +#endif + + public class Response + { + public string Name { get; set; } + public JToken Data { get; set; } + } + + [Test] + public void DeserializeJToken() + { + Response response = new Response + { + Name = "Success", + Data = new JObject(new JProperty("First", "Value1"), new JProperty("Second", "Value2")) + }; + + string json = JsonConvert.SerializeObject(response, Formatting.Indented); + + Response deserializedResponse = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("Success", deserializedResponse.Name); + Assert.IsTrue(deserializedResponse.Data.DeepEquals(response.Data)); + } + + public abstract class Test + { + public abstract T Value { get; set; } + } + + [JsonObject(MemberSerialization.OptIn)] + public class DecimalTest : Test + { + protected DecimalTest() { } + public DecimalTest(decimal val) + { + Value = val; + } + + [JsonProperty] + public override decimal Value { get; set; } + } + + [Test] + public void OnError() + { + var data = new DecimalTest(decimal.MinValue); + var json = JsonConvert.SerializeObject(data); + var obj = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(decimal.MinValue, obj.Value); + } + + public class NonPublicConstructorWithJsonConstructor + { + public string Value { get; private set; } + public string Constructor { get; private set; } + + [JsonConstructor] + private NonPublicConstructorWithJsonConstructor() + { + Constructor = "NonPublic"; + } + + public NonPublicConstructorWithJsonConstructor(string value) + { + Value = value; + Constructor = "Public Paramatized"; + } + } + + [Test] + public void NonPublicConstructorWithJsonConstructorTest() + { + NonPublicConstructorWithJsonConstructor c = JsonConvert.DeserializeObject("{}"); + Assert.AreEqual("NonPublic", c.Constructor); + } + + public class PublicConstructorOverridenByJsonConstructor + { + public string Value { get; private set; } + public string Constructor { get; private set; } + + public PublicConstructorOverridenByJsonConstructor() + { + Constructor = "NonPublic"; + } + + [JsonConstructor] + public PublicConstructorOverridenByJsonConstructor(string value) + { + Value = value; + Constructor = "Public Paramatized"; + } + } + + [Test] + public void PublicConstructorOverridenByJsonConstructorTest() + { + PublicConstructorOverridenByJsonConstructor c = JsonConvert.DeserializeObject("{Value:'value!'}"); + Assert.AreEqual("Public Paramatized", c.Constructor); + Assert.AreEqual("value!", c.Value); + } + + public class MultipleParamatrizedConstructorsJsonConstructor + { + public string Value { get; private set; } + public int Age { get; private set; } + public string Constructor { get; private set; } + + public MultipleParamatrizedConstructorsJsonConstructor(string value) + { + Value = value; + Constructor = "Public Paramatized 1"; + } + + [JsonConstructor] + public MultipleParamatrizedConstructorsJsonConstructor(string value, int age) + { + Value = value; + Age = age; + Constructor = "Public Paramatized 2"; + } + } + + [Test] + public void MultipleParamatrizedConstructorsJsonConstructorTest() + { + MultipleParamatrizedConstructorsJsonConstructor c = JsonConvert.DeserializeObject("{Value:'value!', Age:1}"); + Assert.AreEqual("Public Paramatized 2", c.Constructor); + Assert.AreEqual("value!", c.Value); + Assert.AreEqual(1, c.Age); + } + + public class EnumerableClass + { + public IEnumerable Enumerable { get; set; } + } + + [Test] + public void DeserializeEnumerable() + { + EnumerableClass c = new EnumerableClass + { + Enumerable = new List { "One", "Two", "Three" } + }; + + string json = JsonConvert.SerializeObject(c, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Enumerable"": [ + ""One"", + ""Two"", + ""Three"" + ] +}", json); + + EnumerableClass c2 = JsonConvert.DeserializeObject(json); + + Assert.AreEqual("One", c2.Enumerable.ElementAt(0)); + Assert.AreEqual("Two", c2.Enumerable.ElementAt(1)); + Assert.AreEqual("Three", c2.Enumerable.ElementAt(2)); + } + + [JsonObject(MemberSerialization.OptIn)] + public class ItemBase + { + [JsonProperty] + public string Name { get; set; } + } + + public class ComplexItem : ItemBase + { + public Stream Source { get; set; } + } + + [Test] + public void SerializeAttributesOnBase() + { + ComplexItem i = new ComplexItem(); + + string json = JsonConvert.SerializeObject(i, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Name"": null +}", json); + } + + public class DeserializeStringConvert + { + public string Name { get; set; } + public int Age { get; set; } + public double Height { get; set; } + public decimal Price { get; set; } + } + + [Test] + public void DeserializeStringEnglish() + { + string json = @"{ + 'Name': 'James Hughes', + 'Age': '40', + 'Height': '44.4', + 'Price': '4' +}"; + + DeserializeStringConvert p = JsonConvert.DeserializeObject(json); + Assert.AreEqual(40, p.Age); + Assert.AreEqual(44.4, p.Height); + Assert.AreEqual(4d, p.Price); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Error converting value {null} to type 'System.DateTime'.")] + public void DeserializeNullDateTimeValueTest() + { + JsonConvert.DeserializeObject("null", typeof(DateTime)); + } + + [Test] + public void DeserializeNullNullableDateTimeValueTest() + { + object dateTime = JsonConvert.DeserializeObject("null", typeof(DateTime?)); + + Assert.IsNull(dateTime); + } + + [Test] + public void MultiIndexSuperTest() + { + MultiIndexSuper e = new MultiIndexSuper(); + + string json = JsonConvert.SerializeObject(e, Formatting.Indented); + + Assert.AreEqual(@"{}", json); + } + + public class MultiIndexSuper : MultiIndexBase + { + + } + + public abstract class MultiIndexBase + { + protected internal object this[string propertyName] + { + get { return null; } + set { } + } + protected internal object this[object property] + { + get { return null; } + set { } + } + } + + public class CommentTestClass + { + public bool Indexed { get; set; } + public int StartYear { get; set; } + public IList Values { get; set; } + } + + [Test] + public void CommentTestClassTest() + { + string json = @"{""indexed"":true, ""startYear"":1939, ""values"": + [ 3000, /* 1940-1949 */ + 3000, 3600, 3600, 3600, 3600, 4200, 4200, 4200, 4200, 4800, /* 1950-1959 */ + 4800, 4800, 4800, 4800, 4800, 4800, 6600, 6600, 7800, 7800, /* 1960-1969 */ + 7800, 7800, 9000, 10800, 13200, 14100, 15300, 16500, 17700, 22900, /* 1970-1979 */ + 25900, 29700, 32400, 35700, 37800, 39600, 42000, 43800, 45000, 48000, /* 1980-1989 */ + 51300, 53400, 55500, 57600, 60600, 61200, 62700, 65400, 68400, 72600, /* 1990-1999 */ + 76200, 80400, 84900, 87000, 87900, 90000, 94200, 97500, 102000, 106800, /* 2000-2009 */ + 106800, 106800] /* 2010-2011 */ + }"; + + CommentTestClass commentTestClass = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(true, commentTestClass.Indexed); + Assert.AreEqual(1939, commentTestClass.StartYear); + Assert.AreEqual(63, commentTestClass.Values.Count); + } + + class DTOWithParameterisedConstructor + { + public DTOWithParameterisedConstructor(string A) + { + this.A = A; + B = 2; + } + + public string A { get; set; } + public int? B { get; set; } + } + + class DTOWithoutParameterisedConstructor + { + public DTOWithoutParameterisedConstructor() + { + B = 2; + } + + public string A { get; set; } + public int? B { get; set; } + } + + [Test] + public void PopulationBehaviourForOmittedPropertiesIsTheSameForParameterisedConstructorAsForDefaultConstructor() + { + string json = @"{A:""Test""}"; + + var withoutParameterisedConstructor = JsonConvert.DeserializeObject(json); + var withParameterisedConstructor = JsonConvert.DeserializeObject(json); + Assert.AreEqual(withoutParameterisedConstructor.B, withParameterisedConstructor.B); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/MissingMemberHandlingTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/MissingMemberHandlingTests.cs new file mode 100644 index 0000000..fcdef4c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/MissingMemberHandlingTests.cs @@ -0,0 +1,137 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.IO; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class MissingMemberHandlingTests : TestFixtureBase + { + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Could not find member 'Price' on object of type 'ProductShort'")] + public void MissingMemberDeserialize() + { + Product product = new Product(); + + product.Name = "Apple"; + product.ExpiryDate = new DateTime(2008, 12, 28); + product.Price = 3.99M; + product.Sizes = new string[] { "Small", "Medium", "Large" }; + + string output = JsonConvert.SerializeObject(product); + //{ + // "Name": "Apple", + // "ExpiryDate": new Date(1230422400000), + // "Price": 3.99, + // "Sizes": [ + // "Small", + // "Medium", + // "Large" + // ] + //} + + ProductShort deserializedProductShort = (ProductShort)JsonConvert.DeserializeObject(output, typeof(ProductShort), new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Error }); + } + + [Test] + public void MissingMemberDeserializeOkay() + { + Product product = new Product(); + + product.Name = "Apple"; + product.ExpiryDate = new DateTime(2008, 12, 28); + product.Price = 3.99M; + product.Sizes = new string[] { "Small", "Medium", "Large" }; + + string output = JsonConvert.SerializeObject(product); + //{ + // "Name": "Apple", + // "ExpiryDate": new Date(1230422400000), + // "Price": 3.99, + // "Sizes": [ + // "Small", + // "Medium", + // "Large" + // ] + //} + + JsonSerializer jsonSerializer = new JsonSerializer(); + jsonSerializer.MissingMemberHandling = MissingMemberHandling.Ignore; + + object deserializedValue; + + using (JsonReader jsonReader = new JsonTextReader(new StringReader(output))) + { + deserializedValue = jsonSerializer.Deserialize(jsonReader, typeof(ProductShort)); + } + + ProductShort deserializedProductShort = (ProductShort)deserializedValue; + + Assert.AreEqual("Apple", deserializedProductShort.Name); + Assert.AreEqual(new DateTime(2008, 12, 28), deserializedProductShort.ExpiryDate); + Assert.AreEqual("Small", deserializedProductShort.Sizes[0]); + Assert.AreEqual("Medium", deserializedProductShort.Sizes[1]); + Assert.AreEqual("Large", deserializedProductShort.Sizes[2]); + } + + [Test] + public void MissingMemberIgnoreComplexValue() + { + JsonSerializer serializer = new JsonSerializer { MissingMemberHandling = MissingMemberHandling.Ignore }; + serializer.Converters.Add(new JavaScriptDateTimeConverter()); + + string response = @"{""PreProperty"":1,""DateProperty"":new Date(1225962698973),""PostProperty"":2}"; + + MyClass myClass = (MyClass)serializer.Deserialize(new StringReader(response), typeof(MyClass)); + + Assert.AreEqual(1, myClass.PreProperty); + Assert.AreEqual(2, myClass.PostProperty); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Could not find member 'Missing' on object of type 'DoubleClass'")] + public void MissingMemeber() + { + string json = @"{""Missing"":1}"; + + JsonConvert.DeserializeObject(json, new JsonSerializerSettings { MissingMemberHandling = MissingMemberHandling.Error }); + } + + [Test] + public void MissingJson() + { + string json = @"{}"; + + JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + MissingMemberHandling = MissingMemberHandling.Error + }); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/NullValueHandlingTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/NullValueHandlingTests.cs new file mode 100644 index 0000000..93acb9f --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/NullValueHandlingTests.cs @@ -0,0 +1,121 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class NullValueHandlingTests : TestFixtureBase + { + [Test] + public void DeserializeNullIntoDateTime() + { + DateTimeTestClass c = JsonConvert.DeserializeObject(@"{DateTimeField:null}", new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + Assert.AreEqual(c.DateTimeField, default(DateTime)); + } + + [Test] + public void DeserializeEmptyStringIntoDateTimeWithEmptyStringDefaultValue() + { + DateTimeTestClass c = JsonConvert.DeserializeObject(@"{DateTimeField:""""}", new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + Assert.AreEqual(c.DateTimeField, default(DateTime)); + } + + [Test] + public void NullValueHandlingSerialization() + { + Store s1 = new Store(); + + JsonSerializer jsonSerializer = new JsonSerializer(); + jsonSerializer.NullValueHandling = NullValueHandling.Ignore; + + StringWriter sw = new StringWriter(); + jsonSerializer.Serialize(sw, s1); + + //JsonConvert.ConvertDateTimeToJavaScriptTicks(s1.Establised.DateTime) + + Assert.AreEqual(@"{""Color"":4,""Establised"":""\/Date(1264122061000)\/"",""Width"":1.1,""Employees"":999,""RoomsPerFloor"":[1,2,3,4,5,6,7,8,9],""Open"":false,""Symbol"":""@"",""Mottos"":[""Hello World"",""öäüÖÄÜ\\'{new Date(12345);}[222]_µ@²³~"",null,"" ""],""Cost"":100980.1,""Escape"":""\r\n\t\f\b?{\\r\\n\""'"",""product"":[{""Name"":""Rocket"",""ExpiryDate"":""\/Date(949532490000)\/"",""Price"":0.0},{""Name"":""Alien"",""ExpiryDate"":""\/Date(946684800000)\/"",""Price"":0.0}]}", sw.GetStringBuilder().ToString()); + + Store s2 = (Store)jsonSerializer.Deserialize(new JsonTextReader(new StringReader("{}")), typeof(Store)); + Assert.AreEqual("\r\n\t\f\b?{\\r\\n\"\'", s2.Escape); + + Store s3 = (Store)jsonSerializer.Deserialize(new JsonTextReader(new StringReader(@"{""Escape"":null}")), typeof(Store)); + Assert.AreEqual("\r\n\t\f\b?{\\r\\n\"\'", s3.Escape); + + Store s4 = (Store)jsonSerializer.Deserialize(new JsonTextReader(new StringReader(@"{""Color"":2,""Establised"":""\/Date(1264071600000+1300)\/"",""Width"":1.1,""Employees"":999,""RoomsPerFloor"":[1,2,3,4,5,6,7,8,9],""Open"":false,""Symbol"":""@"",""Mottos"":[""Hello World"",""öäüÖÄÜ\\'{new Date(12345);}[222]_µ@²³~"",null,"" ""],""Cost"":100980.1,""Escape"":""\r\n\t\f\b?{\\r\\n\""'"",""product"":[{""Name"":""Rocket"",""ExpiryDate"":""\/Date(949485690000+1300)\/"",""Price"":0},{""Name"":""Alien"",""ExpiryDate"":""\/Date(946638000000)\/"",""Price"":0.0}]}")), typeof(Store)); + Assert.AreEqual(s1.Establised, s3.Establised); + } + + [Test] + public void NullValueHandlingBlogPost() + { + Movie movie = new Movie(); + movie.Name = "Bad Boys III"; + movie.Description = "It's no Bad Boys"; + + string included = JsonConvert.SerializeObject(movie, + Formatting.Indented, + new JsonSerializerSettings { }); + + // { + // "Name": "Bad Boys III", + // "Description": "It's no Bad Boys", + // "Classification": null, + // "Studio": null, + // "ReleaseDate": null, + // "ReleaseCountries": null + // } + + string ignored = JsonConvert.SerializeObject(movie, + Formatting.Indented, + new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }); + + // { + // "Name": "Bad Boys III", + // "Description": "It's no Bad Boys" + // } + + Assert.AreEqual(@"{ + ""Name"": ""Bad Boys III"", + ""Description"": ""It's no Bad Boys"", + ""Classification"": null, + ""Studio"": null, + ""ReleaseDate"": null, + ""ReleaseCountries"": null +}", included); + + Assert.AreEqual(@"{ + ""Name"": ""Bad Boys III"", + ""Description"": ""It's no Bad Boys"" +}", ignored); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/PopulateTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/PopulateTests.cs new file mode 100644 index 0000000..8985f98 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/PopulateTests.cs @@ -0,0 +1,143 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class PopulateTests : TestFixtureBase + { + [Test] + public void PopulatePerson() + { + Person p = new Person(); + + JsonConvert.PopulateObject(@"{""Name"":""James""}", p); + + Assert.AreEqual("James", p.Name); + } + + [Test] + public void PopulateStore() + { + Store s = new Store(); + s.Color = StoreColor.Red; + s.product = new List + { + new Product + { + ExpiryDate = new DateTime(2000, 12, 3, 0, 0, 0, DateTimeKind.Utc), + Name = "ProductName!", + Price = 9.9m + } + }; + s.Width = 99.99d; + s.Mottos = new List { "Can do!", "We deliver!" }; + + string json = @"{ + ""Color"": 2, + ""Establised"": ""\/Date(1264122061000+0000)\/"", + ""Width"": 99.99, + ""Employees"": 999, + ""RoomsPerFloor"": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9 + ], + ""Open"": false, + ""Symbol"": ""@"", + ""Mottos"": [ + ""Fail whale"" + ], + ""Cost"": 100980.1, + ""Escape"": ""\r\n\t\f\b?{\\r\\n\""'"", + ""product"": [ + { + ""Name"": ""ProductName!"", + ""ExpiryDate"": ""\/Date(975801600000)\/"", + ""Price"": 9.9, + ""Sizes"": null + } + ] +}"; + + JsonConvert.PopulateObject(json, s, new JsonSerializerSettings + { + ObjectCreationHandling = ObjectCreationHandling.Replace + }); + + Assert.AreEqual(1, s.Mottos.Count); + Assert.AreEqual("Fail whale", s.Mottos[0]); + Assert.AreEqual(1, s.product.Count); + + //Assert.AreEqual("James", p.Name); + } + + [Test] + public void PopulateListOfPeople() + { + List p = new List(); + + JsonSerializer serializer = new JsonSerializer(); + serializer.Populate(new StringReader(@"[{""Name"":""James""},{""Name"":""Jim""}]"), p); + + Assert.AreEqual(2, p.Count); + Assert.AreEqual("James", p[0].Name); + Assert.AreEqual("Jim", p[1].Name); + } + + [Test] + public void PopulateDictionary() + { + Dictionary p = new Dictionary(); + + JsonSerializer serializer = new JsonSerializer(); + serializer.Populate(new StringReader(@"{""Name"":""James""}"), p); + + Assert.AreEqual(1, p.Count); + Assert.AreEqual("James", p["Name"]); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Unexpected initial token 'Integer' when populating object. Expected JSON object or array.")] + public void PopulateWithBadJson() + { + JsonConvert.PopulateObject("1", new Person()); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs new file mode 100644 index 0000000..1bbef89 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/PreserveReferencesHandlingTests.cs @@ -0,0 +1,863 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class PreserveReferencesHandlingTests : TestFixtureBase + { + [Test] + public void SerializeDictionarysWithPreserveObjectReferences() + { + CircularDictionary circularDictionary = new CircularDictionary(); + circularDictionary.Add("other", new CircularDictionary { { "blah", null } }); + circularDictionary.Add("self", circularDictionary); + + string json = JsonConvert.SerializeObject(circularDictionary, Formatting.Indented, + new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All }); + + Assert.AreEqual(@"{ + ""$id"": ""1"", + ""other"": { + ""$id"": ""2"", + ""blah"": null + }, + ""self"": { + ""$ref"": ""1"" + } +}", json); + } + + [Test] + public void DeserializeDictionarysWithPreserveObjectReferences() + { + string json = @"{ + ""$id"": ""1"", + ""other"": { + ""$id"": ""2"", + ""blah"": null + }, + ""self"": { + ""$ref"": ""1"" + } +}"; + + CircularDictionary circularDictionary = JsonConvert.DeserializeObject(json, + new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.All + }); + + Assert.AreEqual(2, circularDictionary.Count); + Assert.AreEqual(1, circularDictionary["other"].Count); + Assert.AreEqual(circularDictionary, circularDictionary["self"]); + } + + public class CircularList : List + { + } + + [Test] + [ExpectedException(typeof(JsonSerializationException))] + public void SerializeCircularListsError() + { + CircularList circularList = new CircularList(); + circularList.Add(null); + circularList.Add(new CircularList { null }); + circularList.Add(new CircularList { new CircularList { circularList } }); + + JsonConvert.SerializeObject(circularList, Formatting.Indented); + } + + [Test] + public void SerializeCircularListsIgnore() + { + CircularList circularList = new CircularList(); + circularList.Add(null); + circularList.Add(new CircularList { null }); + circularList.Add(new CircularList { new CircularList { circularList } }); + + string json = JsonConvert.SerializeObject(circularList, + Formatting.Indented, + new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); + + Assert.AreEqual(@"[ + null, + [ + null + ], + [ + [] + ] +]", json); + } + + [Test] + public void SerializeListsWithPreserveObjectReferences() + { + CircularList circularList = new CircularList(); + circularList.Add(null); + circularList.Add(new CircularList { null }); + circularList.Add(new CircularList { new CircularList { circularList } }); + + string json = JsonConvert.SerializeObject(circularList, Formatting.Indented, + new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All }); + + WriteEscapedJson(json); + Assert.AreEqual(@"{ + ""$id"": ""1"", + ""$values"": [ + null, + { + ""$id"": ""2"", + ""$values"": [ + null + ] + }, + { + ""$id"": ""3"", + ""$values"": [ + { + ""$id"": ""4"", + ""$values"": [ + { + ""$ref"": ""1"" + } + ] + } + ] + } + ] +}", json); + } + + [Test] + public void DeserializeListsWithPreserveObjectReferences() + { + string json = @"{ + ""$id"": ""1"", + ""$values"": [ + null, + { + ""$id"": ""2"", + ""$values"": [ + null + ] + }, + { + ""$id"": ""3"", + ""$values"": [ + { + ""$id"": ""4"", + ""$values"": [ + { + ""$ref"": ""1"" + } + ] + } + ] + } + ] +}"; + + CircularList circularList = JsonConvert.DeserializeObject(json, + new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All }); + + Assert.AreEqual(3, circularList.Count); + Assert.AreEqual(null, circularList[0]); + Assert.AreEqual(1, circularList[1].Count); + Assert.AreEqual(1, circularList[2].Count); + Assert.AreEqual(1, circularList[2][0].Count); + Assert.IsTrue(ReferenceEquals(circularList, circularList[2][0][0])); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Cannot preserve reference to array or readonly list: System.String[][]")] + public void DeserializeArraysWithPreserveObjectReferences() + { + string json = @"{ + ""$id"": ""1"", + ""$values"": [ + null, + { + ""$id"": ""2"", + ""$values"": [ + null + ] + }, + { + ""$id"": ""3"", + ""$values"": [ + { + ""$id"": ""4"", + ""$values"": [ + { + ""$ref"": ""1"" + } + ] + } + ] + } + ] +}"; + + JsonConvert.DeserializeObject(json, + new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All }); + } + + public class CircularDictionary : Dictionary + { + } + + [Test] + [ExpectedException(typeof(JsonSerializationException))] + public void SerializeCircularDictionarysError() + { + CircularDictionary circularDictionary = new CircularDictionary(); + circularDictionary.Add("other", new CircularDictionary { { "blah", null } }); + circularDictionary.Add("self", circularDictionary); + + JsonConvert.SerializeObject(circularDictionary, Formatting.Indented); + } + + [Test] + public void SerializeCircularDictionarysIgnore() + { + CircularDictionary circularDictionary = new CircularDictionary(); + circularDictionary.Add("other", new CircularDictionary { { "blah", null } }); + circularDictionary.Add("self", circularDictionary); + + string json = JsonConvert.SerializeObject(circularDictionary, Formatting.Indented, + new JsonSerializerSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }); + + Assert.AreEqual(@"{ + ""other"": { + ""blah"": null + } +}", json); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = @"Unexpected end when deserializing object.")] + public void UnexpectedEnd() + { + string json = @"{ + ""$id"":"; + + JsonConvert.DeserializeObject(json, + new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All }); + } + + public class CircularReferenceClassConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + CircularReferenceClass circularReferenceClass = (CircularReferenceClass)value; + + string reference = serializer.ReferenceResolver.GetReference(serializer, circularReferenceClass); + + JObject me = new JObject(); + me["$id"] = new JValue(reference); + me["$type"] = new JValue(value.GetType().Name); + me["Name"] = new JValue(circularReferenceClass.Name); + + JObject o = JObject.FromObject(circularReferenceClass.Child, serializer); + me["Child"] = o; + + me.WriteTo(writer); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + JObject o = JObject.Load(reader); + string id = (string)o["$id"]; + if (id != null) + { + CircularReferenceClass circularReferenceClass = new CircularReferenceClass(); + serializer.Populate(o.CreateReader(), circularReferenceClass); + return circularReferenceClass; + } + else + { + string reference = (string)o["$ref"]; + return serializer.ReferenceResolver.ResolveReference(serializer, reference); + } + } + + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(CircularReferenceClass)); + } + } + + [Test] + public void SerializeCircularReferencesWithConverter() + { + CircularReferenceClass c1 = new CircularReferenceClass { Name = "c1" }; + CircularReferenceClass c2 = new CircularReferenceClass { Name = "c2" }; + CircularReferenceClass c3 = new CircularReferenceClass { Name = "c3" }; + + c1.Child = c2; + c2.Child = c3; + c3.Child = c1; + + string json = JsonConvert.SerializeObject(c1, Formatting.Indented, new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.Objects, + Converters = new List { new CircularReferenceClassConverter() } + }); + + Assert.AreEqual(@"{ + ""$id"": ""1"", + ""$type"": ""CircularReferenceClass"", + ""Name"": ""c1"", + ""Child"": { + ""$id"": ""2"", + ""$type"": ""CircularReferenceClass"", + ""Name"": ""c2"", + ""Child"": { + ""$id"": ""3"", + ""$type"": ""CircularReferenceClass"", + ""Name"": ""c3"", + ""Child"": { + ""$ref"": ""1"" + } + } + } +}", json); + } + + [Test] + public void DeserializeCircularReferencesWithConverter() + { + string json = @"{ + ""$id"": ""1"", + ""$type"": ""CircularReferenceClass"", + ""Name"": ""c1"", + ""Child"": { + ""$id"": ""2"", + ""$type"": ""CircularReferenceClass"", + ""Name"": ""c2"", + ""Child"": { + ""$id"": ""3"", + ""$type"": ""CircularReferenceClass"", + ""Name"": ""c3"", + ""Child"": { + ""$ref"": ""1"" + } + } + } +}"; + + CircularReferenceClass c1 = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.Objects, + Converters = new List { new CircularReferenceClassConverter() } + }); + + Assert.AreEqual("c1", c1.Name); + Assert.AreEqual("c2", c1.Child.Name); + Assert.AreEqual("c3", c1.Child.Child.Name); + Assert.AreEqual("c1", c1.Child.Child.Child.Name); + } + + [Test] + public void SerializeEmployeeReference() + { + EmployeeReference mikeManager = new EmployeeReference + { + Name = "Mike Manager" + }; + EmployeeReference joeUser = new EmployeeReference + { + Name = "Joe User", + Manager = mikeManager + }; + + List employees = new List + { + mikeManager, + joeUser + }; + + string json = JsonConvert.SerializeObject(employees, Formatting.Indented); + Assert.AreEqual(@"[ + { + ""$id"": ""1"", + ""Name"": ""Mike Manager"", + ""Manager"": null + }, + { + ""$id"": ""2"", + ""Name"": ""Joe User"", + ""Manager"": { + ""$ref"": ""1"" + } + } +]", json); + } + + [Test] + public void DeserializeEmployeeReference() + { + string json = @"[ + { + ""$id"": ""1"", + ""Name"": ""Mike Manager"", + ""Manager"": null + }, + { + ""$id"": ""2"", + ""Name"": ""Joe User"", + ""Manager"": { + ""$ref"": ""1"" + } + } +]"; + + List employees = JsonConvert.DeserializeObject>(json); + + Assert.AreEqual(2, employees.Count); + Assert.AreEqual("Mike Manager", employees[0].Name); + Assert.AreEqual("Joe User", employees[1].Name); + Assert.AreEqual(employees[0], employees[1].Manager); + } + + [Test] + public void SerializeCircularReference() + { + CircularReferenceClass c1 = new CircularReferenceClass { Name = "c1" }; + CircularReferenceClass c2 = new CircularReferenceClass { Name = "c2" }; + CircularReferenceClass c3 = new CircularReferenceClass { Name = "c3" }; + + c1.Child = c2; + c2.Child = c3; + c3.Child = c1; + + string json = JsonConvert.SerializeObject(c1, Formatting.Indented, new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.Objects + }); + + Assert.AreEqual(@"{ + ""$id"": ""1"", + ""Name"": ""c1"", + ""Child"": { + ""$id"": ""2"", + ""Name"": ""c2"", + ""Child"": { + ""$id"": ""3"", + ""Name"": ""c3"", + ""Child"": { + ""$ref"": ""1"" + } + } + } +}", json); + } + + [Test] + public void DeserializeCircularReference() + { + string json = @"{ + ""$id"": ""1"", + ""Name"": ""c1"", + ""Child"": { + ""$id"": ""2"", + ""Name"": ""c2"", + ""Child"": { + ""$id"": ""3"", + ""Name"": ""c3"", + ""Child"": { + ""$ref"": ""1"" + } + } + } +}"; + + CircularReferenceClass c1 = + JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + PreserveReferencesHandling = PreserveReferencesHandling.Objects + }); + + Assert.AreEqual("c1", c1.Name); + Assert.AreEqual("c2", c1.Child.Name); + Assert.AreEqual("c3", c1.Child.Child.Name); + Assert.AreEqual("c1", c1.Child.Child.Child.Name); + } + + [Test] + public void SerializeReferenceInList() + { + EmployeeReference e1 = new EmployeeReference { Name = "e1" }; + EmployeeReference e2 = new EmployeeReference { Name = "e2" }; + + List employees = new List { e1, e2, e1, e2 }; + + string json = JsonConvert.SerializeObject(employees, Formatting.Indented); + + Assert.AreEqual(@"[ + { + ""$id"": ""1"", + ""Name"": ""e1"", + ""Manager"": null + }, + { + ""$id"": ""2"", + ""Name"": ""e2"", + ""Manager"": null + }, + { + ""$ref"": ""1"" + }, + { + ""$ref"": ""2"" + } +]", json); + } + + [Test] + public void DeserializeReferenceInList() + { + string json = @"[ + { + ""$id"": ""1"", + ""Name"": ""e1"", + ""Manager"": null + }, + { + ""$id"": ""2"", + ""Name"": ""e2"", + ""Manager"": null + }, + { + ""$ref"": ""1"" + }, + { + ""$ref"": ""2"" + } +]"; + + List employees = JsonConvert.DeserializeObject>(json); + Assert.AreEqual(4, employees.Count); + + Assert.AreEqual("e1", employees[0].Name); + Assert.AreEqual("e2", employees[1].Name); + Assert.AreEqual("e1", employees[2].Name); + Assert.AreEqual("e2", employees[3].Name); + + Assert.AreEqual(employees[0], employees[2]); + Assert.AreEqual(employees[1], employees[3]); + } + + [Test] + public void SerializeReferenceInDictionary() + { + EmployeeReference e1 = new EmployeeReference { Name = "e1" }; + EmployeeReference e2 = new EmployeeReference { Name = "e2" }; + + Dictionary employees = new Dictionary + { + {"One", e1}, + {"Two", e2}, + {"Three", e1}, + {"Four", e2} + }; + + string json = JsonConvert.SerializeObject(employees, Formatting.Indented); + + Assert.AreEqual(@"{ + ""One"": { + ""$id"": ""1"", + ""Name"": ""e1"", + ""Manager"": null + }, + ""Two"": { + ""$id"": ""2"", + ""Name"": ""e2"", + ""Manager"": null + }, + ""Three"": { + ""$ref"": ""1"" + }, + ""Four"": { + ""$ref"": ""2"" + } +}", json); + } + + [Test] + public void DeserializeReferenceInDictionary() + { + string json = @"{ + ""One"": { + ""$id"": ""1"", + ""Name"": ""e1"", + ""Manager"": null + }, + ""Two"": { + ""$id"": ""2"", + ""Name"": ""e2"", + ""Manager"": null + }, + ""Three"": { + ""$ref"": ""1"" + }, + ""Four"": { + ""$ref"": ""2"" + } +}"; + + Dictionary employees = JsonConvert.DeserializeObject>(json); + Assert.AreEqual(4, employees.Count); + + EmployeeReference e1 = employees["One"]; + EmployeeReference e2 = employees["Two"]; + + Assert.AreEqual("e1", e1.Name); + Assert.AreEqual("e2", e2.Name); + + Assert.AreEqual(e1, employees["Three"]); + Assert.AreEqual(e2, employees["Four"]); + } + + [Test] + public void ExampleWithout() + { + Person p = new Person + { + BirthDate = new DateTime(1980, 12, 23, 0, 0, 0, DateTimeKind.Utc), + LastModified = new DateTime(2009, 2, 20, 12, 59, 21, DateTimeKind.Utc), + Department = "IT", + Name = "James" + }; + + List people = new List(); + people.Add(p); + people.Add(p); + + string json = JsonConvert.SerializeObject(people, Formatting.Indented); + //[ + // { + // "Name": "James", + // "BirthDate": "\/Date(346377600000)\/", + // "LastModified": "\/Date(1235134761000)\/" + // }, + // { + // "Name": "James", + // "BirthDate": "\/Date(346377600000)\/", + // "LastModified": "\/Date(1235134761000)\/" + // } + //] + } + + [Test] + public void ExampleWith() + { + Person p = new Person + { + BirthDate = new DateTime(1980, 12, 23, 0, 0, 0, DateTimeKind.Utc), + LastModified = new DateTime(2009, 2, 20, 12, 59, 21, DateTimeKind.Utc), + Department = "IT", + Name = "James" + }; + + List people = new List(); + people.Add(p); + people.Add(p); + + string json = JsonConvert.SerializeObject(people, Formatting.Indented, + new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects }); + //[ + // { + // "$id": "1", + // "Name": "James", + // "BirthDate": "\/Date(346377600000)\/", + // "LastModified": "\/Date(1235134761000)\/" + // }, + // { + // "$ref": "1" + // } + //] + + List deserializedPeople = JsonConvert.DeserializeObject>(json, + new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects }); + + Console.WriteLine(deserializedPeople.Count); + // 2 + + Person p1 = deserializedPeople[0]; + Person p2 = deserializedPeople[1]; + + Console.WriteLine(p1.Name); + // James + Console.WriteLine(p2.Name); + // James + + bool equal = Object.ReferenceEquals(p1, p2); + // true + } + + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class User + { + #region properties + + [JsonProperty(Required = Required.Always, PropertyName = "SecretType")] + private string secretType; + + [JsonProperty(Required = Required.Always)] + public string Login { get; set; } + + public Type SecretType + { + get { return Type.GetType(secretType); } + set { secretType = value.AssemblyQualifiedName; } + } + + [JsonProperty] + public User Friend { get; set; } + + #endregion + + #region constructors + + public User() + { + + } + + public User(string login, Type secretType) + : this() + { + this.Login = login; + this.SecretType = secretType; + } + + #endregion + + #region methods + + public override int GetHashCode() + { + return SecretType.GetHashCode(); + } + + public override string ToString() + { + return string.Format("SecretType: {0}, Login: {1}", secretType, Login); + } + + #endregion + } + + [Test] + public void DeserializeTypeWithDubiousGetHashcode() + { + User user1 = new User("Peter", typeof(Version)); + User user2 = new User("Michael", typeof(Version)); + + user1.Friend = user2; + + JsonSerializerSettings serializerSettings = new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All, + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor, + PreserveReferencesHandling = PreserveReferencesHandling.Objects + }; + + string json = JsonConvert.SerializeObject(user1, Formatting.Indented, serializerSettings); + + User deserializedUser = JsonConvert.DeserializeObject(json, serializerSettings); + Assert.IsNotNull(deserializedUser); + } + + [Test] + public void PreserveReferencesHandlingWithReusedJsonSerializer() + { + MyClass c = new MyClass(); + + IList myClasses1 = new List + { + c, + c + }; + + var ser = new JsonSerializer() + { + PreserveReferencesHandling = PreserveReferencesHandling.All + }; + + MemoryStream ms = new MemoryStream(); + + using (var sw = new StreamWriter(ms)) + using (var writer = new JsonTextWriter(sw) { Formatting = Formatting.Indented }) + { + ser.Serialize(writer, myClasses1); + } + + byte[] data = ms.ToArray(); + string json = Encoding.UTF8.GetString(data, 0, data.Length); + + Assert.AreEqual(@"{ + ""$id"": ""1"", + ""$values"": [ + { + ""$id"": ""2"", + ""PreProperty"": 0, + ""PostProperty"": 0 + }, + { + ""$ref"": ""2"" + } + ] +}", json); + + ms = new MemoryStream(data); + IList myClasses2; + + using (var sr = new StreamReader(ms)) + using (var reader = new JsonTextReader(sr)) + { + myClasses2 = ser.Deserialize>(reader); + } + + Assert.AreEqual(2, myClasses2.Count); + Assert.AreEqual(myClasses2[0], myClasses2[1]); + + Assert.AreNotEqual(myClasses1[0], myClasses2[0]); + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs new file mode 100644 index 0000000..b28954c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/SerializationErrorHandlingTests.cs @@ -0,0 +1,294 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; +using Newtonsoft.Json.Serialization; +using System.IO; +using ErrorEventArgs=Newtonsoft.Json.Serialization.ErrorEventArgs; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class SerializationErrorHandlingTests : TestFixtureBase + { + [Test] + public void ErrorDeserializingListHandled() + { + string json = @"[ + { + ""Name"": ""Jim"", + ""BirthDate"": ""\/Date(978048000000)\/"", + ""LastModified"": ""\/Date(978048000000)\/"" + }, + { + ""Name"": ""Jim"", + ""BirthDate"": ""\/Date(978048000000)\/"", + ""LastModified"": ""\/Date(978048000000)\/"" + } +]"; + + VersionKeyedCollection c = JsonConvert.DeserializeObject(json); + Assert.AreEqual(1, c.Count); + Assert.AreEqual(1, c.Messages.Count); + Assert.AreEqual("Error message for member 1 = An item with the same key has already been added.", c.Messages[0]); + } + + [Test] + public void DeserializingErrorInChildObject() + { + ListErrorObjectCollection c = JsonConvert.DeserializeObject(@"[ + { + ""Member"": ""Value1"", + ""Member2"": null + }, + { + ""Member"": ""Value2"" + }, + { + ""ThrowError"": ""Value"", + ""Object"": { + ""Array"": [ + 1, + 2 + ] + } + }, + { + ""ThrowError"": ""Handle this!"", + ""Member"": ""Value3"" + } +]"); + + Assert.AreEqual(3, c.Count); + Assert.AreEqual("Value1", c[0].Member); + Assert.AreEqual("Value2", c[1].Member); + Assert.AreEqual("Value3", c[2].Member); + Assert.AreEqual("Handle this!", c[2].ThrowError); + } + + [Test] + public void SerializingErrorInChildObject() + { + ListErrorObjectCollection c = new ListErrorObjectCollection + { + new ListErrorObject + { + Member = "Value1", + ThrowError = "Handle this!", + Member2 = "Member1" + }, + new ListErrorObject + { + Member = "Value2", + Member2 = "Member2" + }, + new ListErrorObject + { + Member = "Value3", + ThrowError = "Handle that!", + Member2 = "Member3" + } + }; + + string json = JsonConvert.SerializeObject(c, Formatting.Indented); + + Assert.AreEqual(@"[ + { + ""Member"": ""Value1"", + ""ThrowError"": ""Handle this!"", + ""Member2"": ""Member1"" + }, + { + ""Member"": ""Value2"" + }, + { + ""Member"": ""Value3"", + ""ThrowError"": ""Handle that!"", + ""Member2"": ""Member3"" + } +]", json); + } + + [Test] + public void DeserializingErrorInDateTimeCollection() + { + DateTimeErrorObjectCollection c = JsonConvert.DeserializeObject(@"[ + ""2009-09-09T00:00:00Z"", + ""kjhkjhkjhkjh"", + [ + 1 + ], + ""1977-02-20T00:00:00Z"", + null, + ""2000-12-01T00:00:00Z"" +]", new IsoDateTimeConverter()); + + Assert.AreEqual(3, c.Count); + Assert.AreEqual(new DateTime(2009, 9, 9, 0, 0, 0, DateTimeKind.Utc), c[0]); + Assert.AreEqual(new DateTime(1977, 2, 20, 0, 0, 0, DateTimeKind.Utc), c[1]); + Assert.AreEqual(new DateTime(2000, 12, 1, 0, 0, 0, DateTimeKind.Utc), c[2]); + } + + [Test] + public void DeserializingErrorHandlingUsingEvent() + { + List errors = new List(); + + List c = JsonConvert.DeserializeObject>(@"[ + ""2009-09-09T00:00:00Z"", + ""I am not a date and will error!"", + [ + 1 + ], + ""1977-02-20T00:00:00Z"", + null, + ""2000-12-01T00:00:00Z"" + ]", + new JsonSerializerSettings + { + Error = delegate(object sender, ErrorEventArgs args) + { + errors.Add(args.ErrorContext.Error.Message); + args.ErrorContext.Handled = true; + }, + Converters = { new IsoDateTimeConverter() } + }); + + // 2009-09-09T00:00:00Z + // 1977-02-20T00:00:00Z + // 2000-12-01T00:00:00Z + + // The string was not recognized as a valid DateTime. There is a unknown word starting at index 0. + // Unexpected token parsing date. Expected String, got StartArray. + // Cannot convert null value to System.DateTime. + + Assert.AreEqual(3, c.Count); + Assert.AreEqual(new DateTime(2009, 9, 9, 0, 0, 0, DateTimeKind.Utc), c[0]); + Assert.AreEqual(new DateTime(1977, 2, 20, 0, 0, 0, DateTimeKind.Utc), c[1]); + Assert.AreEqual(new DateTime(2000, 12, 1, 0, 0, 0, DateTimeKind.Utc), c[2]); + + Assert.AreEqual(3, errors.Count); +#if !(NET20 || NET35 || WINDOWS_PHONE) + Assert.AreEqual("The string was not recognized as a valid DateTime. There is an unknown word starting at index 0.", errors[0]); +#else + Assert.AreEqual("The string was not recognized as a valid DateTime. There is a unknown word starting at index 0.", errors[0]); +#endif + Assert.AreEqual("Unexpected token parsing date. Expected String, got StartArray.", errors[1]); + Assert.AreEqual("Cannot convert null value to System.DateTime.", errors[2]); + } + + [Test] + public void DeserializingErrorInDateTimeCollectionWithAttributeWithEventNotCalled() + { + bool eventErrorHandlerCalled = false; + + DateTimeErrorObjectCollection c = JsonConvert.DeserializeObject(@"[ + ""2009-09-09T00:00:00Z"", + ""kjhkjhkjhkjh"", + [ + 1 + ], + ""1977-02-20T00:00:00Z"", + null, + ""2000-12-01T00:00:00Z"" +]", + new JsonSerializerSettings + { + Error = (s, a) => eventErrorHandlerCalled = true, + Converters = + { + new IsoDateTimeConverter() + } + }); + + Assert.AreEqual(3, c.Count); + Assert.AreEqual(new DateTime(2009, 9, 9, 0, 0, 0, DateTimeKind.Utc), c[0]); + Assert.AreEqual(new DateTime(1977, 2, 20, 0, 0, 0, DateTimeKind.Utc), c[1]); + Assert.AreEqual(new DateTime(2000, 12, 1, 0, 0, 0, DateTimeKind.Utc), c[2]); + + Assert.AreEqual(false, eventErrorHandlerCalled); + } + + [Test] + public void SerializePerson() + { + PersonError person = new PersonError + { + Name = "George Michael Bluth", + Age = 16, + Roles = null, + Title = "Mister Manager" + }; + + string json = JsonConvert.SerializeObject(person, Formatting.Indented); + + Console.WriteLine(json); + //{ + // "Name": "George Michael Bluth", + // "Age": 16, + // "Title": "Mister Manager" + //} + + Assert.AreEqual(@"{ + ""Name"": ""George Michael Bluth"", + ""Age"": 16, + ""Title"": ""Mister Manager"" +}", json); + } + + [Test] + public void DeserializeNestedUnhandled() + { + List errors = new List(); + + string json = @"[[""kjhkjhkjhkjh""]]"; + + try + { + JsonSerializer serializer = new JsonSerializer(); + serializer.Error += delegate(object sender, ErrorEventArgs args) + { + // only log an error once + if (args.CurrentObject == args.ErrorContext.OriginalObject) + errors.Add(args.ErrorContext.Error.Message); + }; + + serializer.Deserialize(new StringReader(json), typeof(List>)); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + + Assert.AreEqual(1, errors.Count); + Assert.AreEqual(@"Error converting value ""kjhkjhkjhkjh"" to type 'System.DateTime'.", errors[0]); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/SerializationEventAttributeTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/SerializationEventAttributeTests.cs new file mode 100644 index 0000000..e4abd52 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/SerializationEventAttributeTests.cs @@ -0,0 +1,267 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC +using System; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using System.Text; +using Newtonsoft.Json.Tests; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Converters; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class SerializationEventAttributeTests : TestFixtureBase + { + [Test] + public void ObjectEvents() + { + SerializationEventTestObject obj = new SerializationEventTestObject(); + + Assert.AreEqual(11, obj.Member1); + Assert.AreEqual("Hello World!", obj.Member2); + Assert.AreEqual("This is a nonserialized value", obj.Member3); + Assert.AreEqual(null, obj.Member4); + Assert.AreEqual(null, obj.Member5); + + string json = JsonConvert.SerializeObject(obj, Formatting.Indented); + Assert.AreEqual(@"{ + ""Member1"": 11, + ""Member2"": ""This value went into the data file during serialization."", + ""Member4"": null +}", json); + + Assert.AreEqual(11, obj.Member1); + Assert.AreEqual("This value was reset after serialization.", obj.Member2); + Assert.AreEqual("This is a nonserialized value", obj.Member3); + Assert.AreEqual(null, obj.Member4); + Assert.AreEqual("Error message for member Member6 = Error getting value from 'Member6' on 'Newtonsoft.Json.Tests.TestObjects.SerializationEventTestObject'.", obj.Member5); + + JObject o = JObject.Parse(@"{ + ""Member1"": 11, + ""Member2"": ""This value went into the data file during serialization."", + ""Member4"": null +}"); + o["Member6"] = "Dummy text for error"; + + obj = JsonConvert.DeserializeObject(o.ToString()); + + Assert.AreEqual(11, obj.Member1); + Assert.AreEqual("This value went into the data file during serialization.", obj.Member2); + Assert.AreEqual("This value was set during deserialization", obj.Member3); + Assert.AreEqual("This value was set after deserialization.", obj.Member4); + Assert.AreEqual("Error message for member Member6 = Error setting value to 'Member6' on 'Newtonsoft.Json.Tests.TestObjects.SerializationEventTestObject'.", obj.Member5); + } + + [Test] + public void ObjectWithConstructorEvents() + { + SerializationEventTestObjectWithConstructor obj = new SerializationEventTestObjectWithConstructor(11, "Hello World!", null); + + Assert.AreEqual(11, obj.Member1); + Assert.AreEqual("Hello World!", obj.Member2); + Assert.AreEqual("This is a nonserialized value", obj.Member3); + Assert.AreEqual(null, obj.Member4); + + string json = JsonConvert.SerializeObject(obj, Formatting.Indented); + Assert.AreEqual(@"{ + ""Member1"": 11, + ""Member2"": ""This value went into the data file during serialization."", + ""Member4"": null +}", json); + + Assert.AreEqual(11, obj.Member1); + Assert.AreEqual("This value was reset after serialization.", obj.Member2); + Assert.AreEqual("This is a nonserialized value", obj.Member3); + Assert.AreEqual(null, obj.Member4); + + obj = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(11, obj.Member1); + Assert.AreEqual("This value went into the data file during serialization.", obj.Member2); + Assert.AreEqual("This value was set during deserialization", obj.Member3); + Assert.AreEqual("This value was set after deserialization.", obj.Member4); + } + + [Test] + public void ListEvents() + { + SerializationEventTestList obj = new SerializationEventTestList + { + 1.1m, + 2.222222222m, + int.MaxValue, + Convert.ToDecimal(Math.PI) + }; + + Assert.AreEqual(11, obj.Member1); + Assert.AreEqual("Hello World!", obj.Member2); + Assert.AreEqual("This is a nonserialized value", obj.Member3); + Assert.AreEqual(null, obj.Member4); + + string json = JsonConvert.SerializeObject(obj, Formatting.Indented); + Assert.AreEqual(@"[ + -1.0, + 1.1, + 2.222222222, + 2147483647.0, + 3.14159265358979 +]", json); + + Assert.AreEqual(11, obj.Member1); + Assert.AreEqual("This value was reset after serialization.", obj.Member2); + Assert.AreEqual("This is a nonserialized value", obj.Member3); + Assert.AreEqual(null, obj.Member4); + + obj = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(11, obj.Member1); + Assert.AreEqual("Hello World!", obj.Member2); + Assert.AreEqual("This value was set during deserialization", obj.Member3); + Assert.AreEqual("This value was set after deserialization.", obj.Member4); + } + + [Test] + public void DictionaryEvents() + { + SerializationEventTestDictionary obj = new SerializationEventTestDictionary + { + { 1.1m, "first" }, + { 2.222222222m, "second" }, + { int.MaxValue, "third" }, + { Convert.ToDecimal(Math.PI), "fourth" } + }; + + Assert.AreEqual(11, obj.Member1); + Assert.AreEqual("Hello World!", obj.Member2); + Assert.AreEqual("This is a nonserialized value", obj.Member3); + Assert.AreEqual(null, obj.Member4); + + string json = JsonConvert.SerializeObject(obj, Formatting.Indented); + Assert.AreEqual(@"{ + ""1.1"": ""first"", + ""2.222222222"": ""second"", + ""2147483647"": ""third"", + ""3.14159265358979"": ""fourth"", + ""79228162514264337593543950335"": ""Inserted on serializing"" +}", json); + + Assert.AreEqual(11, obj.Member1); + Assert.AreEqual("This value was reset after serialization.", obj.Member2); + Assert.AreEqual("This is a nonserialized value", obj.Member3); + Assert.AreEqual(null, obj.Member4); + + obj = JsonConvert.DeserializeObject(json); + + Assert.AreEqual(11, obj.Member1); + Assert.AreEqual("Hello World!", obj.Member2); + Assert.AreEqual("This value was set during deserialization", obj.Member3); + Assert.AreEqual("This value was set after deserialization.", obj.Member4); + } + + [Test] + public void ObjectEventsDocumentationExample() + { + SerializationEventTestObject obj = new SerializationEventTestObject(); + + Console.WriteLine(obj.Member1); + // 11 + Console.WriteLine(obj.Member2); + // Hello World! + Console.WriteLine(obj.Member3); + // This is a nonserialized value + Console.WriteLine(obj.Member4); + // null + Console.WriteLine(obj.Member5); + // null + + string json = JsonConvert.SerializeObject(obj, Formatting.Indented); + // { + // "Member1": 11, + // "Member2": "This value went into the data file during serialization.", + // "Member4": null + // } + + Console.WriteLine(obj.Member1); + // 11 + Console.WriteLine(obj.Member2); + // This value was reset after serialization. + Console.WriteLine(obj.Member3); + // This is a nonserialized value + Console.WriteLine(obj.Member4); + // null + Console.WriteLine(obj.Member5); + // Error message for member Member6 = Exception has been thrown by the target of an invocation. + + obj = JsonConvert.DeserializeObject(json); + + Console.WriteLine(obj.Member1); + // 11 + Console.WriteLine(obj.Member2); + // This value went into the data file during serialization. + Console.WriteLine(obj.Member3); + // This value was set during deserialization + Console.WriteLine(obj.Member4); + // This value was set after deserialization. + } + +#if !SILVERLIGHT + public class SerializationEventContextTestObject + { + public string TestMember { get; set; } + + [OnSerializing] + internal void OnSerializingMethod(StreamingContext context) + { + TestMember = context.State + " " + context.Context; + } + } + + [Test] + public void SerializationEventContextTest() + { + SerializationEventContextTestObject value = new SerializationEventContextTestObject(); + + string json = JsonConvert.SerializeObject(value, Formatting.Indented, new JsonSerializerSettings + { + Context = + new StreamingContext( + StreamingContextStates.Remoting, + "ContextValue") + }); + + Assert.AreEqual(@"{ + ""TestMember"": ""Remoting ContextValue"" +}", json); + } +#endif + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs new file mode 100644 index 0000000..a3e9b19 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Serialization/TypeNameHandlingTests.cs @@ -0,0 +1,721 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization.Formatters; +using System.Text; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Tests.TestObjects; +using NUnit.Framework; +using Newtonsoft.Json.Utilities; +using System.Net; +using System.Runtime.Serialization; +using System.IO; + +namespace Newtonsoft.Json.Tests.Serialization +{ + public class TypeNameHandlingTests : TestFixtureBase + { + [Test] + public void WriteTypeNameForObjects() + { + string employeeRef = ReflectionUtils.GetTypeName(typeof(EmployeeReference), FormatterAssemblyStyle.Simple); + + EmployeeReference employee = new EmployeeReference(); + + string json = JsonConvert.SerializeObject(employee, Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects + }); + + Assert.AreEqual(@"{ + ""$id"": ""1"", + ""$type"": """ + employeeRef + @""", + ""Name"": null, + ""Manager"": null +}", json); + } + + [Test] + public void DeserializeTypeName() + { + string employeeRef = ReflectionUtils.GetTypeName(typeof(EmployeeReference), FormatterAssemblyStyle.Simple); + + string json = @"{ + ""$id"": ""1"", + ""$type"": """ + employeeRef + @""", + ""Name"": ""Name!"", + ""Manager"": null +}"; + + object employee = JsonConvert.DeserializeObject(json, null, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects + }); + + Assert.IsInstanceOfType(typeof(EmployeeReference), employee); + Assert.AreEqual("Name!", ((EmployeeReference)employee).Name); + } + +#if !SILVERLIGHT && !PocketPC + [Test] + public void DeserializeTypeNameFromGacAssembly() + { + string cookieRef = ReflectionUtils.GetTypeName(typeof(Cookie), FormatterAssemblyStyle.Simple); + + string json = @"{ + ""$id"": ""1"", + ""$type"": """ + cookieRef + @""" +}"; + + object cookie = JsonConvert.DeserializeObject(json, null, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects + }); + + Assert.IsInstanceOfType(typeof(Cookie), cookie); + } +#endif + + [Test] + public void SerializeGenericObjectListWithTypeName() + { + string employeeRef = typeof(EmployeeReference).AssemblyQualifiedName; + string personRef = typeof(Person).AssemblyQualifiedName; + + List values = new List + { + new EmployeeReference + { + Name = "Bob", + Manager = new EmployeeReference {Name = "Frank"} + }, + new Person + { + Department = "Department", + BirthDate = new DateTime(2000, 12, 30, 0, 0, 0, DateTimeKind.Utc), + LastModified = new DateTime(2000, 12, 30, 0, 0, 0, DateTimeKind.Utc) + }, + "String!", + int.MinValue + }; + + string json = JsonConvert.SerializeObject(values, Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Full + }); + + Assert.AreEqual(@"[ + { + ""$id"": ""1"", + ""$type"": """ + employeeRef + @""", + ""Name"": ""Bob"", + ""Manager"": { + ""$id"": ""2"", + ""$type"": """ + employeeRef + @""", + ""Name"": ""Frank"", + ""Manager"": null + } + }, + { + ""$type"": """ + personRef + @""", + ""Name"": null, + ""BirthDate"": ""\/Date(978134400000)\/"", + ""LastModified"": ""\/Date(978134400000)\/"" + }, + ""String!"", + -2147483648 +]", json); + } + + [Test] + public void DeserializeGenericObjectListWithTypeName() + { + string employeeRef = typeof(EmployeeReference).AssemblyQualifiedName; + string personRef = typeof(Person).AssemblyQualifiedName; + + string json = @"[ + { + ""$id"": ""1"", + ""$type"": """ + employeeRef + @""", + ""Name"": ""Bob"", + ""Manager"": { + ""$id"": ""2"", + ""$type"": """ + employeeRef + @""", + ""Name"": ""Frank"", + ""Manager"": null + } + }, + { + ""$type"": """ + personRef + @""", + ""Name"": null, + ""BirthDate"": ""\/Date(978134400000)\/"", + ""LastModified"": ""\/Date(978134400000)\/"" + }, + ""String!"", + -2147483648 +]"; + + List values = (List)JsonConvert.DeserializeObject(json, typeof(List), new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Full + }); + + Assert.AreEqual(4, values.Count); + + EmployeeReference e = (EmployeeReference)values[0]; + Person p = (Person)values[1]; + + Assert.AreEqual("Bob", e.Name); + Assert.AreEqual("Frank", e.Manager.Name); + + Assert.AreEqual(null, p.Name); + Assert.AreEqual(new DateTime(2000, 12, 30, 0, 0, 0, DateTimeKind.Utc), p.BirthDate); + Assert.AreEqual(new DateTime(2000, 12, 30, 0, 0, 0, DateTimeKind.Utc), p.LastModified); + + Assert.AreEqual("String!", values[2]); + Assert.AreEqual(int.MinValue, values[3]); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException))] + public void DeserializeWithBadTypeName() + { + string employeeRef = typeof(EmployeeReference).AssemblyQualifiedName; + + string json = @"{ + ""$id"": ""1"", + ""$type"": """ + employeeRef + @""", + ""Name"": ""Name!"", + ""Manager"": null +}"; + + JsonConvert.DeserializeObject(json, typeof(Person), new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Full + }); + } + + [Test] + public void DeserializeTypeNameWithNoTypeNameHandling() + { + string employeeRef = typeof(EmployeeReference).AssemblyQualifiedName; + + string json = @"{ + ""$id"": ""1"", + ""$type"": """ + employeeRef + @""", + ""Name"": ""Name!"", + ""Manager"": null +}"; + + JObject o = (JObject)JsonConvert.DeserializeObject(json); + + Assert.AreEqual(@"{ + ""Name"": ""Name!"", + ""Manager"": null +}", o.ToString()); + } + + [Test] + [ExpectedException(typeof(JsonSerializationException), ExpectedMessage = "Type specified in JSON 'Newtonsoft.Json.Tests.TestObjects.Employee' was not resolved.")] + public void DeserializeTypeNameOnly() + { + string json = @"{ + ""$id"": ""1"", + ""$type"": ""Newtonsoft.Json.Tests.TestObjects.Employee"", + ""Name"": ""Name!"", + ""Manager"": null +}"; + + JsonConvert.DeserializeObject(json, null, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects + }); + } + + public interface ICorrelatedMessage + { + string CorrelationId { get; set; } + } + + public class SendHttpRequest : ICorrelatedMessage + { + public SendHttpRequest() + { + RequestEncoding = "UTF-8"; + Method = "GET"; + } + public string Method { get; set; } + public Dictionary Headers { get; set; } + public string Url { get; set; } + public Dictionary RequestData; + public string RequestBodyText { get; set; } + public string User { get; set; } + public string Passwd { get; set; } + public string RequestEncoding { get; set; } + public string CorrelationId { get; set; } + } + + [Test] + public void DeserializeGenericTypeName() + { + string typeName = typeof(SendHttpRequest).AssemblyQualifiedName; + + string json = @"{ +""$type"": """ + typeName + @""", +""RequestData"": { +""$type"": ""System.Collections.Generic.Dictionary`2[[System.String, mscorlib,Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"", +""Id"": ""siedemnaście"", +""X"": ""323"" +}, +""Method"": ""GET"", +""Url"": ""http://www.onet.pl"", +""RequestEncoding"": ""UTF-8"", +""CorrelationId"": ""xyz"" +}"; + + ICorrelatedMessage message = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Full + }); + + Assert.IsInstanceOfType(typeof(SendHttpRequest), message); + + SendHttpRequest request = (SendHttpRequest)message; + Assert.AreEqual("xyz", request.CorrelationId); + Assert.AreEqual(2, request.RequestData.Count); + Assert.AreEqual("siedemnaście", request.RequestData["Id"]); + } + + [Test] + public void SerializeObjectWithMultipleGenericLists() + { + string containerTypeName = typeof(Container).AssemblyQualifiedName; + string productListTypeName = typeof(List).AssemblyQualifiedName; + + Container container = new Container + { + In = new List(), + Out = new List() + }; + + string json = JsonConvert.SerializeObject(container, Formatting.Indented, + new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore, + TypeNameHandling = TypeNameHandling.All, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Full + }); + + Assert.AreEqual(@"{ + ""$type"": """ + containerTypeName + @""", + ""In"": { + ""$type"": """ + productListTypeName + @""", + ""$values"": [] + }, + ""Out"": { + ""$type"": """ + productListTypeName + @""", + ""$values"": [] + } +}", json); + } + + public class TypeNameProperty + { + public string Name { get; set; } + [JsonProperty(TypeNameHandling = TypeNameHandling.All)] + public object Value { get; set; } + } + + [Test] + public void WriteObjectTypeNameForProperty() + { + string typeNamePropertyRef = ReflectionUtils.GetTypeName(typeof(TypeNameProperty), FormatterAssemblyStyle.Simple); + + TypeNameProperty typeNameProperty = new TypeNameProperty + { + Name = "Name!", + Value = new TypeNameProperty + { + Name = "Nested!" + } + }; + + string json = JsonConvert.SerializeObject(typeNameProperty, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Name"": ""Name!"", + ""Value"": { + ""$type"": """ + typeNamePropertyRef + @""", + ""Name"": ""Nested!"", + ""Value"": null + } +}", json); + + TypeNameProperty deserialized = JsonConvert.DeserializeObject(json); + Assert.AreEqual("Name!", deserialized.Name); + Assert.IsInstanceOfType(typeof(TypeNameProperty), deserialized.Value); + + TypeNameProperty nested = (TypeNameProperty)deserialized.Value; + Assert.AreEqual("Nested!", nested.Name); + Assert.AreEqual(null, nested.Value); + } + + [Test] + public void WriteListTypeNameForProperty() + { + string listRef = ReflectionUtils.GetTypeName(typeof(List), FormatterAssemblyStyle.Simple); + + TypeNameProperty typeNameProperty = new TypeNameProperty + { + Name = "Name!", + Value = new List { 1, 2, 3, 4, 5 } + }; + + string json = JsonConvert.SerializeObject(typeNameProperty, Formatting.Indented); + + Assert.AreEqual(@"{ + ""Name"": ""Name!"", + ""Value"": { + ""$type"": """ + listRef + @""", + ""$values"": [ + 1, + 2, + 3, + 4, + 5 + ] + } +}", json); + + TypeNameProperty deserialized = JsonConvert.DeserializeObject(json); + Assert.AreEqual("Name!", deserialized.Name); + Assert.IsInstanceOfType(typeof(List), deserialized.Value); + + List nested = (List)deserialized.Value; + Assert.AreEqual(5, nested.Count); + Assert.AreEqual(1, nested[0]); + Assert.AreEqual(2, nested[1]); + Assert.AreEqual(3, nested[2]); + Assert.AreEqual(4, nested[3]); + Assert.AreEqual(5, nested[4]); + } + +#if !SILVERLIGHT && !PocketPC + [Test] + public void DeserializeUsingCustomBinder() + { + string json = @"{ + ""$id"": ""1"", + ""$type"": ""Newtonsoft.Json.Tests.TestObjects.Employee"", + ""Name"": ""Name!"" +}"; + + object p = JsonConvert.DeserializeObject(json, null, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Objects, + Binder = new CustomSerializationBinder() + }); + + Assert.IsInstanceOfType(typeof(Person), p); + + Person person = (Person)p; + + Assert.AreEqual("Name!", person.Name); + } + + public class CustomSerializationBinder : SerializationBinder + { + public override Type BindToType(string assemblyName, string typeName) + { + return typeof (Person); + } + } +#endif + + [Test] + public void CollectionWithAbstractItems() + { + HolderClass testObject = new HolderClass(); + testObject.TestMember = new ContentSubClass("First One"); + testObject.AnotherTestMember = new Dictionary>(); + testObject.AnotherTestMember.Add(1, new List()); + testObject.AnotherTestMember[1].Add(new ContentSubClass("Second One")); + testObject.AThirdTestMember = new ContentSubClass("Third One"); + + + JsonSerializer serializingTester = new JsonSerializer(); + serializingTester.ReferenceLoopHandling = ReferenceLoopHandling.Ignore; + + StringWriter sw = new StringWriter(); + using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = Formatting.Indented; + serializingTester.TypeNameHandling = TypeNameHandling.Auto; + serializingTester.Serialize(jsonWriter, testObject); + } + + string json = sw.ToString(); + + string contentSubClassRef = ReflectionUtils.GetTypeName(typeof(ContentSubClass), FormatterAssemblyStyle.Simple); + string dictionaryRef = ReflectionUtils.GetTypeName(typeof(Dictionary>), FormatterAssemblyStyle.Simple); + string listRef = ReflectionUtils.GetTypeName(typeof(List), FormatterAssemblyStyle.Simple); + + + Assert.AreEqual(@"{ + ""TestMember"": { + ""$type"": """ + contentSubClassRef + @""", + ""SomeString"": ""First One"" + }, + ""AnotherTestMember"": { + ""$type"": """ + dictionaryRef + @""", + ""1"": { + ""$type"": """ + listRef + @""", + ""$values"": [ + { + ""$type"": """ + contentSubClassRef + @""", + ""SomeString"": ""Second One"" + } + ] + } + }, + ""AThirdTestMember"": { + ""$type"": """ + contentSubClassRef + @""", + ""SomeString"": ""Third One"" + } +}", json); + Console.WriteLine(json); + + StringReader sr = new StringReader(json); + + JsonSerializer deserializingTester = new JsonSerializer(); + + HolderClass anotherTestObject; + + using (JsonTextReader jsonReader = new JsonTextReader(sr)) + { + deserializingTester.TypeNameHandling = TypeNameHandling.Auto; + + anotherTestObject = deserializingTester.Deserialize(jsonReader); + } + + Assert.IsNotNull(anotherTestObject); + Assert.IsInstanceOfType(typeof(ContentSubClass), anotherTestObject.TestMember); + Assert.IsInstanceOfType(typeof(Dictionary>), anotherTestObject.AnotherTestMember); + Assert.AreEqual(1, anotherTestObject.AnotherTestMember.Count); + + IList list = anotherTestObject.AnotherTestMember[1]; + + Assert.IsInstanceOfType(typeof(List), list); + Assert.AreEqual(1, list.Count); + Assert.IsInstanceOfType(typeof(ContentSubClass), list[0]); + } + + [Test] + public void WriteObjectTypeNameForPropertyDemo() + { + Message message = new Message(); + message.Address = "http://www.google.com"; + message.Body = new SearchDetails + { + Query = "Json.NET", + Language = "en-us" + }; + + string json = JsonConvert.SerializeObject(message, Formatting.Indented); + // { + // "Address": "http://www.google.com", + // "Body": { + // "$type": "Newtonsoft.Json.Tests.Serialization.SearchDetails, Newtonsoft.Json.Tests", + // "Query": "Json.NET", + // "Language": "en-us" + // } + // } + + Message deserialized = JsonConvert.DeserializeObject(json); + + SearchDetails searchDetails = (SearchDetails) deserialized.Body; + // Json.NET + } + + public class UrlStatus + { + public int Status { get; set; } + public string Url { get; set; } + } + + + [Test] + public void GenericDictionaryObject() + { + Dictionary collection = new Dictionary() + { + {"First", new UrlStatus{ Status = 404, Url = @"http://www.bing.com"}}, + {"Second", new UrlStatus{Status = 400, Url = @"http://www.google.com"}}, + {"List", new List + { + new UrlStatus {Status = 300, Url = @"http://www.yahoo.com"}, + new UrlStatus {Status = 200, Url = @"http://www.askjeeves.com"} + } + } + }; + + string json = JsonConvert.SerializeObject(collection, Formatting.Indented, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple + }); + + string urlStatusTypeName = ReflectionUtils.GetTypeName(typeof (UrlStatus), FormatterAssemblyStyle.Simple); + + Assert.AreEqual(@"{ + ""$type"": ""System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[System.Object, mscorlib]], mscorlib"", + ""First"": { + ""$type"": """ + urlStatusTypeName + @""", + ""Status"": 404, + ""Url"": ""http://www.bing.com"" + }, + ""Second"": { + ""$type"": """ + urlStatusTypeName + @""", + ""Status"": 400, + ""Url"": ""http://www.google.com"" + }, + ""List"": { + ""$type"": ""System.Collections.Generic.List`1[[" + urlStatusTypeName + @"]], mscorlib"", + ""$values"": [ + { + ""$type"": """ + urlStatusTypeName + @""", + ""Status"": 300, + ""Url"": ""http://www.yahoo.com"" + }, + { + ""$type"": """ + urlStatusTypeName + @""", + ""Status"": 200, + ""Url"": ""http://www.askjeeves.com"" + } + ] + } +}", json); + + object c = JsonConvert.DeserializeObject(json, new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.All, + TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple + }); + + Assert.IsInstanceOfType(typeof(Dictionary), c); + + Dictionary newCollection = (Dictionary)c; + Assert.AreEqual(3, newCollection.Count); + Assert.AreEqual(@"http://www.bing.com", ((UrlStatus)newCollection["First"]).Url); + + List statues = (List) newCollection["List"]; + Assert.AreEqual(2, statues.Count); + } + + + [Test] + public void SerializingIEnumerableOfTShouldRetainGenericTypeInfo() + { + string productClassRef = ReflectionUtils.GetTypeName(typeof(Product[]), FormatterAssemblyStyle.Simple); + + CustomEnumerable products = new CustomEnumerable(); + + string json = JsonConvert.SerializeObject(products, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); + + Assert.AreEqual(@"{ + ""$type"": """ + productClassRef + @""", + ""$values"": [] +}", json); + } + + public class CustomEnumerable : IEnumerable + { + //NOTE: a simple linked list + private readonly T value; + private readonly CustomEnumerable next; + private readonly int count; + + private CustomEnumerable(T value, CustomEnumerable next) + { + this.value = value; + this.next = next; + count = this.next.count + 1; + } + + public CustomEnumerable() + { + count = 0; + } + + public CustomEnumerable AddFirst(T newVal) + { + return new CustomEnumerable(newVal, this); + } + + public IEnumerator GetEnumerator() + { + if (count == 0) // last node + yield break; + yield return value; + + var nextInLine = next; + while (nextInLine != null) + { + if (nextInLine.count != 0) + yield return nextInLine.value; + nextInLine = nextInLine.next; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + } + + public class Message + { + public string Address { get; set; } + + [JsonProperty(TypeNameHandling = TypeNameHandling.All)] + public object Body { get; set; } + } + + public class SearchDetails + { + public string Query { get; set; } + public string Language { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/SilverlightTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/SilverlightTests.cs new file mode 100644 index 0000000..4b1b579 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/SilverlightTests.cs @@ -0,0 +1,21 @@ +using System; +using System.Reflection; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests +{ + // todo: need to fix this to get WP unit tests running off right dlls +#if SILVERLIGHT + [TestFixture] + public class SilverlightTests + { + [Test] + public void SystemVersion() + { + Assembly systemAssembly = typeof(Uri).Assembly; + StringAssert.Contains("=4.0.0.0,", systemAssembly.FullName, + "Check we're testing a Silverlight 4.0 assembly"); + } + } +#endif +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs new file mode 100644 index 0000000..73690c9 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestFixtureBase.cs @@ -0,0 +1,64 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading; +using NUnit.Framework; + +namespace Newtonsoft.Json.Tests +{ + [TestFixture] + public abstract class TestFixtureBase + { + [SetUp] + protected void TestSetup() + { + //CultureInfo turkey = CultureInfo.CreateSpecificCulture("tr"); + //Thread.CurrentThread.CurrentCulture = turkey; + //Thread.CurrentThread.CurrentUICulture = turkey; + } + + //public string GetOffset(DateTime value) + //{ + // TimeSpan utcOffset = TimeZone.CurrentTimeZone.GetUtcOffset(value.ToLocalTime()); + + // return utcOffset.Hours.ToString("+00;-00") + utcOffset.Minutes.ToString("00;00"); + //} + + protected void WriteEscapedJson(string json) + { + Console.WriteLine(EscapeJson(json)); + } + + protected string EscapeJson(string json) + { + return @"@""" + json.Replace(@"""", @"""""") + @""""; + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/AbstractGenericBase.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/AbstractGenericBase.cs new file mode 100644 index 0000000..942f684 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/AbstractGenericBase.cs @@ -0,0 +1,32 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public abstract class AbstractGenericBase + { + public abstract TKey Id { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ArgumentConverterPrecedenceClassConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ArgumentConverterPrecedenceClassConverter.cs new file mode 100644 index 0000000..9c29d1a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ArgumentConverterPrecedenceClassConverter.cs @@ -0,0 +1,35 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ArgumentConverterPrecedenceClassConverter : ConverterPrecedenceClassConverter + { + public override string ConverterType + { + get { return "Argument"; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Article.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Article.cs new file mode 100644 index 0000000..dfde60c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Article.cs @@ -0,0 +1,41 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class Article + { + public string Name; + + public Article() + { + } + + public Article(string name) + { + Name = name; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ArticleCollection.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ArticleCollection.cs new file mode 100644 index 0000000..e7f4ae1 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ArticleCollection.cs @@ -0,0 +1,33 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ArticleCollection : List
+ { + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/BadJsonPropertyClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/BadJsonPropertyClass.cs new file mode 100644 index 0000000..18d0a03 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/BadJsonPropertyClass.cs @@ -0,0 +1,35 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class BadJsonPropertyClass + { + [JsonProperty("pie")] + public string Pie = "Yum"; + + public string pie = "PieChart!"; + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Bar.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Bar.cs new file mode 100644 index 0000000..d70adc6 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Bar.cs @@ -0,0 +1,32 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class Bar + { + public int Id { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Car.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Car.cs new file mode 100644 index 0000000..7071d6c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Car.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class Car + { + // included in JSON + public string Model { get; set; } + public DateTime Year { get; set; } + public List Features { get; set; } + + // ignored + [JsonIgnore] + public DateTime LastModified { get; set; } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/CircularReferenceClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/CircularReferenceClass.cs new file mode 100644 index 0000000..4db092c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/CircularReferenceClass.cs @@ -0,0 +1,40 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class CircularReferenceClass + { + [JsonProperty(Required = Required.Always)] + public string Name { get; set; } + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public CircularReferenceClass Child { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/CircularReferenceWithIdClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/CircularReferenceWithIdClass.cs new file mode 100644 index 0000000..f852d8f --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/CircularReferenceWithIdClass.cs @@ -0,0 +1,40 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + [JsonObject(Id = "MyExplicitId")] + public class CircularReferenceWithIdClass + { + [JsonProperty(Required = Required.AllowNull)] + public string Name { get; set; } + public CircularReferenceWithIdClass Child { get; set; } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ClassAndMemberConverterClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ClassAndMemberConverterClass.cs new file mode 100644 index 0000000..98bd253 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ClassAndMemberConverterClass.cs @@ -0,0 +1,34 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ClassAndMemberConverterClass + { + public ConverterPrecedenceClass DefaultConverter { get; set; } + [JsonConverter(typeof(MemberConverterPrecedenceClassConverter))] + public ConverterPrecedenceClass MemberConverter { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ClassConverterPrecedenceClassConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ClassConverterPrecedenceClassConverter.cs new file mode 100644 index 0000000..892fe29 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ClassConverterPrecedenceClassConverter.cs @@ -0,0 +1,35 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ClassConverterPrecedenceClassConverter : ConverterPrecedenceClassConverter + { + public override string ConverterType + { + get { return "Class"; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ClassWithArray.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ClassWithArray.cs new file mode 100644 index 0000000..74c042e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ClassWithArray.cs @@ -0,0 +1,54 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ClassWithArray + { + private readonly IList bar; + private string foo; + + public ClassWithArray() + { + bar = new List() { int.MaxValue }; + } + + [JsonProperty("foo")] + public string Foo + { + get { return foo; } + set { foo = value; } + } + + [JsonProperty(PropertyName = "bar")] + public IList Bar + { + get { return bar; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ClassWithGuid.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ClassWithGuid.cs new file mode 100644 index 0000000..82e345a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ClassWithGuid.cs @@ -0,0 +1,34 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ClassWithGuid + { + public Guid GuidField; + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Computer.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Computer.cs new file mode 100644 index 0000000..25b0bab --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Computer.cs @@ -0,0 +1,48 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Runtime.Serialization; + +#if !PocketPC && !NET20 +namespace Newtonsoft.Json.Tests.TestObjects +{ + [DataContract] + public class Computer + { + // included in JSON + [DataMember] + public string Name { get; set; } + [DataMember] + public decimal SalePrice { get; set; } + + // ignored + public string Manufacture { get; set; } + public int StockCount { get; set; } + public decimal WholeSalePrice { get; set; } + public DateTime NextShipmentDate { get; set; } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConstructorCaseSensitivityClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConstructorCaseSensitivityClass.cs new file mode 100644 index 0000000..17edf25 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConstructorCaseSensitivityClass.cs @@ -0,0 +1,41 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ConstructorCaseSensitivityClass + { + public string param1 { get; set; } + public string Param1 { get; set; } + public string Param2 { get; set; } + + public ConstructorCaseSensitivityClass(string param1, string Param1, string param2) + { + this.param1 = param1; + this.Param1 = Param1; + this.Param2 = param2; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConstructorReadonlyFields.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConstructorReadonlyFields.cs new file mode 100644 index 0000000..4bbaf35 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConstructorReadonlyFields.cs @@ -0,0 +1,39 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ConstructorReadonlyFields + { + public readonly string A; + public readonly int B; + + public ConstructorReadonlyFields(string a, int b) + { + A = a; + B = b; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Container.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Container.cs new file mode 100644 index 0000000..451149a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Container.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class Container + { + public IList In { get; set; } + public IList Out { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Content.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Content.cs new file mode 100644 index 0000000..6f8ae78 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Content.cs @@ -0,0 +1,49 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + [JsonObject(MemberSerialization = MemberSerialization.OptIn)] + public class Content : IEnumerable + { + [JsonProperty] + public List Children; + [JsonProperty] + public string Text; + + public IEnumerator GetEnumerator() + { + return Children.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return Children.GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ContentBaseClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ContentBaseClass.cs new file mode 100644 index 0000000..210242e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ContentBaseClass.cs @@ -0,0 +1,11 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public abstract class ContentBaseClass + { + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ContentSubClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ContentSubClass.cs new file mode 100644 index 0000000..6df0229 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ContentSubClass.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ContentSubClass : ContentBaseClass + { + public ContentSubClass() { } + public ContentSubClass(string EasyIn) + { + SomeString = EasyIn; + } + + public string SomeString { get; set; } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConverableMembers.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConverableMembers.cs new file mode 100644 index 0000000..4fcc93b --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConverableMembers.cs @@ -0,0 +1,48 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + [JsonObject] + public class ConverableMembers + { + public string String = "string"; + public int Int32 = int.MaxValue; + public uint UInt32 = uint.MaxValue; + public byte Byte = byte.MaxValue; + public sbyte SByte = sbyte.MaxValue; + public short Short = short.MaxValue; + public ushort UShort = ushort.MaxValue; + public long Long = long.MaxValue; + public ulong ULong = long.MaxValue; + public double Double = double.MaxValue; + public float Float = float.MaxValue; + public DBNull DBNull = DBNull.Value; + public bool Bool = true; + public char Char = '\0'; + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConverterPrecedenceClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConverterPrecedenceClass.cs new file mode 100644 index 0000000..145427c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConverterPrecedenceClass.cs @@ -0,0 +1,38 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + [JsonConverter(typeof(ClassConverterPrecedenceClassConverter))] + public class ConverterPrecedenceClass + { + public string TestValue { get; set; } + + public ConverterPrecedenceClass(string testValue) + { + TestValue = testValue; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConverterPrecedenceClassConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConverterPrecedenceClassConverter.cs new file mode 100644 index 0000000..f605b22 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ConverterPrecedenceClassConverter.cs @@ -0,0 +1,63 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Globalization; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public abstract class ConverterPrecedenceClassConverter : JsonConverter + { + public abstract string ConverterType { get; } + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + ConverterPrecedenceClass c = (ConverterPrecedenceClass)value; + + JToken j = new JArray(ConverterType, c.TestValue); + + j.WriteTo(writer); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + JToken j = JArray.Load(reader); + + string converter = (string)j[0]; + if (converter != ConverterType) + throw new Exception(StringUtils.FormatWith("Serialize converter {0} and deserialize converter {1} do not match.", CultureInfo.InvariantCulture, converter, ConverterType)); + + string testValue = (string)j[1]; + return new ConverterPrecedenceClass(testValue); + } + + public override bool CanConvert(Type objectType) + { + return (objectType == typeof(ConverterPrecedenceClass)); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DateTimeErrorObjectCollection.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DateTimeErrorObjectCollection.cs new file mode 100644 index 0000000..0f4d724 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DateTimeErrorObjectCollection.cs @@ -0,0 +1,41 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.ObjectModel; +using System.Runtime.Serialization; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class DateTimeErrorObjectCollection : Collection + { + [OnError] + internal void OnErrorMethod(StreamingContext context, ErrorContext errorContext) + { + errorContext.Handled = true; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DateTimeTestClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DateTimeTestClass.cs new file mode 100644 index 0000000..17d328c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DateTimeTestClass.cs @@ -0,0 +1,45 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC && !NET20 +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class DateTimeTestClass + { + public string PreField { get; set; } + [DefaultValue("")] + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + public DateTime DateTimeField { get; set; } + public DateTimeOffset DateTimeOffsetField { get; set; } + public string PostField { get; set; } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DefaultValueAttributeTestClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DefaultValueAttributeTestClass.cs new file mode 100644 index 0000000..b6b7c69 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DefaultValueAttributeTestClass.cs @@ -0,0 +1,41 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.ComponentModel; + +namespace Newtonsoft.Json.Tests.TestObjects +{ +#if !PocketPC + [Description("DefaultValueAttributeTestClass description!")] +#endif + public sealed class DefaultValueAttributeTestClass + { + [DefaultValue("TestProperty1Value")] + public string TestProperty1 { get; set; } + + [DefaultValue(21)] + public int TestField1; + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DictionaryInterfaceClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DictionaryInterfaceClass.cs new file mode 100644 index 0000000..6d8e12a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DictionaryInterfaceClass.cs @@ -0,0 +1,57 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; +using Newtonsoft.Json.Tests.TestObjects; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class DictionaryInterfaceClass + { + public string Name { get; set; } + public IDictionary Dictionary { get; set; } + public ICollection Collection { get; set; } + public EmployeeReference Employee { get; set; } + public object Random { get; set; } + + public DictionaryInterfaceClass() + { + Dictionary = new Dictionary + { + { "existing", 1 } + }; + Collection = new List + { + 1, + 2, + 3 + }; + Employee = new EmployeeReference + { + Name = "EmployeeName!" + }; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DoubleClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DoubleClass.cs new file mode 100644 index 0000000..df110cb --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/DoubleClass.cs @@ -0,0 +1,32 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class DoubleClass + { + public double? Height { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/EmployeeReference.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/EmployeeReference.cs new file mode 100644 index 0000000..241fb65 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/EmployeeReference.cs @@ -0,0 +1,39 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + [JsonObject(IsReference = true)] + public class EmployeeReference + { + public string Name { get; set; } + public EmployeeReference Manager { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Event.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Event.cs new file mode 100644 index 0000000..b58b126 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Event.cs @@ -0,0 +1,148 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + /// + /// What types of events are there? Just sticking to a basic set of four for now. + /// + /// + public enum EventType + { + Debug = 0, + Info = 1, + Warning = 2, + Error = 3 + } + + public sealed class Event + { + + /// + /// If no current user is specified, returns Nothing (0 from VB) + /// + /// + /// + private static int GetCurrentUserId() + { + return 0; + } + + /// + /// Gets either the application path or the current stack trace. + /// NOTE: You MUST call this from the top level entry point. Otherwise, + /// the stack trace will be buried in Logger itself. + /// + /// + /// + private static string GetCurrentSubLocation() + { + return ""; + } + + private string _sublocation; + private int _userId; + private EventType _type; + private string _summary; + private string _details; + private string _stackTrace; + private string _tag; + private DateTime _time; + + public Event(string summary) + { + _summary = summary; + _time = DateTime.Now; + + if (_userId == 0) _userId = GetCurrentUserId(); + //This call only works at top level for now. + //If _stackTrace = Nothing Then _stackTrace = Environment.StackTrace + if (_sublocation == null) _sublocation = GetCurrentSubLocation(); + } + + public Event(string sublocation, int userId, EventType type, string summary, string details, string stackTrace, string tag) + { + _sublocation = sublocation; + _userId = userId; + _type = type; + _summary = summary; + _details = details; + _stackTrace = stackTrace; + _tag = tag; + _time = DateTime.Now; + + if (_userId == 0) _userId = GetCurrentUserId(); + //If _stackTrace = Nothing Then _stackTrace = Environment.StackTrace + if (_sublocation == null) _sublocation = GetCurrentSubLocation(); + } + + public override string ToString() + { + return string.Format("{{ sublocation = {0}, userId = {1}, type = {2}, summary = {3}, details = {4}, stackTrace = {5}, tag = {6} }}", _sublocation, _userId, _type, _summary, _details, _stackTrace, _tag); + } + + public string sublocation + { + get { return _sublocation; } + set { _sublocation = value; } + } + public int userId + { + get { return _userId; } + set { _userId = value; } + } + public EventType type + { + get { return _type; } + set { _type = value; } + } + public string summary + { + get { return _summary; } + set { _summary = value; } + } + public string details + { + get { return _details; } + set { _details = value; } + } + public string stackTrace + { + get { return _stackTrace; } + set { _stackTrace = value; } + } + public string tag + { + get { return _tag; } + set { _tag = value; } + } + public DateTime time + { + get { return _time; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Foo.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Foo.cs new file mode 100644 index 0000000..657037b --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Foo.cs @@ -0,0 +1,40 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class Foo + { + public Foo() + { + Bars = new List(); + } + + [JsonConverter(typeof(ListOfIds))] + public List Bars { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/GenericImpl.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/GenericImpl.cs new file mode 100644 index 0000000..e4b82c8 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/GenericImpl.cs @@ -0,0 +1,32 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class GenericImpl : AbstractGenericBase + { + public override int Id { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/GenericListAndDictionaryInterfaceProperties.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/GenericListAndDictionaryInterfaceProperties.cs new file mode 100644 index 0000000..87fefc4 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/GenericListAndDictionaryInterfaceProperties.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class GenericListAndDictionaryInterfaceProperties + { + public IEnumerable IEnumerableProperty { get; set; } + public IList IListProperty { get; set; } + public IDictionary IDictionaryProperty { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/GetOnlyPropertyClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/GetOnlyPropertyClass.cs new file mode 100644 index 0000000..4243aa1 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/GetOnlyPropertyClass.cs @@ -0,0 +1,37 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class GetOnlyPropertyClass + { + public string Field = "Field"; + + public string GetOnlyProperty + { + get { return "GetOnlyProperty"; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/GoogleMapGeocoderStructure.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/GoogleMapGeocoderStructure.cs new file mode 100644 index 0000000..ceee06f --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/GoogleMapGeocoderStructure.cs @@ -0,0 +1,95 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class GoogleMapGeocoderStructure + { + public string Name; + public Status Status; + public List Placemark; + } + + public class Status + { + public string Request; + public string Code; + } + + public class Placemark + { + public string Address; + public AddressDetails AddressDetails; + public Point Point; + } + + public class AddressDetails + { + public int Accuracy; + public Country Country; + } + + public class Country + { + public string CountryNameCode; + public AdministrativeArea AdministrativeArea; + } + + public class AdministrativeArea + { + public string AdministrativeAreaName; + public SubAdministrativeArea SubAdministrativeArea; + } + + public class SubAdministrativeArea + { + public string SubAdministrativeAreaName; + public Locality Locality; + } + + public class Locality + { + public string LocalityName; + public Thoroughfare Thoroughfare; + public PostalCode PostalCode; + } + + public class Thoroughfare + { + public string ThoroughfareName; + } + + public class PostalCode + { + public string PostalCodeNumber; + } + + public class Point + { + public List Coordinates; + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/HolderClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/HolderClass.cs new file mode 100644 index 0000000..4ddffb2 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/HolderClass.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class HolderClass + { + public HolderClass() { } + + [Newtonsoft.Json.JsonProperty(TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All)] + public ContentBaseClass TestMember { get; set; } + + [Newtonsoft.Json.JsonProperty(TypeNameHandling = Newtonsoft.Json.TypeNameHandling.All)] + public Dictionary> AnotherTestMember { get; set; } + + public ContentBaseClass AThirdTestMember { get; set; } + + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/IncompatibleJsonAttributeClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/IncompatibleJsonAttributeClass.cs new file mode 100644 index 0000000..d1f961e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/IncompatibleJsonAttributeClass.cs @@ -0,0 +1,34 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using Newtonsoft.Json.Converters; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + [JsonConverter(typeof(IsoDateTimeConverter))] + public class IncompatibleJsonAttributeClass + { + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/InterfacePropertyTestClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/InterfacePropertyTestClass.cs new file mode 100644 index 0000000..9a3eb56 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/InterfacePropertyTestClass.cs @@ -0,0 +1,51 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class Co : ICo + { + public string Name { get; set; } + } + + public interface ICo + { + string Name { get; set; } + } + + public interface IInterfacePropertyTestClass + { + ICo co { get; set; } + } + + public class InterfacePropertyTestClass : IInterfacePropertyTestClass + { + public ICo co { get; set; } + + public InterfacePropertyTestClass() + { + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Invoice.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Invoice.cs new file mode 100644 index 0000000..bdce565 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Invoice.cs @@ -0,0 +1,47 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.ComponentModel; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class Invoice + { + public string Company { get; set; } + public decimal Amount { get; set; } + + // false is default value of bool + public bool Paid { get; set; } + // null is default value of nullable + public DateTime? PaidDate { get; set; } + + // customize default values + [DefaultValue(30)] + public int FollowUpDays { get; set; } + [DefaultValue("")] + public string FollowUpEmailAddress { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JaggedArray.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JaggedArray.cs new file mode 100644 index 0000000..63cac1c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JaggedArray.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class JaggedArray + { + public string Before { get; set; } + public int[][] Coordinates { get; set; } + public string After { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JsonIgnoreAttributeOnClassTestClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JsonIgnoreAttributeOnClassTestClass.cs new file mode 100644 index 0000000..27ff136 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JsonIgnoreAttributeOnClassTestClass.cs @@ -0,0 +1,52 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + [JsonObject(MemberSerialization.OptIn)] + public class JsonIgnoreAttributeOnClassTestClass + { + private int _property = 21; + private int _ignoredProperty = 12; + + [JsonProperty("TheField")] + public int Field; + + [JsonProperty] + public int Property + { + get { return _property; } + } + + public int IgnoredField; + + [JsonProperty] + [JsonIgnore] // JsonIgnore should take priority + public int IgnoredProperty + { + get { return _ignoredProperty; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JsonIgnoreAttributeTestClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JsonIgnoreAttributeTestClass.cs new file mode 100644 index 0000000..cb1fbde --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JsonIgnoreAttributeTestClass.cs @@ -0,0 +1,51 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class JsonIgnoreAttributeTestClass + { + private int _property = 21; + private int _ignoredProperty = 12; + + public int Field; + public int Property + { + get { return _property; } + } + + [JsonIgnore] + public int IgnoredField; + + [JsonIgnore] + public int IgnoredProperty + { + get { return _ignoredProperty; } + } + + [JsonIgnore] + public Product IgnoredObject = new Product(); + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JsonPropertyClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JsonPropertyClass.cs new file mode 100644 index 0000000..1714f01 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JsonPropertyClass.cs @@ -0,0 +1,47 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class JsonPropertyClass + { + [JsonProperty("pie")] + public string Pie = "Yum"; + + [JsonIgnore] + public string pie = "No pie for you!"; + + public string pie1 = "PieChart!"; + + private int _sweetCakesCount; + + [JsonProperty("sweet_cakes_count")] + public int SweetCakesCount + { + get { return _sweetCakesCount; } + set { _sweetCakesCount = value; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JsonPropertyWithHandlingValues.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JsonPropertyWithHandlingValues.cs new file mode 100644 index 0000000..6feb12d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/JsonPropertyWithHandlingValues.cs @@ -0,0 +1,55 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.ComponentModel; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class JsonPropertyWithHandlingValues + { + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [DefaultValue("Default!")] + public string DefaultValueHandlingIgnoreProperty { get; set; } + + [JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)] + [DefaultValue("Default!")] + public string DefaultValueHandlingIncludeProperty { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string NullValueHandlingIgnoreProperty { get; set; } + + [JsonProperty(NullValueHandling = NullValueHandling.Include)] + public string NullValueHandlingIncludeProperty { get; set; } + + [JsonProperty(ReferenceLoopHandling = ReferenceLoopHandling.Error)] + public JsonPropertyWithHandlingValues ReferenceLoopHandlingErrorProperty { get; set; } + + [JsonProperty(ReferenceLoopHandling = ReferenceLoopHandling.Ignore)] + public JsonPropertyWithHandlingValues ReferenceLoopHandlingIgnoreProperty { get; set; } + + [JsonProperty(ReferenceLoopHandling = ReferenceLoopHandling.Serialize)] + public JsonPropertyWithHandlingValues ReferenceLoopHandlingSerializeProperty { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ListErrorObject.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ListErrorObject.cs new file mode 100644 index 0000000..800e682 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ListErrorObject.cs @@ -0,0 +1,58 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ListErrorObject + { + public string Member { get; set; } + + private string _throwError; + public string ThrowError + { + get + { + if (_throwError != null) + return _throwError; + + throw new Exception("ListErrorObject.ThrowError get error!"); + } + set + { + if (value != null && value.StartsWith("Handle")) + { + _throwError = value; + return; + } + + throw new Exception("ListErrorObject.ThrowError set error!"); + } + } + + public string Member2 { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ListErrorObjectCollection.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ListErrorObjectCollection.cs new file mode 100644 index 0000000..2b37501 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ListErrorObjectCollection.cs @@ -0,0 +1,40 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.ObjectModel; +using System.Runtime.Serialization; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ListErrorObjectCollection : Collection + { + [OnError] + internal void OnErrorMethod(StreamingContext context, ErrorContext errorContext) + { + errorContext.Handled = true; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ListOfIds.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ListOfIds.cs new file mode 100644 index 0000000..b7d68a3 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ListOfIds.cs @@ -0,0 +1,70 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ListOfIds : JsonConverter where T : Bar, new() + { + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + IList list = (IList)value; + + writer.WriteStartArray(); + foreach (T item in list) + { + writer.WriteValue(item.Id); + } + writer.WriteEndArray(); + } + + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + IList list = new List(); + + reader.Read(); + while (reader.TokenType != JsonToken.EndArray) + { + long id = (long)reader.Value; + + list.Add(new T + { + Id = Convert.ToInt32(id) + }); + + reader.Read(); + } + + return list; + } + + public override bool CanConvert(Type objectType) + { + return typeof(IList).IsAssignableFrom(objectType); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ListTestClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ListTestClass.cs new file mode 100644 index 0000000..74ea35d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ListTestClass.cs @@ -0,0 +1,45 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + [JsonObject(MemberSerialization.OptIn)] + public class ListTestClass + { + [JsonProperty] + public string id { get; set; } + [JsonProperty] + public List items { get; set; } + } + + [JsonObject(MemberSerialization.OptIn)] + public class ListItem + { + [JsonProperty] + public string id { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/LogEntry.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/LogEntry.cs new file mode 100644 index 0000000..0dbd63f --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/LogEntry.cs @@ -0,0 +1,35 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class LogEntry + { + public string Details { get; set; } + public DateTime LogDate { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/MemberConverterClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/MemberConverterClass.cs new file mode 100644 index 0000000..89ab7a6 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/MemberConverterClass.cs @@ -0,0 +1,37 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using Newtonsoft.Json.Converters; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class MemberConverterClass + { + public DateTime DefaultConverter { get; set; } + [JsonConverter(typeof(IsoDateTimeConverter))] + public DateTime MemberConverter { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/MemberConverterPrecedenceClassConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/MemberConverterPrecedenceClassConverter.cs new file mode 100644 index 0000000..afe3498 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/MemberConverterPrecedenceClassConverter.cs @@ -0,0 +1,35 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class MemberConverterPrecedenceClassConverter : ConverterPrecedenceClassConverter + { + public override string ConverterType + { + get { return "Member"; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/MethodExecutorObject.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/MethodExecutorObject.cs new file mode 100644 index 0000000..7586d05 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/MethodExecutorObject.cs @@ -0,0 +1,34 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class MethodExecutorObject + { + public string serverClassName; + public object[] serverMethodParams; + public string clientGetResultFunction; + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Movie.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Movie.cs new file mode 100644 index 0000000..3d568a9 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Movie.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class Movie + { + public string Name { get; set; } + public string Description { get; set; } + public string Classification { get; set; } + public string Studio { get; set; } + public DateTime? ReleaseDate { get; set; } + public List ReleaseCountries { get; set; } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/MyClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/MyClass.cs new file mode 100644 index 0000000..d44da3d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/MyClass.cs @@ -0,0 +1,34 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class MyClass + { + public int PreProperty { get; set; } + //public DateTime DateProperty { get; set; } + public int PostProperty { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Name.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Name.cs new file mode 100644 index 0000000..5e61336 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Name.cs @@ -0,0 +1,41 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class Name + { + public string personsName; + + public List pNumbers = new List(); + + public Name(string personsName) + { + this.personsName = personsName; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/NonRequest.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/NonRequest.cs new file mode 100644 index 0000000..8f90268 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/NonRequest.cs @@ -0,0 +1,37 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class NonRequest + { + public Guid Sid { get; set; } + public Guid Uid { get; set; } + public IList FidOrder { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/NullableDateTimeTestClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/NullableDateTimeTestClass.cs new file mode 100644 index 0000000..2ede479 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/NullableDateTimeTestClass.cs @@ -0,0 +1,42 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC && !NET20 +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class NullableDateTimeTestClass + { + public string PreField { get; set; } + public DateTime? DateTimeField { get; set; } + public DateTimeOffset? DateTimeOffsetField { get; set; } + public string PostField { get; set; } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ObjectArrayPropertyTest.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ObjectArrayPropertyTest.cs new file mode 100644 index 0000000..1bea547 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ObjectArrayPropertyTest.cs @@ -0,0 +1,34 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ObjectArrayPropertyTest + { + public string Action { get; set; } + public string Method { get; set; } + public object[] Data { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Person.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Person.cs new file mode 100644 index 0000000..7e93090 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Person.cs @@ -0,0 +1,54 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.ComponentModel; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + [JsonObject(Id = "Person", Title = "Title!", Description = "JsonObjectAttribute description!", MemberSerialization = MemberSerialization.OptIn)] +#if !PocketPC + [Description("DescriptionAttribute description!")] +#endif + public class Person + { + // "John Smith" + [JsonProperty] + public string Name { get; set; } + + // "2000-12-15T22:11:03" + [JsonProperty] + //[JsonConverter(typeof(IsoDateTimeConverter))] + public DateTime BirthDate { get; set; } + + // new Date(976918263055) + [JsonProperty] + //[JsonConverter(typeof(JavaScriptDateTimeConverter))] + public DateTime LastModified { get; set; } + + // not serialized + public string Department { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PersonError.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PersonError.cs new file mode 100644 index 0000000..6542198 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PersonError.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class PersonError + { + private List _roles; + + public string Name { get; set; } + public int Age { get; set; } + public List Roles + { + get + { + if (_roles == null) + throw new Exception("Roles not loaded!"); + + return _roles; + } + set { _roles = value; } + } + public string Title { get; set; } + + [OnError] + internal void HandleError(StreamingContext context, ErrorContext errorContext) + { + errorContext.Handled = true; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PersonPropertyClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PersonPropertyClass.cs new file mode 100644 index 0000000..f7ab01c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PersonPropertyClass.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class PersonPropertyClass + { + public Person Person { get; set; } + + public PersonPropertyClass() + { + Person = new WagePerson(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PersonRaw.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PersonRaw.cs new file mode 100644 index 0000000..82f67ee --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PersonRaw.cs @@ -0,0 +1,65 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class PersonRaw + { + private Guid _internalId; + private string _firstName; + private string _lastName; + private JRaw _rawContent; + + [JsonIgnore] + public Guid InternalId + { + get { return _internalId; } + set { _internalId = value; } + } + + [JsonProperty("first_name")] + public string FirstName + { + get { return _firstName; } + set { _firstName = value; } + } + + public JRaw RawContent + { + get { return _rawContent; } + set { _rawContent = value; } + } + + [JsonProperty("last_name")] + public string LastName + { + get { return _lastName; } + set { _lastName = value; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PhoneNumber.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PhoneNumber.cs new file mode 100644 index 0000000..411c6a2 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PhoneNumber.cs @@ -0,0 +1,37 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class PhoneNumber + { + public string phoneNumber; + + public PhoneNumber(string phoneNumber) + { + this.phoneNumber = phoneNumber; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PrivateConstructorTestClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PrivateConstructorTestClass.cs new file mode 100644 index 0000000..5344c60 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PrivateConstructorTestClass.cs @@ -0,0 +1,37 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class PrivateConstructorTestClass + { + public string Name { get; set; } + public int Age { get; set; } + + private PrivateConstructorTestClass() + { + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PrivateConstructorWithPublicParametizedConstructorTestClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PrivateConstructorWithPublicParametizedConstructorTestClass.cs new file mode 100644 index 0000000..7077d2b --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PrivateConstructorWithPublicParametizedConstructorTestClass.cs @@ -0,0 +1,45 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class PrivateConstructorWithPublicParametizedConstructorTestClass + { + public string Name { get; set; } + public int Age { get; set; } + + private PrivateConstructorWithPublicParametizedConstructorTestClass() + { + Age = 1; + } + + public PrivateConstructorWithPublicParametizedConstructorTestClass(string dummy) + { + throw new Exception("Should never get here."); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PrivateMembersClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PrivateMembersClass.cs new file mode 100644 index 0000000..01932ed --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PrivateMembersClass.cs @@ -0,0 +1,55 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class PrivateMembersClass + { + public PrivateMembersClass(string privateString, string internalString) + { + _privateString = privateString; + _internalString = internalString; + } + + public PrivateMembersClass() + { + i = default(int); + } + + private string _privateString; + private readonly int i; + internal string _internalString; + + public int UseValue() + { + return i; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Product.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Product.cs new file mode 100644 index 0000000..f0ba4e1 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Product.cs @@ -0,0 +1,54 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class Product + { + public string Name; + public DateTime ExpiryDate = new DateTime(2000, 1, 1, 0, 0, 0, DateTimeKind.Utc); + public decimal Price; + public string[] Sizes; + + public override bool Equals(object obj) + { + if (obj is Product) + { + Product p = (Product)obj; + + return (p.Name == Name && p.ExpiryDate == ExpiryDate && p.Price == Price); + } + + return base.Equals(obj); + } + + public override int GetHashCode() + { + return (Name ?? string.Empty).GetHashCode(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ProductCollection.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ProductCollection.cs new file mode 100644 index 0000000..9e22492 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ProductCollection.cs @@ -0,0 +1,33 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ProductCollection : List + { + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ProductShort.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ProductShort.cs new file mode 100644 index 0000000..902f1da --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/ProductShort.cs @@ -0,0 +1,37 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class ProductShort + { + public string Name; + public DateTime ExpiryDate; + //public decimal Price; + public string[] Sizes; + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PropertyCase.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PropertyCase.cs new file mode 100644 index 0000000..e98e070 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/PropertyCase.cs @@ -0,0 +1,35 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class PropertyCase + { + public string firstName { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + public string lastName { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/RequestOnly.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/RequestOnly.cs new file mode 100644 index 0000000..0b47bce --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/RequestOnly.cs @@ -0,0 +1,32 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class RequestOnly + { + public string Request { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/RequiredMembersClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/RequiredMembersClass.cs new file mode 100644 index 0000000..8731e69 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/RequiredMembersClass.cs @@ -0,0 +1,47 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class RequiredMembersClass + { + [JsonProperty(Required = Required.Always)] + public string FirstName { get; set; } + + [JsonProperty] + public string MiddleName { get; set; } + + [JsonProperty(Required = Required.AllowNull)] + public string LastName { get; set; } + + [JsonProperty(Required = Required.Default)] + public DateTime BirthDate { get; set; } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/RoleTransfer.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/RoleTransfer.cs new file mode 100644 index 0000000..c6fe92f --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/RoleTransfer.cs @@ -0,0 +1,46 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public enum RoleTransferOperation + { + First, + Second + } + + public enum RoleTransferDirection + { + First, + Second + } + + public class RoleTransfer + { + public RoleTransferOperation Operation { get; set; } //This is enum type + public string RoleName { get; set; } + public RoleTransferDirection Direction { get; set; } //This is enum type + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SearchResult.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SearchResult.cs new file mode 100644 index 0000000..d326ef3 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SearchResult.cs @@ -0,0 +1,34 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class SearchResult + { + public string Title { get; set; } + public string Content { get; set; } + public string Url { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestDictionary.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestDictionary.cs new file mode 100644 index 0000000..e613704 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestDictionary.cs @@ -0,0 +1,82 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class SerializationEventTestDictionary : Dictionary + { + // This member is serialized and deserialized with no change. + public int Member1 { get; private set; } + + // The value of this field is set and reset during and + // after serialization. + public string Member2 { get; private set; } + + // This field is not serialized. The OnDeserializedAttribute + // is used to set the member value after serialization. + public string Member3 { get; private set; } + + // This field is set to null, but populated after deserialization. + public string Member4 { get; private set; } + + public SerializationEventTestDictionary() + { + Member1 = 11; + Member2 = "Hello World!"; + Member3 = "This is a nonserialized value"; + Member4 = null; + } + + [OnSerializing] + internal void OnSerializingMethod(StreamingContext context) + { + Member2 = "This value went into the data file during serialization."; + Add(decimal.MaxValue, "Inserted on serializing"); + } + + [OnSerialized] + internal void OnSerializedMethod(StreamingContext context) + { + Member2 = "This value was reset after serialization."; + } + + [OnDeserializing] + internal void OnDeserializingMethod(StreamingContext context) + { + Member3 = "This value was set during deserialization"; + } + + [OnDeserialized] + internal void OnDeserializedMethod(StreamingContext context) + { + Member4 = "This value was set after deserialization."; + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestList.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestList.cs new file mode 100644 index 0000000..2eb84fb --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestList.cs @@ -0,0 +1,83 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC +using System.Collections.ObjectModel; +using System.Runtime.Serialization; +using Newtonsoft.Json.Tests.Serialization; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class SerializationEventTestList : Collection + { + // This member is serialized and deserialized with no change. + public int Member1 { get; private set; } + + // The value of this field is set and reset during and + // after serialization. + public string Member2 { get; private set; } + + // This field is not serialized. The OnDeserializedAttribute + // is used to set the member value after serialization. + public string Member3 { get; private set; } + + // This field is set to null, but populated after deserialization. + public string Member4 { get; private set; } + + public SerializationEventTestList() + { + Member1 = 11; + Member2 = "Hello World!"; + Member3 = "This is a nonserialized value"; + Member4 = null; + } + + [OnSerializing] + internal void OnSerializingMethod(StreamingContext context) + { + Member2 = "This value went into the data file during serialization."; + Insert(0, -1); + } + + [OnSerialized] + internal void OnSerializedMethod(StreamingContext context) + { + Member2 = "This value was reset after serialization."; + } + + [OnDeserializing] + internal void OnDeserializingMethod(StreamingContext context) + { + Member3 = "This value was set during deserialization"; + } + + [OnDeserialized] + internal void OnDeserializedMethod(StreamingContext context) + { + Member4 = "This value was set after deserialization."; + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObject.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObject.cs new file mode 100644 index 0000000..a6cd39c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObject.cs @@ -0,0 +1,103 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC +using System; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class SerializationEventTestObject + { + // This member is serialized and deserialized with no change. + public int Member1 { get; set; } + + // The value of this field is set and reset during and + // after serialization. + public string Member2 { get; set; } + + // This field is not serialized. The OnDeserializedAttribute + // is used to set the member value after serialization. + [JsonIgnore] + public string Member3 { get; set; } + + // This field is set to null, but populated after deserialization. + public string Member4 { get; set; } + + // This field is set to null, but populated after error. + [JsonIgnore] + public string Member5 { get; set; } + + // Getting or setting this field will throw an error. + public string Member6 + { + get { throw new Exception("Member5 get error!"); } + set { throw new Exception("Member5 set error!"); } + } + + public SerializationEventTestObject() + { + Member1 = 11; + Member2 = "Hello World!"; + Member3 = "This is a nonserialized value"; + Member4 = null; + } + + [OnSerializing] + internal void OnSerializingMethod(StreamingContext context) + { + Member2 = "This value went into the data file during serialization."; + } + + [OnSerialized] + internal void OnSerializedMethod(StreamingContext context) + { + Member2 = "This value was reset after serialization."; + } + + [OnDeserializing] + internal void OnDeserializingMethod(StreamingContext context) + { + Member3 = "This value was set during deserialization"; + } + + [OnDeserialized] + internal void OnDeserializedMethod(StreamingContext context) + { + Member4 = "This value was set after deserialization."; + } + + [OnError] + internal void OnErrorMethod(StreamingContext context, ErrorContext errorContext) + { + Member5 = "Error message for member " + errorContext.Member + " = " + errorContext.Error.Message; + errorContext.Handled = true; + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObjectWithConstructor.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObjectWithConstructor.cs new file mode 100644 index 0000000..2aa159d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SerializationEventTestObjectWithConstructor.cs @@ -0,0 +1,85 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC +using System; +using System.Runtime.Serialization; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class SerializationEventTestObjectWithConstructor + { + // This member is serialized and deserialized with no change. + public int Member1 { get; private set; } + + // The value of this field is set and reset during and + // after serialization. + public string Member2 { get; private set; } + + // This field is not serialized. The OnDeserializedAttribute + // is used to set the member value after serialization. + [JsonIgnore] + public string Member3 { get; private set; } + + // This field is set to null, but populated after deserialization. + public string Member4 { get; private set; } + + public SerializationEventTestObjectWithConstructor(int member1, + string member2, + string member4) + { + Member1 = member1; + Member2 = member2; + Member3 = "This is a nonserialized value"; + Member4 = member4; + } + + [OnSerializing] + internal void OnSerializingMethod(StreamingContext context) + { + Member2 = "This value went into the data file during serialization."; + } + + [OnSerialized] + internal void OnSerializedMethod(StreamingContext context) + { + Member2 = "This value was reset after serialization."; + } + + [OnDeserializing] + internal void OnDeserializingMethod(StreamingContext context) + { + Member3 = "This value was set during deserialization"; + } + + [OnDeserialized] + internal void OnDeserializedMethod(StreamingContext context) + { + Member4 = "This value was set after deserialization."; + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SetOnlyPropertyClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SetOnlyPropertyClass.cs new file mode 100644 index 0000000..da57e65 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SetOnlyPropertyClass.cs @@ -0,0 +1,37 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class SetOnlyPropertyClass + { + public string Field = "Field"; + + public string SetOnlyProperty + { + set { } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SetOnlyPropertyClass2.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SetOnlyPropertyClass2.cs new file mode 100644 index 0000000..6b6d0be --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SetOnlyPropertyClass2.cs @@ -0,0 +1,40 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class SetOnlyPropertyClass2 + { + private object _value; + public object SetOnlyProperty + { + set { _value = value; } + } + public object GetValue() + { + return _value; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Shortie.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Shortie.cs new file mode 100644 index 0000000..e08db45 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Shortie.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Serialization; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class Shortie + { + public string Original { get; set; } + public string Shortened { get; set; } + public string Short { get; set; } + public ShortieException Error { get; set; } + } + + public class ShortieException + { + public int Code { get; set; } + public string ErrorMessage { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Store.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Store.cs new file mode 100644 index 0000000..7a6f0aa --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/Store.cs @@ -0,0 +1,64 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class Store + { + public StoreColor Color = StoreColor.Yellow; + public DateTime Establised = new DateTime(2010, 1, 22, 1, 1, 1, DateTimeKind.Utc); + public double Width = 1.1; + public int Employees = 999; + public int[] RoomsPerFloor = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + public bool Open = false; + public char Symbol = '@'; + [JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)] + public List Mottos = new List(); + public decimal Cost = 100980.1M; + public string Escape = "\r\n\t\f\b?{\\r\\n\"\'"; + [JsonProperty(ObjectCreationHandling = ObjectCreationHandling.Replace)] + public List product = new List(); + + public Store() + { + Mottos.Add("Hello World"); + Mottos.Add("öäüÖÄÜ\\'{new Date(12345);}[222]_µ@²³~"); + Mottos.Add(null); + Mottos.Add(" "); + + Product rocket = new Product(); + rocket.Name = "Rocket"; + rocket.ExpiryDate = new DateTime(2000, 2, 2, 23, 1, 30, DateTimeKind.Utc); + Product alien = new Product(); + alien.Name = "Alien"; + + product.Add(rocket); + product.Add(alien); + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/StoreColor.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/StoreColor.cs new file mode 100644 index 0000000..bc384b1 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/StoreColor.cs @@ -0,0 +1,39 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + [Flags] + public enum StoreColor + { + Black = 1, + Red = 2, + Yellow = 4, + White = 8, + DarkGoldenrod = 16 + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/StructTest.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/StructTest.cs new file mode 100644 index 0000000..45d492f --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/StructTest.cs @@ -0,0 +1,35 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public struct StructTest + { + public string StringProperty { get; set; } + public string StringField; + public int IntProperty { get; set; } + public int IntField; + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SubKlass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SubKlass.cs new file mode 100644 index 0000000..9294a8e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SubKlass.cs @@ -0,0 +1,33 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class SubKlass : SuperKlass + { + public string SubProp { get; set; } + public SubKlass(string subprop) { SubProp = subprop; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SuperKlass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SuperKlass.cs new file mode 100644 index 0000000..2c581ac --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/SuperKlass.cs @@ -0,0 +1,33 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class SuperKlass + { + public string SuperProp { get; set; } + public SuperKlass() { SuperProp = "default superprop"; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/TypeClass.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/TypeClass.cs new file mode 100644 index 0000000..21333a9 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/TypeClass.cs @@ -0,0 +1,34 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class TypeClass + { + public Type TypeProperty { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/TypedSubHashtable.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/TypedSubHashtable.cs new file mode 100644 index 0000000..636d417 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/TypedSubHashtable.cs @@ -0,0 +1,37 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections; + +namespace Newtonsoft.Json.Tests.TestObjects +{ +#if !SILVERLIGHT + public class TypedSubHashtable + { + public string Name; + public Hashtable Hash; + } +#endif +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/UserNullable.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/UserNullable.cs new file mode 100644 index 0000000..8fb8cf6 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/UserNullable.cs @@ -0,0 +1,40 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class UserNullable + { + public Guid Id; + public string FName; + public string LName; + public int RoleId; + public int? NullableRoleId; + public int? NullRoleId; + public bool? Active; + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/VersionKeyedCollection.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/VersionKeyedCollection.cs new file mode 100644 index 0000000..17d62af --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/VersionKeyedCollection.cs @@ -0,0 +1,73 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Runtime.Serialization; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Tests.TestObjects; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class VersionKeyedCollection : KeyedCollection, IEnumerable + { + public List Messages { get; set; } + + public VersionKeyedCollection() + { + Messages = new List(); + } + + protected override string GetKeyForItem(Person item) + { + return item.Name; + } + + [OnError] + internal void OnErrorMethod(StreamingContext context, ErrorContext errorContext) + { + Messages.Add("Error message for member " + errorContext.Member + " = " + errorContext.Error.Message); + errorContext.Handled = true; + } + + IEnumerator IEnumerable.GetEnumerator() + { + for (int i = 0; i < Count; i++) + { + if (i % 2 == 0) + throw new Exception("Index even: " + i); + + yield return this[i]; + } + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable) this).GetEnumerator(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/WagePerson.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/WagePerson.cs new file mode 100644 index 0000000..b99f00d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/TestObjects/WagePerson.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Tests.TestObjects +{ + public class WagePerson : Person + { + [JsonProperty] + public decimal HourlyWage { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Utilities/DynamicReflectionDelegateFactoryTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Utilities/DynamicReflectionDelegateFactoryTests.cs new file mode 100644 index 0000000..bdb6d1c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Utilities/DynamicReflectionDelegateFactoryTests.cs @@ -0,0 +1,112 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC && !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Tests.TestObjects; +using Newtonsoft.Json.Tests.Serialization; + +namespace Newtonsoft.Json.Tests.Utilities +{ + public class DynamicReflectionDelegateFactoryTests : TestFixtureBase + { + [Test] + [ExpectedException(typeof(InvalidCastException), ExpectedMessage = "Unable to cast object of type 'Newtonsoft.Json.Tests.TestObjects.Person' to type 'Newtonsoft.Json.Tests.TestObjects.Movie'.")] + public void CreateGetWithBadObjectTarget() + { + Person p = new Person(); + p.Name = "Hi"; + + Func setter = DynamicReflectionDelegateFactory.Instance.CreateGet(typeof(Movie).GetProperty("Name")); + + setter(p); + } + + [Test] + [ExpectedException(typeof(InvalidCastException), ExpectedMessage = "Unable to cast object of type 'Newtonsoft.Json.Tests.TestObjects.Person' to type 'Newtonsoft.Json.Tests.TestObjects.Movie'.")] + public void CreateSetWithBadObjectTarget() + { + Person p = new Person(); + Movie m = new Movie(); + + Action setter = DynamicReflectionDelegateFactory.Instance.CreateSet(typeof(Movie).GetProperty("Name")); + + setter(m, "Hi"); + + Assert.AreEqual(m.Name, "Hi"); + + setter(p, "Hi"); + } + + [Test] + [ExpectedException(typeof(InvalidCastException), ExpectedMessage = "Specified cast is not valid.")] + public void CreateSetWithBadTarget() + { + object structTest = new StructTest(); + + Action setter = DynamicReflectionDelegateFactory.Instance.CreateSet(typeof(StructTest).GetProperty("StringProperty")); + + setter(structTest, "Hi"); + + Assert.AreEqual("Hi", ((StructTest)structTest).StringProperty); + + setter(new TimeSpan(), "Hi"); + } + + [Test] + [ExpectedException(typeof(InvalidCastException), ExpectedMessage = "Unable to cast object of type 'System.Version' to type 'System.String'.")] + public void CreateSetWithBadObjectValue() + { + Movie m = new Movie(); + + Action setter = DynamicReflectionDelegateFactory.Instance.CreateSet(typeof(Movie).GetProperty("Name")); + + setter(m, new Version()); + } + + [Test] + public void CreateStaticMethodCall() + { + MethodInfo castMethodInfo = typeof(JsonSerializerTest.DictionaryKey).GetMethod("op_Implicit", new[] { typeof(string) }); + + Assert.IsNotNull(castMethodInfo); + + MethodCall call = DynamicReflectionDelegateFactory.Instance.CreateMethodCall(castMethodInfo); + + object result = call(null, "First!"); + Assert.IsNotNull(result); + + JsonSerializerTest.DictionaryKey key = (JsonSerializerTest.DictionaryKey) result; + Assert.AreEqual("First!", key.Value); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Utilities/ReflectionUtilsTests.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Utilities/ReflectionUtilsTests.cs new file mode 100644 index 0000000..9124e15 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/Utilities/ReflectionUtilsTests.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization.Formatters; +using System.Text; +using NUnit.Framework; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Tests.Utilities +{ + public class ReflectionUtilsTests : TestFixtureBase + { + [Test] + public void GetTypeNameSimpleForGenericTypes() + { + string typeName; + + typeName = ReflectionUtils.GetTypeName(typeof(IList), FormatterAssemblyStyle.Simple); + Assert.AreEqual("System.Collections.Generic.IList`1[[System.Type, mscorlib]], mscorlib", typeName); + + typeName = ReflectionUtils.GetTypeName(typeof(IDictionary, IList>), FormatterAssemblyStyle.Simple); + Assert.AreEqual("System.Collections.Generic.IDictionary`2[[System.Collections.Generic.IList`1[[System.Type, mscorlib]], mscorlib],[System.Collections.Generic.IList`1[[System.Type, mscorlib]], mscorlib]], mscorlib", typeName); + + typeName = ReflectionUtils.GetTypeName(typeof(IList<>), FormatterAssemblyStyle.Simple); + Assert.AreEqual("System.Collections.Generic.IList`1, mscorlib", typeName); + + typeName = ReflectionUtils.GetTypeName(typeof(IDictionary<,>), FormatterAssemblyStyle.Simple); + Assert.AreEqual("System.Collections.Generic.IDictionary`2, mscorlib", typeName); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/bunny_pancake.jpg b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/bunny_pancake.jpg new file mode 100644 index 0000000..59e882c Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.Tests/bunny_pancake.jpg differ diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.WindowsPhone.sln b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.WindowsPhone.sln new file mode 100644 index 0000000..e4cdfae --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.WindowsPhone.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{F69285DD-24FB-4A60-AE8B-4C2744285D0F}" + ProjectSection(SolutionItems) = preProject + Lib\NUnit\Silverlight\nunit.framework.dll = Lib\NUnit\Silverlight\nunit.framework.dll + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json.WindowsPhone", "Newtonsoft.Json\Newtonsoft.Json.WindowsPhone.csproj", "{7A7F70AB-5C07-47ED-BDD2-ECC14DBACA5E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json.Tests.WindowsPhone", "Newtonsoft.Json.Tests\Newtonsoft.Json.Tests.WindowsPhone.csproj", "{5ED71C8C-D0A6-487C-9A8C-BB855ECEAF75}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {7A7F70AB-5C07-47ED-BDD2-ECC14DBACA5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7A7F70AB-5C07-47ED-BDD2-ECC14DBACA5E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7A7F70AB-5C07-47ED-BDD2-ECC14DBACA5E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7A7F70AB-5C07-47ED-BDD2-ECC14DBACA5E}.Release|Any CPU.Build.0 = Release|Any CPU + {5ED71C8C-D0A6-487C-9A8C-BB855ECEAF75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5ED71C8C-D0A6-487C-9A8C-BB855ECEAF75}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5ED71C8C-D0A6-487C-9A8C-BB855ECEAF75}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5ED71C8C-D0A6-487C-9A8C-BB855ECEAF75}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.sln b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.sln new file mode 100644 index 0000000..f94c4c3 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json.sln @@ -0,0 +1,32 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Lib", "Lib", "{620042D9-2753-48F5-BEDE-3905248781D2}" + ProjectSection(SolutionItems) = preProject + Lib\NUnit\DotNet\nunit.framework.dll = Lib\NUnit\DotNet\nunit.framework.dll + Lib\NUnit\DotNet\nunit.framework.xml = Lib\NUnit\DotNet\nunit.framework.xml + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json", "Newtonsoft.Json\Newtonsoft.Json.csproj", "{A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json.Tests", "Newtonsoft.Json.Tests\Newtonsoft.Json.Tests.csproj", "{3E6E2335-B079-4B5B-A65A-9D586914BCBB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Release|Any CPU.Build.0 = Release|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3E6E2335-B079-4B5B-A65A-9D586914BCBB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonBinaryType.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonBinaryType.cs new file mode 100644 index 0000000..0826446 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonBinaryType.cs @@ -0,0 +1,40 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Bson +{ + internal enum BsonBinaryType : byte + { + Binary = 0x00, + Function = 0x01, + [Obsolete("This type has been deprecated in the BSON specification. Use Binary instead.")] + Data = 0x02, + Uuid = 0x03, + Md5 = 0x05, + UserDefined = 0x80 + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonBinaryWriter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonBinaryWriter.cs new file mode 100644 index 0000000..f74e0e5 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonBinaryWriter.cs @@ -0,0 +1,302 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Bson +{ + internal class BsonBinaryWriter + { + private static readonly Encoding Encoding = Encoding.UTF8; + + private readonly BinaryWriter _writer; + + private byte[] _largeByteBuffer; + private int _maxChars; + + public DateTimeKind DateTimeKindHandling { get; set; } + + public BsonBinaryWriter(Stream stream) + { + DateTimeKindHandling = DateTimeKind.Utc; + _writer = new BinaryWriter(stream); + } + + public void Flush() + { + _writer.Flush(); + } + + public void Close() + { + _writer.Close(); + } + + public void WriteToken(BsonToken t) + { + CalculateSize(t); + WriteTokenInternal(t); + } + + private void WriteTokenInternal(BsonToken t) + { + switch (t.Type) + { + case BsonType.Object: + { + BsonObject value = (BsonObject) t; + _writer.Write(value.CalculatedSize); + foreach (BsonProperty property in value) + { + _writer.Write((sbyte)property.Value.Type); + WriteString((string)property.Name.Value, property.Name.ByteCount, null); + WriteTokenInternal(property.Value); + } + _writer.Write((byte)0); + } + break; + case BsonType.Array: + { + BsonArray value = (BsonArray) t; + _writer.Write(value.CalculatedSize); + int index = 0; + foreach (BsonToken c in value) + { + _writer.Write((sbyte)c.Type); + WriteString(index.ToString(CultureInfo.InvariantCulture), MathUtils.IntLength(index), null); + WriteTokenInternal(c); + index++; + } + _writer.Write((byte)0); + } + break; + case BsonType.Integer: + { + BsonValue value = (BsonValue) t; + _writer.Write(Convert.ToInt32(value.Value, CultureInfo.InvariantCulture)); + } + break; + case BsonType.Long: + { + BsonValue value = (BsonValue)t; + _writer.Write(Convert.ToInt64(value.Value, CultureInfo.InvariantCulture)); + } + break; + case BsonType.Number: + { + BsonValue value = (BsonValue)t; + _writer.Write(Convert.ToDouble(value.Value, CultureInfo.InvariantCulture)); + } + break; + case BsonType.String: + { + BsonString value = (BsonString)t; + WriteString((string)value.Value, value.ByteCount, value.CalculatedSize - 4); + } + break; + case BsonType.Boolean: + { + BsonValue value = (BsonValue)t; + _writer.Write((bool)value.Value); + } + break; + case BsonType.Null: + case BsonType.Undefined: + break; + case BsonType.Date: + { + BsonValue value = (BsonValue)t; + + long ticks = 0; + + if (value.Value is DateTime) + { + DateTime dateTime = (DateTime) value.Value; + if (DateTimeKindHandling == DateTimeKind.Utc) + dateTime = dateTime.ToUniversalTime(); + else if (DateTimeKindHandling == DateTimeKind.Local) + dateTime = dateTime.ToLocalTime(); + + ticks = JsonConvert.ConvertDateTimeToJavaScriptTicks(dateTime, false); + } +#if !PocketPC && !NET20 + else + { + DateTimeOffset dateTimeOffset = (DateTimeOffset) value.Value; + ticks = JsonConvert.ConvertDateTimeToJavaScriptTicks(dateTimeOffset.UtcDateTime, dateTimeOffset.Offset); + } +#endif + + _writer.Write(ticks); + } + break; + case BsonType.Binary: + { + BsonValue value = (BsonValue)t; + + byte[] data = (byte[])value.Value; + _writer.Write(data.Length); + _writer.Write((byte)BsonBinaryType.Binary); + _writer.Write(data); + } + break; + case BsonType.Oid: + { + BsonValue value = (BsonValue)t; + + byte[] data = (byte[])value.Value; + _writer.Write(data); + } + break; + case BsonType.Regex: + { + BsonRegex value = (BsonRegex) t; + + WriteString((string)value.Pattern.Value, value.Pattern.ByteCount, null); + WriteString((string)value.Options.Value, value.Options.ByteCount, null); + } + break; + default: + throw new ArgumentOutOfRangeException("t", "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type)); + } + } + + private void WriteString(string s, int byteCount, int? calculatedlengthPrefix) + { + if (calculatedlengthPrefix != null) + _writer.Write(calculatedlengthPrefix.Value); + + if (s != null) + { + if (_largeByteBuffer == null) + { + _largeByteBuffer = new byte[256]; + _maxChars = 256/Encoding.GetMaxByteCount(1); + } + if (byteCount <= 256) + { + Encoding.GetBytes(s, 0, s.Length, _largeByteBuffer, 0); + _writer.Write(_largeByteBuffer, 0, byteCount); + } + else + { + int charCount; + int totalCharsWritten = 0; + for (int i = s.Length; i > 0; i -= charCount) + { + charCount = (i > _maxChars) ? _maxChars : i; + int count = Encoding.GetBytes(s, totalCharsWritten, charCount, _largeByteBuffer, 0); + _writer.Write(_largeByteBuffer, 0, count); + totalCharsWritten += charCount; + } + } + } + + _writer.Write((byte)0); + } + + private int CalculateSize(int stringByteCount) + { + return stringByteCount + 1; + } + + private int CalculateSizeWithLength(int stringByteCount, bool includeSize) + { + int baseSize = (includeSize) + ? 5 // size bytes + terminator + : 1; // terminator + + return baseSize + stringByteCount; + } + + private int CalculateSize(BsonToken t) + { + switch (t.Type) + { + case BsonType.Object: + { + BsonObject value = (BsonObject) t; + + int bases = 4; + foreach (BsonProperty p in value) + { + int size = 1; + size += CalculateSize(p.Name); + size += CalculateSize(p.Value); + + bases += size; + } + bases += 1; + value.CalculatedSize = bases; + return bases; + } + case BsonType.Array: + { + BsonArray value = (BsonArray)t; + + int size = 4; + int index = 0; + foreach (BsonToken c in value) + { + size += 1; + size += CalculateSize(MathUtils.IntLength(index)); + size += CalculateSize(c); + index++; + } + size += 1; + value.CalculatedSize = size; + + return value.CalculatedSize; + } + case BsonType.Integer: + return 4; + case BsonType.Long: + return 8; + case BsonType.Number: + return 8; + case BsonType.String: + { + BsonString value = (BsonString)t; + string s = (string) value.Value; + value.ByteCount = (s != null) ? Encoding.GetByteCount(s) : 0; + value.CalculatedSize = CalculateSizeWithLength(value.ByteCount, value.IncludeLength); + + return value.CalculatedSize; + } + case BsonType.Boolean: + return 1; + case BsonType.Null: + case BsonType.Undefined: + return 0; + case BsonType.Date: + return 8; + case BsonType.Binary: + { + BsonValue value = (BsonValue) t; + + byte[] data = (byte[])value.Value; + value.CalculatedSize = 4 + 1 + data.Length; + + return value.CalculatedSize; + } + case BsonType.Oid: + return 12; + case BsonType.Regex: + { + BsonRegex value = (BsonRegex) t; + int size = 0; + size += CalculateSize(value.Pattern); + size += CalculateSize(value.Options); + value.CalculatedSize = size; + + return value.CalculatedSize; + } + default: + throw new ArgumentOutOfRangeException("t", "Unexpected token when writing BSON: {0}".FormatWith(CultureInfo.InvariantCulture, t.Type)); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonObjectId.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonObjectId.cs new file mode 100644 index 0000000..e8333c5 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonObjectId.cs @@ -0,0 +1,58 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Bson +{ + /// + /// Represents a BSON Oid (object id). + /// + public class BsonObjectId + { + /// + /// Gets or sets the value of the Oid. + /// + /// The value of the Oid. + public byte[] Value { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The Oid value. + public BsonObjectId(byte[] value) + { + ValidationUtils.ArgumentNotNull(value, "value"); + if (value.Length != 12) + throw new Exception("An ObjectId must be 12 bytes"); + + Value = value; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonReader.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonReader.cs new file mode 100644 index 0000000..cf5eb06 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonReader.cs @@ -0,0 +1,795 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Text; +using System.IO; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Bson +{ + /// + /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + /// + public class BsonReader : JsonReader + { + private const int MaxCharBytesSize = 128; + private static readonly byte[] _seqRange1 = new byte[] { 0, 127 }; // range of 1-byte sequence + private static readonly byte[] _seqRange2 = new byte[] { 194, 223 }; // range of 2-byte sequence + private static readonly byte[] _seqRange3 = new byte[] { 224, 239 }; // range of 3-byte sequence + private static readonly byte[] _seqRange4 = new byte[] { 240, 244 }; // range of 4-byte sequence + + private readonly BinaryReader _reader; + private readonly List _stack; + + private byte[] _byteBuffer; + private char[] _charBuffer; + + private BsonType _currentElementType; + private BsonReaderState _bsonReaderState; + private ContainerContext _currentContext; + + private bool _readRootValueAsArray; + private bool _jsonNet35BinaryCompatibility; + private DateTimeKind _dateTimeKindHandling; + + private enum BsonReaderState + { + Normal, + ReferenceStart, + ReferenceRef, + ReferenceId, + CodeWScopeStart, + CodeWScopeCode, + CodeWScopeScope, + CodeWScopeScopeObject, + CodeWScopeScopeEnd + } + + private class ContainerContext + { + public readonly BsonType Type; + public int Length; + public int Position; + + public ContainerContext(BsonType type) + { + Type = type; + } + } + + /// + /// Gets or sets a value indicating whether binary data reading should compatible with incorrect Json.NET 3.5 written binary. + /// + /// + /// true if binary data reading will be compatible with incorrect Json.NET 3.5 written binary; otherwise, false. + /// + public bool JsonNet35BinaryCompatibility + { + get { return _jsonNet35BinaryCompatibility; } + set { _jsonNet35BinaryCompatibility = value; } + } + + /// + /// Gets or sets a value indicating whether the root object will be read as a JSON array. + /// + /// + /// true if the root object will be read as a JSON array; otherwise, false. + /// + public bool ReadRootValueAsArray + { + get { return _readRootValueAsArray; } + set { _readRootValueAsArray = value; } + } + + /// + /// Gets or sets the used when reading values from BSON. + /// + /// The used when reading values from BSON. + public DateTimeKind DateTimeKindHandling + { + get { return _dateTimeKindHandling; } + set { _dateTimeKindHandling = value; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + public BsonReader(Stream stream) + : this(stream, false, DateTimeKind.Local) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + /// if set to true the root object will be read as a JSON array. + /// The used when reading values from BSON. + public BsonReader(Stream stream, bool readRootValueAsArray, DateTimeKind dateTimeKindHandling) + { + ValidationUtils.ArgumentNotNull(stream, "stream"); + _reader = new BinaryReader(stream); + _stack = new List(); + _readRootValueAsArray = readRootValueAsArray; + _dateTimeKindHandling = dateTimeKindHandling; + } + + private string ReadElement() + { + _currentElementType = ReadType(); + string elementName = ReadString(); + return elementName; + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// + /// A or a null reference if the next JSON token is null. + /// + public override byte[] ReadAsBytes() + { + Read(); + + if (TokenType == JsonToken.Null) + return null; + if (TokenType == JsonToken.Bytes) + return (byte[])Value; + + throw new JsonReaderException("Error reading bytes. Expected bytes but got {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override decimal? ReadAsDecimal() + { + Read(); + + if (TokenType == JsonToken.Null) + return null; + if (TokenType == JsonToken.Integer || TokenType == JsonToken.Float) + { + SetToken(JsonToken.Float, Convert.ToDecimal(Value, CultureInfo.InvariantCulture)); + return (decimal)Value; + } + + throw new JsonReaderException("Error reading decimal. Expected a number but got {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); + } + +#if !NET20 + /// + /// Reads the next JSON token from the stream as a . + /// + /// + /// A . + /// + public override DateTimeOffset? ReadAsDateTimeOffset() + { + Read(); + + if (TokenType == JsonToken.Null) + return null; + if (TokenType == JsonToken.Date) + { + SetToken(JsonToken.Date, new DateTimeOffset((DateTime)Value)); + return (DateTimeOffset)Value; + } + + throw new JsonReaderException("Error reading date. Expected bytes but got {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); + } +#endif + + /// + /// Reads the next JSON token from the stream. + /// + /// + /// true if the next token was read successfully; false if there are no more tokens to read. + /// + public override bool Read() + { + try + { + switch (_bsonReaderState) + { + case BsonReaderState.Normal: + return ReadNormal(); + case BsonReaderState.ReferenceStart: + case BsonReaderState.ReferenceRef: + case BsonReaderState.ReferenceId: + return ReadReference(); + case BsonReaderState.CodeWScopeStart: + case BsonReaderState.CodeWScopeCode: + case BsonReaderState.CodeWScopeScope: + case BsonReaderState.CodeWScopeScopeObject: + case BsonReaderState.CodeWScopeScopeEnd: + return ReadCodeWScope(); + default: + throw new JsonReaderException("Unexpected state: {0}".FormatWith(CultureInfo.InvariantCulture, _bsonReaderState)); + } + } + catch (EndOfStreamException) + { + return false; + } + } + + /// + /// Changes the to Closed. + /// + public override void Close() + { + base.Close(); + + if (CloseInput && _reader != null) + _reader.Close(); + } + + private bool ReadCodeWScope() + { + switch (_bsonReaderState) + { + case BsonReaderState.CodeWScopeStart: + SetToken(JsonToken.PropertyName, "$code"); + _bsonReaderState = BsonReaderState.CodeWScopeCode; + return true; + case BsonReaderState.CodeWScopeCode: + // total CodeWScope size - not used + ReadInt32(); + + SetToken(JsonToken.String, ReadLengthString()); + _bsonReaderState = BsonReaderState.CodeWScopeScope; + return true; + case BsonReaderState.CodeWScopeScope: + if (CurrentState == State.PostValue) + { + SetToken(JsonToken.PropertyName, "$scope"); + return true; + } + else + { + SetToken(JsonToken.StartObject); + _bsonReaderState = BsonReaderState.CodeWScopeScopeObject; + + ContainerContext newContext = new ContainerContext(BsonType.Object); + PushContext(newContext); + newContext.Length = ReadInt32(); + + return true; + } + case BsonReaderState.CodeWScopeScopeObject: + bool result = ReadNormal(); + if (result && TokenType == JsonToken.EndObject) + _bsonReaderState = BsonReaderState.CodeWScopeScopeEnd; + + return result; + case BsonReaderState.CodeWScopeScopeEnd: + SetToken(JsonToken.EndObject); + _bsonReaderState = BsonReaderState.Normal; + return true; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private bool ReadReference() + { + switch (CurrentState) + { + case State.ObjectStart: + { + SetToken(JsonToken.PropertyName, "$ref"); + _bsonReaderState = BsonReaderState.ReferenceRef; + return true; + } + case State.Property: + { + if (_bsonReaderState == BsonReaderState.ReferenceRef) + { + SetToken(JsonToken.String, ReadLengthString()); + return true; + } + else if (_bsonReaderState == BsonReaderState.ReferenceId) + { + SetToken(JsonToken.Bytes, ReadBytes(12)); + return true; + } + else + { + throw new JsonReaderException("Unexpected state when reading BSON reference: " + _bsonReaderState); + } + } + case State.PostValue: + { + if (_bsonReaderState == BsonReaderState.ReferenceRef) + { + SetToken(JsonToken.PropertyName, "$id"); + _bsonReaderState = BsonReaderState.ReferenceId; + return true; + } + else if (_bsonReaderState == BsonReaderState.ReferenceId) + { + SetToken(JsonToken.EndObject); + _bsonReaderState = BsonReaderState.Normal; + return true; + } + else + { + throw new JsonReaderException("Unexpected state when reading BSON reference: " + _bsonReaderState); + } + } + default: + throw new JsonReaderException("Unexpected state when reading BSON reference: " + CurrentState); + } + } + + private bool ReadNormal() + { + switch (CurrentState) + { + case State.Start: + { + JsonToken token = (!_readRootValueAsArray) ? JsonToken.StartObject : JsonToken.StartArray; + BsonType type = (!_readRootValueAsArray) ? BsonType.Object : BsonType.Array; + + SetToken(token); + ContainerContext newContext = new ContainerContext(type); + PushContext(newContext); + newContext.Length = ReadInt32(); + return true; + } + case State.Complete: + case State.Closed: + return false; + case State.Property: + { + ReadType(_currentElementType); + return true; + } + case State.ObjectStart: + case State.ArrayStart: + case State.PostValue: + ContainerContext context = _currentContext; + if (context == null) + return false; + + int lengthMinusEnd = context.Length - 1; + + if (context.Position < lengthMinusEnd) + { + if (context.Type == BsonType.Array) + { + ReadElement(); + ReadType(_currentElementType); + return true; + } + else + { + SetToken(JsonToken.PropertyName, ReadElement()); + return true; + } + } + else if (context.Position == lengthMinusEnd) + { + if (ReadByte() != 0) + throw new JsonReaderException("Unexpected end of object byte value."); + + PopContext(); + if (_currentContext != null) + MovePosition(context.Length); + + JsonToken endToken = (context.Type == BsonType.Object) ? JsonToken.EndObject : JsonToken.EndArray; + SetToken(endToken); + return true; + } + else + { + throw new JsonReaderException("Read past end of current container context."); + } + case State.ConstructorStart: + break; + case State.Constructor: + break; + case State.Error: + break; + case State.Finished: + break; + default: + throw new ArgumentOutOfRangeException(); + } + + return false; + } + + private void PopContext() + { + _stack.RemoveAt(_stack.Count - 1); + if (_stack.Count == 0) + _currentContext = null; + else + _currentContext = _stack[_stack.Count - 1]; + } + + private void PushContext(ContainerContext newContext) + { + _stack.Add(newContext); + _currentContext = newContext; + } + + private byte ReadByte() + { + MovePosition(1); + return _reader.ReadByte(); + } + + private void ReadType(BsonType type) + { + switch (type) + { + case BsonType.Number: + SetToken(JsonToken.Float, ReadDouble()); + break; + case BsonType.String: + case BsonType.Symbol: + SetToken(JsonToken.String, ReadLengthString()); + break; + case BsonType.Object: + { + SetToken(JsonToken.StartObject); + + ContainerContext newContext = new ContainerContext(BsonType.Object); + PushContext(newContext); + newContext.Length = ReadInt32(); + break; + } + case BsonType.Array: + { + SetToken(JsonToken.StartArray); + + ContainerContext newContext = new ContainerContext(BsonType.Array); + PushContext(newContext); + newContext.Length = ReadInt32(); + break; + } + case BsonType.Binary: + SetToken(JsonToken.Bytes, ReadBinary()); + break; + case BsonType.Undefined: + SetToken(JsonToken.Undefined); + break; + case BsonType.Oid: + byte[] oid = ReadBytes(12); + SetToken(JsonToken.Bytes, oid); + break; + case BsonType.Boolean: + bool b = Convert.ToBoolean(ReadByte()); + SetToken(JsonToken.Boolean, b); + break; + case BsonType.Date: + long ticks = ReadInt64(); + DateTime utcDateTime = JsonConvert.ConvertJavaScriptTicksToDateTime(ticks); + + DateTime dateTime; + switch (DateTimeKindHandling) + { + case DateTimeKind.Unspecified: + dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified); + break; + case DateTimeKind.Local: + dateTime = utcDateTime.ToLocalTime(); + break; + default: + dateTime = utcDateTime; + break; + } + + SetToken(JsonToken.Date, dateTime); + break; + case BsonType.Null: + SetToken(JsonToken.Null); + break; + case BsonType.Regex: + string expression = ReadString(); + string modifiers = ReadString(); + + string regex = @"/" + expression + @"/" + modifiers; + SetToken(JsonToken.String, regex); + break; + case BsonType.Reference: + SetToken(JsonToken.StartObject); + _bsonReaderState = BsonReaderState.ReferenceStart; + break; + case BsonType.Code: + SetToken(JsonToken.String, ReadLengthString()); + break; + case BsonType.CodeWScope: + SetToken(JsonToken.StartObject); + _bsonReaderState = BsonReaderState.CodeWScopeStart; + break; + case BsonType.Integer: + SetToken(JsonToken.Integer, (long)ReadInt32()); + break; + case BsonType.TimeStamp: + case BsonType.Long: + SetToken(JsonToken.Integer, ReadInt64()); + break; + default: + throw new ArgumentOutOfRangeException("type", "Unexpected BsonType value: " + type); + } + } + + private byte[] ReadBinary() + { + int dataLength = ReadInt32(); + + BsonBinaryType binaryType = (BsonBinaryType)ReadByte(); + +#pragma warning disable 612,618 + // the old binary type has the data length repeated in the data for some reason + if (binaryType == BsonBinaryType.Data && !_jsonNet35BinaryCompatibility) + { + dataLength = ReadInt32(); + } +#pragma warning restore 612,618 + + return ReadBytes(dataLength); + } + + private string ReadString() + { + EnsureBuffers(); + + StringBuilder builder = null; + + int totalBytesRead = 0; + // used in case of left over multibyte characters in the buffer + int offset = 0; + do + { + int count = offset; + byte b; + while (count < MaxCharBytesSize && (b = _reader.ReadByte()) > 0) + { + _byteBuffer[count++] = b; + } + int byteCount = count - offset; + totalBytesRead += byteCount; + + if (count < MaxCharBytesSize && builder == null) + { + // pref optimization to avoid reading into a string builder + // if string is smaller than the buffer then return it directly + int length = Encoding.UTF8.GetChars(_byteBuffer, 0, byteCount, _charBuffer, 0); + + MovePosition(totalBytesRead + 1); + return new string(_charBuffer, 0, length); + } + else + { + // calculate the index of the end of the last full character in the buffer + int lastFullCharStop = GetLastFullCharStop(count - 1); + + int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0); + + if (builder == null) + builder = new StringBuilder(MaxCharBytesSize * 2); + + builder.Append(_charBuffer, 0, charCount); + + if (lastFullCharStop < byteCount - 1) + { + offset = byteCount - lastFullCharStop - 1; + // copy left over multi byte characters to beginning of buffer for next iteration + Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, offset); + } + else + { + // reached end of string + if (count < MaxCharBytesSize) + { + MovePosition(totalBytesRead + 1); + return builder.ToString(); + } + + offset = 0; + } + } + } + while (true); + } + + private string ReadLengthString() + { + int length = ReadInt32(); + + MovePosition(length); + + string s = GetString(length - 1); + _reader.ReadByte(); + + return s; + } + + private string GetString(int length) + { + if (length == 0) + return string.Empty; + + EnsureBuffers(); + + StringBuilder builder = null; + + int totalBytesRead = 0; + + // used in case of left over multibyte characters in the buffer + int offset = 0; + do + { + int count = ((length - totalBytesRead) > MaxCharBytesSize - offset) + ? MaxCharBytesSize - offset + : length - totalBytesRead; + + int byteCount = _reader.BaseStream.Read(_byteBuffer, offset, count); + + if (byteCount == 0) + throw new EndOfStreamException("Unable to read beyond the end of the stream."); + + totalBytesRead += byteCount; + + // Above, byteCount is how many bytes we read this time. + // Below, byteCount is how many bytes are in the _byteBuffer. + byteCount += offset; + + if (byteCount == length) + { + // pref optimization to avoid reading into a string builder + // first iteration and all bytes read then return string directly + int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, byteCount, _charBuffer, 0); + return new string(_charBuffer, 0, charCount); + } + else + { + int lastFullCharStop = GetLastFullCharStop(byteCount - 1); + + if (builder == null) + builder = new StringBuilder(length); + + int charCount = Encoding.UTF8.GetChars(_byteBuffer, 0, lastFullCharStop + 1, _charBuffer, 0); + builder.Append(_charBuffer, 0, charCount); + + if (lastFullCharStop < byteCount - 1) + { + offset = byteCount - lastFullCharStop - 1; + // copy left over multi byte characters to beginning of buffer for next iteration + Array.Copy(_byteBuffer, lastFullCharStop + 1, _byteBuffer, 0, offset); + } + else + { + offset = 0; + } + } + } + while (totalBytesRead < length); + + return builder.ToString(); + } + + private int GetLastFullCharStop(int start) + { + int lookbackPos = start; + int bis = 0; + while (lookbackPos >= 0) + { + bis = BytesInSequence(_byteBuffer[lookbackPos]); + if (bis == 0) + { + lookbackPos--; + continue; + } + else if (bis == 1) + { + break; + } + else + { + lookbackPos--; + break; + } + } + if (bis == start - lookbackPos) + { + //Full character. + return start; + } + else + { + return lookbackPos; + } + } + + private int BytesInSequence(byte b) + { + if (b <= _seqRange1[1]) return 1; + if (b >= _seqRange2[0] && b <= _seqRange2[1]) return 2; + if (b >= _seqRange3[0] && b <= _seqRange3[1]) return 3; + if (b >= _seqRange4[0] && b <= _seqRange4[1]) return 4; + return 0; + } + + private void EnsureBuffers() + { + if (_byteBuffer == null) + { + _byteBuffer = new byte[MaxCharBytesSize]; + } + if (_charBuffer == null) + { + int charBufferSize = Encoding.UTF8.GetMaxCharCount(MaxCharBytesSize); + _charBuffer = new char[charBufferSize]; + } + } + + private double ReadDouble() + { + MovePosition(8); + return _reader.ReadDouble(); + } + + private int ReadInt32() + { + MovePosition(4); + return _reader.ReadInt32(); + } + + private long ReadInt64() + { + MovePosition(8); + return _reader.ReadInt64(); + } + + private BsonType ReadType() + { + MovePosition(1); + return (BsonType)_reader.ReadSByte(); + } + + private void MovePosition(int count) + { + _currentContext.Position += count; + } + + private byte[] ReadBytes(int count) + { + MovePosition(count); + return _reader.ReadBytes(count); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonToken.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonToken.cs new file mode 100644 index 0000000..789d3cd --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonToken.cs @@ -0,0 +1,121 @@ +using System.Collections; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Bson +{ + internal abstract class BsonToken + { + public abstract BsonType Type { get; } + public BsonToken Parent { get; set; } + public int CalculatedSize { get; set; } + } + + internal class BsonObject : BsonToken, IEnumerable + { + private readonly List _children = new List(); + + public void Add(string name, BsonToken token) + { + _children.Add(new BsonProperty { Name = new BsonString(name, false), Value = token }); + token.Parent = this; + } + + public override BsonType Type + { + get { return BsonType.Object; } + } + + public IEnumerator GetEnumerator() + { + return _children.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + internal class BsonArray : BsonToken, IEnumerable + { + private readonly List _children = new List(); + + public void Add(BsonToken token) + { + _children.Add(token); + token.Parent = this; + } + + public override BsonType Type + { + get { return BsonType.Array; } + } + + public IEnumerator GetEnumerator() + { + return _children.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + internal class BsonValue : BsonToken + { + private object _value; + private BsonType _type; + + public BsonValue(object value, BsonType type) + { + _value = value; + _type = type; + } + + public object Value + { + get { return _value; } + } + + public override BsonType Type + { + get { return _type; } + } + } + + internal class BsonString : BsonValue + { + public int ByteCount { get; set; } + public bool IncludeLength { get; set; } + + public BsonString(object value, bool includeLength) + : base(value, BsonType.String) + { + IncludeLength = includeLength; + } + } + + internal class BsonRegex : BsonToken + { + public BsonString Pattern { get; set; } + public BsonString Options { get; set; } + + public BsonRegex(string pattern, string options) + { + Pattern = new BsonString(pattern, false); + Options = new BsonString(options, false); + } + + public override BsonType Type + { + get { return BsonType.Regex; } + } + } + + internal class BsonProperty + { + public BsonString Name { get; set; } + public BsonToken Value { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonType.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonType.cs new file mode 100644 index 0000000..f4e1214 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonType.cs @@ -0,0 +1,51 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Bson +{ + internal enum BsonType : sbyte + { + Number = 1, + String = 2, + Object = 3, + Array = 4, + Binary = 5, + Undefined = 6, + Oid = 7, + Boolean = 8, + Date = 9, + Null = 10, + Regex = 11, + Reference = 12, + Code = 13, + Symbol = 14, + CodeWScope = 15, + Integer = 16, + TimeStamp = 17, + Long = 18, + MinKey = -1, + MaxKey = 127 + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonWriter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonWriter.cs new file mode 100644 index 0000000..fe34289 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Bson/BsonWriter.cs @@ -0,0 +1,442 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; +using System.Globalization; + +namespace Newtonsoft.Json.Bson +{ + /// + /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + /// + public class BsonWriter : JsonWriter + { + private readonly BsonBinaryWriter _writer; + + private BsonToken _root; + private BsonToken _parent; + private string _propertyName; + + /// + /// Gets or sets the used when writing values to BSON. + /// When set to no conversion will occur. + /// + /// The used when writing values to BSON. + public DateTimeKind DateTimeKindHandling + { + get { return _writer.DateTimeKindHandling; } + set { _writer.DateTimeKindHandling = value; } + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + public BsonWriter(Stream stream) + { + ValidationUtils.ArgumentNotNull(stream, "stream"); + _writer = new BsonBinaryWriter(stream); + } + + /// + /// Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + /// + public override void Flush() + { + _writer.Flush(); + } + + /// + /// Writes the end. + /// + /// The token. + protected override void WriteEnd(JsonToken token) + { + base.WriteEnd(token); + RemoveParent(); + + if (Top == 0) + { + _writer.WriteToken(_root); + } + } + + /// + /// Writes out a comment /*...*/ containing the specified text. + /// + /// Text to place inside the comment. + public override void WriteComment(string text) + { + throw new JsonWriterException("Cannot write JSON comment as BSON."); + } + + /// + /// Writes the start of a constructor with the given name. + /// + /// The name of the constructor. + public override void WriteStartConstructor(string name) + { + throw new JsonWriterException("Cannot write JSON constructor as BSON."); + } + + /// + /// Writes raw JSON. + /// + /// The raw JSON to write. + public override void WriteRaw(string json) + { + throw new JsonWriterException("Cannot write raw JSON as BSON."); + } + + /// + /// Writes raw JSON where a value is expected and updates the writer's state. + /// + /// The raw JSON to write. + public override void WriteRawValue(string json) + { + throw new JsonWriterException("Cannot write raw JSON as BSON."); + } + + /// + /// Writes the beginning of a Json array. + /// + public override void WriteStartArray() + { + base.WriteStartArray(); + + AddParent(new BsonArray()); + } + + /// + /// Writes the beginning of a Json object. + /// + public override void WriteStartObject() + { + base.WriteStartObject(); + + AddParent(new BsonObject()); + } + + /// + /// Writes the property name of a name/value pair on a Json object. + /// + /// The name of the property. + public override void WritePropertyName(string name) + { + base.WritePropertyName(name); + + _propertyName = name; + } + + /// + /// Closes this stream and the underlying stream. + /// + public override void Close() + { + base.Close(); + + if (CloseOutput && _writer != null) + _writer.Close(); + } + + private void AddParent(BsonToken container) + { + AddToken(container); + _parent = container; + } + + private void RemoveParent() + { + _parent = _parent.Parent; + } + + private void AddValue(object value, BsonType type) + { + AddToken(new BsonValue(value, type)); + } + + internal void AddToken(BsonToken token) + { + if (_parent != null) + { + if (_parent is BsonObject) + { + ((BsonObject)_parent).Add(_propertyName, token); + _propertyName = null; + } + else + { + ((BsonArray)_parent).Add(token); + } + } + else + { + _parent = token; + _root = token; + } + } + + #region WriteValue methods + /// + /// Writes a null value. + /// + public override void WriteNull() + { + base.WriteNull(); + AddValue(null, BsonType.Null); + } + + /// + /// Writes an undefined value. + /// + public override void WriteUndefined() + { + base.WriteUndefined(); + AddValue(null, BsonType.Undefined); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(string value) + { + base.WriteValue(value); + if (value == null) + AddValue(null, BsonType.Null); + else + AddToken(new BsonString(value, true)); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(int value) + { + base.WriteValue(value); + AddValue(value, BsonType.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(uint value) + { + if (value > int.MaxValue) + throw new JsonWriterException("Value is too large to fit in a signed 32 bit integer. BSON does not support unsigned values."); + + base.WriteValue(value); + AddValue(value, BsonType.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(long value) + { + base.WriteValue(value); + AddValue(value, BsonType.Long); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(ulong value) + { + if (value > long.MaxValue) + throw new JsonWriterException("Value is too large to fit in a signed 64 bit integer. BSON does not support unsigned values."); + + base.WriteValue(value); + AddValue(value, BsonType.Long); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(float value) + { + base.WriteValue(value); + AddValue(value, BsonType.Number); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(double value) + { + base.WriteValue(value); + AddValue(value, BsonType.Number); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(bool value) + { + base.WriteValue(value); + AddValue(value, BsonType.Boolean); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(short value) + { + base.WriteValue(value); + AddValue(value, BsonType.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(ushort value) + { + base.WriteValue(value); + AddValue(value, BsonType.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(char value) + { + base.WriteValue(value); + AddToken(new BsonString(value.ToString(), true)); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(byte value) + { + base.WriteValue(value); + AddValue(value, BsonType.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(sbyte value) + { + base.WriteValue(value); + AddValue(value, BsonType.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(decimal value) + { + base.WriteValue(value); + AddValue(value, BsonType.Number); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTime value) + { + base.WriteValue(value); + AddValue(value, BsonType.Date); + } + +#if !PocketPC && !NET20 + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTimeOffset value) + { + base.WriteValue(value); + AddValue(value, BsonType.Date); + } +#endif + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(byte[] value) + { + base.WriteValue(value); + AddValue(value, BsonType.Binary); + } + #endregion + + /// + /// Writes a value that represents a BSON object id. + /// + /// + public void WriteObjectId(byte[] value) + { + ValidationUtils.ArgumentNotNull(value, "value"); + + if (value.Length != 12) + throw new Exception("An object id must be 12 bytes"); + + // hack to update the writer state + AutoComplete(JsonToken.Undefined); + AddValue(value, BsonType.Oid); + } + + /// + /// Writes a BSON regex. + /// + /// The regex pattern. + /// The regex options. + public void WriteRegex(string pattern, string options) + { + ValidationUtils.ArgumentNotNull(pattern, "pattern"); + + // hack to update the writer state + AutoComplete(JsonToken.Undefined); + AddToken(new BsonRegex(pattern, options)); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/ConstructorHandling.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/ConstructorHandling.cs new file mode 100644 index 0000000..f8cb5de --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/ConstructorHandling.cs @@ -0,0 +1,47 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Specifies how constructors are used when initializing objects during deserialization by the . + /// + public enum ConstructorHandling + { + /// + /// First attempt to use the public default constructor then fall back to single paramatized constructor. + /// + Default = 0, + /// + /// Allow Json.NET to use a non-public default constructor. + /// + AllowNonPublicDefaultConstructor = 1 + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/BinaryConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/BinaryConverter.cs new file mode 100644 index 0000000..7bf8c6a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/BinaryConverter.cs @@ -0,0 +1,147 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +#if !SILVERLIGHT +using System.Data.SqlTypes; +#endif +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ +#if !SILVERLIGHT && !PocketPC && !NET20 + internal interface IBinary + { + byte[] ToArray(); + } +#endif + + /// + /// Converts a binary value to and from a base 64 string value. + /// + public class BinaryConverter : JsonConverter + { +#if !SILVERLIGHT && !PocketPC && !NET20 + private const string BinaryTypeName = "System.Data.Linq.Binary"; +#endif + + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + return; + } + + byte[] data = GetByteArray(value); + + writer.WriteValue(data); + } + + private byte[] GetByteArray(object value) + { +#if !SILVERLIGHT && !PocketPC && !NET20 + if (value.GetType().AssignableToTypeName(BinaryTypeName)) + { + IBinary binary = DynamicWrapper.CreateWrapper(value); + return binary.ToArray(); + } +#endif +#if !SILVERLIGHT + if (value is SqlBinary) + return ((SqlBinary) value).Value; +#endif + throw new Exception("Unexpected value type when writing binary: {0}".FormatWith(CultureInfo.InvariantCulture, value.GetType())); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + Type t = (ReflectionUtils.IsNullableType(objectType)) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + if (reader.TokenType == JsonToken.Null) + { + if (!ReflectionUtils.IsNullable(objectType)) + throw new Exception("Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + return null; + } + + if (reader.TokenType != JsonToken.String) + throw new Exception("Unexpected token parsing binary. Expected String, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + // current token is already at base64 string + // unable to call ReadAsBytes so do it the old fashion way + string encodedData = reader.Value.ToString(); + byte[] data = Convert.FromBase64String(encodedData); + +#if !SILVERLIGHT && !PocketPC && !NET20 + if (t.AssignableToTypeName(BinaryTypeName)) + return Activator.CreateInstance(t, data); +#endif +#if !SILVERLIGHT + if (t == typeof(SqlBinary)) + return new SqlBinary(data); +#endif + throw new Exception("Unexpected object type when writing binary: {0}".FormatWith(CultureInfo.InvariantCulture, objectType)); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { +#if !SILVERLIGHT && !PocketPC && !NET20 + if (objectType.AssignableToTypeName(BinaryTypeName)) + return true; +#endif +#if !SILVERLIGHT + if (objectType == typeof(SqlBinary) || objectType == typeof(SqlBinary?)) + return true; +#endif + return false; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/BsonObjectIdConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/BsonObjectIdConverter.cs new file mode 100644 index 0000000..e70a378 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/BsonObjectIdConverter.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Bson; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from JSON and BSON. + /// + public class BsonObjectIdConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + BsonObjectId objectId = (BsonObjectId) value; + + BsonWriter bsonWriter = writer as BsonWriter; + if (bsonWriter != null) + { + bsonWriter.WriteObjectId(objectId.Value); + } + else + { + writer.WriteValue(objectId.Value); + } + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType != JsonToken.Bytes) + throw new JsonSerializationException("Expected Bytes but got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + byte[] value = (byte[])reader.Value; + + return new BsonObjectId(value); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return (objectType == typeof (BsonObjectId)); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/CustomCreationConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/CustomCreationConverter.cs new file mode 100644 index 0000000..368d71b --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/CustomCreationConverter.cs @@ -0,0 +1,98 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Create a custom object + /// + /// + public abstract class CustomCreationConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + throw new NotSupportedException("CustomCreationConverter should only be used while deserializing."); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType == JsonToken.Null) + return null; + + T value = Create(objectType); + if (value == null) + throw new JsonSerializationException("No object created."); + + serializer.Populate(reader, value); + return value; + } + + /// + /// Creates an object which will then be populated by the serializer. + /// + /// Type of the object. + /// + public abstract T Create(Type objectType); + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return typeof (T).IsAssignableFrom(objectType); + } + + /// + /// Gets a value indicating whether this can write JSON. + /// + /// + /// true if this can write JSON; otherwise, false. + /// + public override bool CanWrite + { + get { return false; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/DataSetConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/DataSetConverter.cs new file mode 100644 index 0000000..df88ff9 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/DataSetConverter.cs @@ -0,0 +1,101 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !SILVERLIGHT +using System; +using System.Data; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from JSON. + /// + public class DataSetConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + DataSet dataSet = (DataSet)value; + + DataTableConverter converter = new DataTableConverter(); + + writer.WriteStartObject(); + + foreach (DataTable table in dataSet.Tables) + { + writer.WritePropertyName(table.TableName); + + converter.WriteJson(writer, table, serializer); + } + + writer.WriteEndObject(); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + DataSet ds = new DataSet(); + + DataTableConverter converter = new DataTableConverter(); + + reader.Read(); + + while (reader.TokenType == JsonToken.PropertyName) + { + DataTable dt = (DataTable)converter.ReadJson(reader, typeof (DataTable), null, serializer); + ds.Tables.Add(dt); + + reader.Read(); + } + + return ds; + } + + /// + /// Determines whether this instance can convert the specified value type. + /// + /// Type of the value. + /// + /// true if this instance can convert the specified value type; otherwise, false. + /// + public override bool CanConvert(Type valueType) + { + return (valueType == typeof(DataSet)); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/DataTableConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/DataTableConverter.cs new file mode 100644 index 0000000..9845582 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/DataTableConverter.cs @@ -0,0 +1,151 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !SILVERLIGHT +using System; +using System.Data; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from JSON. + /// + public class DataTableConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + DataTable table = (DataTable)value; + + writer.WriteStartArray(); + + foreach (DataRow row in table.Rows) + { + writer.WriteStartObject(); + foreach (DataColumn column in row.Table.Columns) + { + writer.WritePropertyName(column.ColumnName); + serializer.Serialize(writer, row[column]); + } + writer.WriteEndObject(); + } + + writer.WriteEndArray(); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + DataTable dt; + + if (reader.TokenType == JsonToken.PropertyName) + { + dt = new DataTable((string)reader.Value); + reader.Read(); + } + else + { + dt = new DataTable(); + } + + reader.Read(); + + while (reader.TokenType == JsonToken.StartObject) + { + DataRow dr = dt.NewRow(); + reader.Read(); + + while (reader.TokenType == JsonToken.PropertyName) + { + string columnName = (string) reader.Value; + + reader.Read(); + + if (!dt.Columns.Contains(columnName)) + { + Type columnType = GetColumnDataType(reader.TokenType); + dt.Columns.Add(new DataColumn(columnName, columnType)); + } + + dr[columnName] = reader.Value; + reader.Read(); + } + + dr.EndEdit(); + dt.Rows.Add(dr); + + reader.Read(); + } + + return dt; + } + + private static Type GetColumnDataType(JsonToken tokenType) + { + switch (tokenType) + { + case JsonToken.Integer: + return typeof (long); + case JsonToken.Float: + return typeof (double); + case JsonToken.String: + case JsonToken.Null: + case JsonToken.Undefined: + return typeof (string); + case JsonToken.Boolean: + return typeof (bool); + case JsonToken.Date: + return typeof (DateTime); + default: + throw new ArgumentOutOfRangeException(); + } + } + + /// + /// Determines whether this instance can convert the specified value type. + /// + /// Type of the value. + /// + /// true if this instance can convert the specified value type; otherwise, false. + /// + public override bool CanConvert(Type valueType) + { + return (valueType == typeof(DataTable)); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/DateTimeConverterBase.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/DateTimeConverterBase.cs new file mode 100644 index 0000000..914e755 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/DateTimeConverterBase.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Provides a base class for converting a to and from JSON. + /// + public abstract class DateTimeConverterBase : JsonConverter + { + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + if (objectType == typeof(DateTime) || objectType == typeof(DateTime?)) + return true; +#if !PocketPC && !NET20 + if (objectType == typeof(DateTimeOffset) || objectType == typeof(DateTimeOffset?)) + return true; +#endif + + return false; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/EntityKeyMemberConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/EntityKeyMemberConverter.cs new file mode 100644 index 0000000..fba98f8 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/EntityKeyMemberConverter.cs @@ -0,0 +1,140 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC && !SILVERLIGHT && !NET20 +using System; +using Newtonsoft.Json.Serialization; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + internal interface IEntityKeyMember + { + string Key { get; set; } + object Value { get; set; } + } + + /// + /// Converts an Entity Framework EntityKey to and from JSON. + /// + public class EntityKeyMemberConverter : JsonConverter + { + private const string EntityKeyMemberFullTypeName = "System.Data.EntityKeyMember"; + + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + IEntityKeyMember entityKeyMember = DynamicWrapper.CreateWrapper(value); + Type keyType = (entityKeyMember.Value != null) ? entityKeyMember.Value.GetType() : null; + + writer.WriteStartObject(); + writer.WritePropertyName("Key"); + writer.WriteValue(entityKeyMember.Key); + writer.WritePropertyName("Type"); + writer.WriteValue((keyType != null) ? keyType.FullName : null); + + writer.WritePropertyName("Value"); + + if (keyType != null) + { + string valueJson; + if (JsonSerializerInternalWriter.TryConvertToString(entityKeyMember.Value, keyType, out valueJson)) + writer.WriteValue(valueJson); + else + writer.WriteValue(entityKeyMember.Value); + } + else + { + writer.WriteNull(); + } + + writer.WriteEndObject(); + } + + private static void ReadAndAssertProperty(JsonReader reader, string propertyName) + { + ReadAndAssert(reader); + + if (reader.TokenType != JsonToken.PropertyName || reader.Value.ToString() != propertyName) + throw new JsonSerializationException("Expected JSON property '{0}'.".FormatWith(CultureInfo.InvariantCulture, propertyName)); + } + + private static void ReadAndAssert(JsonReader reader) + { + if (!reader.Read()) + throw new JsonSerializationException("Unexpected end."); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + IEntityKeyMember entityKeyMember = DynamicWrapper.CreateWrapper(Activator.CreateInstance(objectType)); + + ReadAndAssertProperty(reader, "Key"); + ReadAndAssert(reader); + entityKeyMember.Key = reader.Value.ToString(); + + ReadAndAssertProperty(reader, "Type"); + ReadAndAssert(reader); + string type = reader.Value.ToString(); + + Type t = Type.GetType(type); + + ReadAndAssertProperty(reader, "Value"); + ReadAndAssert(reader); + entityKeyMember.Value = serializer.Deserialize(reader, t); + + ReadAndAssert(reader); + + return DynamicWrapper.GetUnderlyingObject(entityKeyMember); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return (objectType.AssignableToTypeName(EntityKeyMemberFullTypeName)); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs new file mode 100644 index 0000000..5158beb --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/ExpandoObjectConverter.cs @@ -0,0 +1,140 @@ +#if !(NET35 || NET20 || WINDOWS_PHONE) + +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts an ExpandoObject to and from JSON. + /// + public class ExpandoObjectConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + // can write is set to false + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + return ReadValue(reader); + } + + private object ReadValue(JsonReader reader) + { + while (reader.TokenType == JsonToken.Comment) + { + if (!reader.Read()) + throw new Exception("Unexpected end."); + } + + switch (reader.TokenType) + { + case JsonToken.StartObject: + return ReadObject(reader); + case JsonToken.StartArray: + return ReadList(reader); + default: + if (JsonReader.IsPrimitiveToken(reader.TokenType)) + return reader.Value; + + throw new Exception("Unexpected token when converting ExpandoObject: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + } + + private object ReadList(JsonReader reader) + { + IList list = new List(); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.Comment: + break; + default: + object v = ReadValue(reader); + + list.Add(v); + break; + case JsonToken.EndArray: + return list; + } + } + + throw new Exception("Unexpected end."); + } + + private object ReadObject(JsonReader reader) + { + IDictionary expandoObject = new ExpandoObject(); + + while (reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string propertyName = reader.Value.ToString(); + + if (!reader.Read()) + throw new Exception("Unexpected end."); + + object v = ReadValue(reader); + + expandoObject[propertyName] = v; + break; + case JsonToken.Comment: + break; + case JsonToken.EndObject: + return expandoObject; + } + } + + throw new Exception("Unexpected end."); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return (objectType == typeof (ExpandoObject)); + } + + /// + /// Gets a value indicating whether this can write JSON. + /// + /// + /// true if this can write JSON; otherwise, false. + /// + public override bool CanWrite + { + get { return false; } + } + } +} + +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/IsoDateTimeConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/IsoDateTimeConverter.cs new file mode 100644 index 0000000..4007626 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/IsoDateTimeConverter.cs @@ -0,0 +1,134 @@ +using System; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from the ISO 8601 date format (e.g. 2008-04-12T12:53Z). + /// + public class IsoDateTimeConverter : DateTimeConverterBase + { + private const string DefaultDateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.FFFFFFFK"; + + private DateTimeStyles _dateTimeStyles = DateTimeStyles.RoundtripKind; + private string _dateTimeFormat; + private CultureInfo _culture; + + /// + /// Gets or sets the date time styles used when converting a date to and from JSON. + /// + /// The date time styles used when converting a date to and from JSON. + public DateTimeStyles DateTimeStyles + { + get { return _dateTimeStyles; } + set { _dateTimeStyles = value; } + } + + /// + /// Gets or sets the date time format used when converting a date to and from JSON. + /// + /// The date time format used when converting a date to and from JSON. + public string DateTimeFormat + { + get { return _dateTimeFormat ?? string.Empty; } + set { _dateTimeFormat = StringUtils.NullEmptyString(value); } + } + + /// + /// Gets or sets the culture used when converting a date to and from JSON. + /// + /// The culture used when converting a date to and from JSON. + public CultureInfo Culture + { + get { return _culture ?? CultureInfo.CurrentCulture; } + set { _culture = value; } + } + + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + string text; + + if (value is DateTime) + { + DateTime dateTime = (DateTime)value; + + if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal + || (_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal) + dateTime = dateTime.ToUniversalTime(); + + text = dateTime.ToString(_dateTimeFormat ?? DefaultDateTimeFormat, Culture); + } +#if !PocketPC && !NET20 + else if (value is DateTimeOffset) + { + DateTimeOffset dateTimeOffset = (DateTimeOffset)value; + if ((_dateTimeStyles & DateTimeStyles.AdjustToUniversal) == DateTimeStyles.AdjustToUniversal + || (_dateTimeStyles & DateTimeStyles.AssumeUniversal) == DateTimeStyles.AssumeUniversal) + dateTimeOffset = dateTimeOffset.ToUniversalTime(); + + text = dateTimeOffset.ToString(_dateTimeFormat ?? DefaultDateTimeFormat, Culture); + } +#endif + else + { + throw new Exception("Unexpected value when converting date. Expected DateTime or DateTimeOffset, got {0}.".FormatWith(CultureInfo.InvariantCulture, ReflectionUtils.GetObjectType(value))); + } + + writer.WriteValue(text); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + bool nullable = ReflectionUtils.IsNullableType(objectType); + Type t = (nullable) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + if (reader.TokenType == JsonToken.Null) + { + if (!ReflectionUtils.IsNullableType(objectType)) + throw new Exception("Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + return null; + } + + if (reader.TokenType != JsonToken.String) + throw new Exception("Unexpected token parsing date. Expected String, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + string dateText = reader.Value.ToString(); + + if (string.IsNullOrEmpty(dateText) && nullable) + return null; + +#if !PocketPC && !NET20 + if (t == typeof(DateTimeOffset)) + { + if (!string.IsNullOrEmpty(_dateTimeFormat)) + return DateTimeOffset.ParseExact(dateText, _dateTimeFormat, Culture, _dateTimeStyles); + else + return DateTimeOffset.Parse(dateText, Culture, _dateTimeStyles); + } +#endif + + if (!string.IsNullOrEmpty(_dateTimeFormat)) + return DateTime.ParseExact(dateText, _dateTimeFormat, Culture, _dateTimeStyles); + else + return DateTime.Parse(dateText, Culture, _dateTimeStyles); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/JavaScriptDateTimeConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/JavaScriptDateTimeConverter.cs new file mode 100644 index 0000000..c59bfe8 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/JavaScriptDateTimeConverter.cs @@ -0,0 +1,93 @@ +using System; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from a JavaScript date constructor (e.g. new Date(52231943)). + /// + public class JavaScriptDateTimeConverter : DateTimeConverterBase + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + long ticks; + + if (value is DateTime) + { + DateTime dateTime = (DateTime)value; + DateTime utcDateTime = dateTime.ToUniversalTime(); + ticks = JsonConvert.ConvertDateTimeToJavaScriptTicks(utcDateTime); + } +#if !PocketPC && !NET20 + else if (value is DateTimeOffset) + { + DateTimeOffset dateTimeOffset = (DateTimeOffset)value; + DateTimeOffset utcDateTimeOffset = dateTimeOffset.ToUniversalTime(); + ticks = JsonConvert.ConvertDateTimeToJavaScriptTicks(utcDateTimeOffset.UtcDateTime); + } +#endif + else + { + throw new Exception("Expected date object value."); + } + + writer.WriteStartConstructor("Date"); + writer.WriteValue(ticks); + writer.WriteEndConstructor(); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing property value of the JSON that is being converted. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + Type t = (ReflectionUtils.IsNullableType(objectType)) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + if (reader.TokenType == JsonToken.Null) + { + if (!ReflectionUtils.IsNullableType(objectType)) + throw new Exception("Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + return null; + } + + if (reader.TokenType != JsonToken.StartConstructor || string.Compare(reader.Value.ToString(), "Date", StringComparison.Ordinal) != 0) + throw new Exception("Unexpected token or value when parsing date. Token: {0}, Value: {1}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType, reader.Value)); + + reader.Read(); + + if (reader.TokenType != JsonToken.Integer) + throw new Exception("Unexpected token parsing date. Expected Integer, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + long ticks = (long)reader.Value; + + DateTime d = JsonConvert.ConvertJavaScriptTicksToDateTime(ticks); + + reader.Read(); + + if (reader.TokenType != JsonToken.EndConstructor) + throw new Exception("Unexpected token parsing date. Expected EndConstructor, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + +#if !PocketPC && !NET20 + if (t == typeof(DateTimeOffset)) + return new DateTimeOffset(d); +#endif + + return d; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/JsonDateTimeSerializationMode.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/JsonDateTimeSerializationMode.cs new file mode 100644 index 0000000..b698f6e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/JsonDateTimeSerializationMode.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Specifies whether a DateTime object represents a local time, a Coordinated Universal Time (UTC), or is not specified as either local time or UTC. + /// + public enum JsonDateTimeSerializationMode + { + /// + /// The time represented is local time. + /// + Local, + /// + /// The time represented is UTC. + /// + Utc, + /// + /// The time represented is not specified as either local time or Coordinated Universal Time (UTC). + /// + Unspecified, + /// + /// Preserves the DateTimeKind field of a date when a DateTime object is converted to a string and the string is then converted back to a DateTime object. + /// + RoundtripKind + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/KeyValuePairConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/KeyValuePairConverter.cs new file mode 100644 index 0000000..e5adbc4 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/KeyValuePairConverter.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; +using System.Reflection; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from JSON. + /// + public class KeyValuePairConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + Type t = value.GetType(); + PropertyInfo keyProperty = t.GetProperty("Key"); + PropertyInfo valueProperty = t.GetProperty("Value"); + + writer.WriteStartObject(); + writer.WritePropertyName("Key"); + serializer.Serialize(writer, ReflectionUtils.GetMemberValue(keyProperty, value)); + writer.WritePropertyName("Value"); + serializer.Serialize(writer, ReflectionUtils.GetMemberValue(valueProperty, value)); + writer.WriteEndObject(); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + IList genericArguments = objectType.GetGenericArguments(); + Type keyType = genericArguments[0]; + Type valueType = genericArguments[1]; + + reader.Read(); + reader.Read(); + object key = serializer.Deserialize(reader, keyType); + reader.Read(); + reader.Read(); + object value = serializer.Deserialize(reader, valueType); + reader.Read(); + + return ReflectionUtils.CreateInstance(objectType, key, value); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + if (objectType.IsValueType && objectType.IsGenericType) + return (objectType.GetGenericTypeDefinition() == typeof (KeyValuePair<,>)); + + return false; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/RegexConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/RegexConverter.cs new file mode 100644 index 0000000..7735cba --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/RegexConverter.cs @@ -0,0 +1,152 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Newtonsoft.Json.Bson; +using System.Globalization; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts a to and from JSON and BSON. + /// + public class RegexConverter : JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + Regex regex = (Regex) value; + + BsonWriter bsonWriter = writer as BsonWriter; + if (bsonWriter != null) + WriteBson(bsonWriter, regex); + else + WriteJson(writer, regex); + } + + private bool HasFlag(RegexOptions options, RegexOptions flag) + { + return ((options & flag) == flag); + } + + private void WriteBson(BsonWriter writer, Regex regex) + { + // Regular expression - The first cstring is the regex pattern, the second + // is the regex options string. Options are identified by characters, which + // must be stored in alphabetical order. Valid options are 'i' for case + // insensitive matching, 'm' for multiline matching, 'x' for verbose mode, + // 'l' to make \w, \W, etc. locale dependent, 's' for dotall mode + // ('.' matches everything), and 'u' to make \w, \W, etc. match unicode. + + string options = null; + + if (HasFlag(regex.Options, RegexOptions.IgnoreCase)) + options += "i"; + + if (HasFlag(regex.Options, RegexOptions.Multiline)) + options += "m"; + + if (HasFlag(regex.Options, RegexOptions.Singleline)) + options += "s"; + + options += "u"; + + if (HasFlag(regex.Options, RegexOptions.ExplicitCapture)) + options += "x"; + + writer.WriteRegex(regex.ToString(), options); + } + + private void WriteJson(JsonWriter writer, Regex regex) + { + writer.WriteStartObject(); + writer.WritePropertyName("Pattern"); + writer.WriteValue(regex.ToString()); + writer.WritePropertyName("Options"); + writer.WriteValue(regex.Options); + writer.WriteEndObject(); + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + BsonReader bsonReader = reader as BsonReader; + + if (bsonReader != null) + return ReadBson(bsonReader); + else + return ReadJson(reader); + } + + private object ReadBson(BsonReader reader) + { + string regexText = (string)reader.Value; + int patternOptionDelimiterIndex = regexText.LastIndexOf(@"/"); + + string patternText = regexText.Substring(1, patternOptionDelimiterIndex - 1); + string optionsText = regexText.Substring(patternOptionDelimiterIndex + 1); + + RegexOptions options = RegexOptions.None; + foreach (char c in optionsText) + { + switch (c) + { + case 'i': + options |= RegexOptions.IgnoreCase; + break; + case 'm': + options |= RegexOptions.Multiline; + break; + case 's': + options |= RegexOptions.Singleline; + break; + case 'x': + options |= RegexOptions.ExplicitCapture; + break; + } + } + + return new Regex(patternText, options); + } + + private Regex ReadJson(JsonReader reader) + { + reader.Read(); + reader.Read(); + string pattern = (string) reader.Value; + + reader.Read(); + reader.Read(); + int options = Convert.ToInt32(reader.Value, CultureInfo.InvariantCulture); + + reader.Read(); + + return new Regex(pattern, (RegexOptions)options); + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + return (objectType == typeof (Regex)); + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs new file mode 100644 index 0000000..1c9e50e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/StringEnumConverter.cs @@ -0,0 +1,194 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Converters +{ + /// + /// Converts an to and from its name string value. + /// + /// + /// Converts an to and from its name string value. + /// + public class StringEnumConverter : JsonConverter + { + private readonly Dictionary> _enumMemberNamesPerType = new Dictionary>(); + + /// + /// Gets or sets a value indicating whether the written enum text should be camel case. + /// + /// true if the written enum text will be camel case; otherwise, false. + public bool CamelCaseText { get; set; } + + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + if (value == null) + { + writer.WriteNull(); + return; + } + + Enum e = (Enum)value; + + string enumName = e.ToString("G"); + + if (char.IsNumber(enumName[0]) || enumName[0] == '-') + { + writer.WriteValue(value); + } + else + { + BidirectionalDictionary map = GetEnumNameMap(e.GetType()); + + string resolvedEnumName; + map.TryGetByFirst(enumName, out resolvedEnumName); + resolvedEnumName = resolvedEnumName ?? enumName; + + if (CamelCaseText) + resolvedEnumName = StringUtils.ToCamelCase(resolvedEnumName); + + writer.WriteValue(resolvedEnumName); + } + } + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + Type t = (ReflectionUtils.IsNullableType(objectType)) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + if (reader.TokenType == JsonToken.Null) + { + if (!ReflectionUtils.IsNullableType(objectType)) + throw new Exception("Cannot convert null value to {0}.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + return null; + } + + if (reader.TokenType == JsonToken.String) + { + var map = GetEnumNameMap(t); + string resolvedEnumName; + map.TryGetBySecond(reader.Value.ToString(), out resolvedEnumName); + resolvedEnumName = resolvedEnumName ?? reader.Value.ToString(); + + return Enum.Parse(t, resolvedEnumName, true); + } + + if (reader.TokenType == JsonToken.Integer) + return ConvertUtils.ConvertOrCast(reader.Value, CultureInfo.InvariantCulture, t); + + throw new Exception("Unexpected token when parsing enum. Expected String or Integer, got {0}.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + + /// + /// A cached representation of the Enum string representation to respect per Enum field name. + /// + /// The type of the Enum. + /// A map of enum field name to either the field name, or the configured enum member name (). + private BidirectionalDictionary GetEnumNameMap(Type t) + { + BidirectionalDictionary map; + + if (!_enumMemberNamesPerType.TryGetValue(t, out map)) + { + lock (_enumMemberNamesPerType) + { + if (_enumMemberNamesPerType.TryGetValue(t, out map)) + return map; + + map = new BidirectionalDictionary( + StringComparer.OrdinalIgnoreCase, + StringComparer.OrdinalIgnoreCase); + + foreach (FieldInfo f in t.GetFields()) + { + string n1 = f.Name; + string n2; + +#if !NET20 + n2 = f.GetCustomAttributes(typeof (EnumMemberAttribute), true) + .Cast() + .Select(a => a.Value) + .SingleOrDefault() ?? f.Name; +#else + n2 = f.Name; +#endif + + string s; + if (map.TryGetBySecond(n2, out s)) + { + throw new Exception("Enum name '{0}' already exists on enum '{1}'." + .FormatWith(CultureInfo.InvariantCulture, n2, t.Name)); + } + + map.Add(n1, n2); + } + + _enumMemberNamesPerType[t] = map; + } + } + + return map; + } + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public override bool CanConvert(Type objectType) + { + Type t = (ReflectionUtils.IsNullableType(objectType)) + ? Nullable.GetUnderlyingType(objectType) + : objectType; + + return t.IsEnum; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs new file mode 100644 index 0000000..b864db3 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Converters/XmlNodeConverter.cs @@ -0,0 +1,1520 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !SILVERLIGHT || WINDOWS_PHONE +using System; +using System.Collections.Generic; +using System.Xml; +#if !NET20 +using System.Xml.Linq; +#endif +using Newtonsoft.Json.Utilities; +using System.Linq; + +namespace Newtonsoft.Json.Converters +{ + #region XmlNodeWrappers +#if !SILVERLIGHT + internal class XmlDocumentWrapper : XmlNodeWrapper, IXmlDocument + { + private XmlDocument _document; + + public XmlDocumentWrapper(XmlDocument document) + : base(document) + { + _document = document; + } + + public IXmlNode CreateComment(string data) + { + return new XmlNodeWrapper(_document.CreateComment(data)); + } + + public IXmlNode CreateTextNode(string text) + { + return new XmlNodeWrapper(_document.CreateTextNode(text)); + } + + public IXmlNode CreateCDataSection(string data) + { + return new XmlNodeWrapper(_document.CreateCDataSection(data)); + } + + public IXmlNode CreateWhitespace(string text) + { + return new XmlNodeWrapper(_document.CreateWhitespace(text)); + } + + public IXmlNode CreateSignificantWhitespace(string text) + { + return new XmlNodeWrapper(_document.CreateSignificantWhitespace(text)); + } + + public IXmlNode CreateXmlDeclaration(string version, string encoding, string standalone) + { + return new XmlNodeWrapper(_document.CreateXmlDeclaration(version, encoding, standalone)); + } + + public IXmlNode CreateProcessingInstruction(string target, string data) + { + return new XmlNodeWrapper(_document.CreateProcessingInstruction(target, data)); + } + + public IXmlElement CreateElement(string elementName) + { + return new XmlElementWrapper(_document.CreateElement(elementName)); + } + + public IXmlElement CreateElement(string qualifiedName, string namespaceURI) + { + return new XmlElementWrapper(_document.CreateElement(qualifiedName, namespaceURI)); + } + + public IXmlNode CreateAttribute(string name, string value) + { + XmlNodeWrapper attribute = new XmlNodeWrapper(_document.CreateAttribute(name)); + attribute.Value = value; + + return attribute; + } + + public IXmlNode CreateAttribute(string qualifiedName, string namespaceURI, string value) + { + XmlNodeWrapper attribute = new XmlNodeWrapper(_document.CreateAttribute(qualifiedName, namespaceURI)); + attribute.Value = value; + + return attribute; + } + + public IXmlElement DocumentElement + { + get + { + if (_document.DocumentElement == null) + return null; + + return new XmlElementWrapper(_document.DocumentElement); + } + } + } + + internal class XmlElementWrapper : XmlNodeWrapper, IXmlElement + { + private XmlElement _element; + + public XmlElementWrapper(XmlElement element) + : base(element) + { + _element = element; + } + + public void SetAttributeNode(IXmlNode attribute) + { + XmlNodeWrapper xmlAttributeWrapper = (XmlNodeWrapper)attribute; + + _element.SetAttributeNode((XmlAttribute) xmlAttributeWrapper.WrappedNode); + } + + public string GetPrefixOfNamespace(string namespaceURI) + { + return _element.GetPrefixOfNamespace(namespaceURI); + } + } + + internal class XmlDeclarationWrapper : XmlNodeWrapper, IXmlDeclaration + { + private XmlDeclaration _declaration; + + public XmlDeclarationWrapper(XmlDeclaration declaration) + : base(declaration) + { + _declaration = declaration; + } + + public string Version + { + get { return _declaration.Version; } + } + + public string Encoding + { + get { return _declaration.Encoding; } + set { _declaration.Encoding = value; } + } + + public string Standalone + { + get { return _declaration.Standalone; } + set { _declaration.Standalone = value; } + } + } + + internal class XmlNodeWrapper : IXmlNode + { + private readonly XmlNode _node; + + public XmlNodeWrapper(XmlNode node) + { + _node = node; + } + + public object WrappedNode + { + get { return _node; } + } + + public XmlNodeType NodeType + { + get { return _node.NodeType; } + } + + public string Name + { + get { return _node.Name; } + } + + public string LocalName + { + get { return _node.LocalName; } + } + + public IList ChildNodes + { + get { return _node.ChildNodes.Cast().Select(n => WrapNode(n)).ToList(); } + } + + private IXmlNode WrapNode(XmlNode node) + { + switch (node.NodeType) + { + case XmlNodeType.Element: + return new XmlElementWrapper((XmlElement) node); + case XmlNodeType.XmlDeclaration: + return new XmlDeclarationWrapper((XmlDeclaration) node); + default: + return new XmlNodeWrapper(node); + } + } + + public IList Attributes + { + get + { + if (_node.Attributes == null) + return null; + + return _node.Attributes.Cast().Select(a => WrapNode(a)).ToList(); + } + } + + public IXmlNode ParentNode + { + get + { + XmlNode node = (_node is XmlAttribute) + ? ((XmlAttribute) _node).OwnerElement + : _node.ParentNode; + + if (node == null) + return null; + + return WrapNode(node); + } + } + + public string Value + { + get { return _node.Value; } + set { _node.Value = value; } + } + + public IXmlNode AppendChild(IXmlNode newChild) + { + XmlNodeWrapper xmlNodeWrapper = (XmlNodeWrapper) newChild; + _node.AppendChild(xmlNodeWrapper._node); + + return newChild; + } + + public string Prefix + { + get { return _node.Prefix; } + } + + public string NamespaceURI + { + get { return _node.NamespaceURI; } + } + } +#endif + #endregion + + #region Interfaces + internal interface IXmlDocument : IXmlNode + { + IXmlNode CreateComment(string text); + IXmlNode CreateTextNode(string text); + IXmlNode CreateCDataSection(string data); + IXmlNode CreateWhitespace(string text); + IXmlNode CreateSignificantWhitespace(string text); + IXmlNode CreateXmlDeclaration(string version, string encoding, string standalone); + IXmlNode CreateProcessingInstruction(string target, string data); + IXmlElement CreateElement(string elementName); + IXmlElement CreateElement(string qualifiedName, string namespaceURI); + IXmlNode CreateAttribute(string name, string value); + IXmlNode CreateAttribute(string qualifiedName, string namespaceURI, string value); + + IXmlElement DocumentElement { get; } + } + + internal interface IXmlDeclaration : IXmlNode + { + string Version { get; } + string Encoding { get; set; } + string Standalone { get; set; } + } + + internal interface IXmlElement : IXmlNode + { + void SetAttributeNode(IXmlNode attribute); + string GetPrefixOfNamespace(string namespaceURI); + } + + internal interface IXmlNode + { + XmlNodeType NodeType { get; } + string LocalName { get; } + IList ChildNodes { get; } + IList Attributes { get; } + IXmlNode ParentNode { get; } + string Value { get; set; } + IXmlNode AppendChild(IXmlNode newChild); + string NamespaceURI { get; } + object WrappedNode { get; } + } + #endregion + + #region XNodeWrappers +#if !NET20 + internal class XDeclarationWrapper : XObjectWrapper, IXmlDeclaration + { + internal readonly XDeclaration _declaration; + + public XDeclarationWrapper(XDeclaration declaration) + : base(null) + { + _declaration = declaration; + } + + public override XmlNodeType NodeType + { + get { return XmlNodeType.XmlDeclaration; } + } + + public string Version + { + get { return _declaration.Version; } + } + + public string Encoding + { + get { return _declaration.Encoding; } + set { _declaration.Encoding = value; } + } + + public string Standalone + { + get { return _declaration.Standalone; } + set { _declaration.Standalone = value; } + } + } + + internal class XDocumentWrapper : XContainerWrapper, IXmlDocument + { + private XDocument Document + { + get { return (XDocument)WrappedNode; } + } + + public XDocumentWrapper(XDocument document) + : base(document) + { + } + + public override IList ChildNodes + { + get + { + IList childNodes = base.ChildNodes; + + if (Document.Declaration != null) + childNodes.Insert(0, new XDeclarationWrapper(Document.Declaration)); + + return childNodes; + } + } + + public IXmlNode CreateComment(string text) + { + return new XObjectWrapper(new XComment(text)); + } + + public IXmlNode CreateTextNode(string text) + { + return new XObjectWrapper(new XText(text)); + } + + public IXmlNode CreateCDataSection(string data) + { + return new XObjectWrapper(new XCData(data)); + } + + public IXmlNode CreateWhitespace(string text) + { + return new XObjectWrapper(new XText(text)); + } + + public IXmlNode CreateSignificantWhitespace(string text) + { + return new XObjectWrapper(new XText(text)); + } + + public IXmlNode CreateXmlDeclaration(string version, string encoding, string standalone) + { + return new XDeclarationWrapper(new XDeclaration(version, encoding, standalone)); + } + + public IXmlNode CreateProcessingInstruction(string target, string data) + { + return new XProcessingInstructionWrapper(new XProcessingInstruction(target, data)); + } + + public IXmlElement CreateElement(string elementName) + { + return new XElementWrapper(new XElement(elementName)); + } + + public IXmlElement CreateElement(string qualifiedName, string namespaceURI) + { + string localName = MiscellaneousUtils.GetLocalName(qualifiedName); + return new XElementWrapper(new XElement(XName.Get(localName, namespaceURI))); + } + + public IXmlNode CreateAttribute(string name, string value) + { + return new XAttributeWrapper(new XAttribute(name, value)); + } + + public IXmlNode CreateAttribute(string qualifiedName, string namespaceURI, string value) + { + string localName = MiscellaneousUtils.GetLocalName(qualifiedName); + return new XAttributeWrapper(new XAttribute(XName.Get(localName, namespaceURI), value)); + } + + public IXmlElement DocumentElement + { + get + { + if (Document.Root == null) + return null; + + return new XElementWrapper(Document.Root); + } + } + + public override IXmlNode AppendChild(IXmlNode newChild) + { + XDeclarationWrapper declarationWrapper = newChild as XDeclarationWrapper; + if (declarationWrapper != null) + { + Document.Declaration = declarationWrapper._declaration; + return declarationWrapper; + } + else + { + return base.AppendChild(newChild); + } + } + } + + internal class XTextWrapper : XObjectWrapper + { + private XText Text + { + get { return (XText)WrappedNode; } + } + + public XTextWrapper(XText text) + : base(text) + { + } + + public override string Value + { + get { return Text.Value; } + set { Text.Value = value; } + } + + public override IXmlNode ParentNode + { + get + { + if (Text.Parent == null) + return null; + + return XContainerWrapper.WrapNode(Text.Parent); + } + } + } + + internal class XCommentWrapper : XObjectWrapper + { + private XComment Text + { + get { return (XComment)WrappedNode; } + } + + public XCommentWrapper(XComment text) + : base(text) + { + } + + public override string Value + { + get { return Text.Value; } + set { Text.Value = value; } + } + + public override IXmlNode ParentNode + { + get + { + if (Text.Parent == null) + return null; + + return XContainerWrapper.WrapNode(Text.Parent); + } + } + } + + internal class XProcessingInstructionWrapper : XObjectWrapper + { + private XProcessingInstruction ProcessingInstruction + { + get { return (XProcessingInstruction)WrappedNode; } + } + + public XProcessingInstructionWrapper(XProcessingInstruction processingInstruction) + : base(processingInstruction) + { + } + + public override string LocalName + { + get { return ProcessingInstruction.Target; } + } + + public override string Value + { + get { return ProcessingInstruction.Data; } + set { ProcessingInstruction.Data = value; } + } + } + + internal class XContainerWrapper : XObjectWrapper + { + private XContainer Container + { + get { return (XContainer)WrappedNode; } + } + + public XContainerWrapper(XContainer container) + : base(container) + { + } + + public override IList ChildNodes + { + get { return Container.Nodes().Select(n => WrapNode(n)).ToList(); } + } + + public override IXmlNode ParentNode + { + get + { + if (Container.Parent == null) + return null; + + return WrapNode(Container.Parent); + } + } + + internal static IXmlNode WrapNode(XObject node) + { + if (node is XDocument) + return new XDocumentWrapper((XDocument)node); + else if (node is XElement) + return new XElementWrapper((XElement)node); + else if (node is XContainer) + return new XContainerWrapper((XContainer)node); + else if (node is XProcessingInstruction) + return new XProcessingInstructionWrapper((XProcessingInstruction)node); + else if (node is XText) + return new XTextWrapper((XText)node); + else if (node is XComment) + return new XCommentWrapper((XComment)node); + else if (node is XAttribute) + return new XAttributeWrapper((XAttribute) node); + else + return new XObjectWrapper(node); + } + + public override IXmlNode AppendChild(IXmlNode newChild) + { + Container.Add(newChild.WrappedNode); + return newChild; + } + } + + internal class XObjectWrapper : IXmlNode + { + private readonly XObject _xmlObject; + + public XObjectWrapper(XObject xmlObject) + { + _xmlObject = xmlObject; + } + + public object WrappedNode + { + get { return _xmlObject; } + } + + public virtual XmlNodeType NodeType + { + get { return _xmlObject.NodeType; } + } + + public virtual string LocalName + { + get { return null; } + } + + public virtual IList ChildNodes + { + get { return new List(); } + } + + public virtual IList Attributes + { + get { return null; } + } + + public virtual IXmlNode ParentNode + { + get { return null; } + } + + public virtual string Value + { + get { return null; } + set { throw new InvalidOperationException(); } + } + + public virtual IXmlNode AppendChild(IXmlNode newChild) + { + throw new InvalidOperationException(); + } + + public virtual string NamespaceURI + { + get { return null; } + } + } + + internal class XAttributeWrapper : XObjectWrapper + { + private XAttribute Attribute + { + get { return (XAttribute)WrappedNode; } + } + + public XAttributeWrapper(XAttribute attribute) + : base(attribute) + { + } + + public override string Value + { + get { return Attribute.Value; } + set { Attribute.Value = value; } + } + + public override string LocalName + { + get { return Attribute.Name.LocalName; } + } + + public override string NamespaceURI + { + get { return Attribute.Name.NamespaceName; } + } + + public override IXmlNode ParentNode + { + get + { + if (Attribute.Parent == null) + return null; + + return XContainerWrapper.WrapNode(Attribute.Parent); + } + } + } + + internal class XElementWrapper : XContainerWrapper, IXmlElement + { + private XElement Element + { + get { return (XElement) WrappedNode; } + } + + public XElementWrapper(XElement element) + : base(element) + { + } + + public void SetAttributeNode(IXmlNode attribute) + { + XObjectWrapper wrapper = (XObjectWrapper)attribute; + Element.Add(wrapper.WrappedNode); + } + + public override IList Attributes + { + get { return Element.Attributes().Select(a => new XAttributeWrapper(a)).Cast().ToList(); } + } + + public override string Value + { + get { return Element.Value; } + set { Element.Value = value; } + } + + public override string LocalName + { + get { return Element.Name.LocalName; } + } + + public override string NamespaceURI + { + get { return Element.Name.NamespaceName; } + } + + public string GetPrefixOfNamespace(string namespaceURI) + { + return Element.GetPrefixOfNamespace(namespaceURI); + } + } +#endif + #endregion + + /// + /// Converts XML to and from JSON. + /// + public class XmlNodeConverter : JsonConverter + { + private const string TextName = "#text"; + private const string CommentName = "#comment"; + private const string CDataName = "#cdata-section"; + private const string WhitespaceName = "#whitespace"; + private const string SignificantWhitespaceName = "#significant-whitespace"; + private const string DeclarationName = "?xml"; + private const string JsonNamespaceUri = "http://james.newtonking.com/projects/json"; + + /// + /// Gets or sets the name of the root element to insert when deserializing to XML if the JSON structure has produces multiple root elements. + /// + /// The name of the deserialize root element. + public string DeserializeRootElementName { get; set; } + + /// + /// Gets or sets a flag to indicate whether to write the Json.NET array attribute. + /// This attribute helps preserve arrays when converting the written XML back to JSON. + /// + /// true if the array attibute is written to the XML; otherwise, false. + public bool WriteArrayAttribute { get; set; } + + /// + /// Gets or sets a value indicating whether to write the root JSON object. + /// + /// true if the JSON root object is omitted; otherwise, false. + public bool OmitRootObject { get; set; } + + #region Writing + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The calling serializer. + /// The value. + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + IXmlNode node = WrapXml(value); + + XmlNamespaceManager manager = new XmlNamespaceManager(new NameTable()); + PushParentNamespaces(node, manager); + + if (!OmitRootObject) + writer.WriteStartObject(); + + SerializeNode(writer, node, manager, !OmitRootObject); + + if (!OmitRootObject) + writer.WriteEndObject(); + } + + private IXmlNode WrapXml(object value) + { +#if !NET20 + if (value is XObject) + return XContainerWrapper.WrapNode((XObject)value); +#endif +#if !SILVERLIGHT + if (value is XmlNode) + return new XmlNodeWrapper((XmlNode)value); +#endif + + throw new ArgumentException("Value must be an XML object.", "value"); + } + + private void PushParentNamespaces(IXmlNode node, XmlNamespaceManager manager) + { + List parentElements = null; + + IXmlNode parent = node; + while ((parent = parent.ParentNode) != null) + { + if (parent.NodeType == XmlNodeType.Element) + { + if (parentElements == null) + parentElements = new List(); + + parentElements.Add(parent); + } + } + + if (parentElements != null) + { + parentElements.Reverse(); + + foreach (IXmlNode parentElement in parentElements) + { + manager.PushScope(); + foreach (IXmlNode attribute in parentElement.Attributes) + { + if (attribute.NamespaceURI == "http://www.w3.org/2000/xmlns/" && attribute.LocalName != "xmlns") + manager.AddNamespace(attribute.LocalName, attribute.Value); + } + } + } + } + + private string ResolveFullName(IXmlNode node, XmlNamespaceManager manager) + { + string prefix = (node.NamespaceURI == null || (node.LocalName == "xmlns" && node.NamespaceURI == "http://www.w3.org/2000/xmlns/")) + ? null + : manager.LookupPrefix(node.NamespaceURI); + + if (!string.IsNullOrEmpty(prefix)) + return prefix + ":" + node.LocalName; + else + return node.LocalName; + } + + private string GetPropertyName(IXmlNode node, XmlNamespaceManager manager) + { + switch (node.NodeType) + { + case XmlNodeType.Attribute: + if (node.NamespaceURI == JsonNamespaceUri) + return "$" + node.LocalName; + else + return "@" + ResolveFullName(node, manager); + case XmlNodeType.CDATA: + return CDataName; + case XmlNodeType.Comment: + return CommentName; + case XmlNodeType.Element: + return ResolveFullName(node, manager); + case XmlNodeType.ProcessingInstruction: + return "?" + ResolveFullName(node, manager); + case XmlNodeType.XmlDeclaration: + return DeclarationName; + case XmlNodeType.SignificantWhitespace: + return SignificantWhitespaceName; + case XmlNodeType.Text: + return TextName; + case XmlNodeType.Whitespace: + return WhitespaceName; + default: + throw new JsonSerializationException("Unexpected XmlNodeType when getting node name: " + node.NodeType); + } + } + + private bool IsArray(IXmlNode node) + { + IXmlNode jsonArrayAttribute = (node.Attributes != null) + ? node.Attributes.SingleOrDefault(a => a.LocalName == "Array" && a.NamespaceURI == JsonNamespaceUri) + : null; + + return (jsonArrayAttribute != null && XmlConvert.ToBoolean(jsonArrayAttribute.Value)); + } + + private void SerializeGroupedNodes(JsonWriter writer, IXmlNode node, XmlNamespaceManager manager, bool writePropertyName) + { + // group nodes together by name + Dictionary> nodesGroupedByName = new Dictionary>(); + + for (int i = 0; i < node.ChildNodes.Count; i++) + { + IXmlNode childNode = node.ChildNodes[i]; + string nodeName = GetPropertyName(childNode, manager); + + List nodes; + if (!nodesGroupedByName.TryGetValue(nodeName, out nodes)) + { + nodes = new List(); + nodesGroupedByName.Add(nodeName, nodes); + } + + nodes.Add(childNode); + } + + // loop through grouped nodes. write single name instances as normal, + // write multiple names together in an array + foreach (KeyValuePair> nodeNameGroup in nodesGroupedByName) + { + List groupedNodes = nodeNameGroup.Value; + bool writeArray; + + if (groupedNodes.Count == 1) + { + writeArray = IsArray(groupedNodes[0]); + } + else + { + writeArray = true; + } + + if (!writeArray) + { + SerializeNode(writer, groupedNodes[0], manager, writePropertyName); + } + else + { + string elementNames = nodeNameGroup.Key; + + if (writePropertyName) + writer.WritePropertyName(elementNames); + + writer.WriteStartArray(); + + for (int i = 0; i < groupedNodes.Count; i++) + { + SerializeNode(writer, groupedNodes[i], manager, false); + } + + writer.WriteEndArray(); + } + } + } + + private void SerializeNode(JsonWriter writer, IXmlNode node, XmlNamespaceManager manager, bool writePropertyName) + { + switch (node.NodeType) + { + case XmlNodeType.Document: + case XmlNodeType.DocumentFragment: + SerializeGroupedNodes(writer, node, manager, writePropertyName); + break; + case XmlNodeType.Element: + if (IsArray(node) && node.ChildNodes.All(n => n.LocalName == node.LocalName)) + { + SerializeGroupedNodes(writer, node, manager, false); + } + else + { + foreach (IXmlNode attribute in node.Attributes) + { + if (attribute.NamespaceURI == "http://www.w3.org/2000/xmlns/") + { + string prefix = (attribute.LocalName != "xmlns") + ? attribute.LocalName + : string.Empty; + + manager.AddNamespace(prefix, attribute.Value); + } + } + + if (writePropertyName) + writer.WritePropertyName(GetPropertyName(node, manager)); + + if (ValueAttributes(node.Attributes).Count() == 0 && node.ChildNodes.Count == 1 + && node.ChildNodes[0].NodeType == XmlNodeType.Text) + { + // write elements with a single text child as a name value pair + writer.WriteValue(node.ChildNodes[0].Value); + } + else if (node.ChildNodes.Count == 0 && CollectionUtils.IsNullOrEmpty(node.Attributes)) + { + // empty element + writer.WriteNull(); + } + else + { + writer.WriteStartObject(); + + for (int i = 0; i < node.Attributes.Count; i++) + { + SerializeNode(writer, node.Attributes[i], manager, true); + } + + SerializeGroupedNodes(writer, node, manager, true); + + writer.WriteEndObject(); + } + } + + break; + case XmlNodeType.Comment: + if (writePropertyName) + writer.WriteComment(node.Value); + break; + case XmlNodeType.Attribute: + case XmlNodeType.Text: + case XmlNodeType.CDATA: + case XmlNodeType.ProcessingInstruction: + case XmlNodeType.Whitespace: + case XmlNodeType.SignificantWhitespace: + if (node.NamespaceURI == "http://www.w3.org/2000/xmlns/" && node.Value == JsonNamespaceUri) + return; + + if (node.NamespaceURI == JsonNamespaceUri) + { + if (node.LocalName == "Array") + return; + } + + if (writePropertyName) + writer.WritePropertyName(GetPropertyName(node, manager)); + writer.WriteValue(node.Value); + break; + case XmlNodeType.XmlDeclaration: + IXmlDeclaration declaration = (IXmlDeclaration)node; + writer.WritePropertyName(GetPropertyName(node, manager)); + writer.WriteStartObject(); + + if (!string.IsNullOrEmpty(declaration.Version)) + { + writer.WritePropertyName("@version"); + writer.WriteValue(declaration.Version); + } + if (!string.IsNullOrEmpty(declaration.Encoding)) + { + writer.WritePropertyName("@encoding"); + writer.WriteValue(declaration.Encoding); + } + if (!string.IsNullOrEmpty(declaration.Standalone)) + { + writer.WritePropertyName("@standalone"); + writer.WriteValue(declaration.Standalone); + } + + writer.WriteEndObject(); + break; + default: + throw new JsonSerializationException("Unexpected XmlNodeType when serializing nodes: " + node.NodeType); + } + } + #endregion + + #region Reading + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + XmlNamespaceManager manager = new XmlNamespaceManager(new NameTable()); + IXmlDocument document = null; + IXmlNode rootNode = null; + +#if !NET20 + if (typeof(XObject).IsAssignableFrom(objectType)) + { + if (objectType != typeof (XDocument) && objectType != typeof (XElement)) + throw new JsonSerializationException("XmlNodeConverter only supports deserializing XDocument or XElement."); + + XDocument d = new XDocument(); + document = new XDocumentWrapper(d); + rootNode = document; + } +#endif +#if !SILVERLIGHT + if (typeof(XmlNode).IsAssignableFrom(objectType)) + { + if (objectType != typeof (XmlDocument)) + throw new JsonSerializationException("XmlNodeConverter only supports deserializing XmlDocuments"); + + XmlDocument d = new XmlDocument(); + document = new XmlDocumentWrapper(d); + rootNode = document; + } +#endif + + if (document == null || rootNode == null) + throw new JsonSerializationException("Unexpected type when converting XML: " + objectType); + + if (reader.TokenType != JsonToken.StartObject) + throw new JsonSerializationException("XmlNodeConverter can only convert JSON that begins with an object."); + + if (!string.IsNullOrEmpty(DeserializeRootElementName)) + { + //rootNode = document.CreateElement(DeserializeRootElementName); + //document.AppendChild(rootNode); + ReadElement(reader, document, rootNode, DeserializeRootElementName, manager); + } + else + { + reader.Read(); + DeserializeNode(reader, document, manager, rootNode); + } + +#if !NET20 + if (objectType == typeof(XElement)) + { + XElement element = (XElement)document.DocumentElement.WrappedNode; + element.Remove(); + + return element; + } +#endif + + return document.WrappedNode; + } + + private void DeserializeValue(JsonReader reader, IXmlDocument document, XmlNamespaceManager manager, string propertyName, IXmlNode currentNode) + { + switch (propertyName) + { + case TextName: + currentNode.AppendChild(document.CreateTextNode(reader.Value.ToString())); + break; + case CDataName: + currentNode.AppendChild(document.CreateCDataSection(reader.Value.ToString())); + break; + case WhitespaceName: + currentNode.AppendChild(document.CreateWhitespace(reader.Value.ToString())); + break; + case SignificantWhitespaceName: + currentNode.AppendChild(document.CreateSignificantWhitespace(reader.Value.ToString())); + break; + default: + // processing instructions and the xml declaration start with ? + if (!string.IsNullOrEmpty(propertyName) && propertyName[0] == '?') + { + CreateInstruction(reader, document, currentNode, propertyName); + } + else + { + if (reader.TokenType == JsonToken.StartArray) + { + // handle nested arrays + ReadArrayElements(reader, document, propertyName, currentNode, manager); + return; + } + + // have to wait until attributes have been parsed before creating element + // attributes may contain namespace info used by the element + ReadElement(reader, document, currentNode, propertyName, manager); + } + break; + } + } + + private void ReadElement(JsonReader reader, IXmlDocument document, IXmlNode currentNode, string propertyName, XmlNamespaceManager manager) + { + if (string.IsNullOrEmpty(propertyName)) + throw new JsonSerializationException("XmlNodeConverter cannot convert JSON with an empty property name to XML."); + + Dictionary attributeNameValues = ReadAttributeElements(reader, manager); + + string elementPrefix = MiscellaneousUtils.GetPrefix(propertyName); + + IXmlElement element = CreateElement(propertyName, document, elementPrefix, manager); + + currentNode.AppendChild(element); + + // add attributes to newly created element + foreach (KeyValuePair nameValue in attributeNameValues) + { + string attributePrefix = MiscellaneousUtils.GetPrefix(nameValue.Key); + + IXmlNode attribute = (!string.IsNullOrEmpty(attributePrefix)) + ? document.CreateAttribute(nameValue.Key, manager.LookupNamespace(attributePrefix), nameValue.Value) + : document.CreateAttribute(nameValue.Key, nameValue.Value); + + element.SetAttributeNode(attribute); + } + + if (reader.TokenType == JsonToken.String) + { + element.AppendChild(document.CreateTextNode(reader.Value.ToString())); + } + else if (reader.TokenType == JsonToken.Integer) + { + element.AppendChild(document.CreateTextNode(XmlConvert.ToString((long)reader.Value))); + } + else if (reader.TokenType == JsonToken.Float) + { + element.AppendChild(document.CreateTextNode(XmlConvert.ToString((double)reader.Value))); + } + else if (reader.TokenType == JsonToken.Boolean) + { + element.AppendChild(document.CreateTextNode(XmlConvert.ToString((bool)reader.Value))); + } + else if (reader.TokenType == JsonToken.Date) + { + DateTime d = (DateTime)reader.Value; + element.AppendChild(document.CreateTextNode(XmlConvert.ToString(d, DateTimeUtils.ToSerializationMode(d.Kind)))); + } + else if (reader.TokenType == JsonToken.Null) + { + // empty element. do nothing + } + else + { + // finished element will have no children to deserialize + if (reader.TokenType != JsonToken.EndObject) + { + manager.PushScope(); + + DeserializeNode(reader, document, manager, element); + + manager.PopScope(); + } + } + } + + private void ReadArrayElements(JsonReader reader, IXmlDocument document, string propertyName, IXmlNode currentNode, XmlNamespaceManager manager) + { + string elementPrefix = MiscellaneousUtils.GetPrefix(propertyName); + + IXmlElement nestedArrayElement = CreateElement(propertyName, document, elementPrefix, manager); + + currentNode.AppendChild(nestedArrayElement); + + int count = 0; + while (reader.Read() && reader.TokenType != JsonToken.EndArray) + { + DeserializeValue(reader, document, manager, propertyName, nestedArrayElement); + count++; + } + + if (WriteArrayAttribute) + { + AddJsonArrayAttribute(nestedArrayElement, document); + } + + if (count == 1 && WriteArrayAttribute) + { + IXmlElement arrayElement = nestedArrayElement.ChildNodes.CastValid().Single(n => n.LocalName == propertyName); + AddJsonArrayAttribute(arrayElement, document); + } + } + + private void AddJsonArrayAttribute(IXmlElement element, IXmlDocument document) + { + element.SetAttributeNode(document.CreateAttribute("json:Array", JsonNamespaceUri, "true")); + +#if !NET20 + // linq to xml doesn't automatically include prefixes via the namespace manager + if (element is XElementWrapper) + { + if (element.GetPrefixOfNamespace(JsonNamespaceUri) == null) + { + element.SetAttributeNode(document.CreateAttribute("xmlns:json", "http://www.w3.org/2000/xmlns/", JsonNamespaceUri)); + } + } +#endif + } + + private Dictionary ReadAttributeElements(JsonReader reader, XmlNamespaceManager manager) + { + Dictionary attributeNameValues = new Dictionary(); + bool finishedAttributes = false; + bool finishedElement = false; + + // a string token means the element only has a single text child + if (reader.TokenType != JsonToken.String + && reader.TokenType != JsonToken.Null + && reader.TokenType != JsonToken.Boolean + && reader.TokenType != JsonToken.Integer + && reader.TokenType != JsonToken.Float + && reader.TokenType != JsonToken.Date + && reader.TokenType != JsonToken.StartConstructor) + { + // read properties until first non-attribute is encountered + while (!finishedAttributes && !finishedElement && reader.Read()) + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string attributeName = reader.Value.ToString(); + string attributeValue; + + if (!string.IsNullOrEmpty(attributeName)) + { + char firstChar = attributeName[0]; + + switch (firstChar) + { + case '@': + attributeName = attributeName.Substring(1); + reader.Read(); + attributeValue = reader.Value.ToString(); + attributeNameValues.Add(attributeName, attributeValue); + + string namespacePrefix; + if (IsNamespaceAttribute(attributeName, out namespacePrefix)) + { + manager.AddNamespace(namespacePrefix, attributeValue); + } + break; + case '$': + attributeName = attributeName.Substring(1); + reader.Read(); + attributeValue = reader.Value.ToString(); + + // check that JsonNamespaceUri is in scope + // if it isn't then add it to document and namespace manager + string jsonPrefix = manager.LookupPrefix(JsonNamespaceUri); + if (jsonPrefix == null) + { + // ensure that the prefix used is free + int? i = null; + while (manager.LookupNamespace("json" + i) != null) + { + i = i.GetValueOrDefault() + 1; + } + jsonPrefix = "json" + i; + + attributeNameValues.Add("xmlns:" + jsonPrefix, JsonNamespaceUri); + manager.AddNamespace(jsonPrefix, JsonNamespaceUri); + } + + attributeNameValues.Add(jsonPrefix + ":" + attributeName, attributeValue); + break; + default: + finishedAttributes = true; + break; + } + } + else + { + finishedAttributes = true; + } + + break; + case JsonToken.EndObject: + finishedElement = true; + break; + default: + throw new JsonSerializationException("Unexpected JsonToken: " + reader.TokenType); + } + } + } + + return attributeNameValues; + } + + private void CreateInstruction(JsonReader reader, IXmlDocument document, IXmlNode currentNode, string propertyName) + { + if (propertyName == DeclarationName) + { + string version = null; + string encoding = null; + string standalone = null; + while (reader.Read() && reader.TokenType != JsonToken.EndObject) + { + switch (reader.Value.ToString()) + { + case "@version": + reader.Read(); + version = reader.Value.ToString(); + break; + case "@encoding": + reader.Read(); + encoding = reader.Value.ToString(); + break; + case "@standalone": + reader.Read(); + standalone = reader.Value.ToString(); + break; + default: + throw new JsonSerializationException("Unexpected property name encountered while deserializing XmlDeclaration: " + reader.Value); + } + } + + IXmlNode declaration = document.CreateXmlDeclaration(version, encoding, standalone); + currentNode.AppendChild(declaration); + } + else + { + IXmlNode instruction = document.CreateProcessingInstruction(propertyName.Substring(1), reader.Value.ToString()); + currentNode.AppendChild(instruction); + } + } + + private IXmlElement CreateElement(string elementName, IXmlDocument document, string elementPrefix, XmlNamespaceManager manager) + { + return (!string.IsNullOrEmpty(elementPrefix)) + ? document.CreateElement(elementName, manager.LookupNamespace(elementPrefix)) + : document.CreateElement(elementName); + } + + private void DeserializeNode(JsonReader reader, IXmlDocument document, XmlNamespaceManager manager, IXmlNode currentNode) + { + do + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + if (currentNode.NodeType == XmlNodeType.Document && document.DocumentElement != null) + throw new JsonSerializationException("JSON root object has multiple properties. The root object must have a single property in order to create a valid XML document. Consider specifing a DeserializeRootElementName."); + + string propertyName = reader.Value.ToString(); + reader.Read(); + + if (reader.TokenType == JsonToken.StartArray) + { + int count = 0; + while (reader.Read() && reader.TokenType != JsonToken.EndArray) + { + DeserializeValue(reader, document, manager, propertyName, currentNode); + count++; + } + + if (count == 1 && WriteArrayAttribute) + { + IXmlElement arrayElement = currentNode.ChildNodes.CastValid().Single(n => n.LocalName == propertyName); + AddJsonArrayAttribute(arrayElement, document); + } + } + else + { + DeserializeValue(reader, document, manager, propertyName, currentNode); + } + break; + case JsonToken.StartConstructor: + string constructorName = reader.Value.ToString(); + + while (reader.Read() && reader.TokenType != JsonToken.EndConstructor) + { + DeserializeValue(reader, document, manager, constructorName, currentNode); + } + break; + case JsonToken.Comment: + currentNode.AppendChild(document.CreateComment((string)reader.Value)); + break; + case JsonToken.EndObject: + case JsonToken.EndArray: + return; + default: + throw new JsonSerializationException("Unexpected JsonToken when deserializing node: " + reader.TokenType); + } + } while (reader.TokenType == JsonToken.PropertyName || reader.Read()); + // don't read if current token is a property. token was already read when parsing element attributes + } + + /// + /// Checks if the attributeName is a namespace attribute. + /// + /// Attribute name to test. + /// The attribute name prefix if it has one, otherwise an empty string. + /// True if attribute name is for a namespace attribute, otherwise false. + private bool IsNamespaceAttribute(string attributeName, out string prefix) + { + if (attributeName.StartsWith("xmlns", StringComparison.Ordinal)) + { + if (attributeName.Length == 5) + { + prefix = string.Empty; + return true; + } + else if (attributeName[5] == ':') + { + prefix = attributeName.Substring(6, attributeName.Length - 6); + return true; + } + } + prefix = null; + return false; + } + + private IEnumerable ValueAttributes(IEnumerable c) + { + return c.Where(a => a.NamespaceURI != JsonNamespaceUri); + } + #endregion + + /// + /// Determines whether this instance can convert the specified value type. + /// + /// Type of the value. + /// + /// true if this instance can convert the specified value type; otherwise, false. + /// + public override bool CanConvert(Type valueType) + { +#if !NET20 + if (typeof(XObject).IsAssignableFrom(valueType)) + return true; +#endif +#if !SILVERLIGHT + if (typeof(XmlNode).IsAssignableFrom(valueType)) + return true; +#endif + + return false; + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/DefaultValueHandling.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/DefaultValueHandling.cs new file mode 100644 index 0000000..43bacdb --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/DefaultValueHandling.cs @@ -0,0 +1,47 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Specifies default value handling options for the . + /// + public enum DefaultValueHandling + { + /// + /// Include default values when serializing and deserializing objects. + /// + Include = 0, + /// + /// Ignore default values when serializing and deserializing objects. + /// + Ignore = 1 + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Dynamic.snk b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Dynamic.snk new file mode 100644 index 0000000..2225208 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Dynamic.snk differ diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/FormatterAssemblyStyle.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/FormatterAssemblyStyle.cs new file mode 100644 index 0000000..d0675bb --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/FormatterAssemblyStyle.cs @@ -0,0 +1,19 @@ +#if SILVERLIGHT || PocketPC +namespace System.Runtime.Serialization.Formatters +{ + /// + /// Indicates the method that will be used during deserialization for locating and loading assemblies. + /// + public enum FormatterAssemblyStyle + { + /// + /// In simple mode, the assembly used during deserialization need not match exactly the assembly used during serialization. Specifically, the version numbers need not match as the LoadWithPartialName method is used to load the assembly. + /// + Simple, + /// + /// In full mode, the assembly used during deserialization must match exactly the assembly used during serialization. The Load method of the Assembly class is used to load the assembly. + /// + Full + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/IJsonLineInfo.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/IJsonLineInfo.cs new file mode 100644 index 0000000..7077b48 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/IJsonLineInfo.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Provides an interface to enable a class to return line and position information. + /// + public interface IJsonLineInfo + { + /// + /// Gets a value indicating whether the class can return line information. + /// + /// + /// true if LineNumber and LinePosition can be provided; otherwise, false. + /// + bool HasLineInfo(); + + /// + /// Gets the current line number. + /// + /// The current line number or 0 if no line information is available (for example, HasLineInfo returns false). + int LineNumber { get; } + /// + /// Gets the current line position. + /// + /// The current line position or 0 if no line information is available (for example, HasLineInfo returns false). + int LinePosition { get; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonArrayAttribute.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonArrayAttribute.cs new file mode 100644 index 0000000..689293b --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonArrayAttribute.cs @@ -0,0 +1,76 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the how to serialize the collection. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] + public sealed class JsonArrayAttribute : JsonContainerAttribute + { + private bool _allowNullItems; + + /// + /// Gets or sets a value indicating whether null items are allowed in the collection. + /// + /// true if null items are allowed in the collection; otherwise, false. + public bool AllowNullItems + { + get { return _allowNullItems; } + set { _allowNullItems = value; } + } + + /// + /// Initializes a new instance of the class. + /// + public JsonArrayAttribute() + { + } + + /// + /// Initializes a new instance of the class with a flag indicating whether the array can contain null items + /// + /// A flag indicating whether the array can contain null items. + public JsonArrayAttribute(bool allowNullItems) + { + _allowNullItems = allowNullItems; + } + + /// + /// Initializes a new instance of the class with the specified container Id. + /// + /// The container Id. + public JsonArrayAttribute(string id) + : base(id) + { + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConstructorAttribute.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConstructorAttribute.cs new file mode 100644 index 0000000..9f68563 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConstructorAttribute.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the not to serialize the public field or public read/write property value. + /// + [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Property, AllowMultiple = false)] + public sealed class JsonConstructorAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonContainerAttribute.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonContainerAttribute.cs new file mode 100644 index 0000000..779cb3e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonContainerAttribute.cs @@ -0,0 +1,87 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the how to serialize the object. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = false)] + public abstract class JsonContainerAttribute : Attribute + { + /// + /// Gets or sets the id. + /// + /// The id. + public string Id { get; set; } + /// + /// Gets or sets the title. + /// + /// The title. + public string Title { get; set; } + /// + /// Gets or sets the description. + /// + /// The description. + public string Description { get; set; } + + // yuck. can't set nullable properties on an attribute in C# + // have to use this approach to get an unset default state + internal bool? _isReference; + + /// + /// Gets or sets a value that indicates whether to preserve object reference data. + /// + /// + /// true to keep object reference; otherwise, false. The default is false. + /// + public bool IsReference + { + get { return _isReference ?? default(bool); } + set { _isReference = value; } + } + + /// + /// Initializes a new instance of the class. + /// + protected JsonContainerAttribute() + { + } + + /// + /// Initializes a new instance of the class with the specified container Id. + /// + /// The container Id. + protected JsonContainerAttribute(string id) + { + Id = id; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConvert.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConvert.cs new file mode 100644 index 0000000..90d1ec2 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConvert.cs @@ -0,0 +1,920 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.IO; +using System.Globalization; +using Newtonsoft.Json.Utilities; +using System.Xml; +using Newtonsoft.Json.Converters; +using System.Text; +#if !NET20 && (!SILVERLIGHT || WINDOWS_PHONE) +using System.Xml.Linq; +#endif + +namespace Newtonsoft.Json +{ + /// + /// Provides methods for converting between common language runtime types and JSON types. + /// + public static class JsonConvert + { + /// + /// Represents JavaScript's boolean value true as a string. This field is read-only. + /// + public static readonly string True = "true"; + + /// + /// Represents JavaScript's boolean value false as a string. This field is read-only. + /// + public static readonly string False = "false"; + + /// + /// Represents JavaScript's null as a string. This field is read-only. + /// + public static readonly string Null = "null"; + + /// + /// Represents JavaScript's undefined as a string. This field is read-only. + /// + public static readonly string Undefined = "undefined"; + + /// + /// Represents JavaScript's positive infinity as a string. This field is read-only. + /// + public static readonly string PositiveInfinity = "Infinity"; + + /// + /// Represents JavaScript's negative infinity as a string. This field is read-only. + /// + public static readonly string NegativeInfinity = "-Infinity"; + + /// + /// Represents JavaScript's NaN as a string. This field is read-only. + /// + public static readonly string NaN = "NaN"; + + internal static readonly long InitialJavaScriptDateTicks = 621355968000000000; + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(DateTime value) + { + using (StringWriter writer = StringUtils.CreateStringWriter(64)) + { + WriteDateTimeString(writer, value, GetUtcOffset(value), value.Kind); + return writer.ToString(); + } + } + +#if !PocketPC && !NET20 + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(DateTimeOffset value) + { + using (StringWriter writer = StringUtils.CreateStringWriter(64)) + { + WriteDateTimeString(writer, value.UtcDateTime, value.Offset, DateTimeKind.Local); + return writer.ToString(); + } + } +#endif + + private static TimeSpan GetUtcOffset(DateTime dateTime) + { +#if SILVERLIGHT + return TimeZoneInfo.Local.GetUtcOffset(dateTime); +#else + return TimeZone.CurrentTimeZone.GetUtcOffset(dateTime); +#endif + } + + internal static void WriteDateTimeString(TextWriter writer, DateTime value) + { + WriteDateTimeString(writer, value, GetUtcOffset(value), value.Kind); + } + + internal static void WriteDateTimeString(TextWriter writer, DateTime value, TimeSpan offset, DateTimeKind kind) + { + long javaScriptTicks = ConvertDateTimeToJavaScriptTicks(value, offset); + + writer.Write(@"""\/Date("); + writer.Write(javaScriptTicks); + + switch (kind) + { + case DateTimeKind.Local: + case DateTimeKind.Unspecified: + writer.Write((offset.Ticks >= 0L) ? "+" : "-"); + + int absHours = Math.Abs(offset.Hours); + if (absHours < 10) + writer.Write(0); + writer.Write(absHours); + int absMinutes = Math.Abs(offset.Minutes); + if (absMinutes < 10) + writer.Write(0); + writer.Write(absMinutes); + break; + } + + writer.Write(@")\/"""); + } + + private static long ToUniversalTicks(DateTime dateTime) + { + if (dateTime.Kind == DateTimeKind.Utc) + return dateTime.Ticks; + + return ToUniversalTicks(dateTime, GetUtcOffset(dateTime)); + } + + private static long ToUniversalTicks(DateTime dateTime, TimeSpan offset) + { + if (dateTime.Kind == DateTimeKind.Utc) + return dateTime.Ticks; + + long ticks = dateTime.Ticks - offset.Ticks; + if (ticks > 3155378975999999999L) + return 3155378975999999999L; + + if (ticks < 0L) + return 0L; + + return ticks; + } + + internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime, TimeSpan offset) + { + long universialTicks = ToUniversalTicks(dateTime, offset); + + return UniversialTicksToJavaScriptTicks(universialTicks); + } + + internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime) + { + return ConvertDateTimeToJavaScriptTicks(dateTime, true); + } + + internal static long ConvertDateTimeToJavaScriptTicks(DateTime dateTime, bool convertToUtc) + { + long ticks = (convertToUtc) ? ToUniversalTicks(dateTime) : dateTime.Ticks; + + return UniversialTicksToJavaScriptTicks(ticks); + } + + private static long UniversialTicksToJavaScriptTicks(long universialTicks) + { + long javaScriptTicks = (universialTicks - InitialJavaScriptDateTicks) / 10000; + + return javaScriptTicks; + } + + internal static DateTime ConvertJavaScriptTicksToDateTime(long javaScriptTicks) + { + DateTime dateTime = new DateTime((javaScriptTicks * 10000) + InitialJavaScriptDateTicks, DateTimeKind.Utc); + + return dateTime; + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(bool value) + { + return (value) ? True : False; + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(char value) + { + return ToString(char.ToString(value)); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(Enum value) + { + return value.ToString("D"); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(int value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(short value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + [CLSCompliant(false)] + public static string ToString(ushort value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + [CLSCompliant(false)] + public static string ToString(uint value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(long value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + [CLSCompliant(false)] + public static string ToString(ulong value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(float value) + { + return EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(double value) + { + return EnsureDecimalPlace(value, value.ToString("R", CultureInfo.InvariantCulture)); + } + + private static string EnsureDecimalPlace(double value, string text) + { + if (double.IsNaN(value) || double.IsInfinity(value) || text.IndexOf('.') != -1 || text.IndexOf('E') != -1) + return text; + + return text + ".0"; + } + + private static string EnsureDecimalPlace(string text) + { + if (text.IndexOf('.') != -1) + return text; + + return text + ".0"; + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(byte value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + [CLSCompliant(false)] + public static string ToString(sbyte value) + { + return value.ToString(null, CultureInfo.InvariantCulture); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(decimal value) + { + return EnsureDecimalPlace(value.ToString(null, CultureInfo.InvariantCulture)); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(Guid value) + { + return '"' + value.ToString("D", CultureInfo.InvariantCulture) + '"'; + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(string value) + { + return ToString(value, '"'); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// The string delimiter character. + /// A JSON string representation of the . + public static string ToString(string value, char delimter) + { + return JavaScriptUtils.ToEscapedJavaScriptString(value, delimter, true); + } + + /// + /// Converts the to its JSON string representation. + /// + /// The value to convert. + /// A JSON string representation of the . + public static string ToString(object value) + { + if (value == null) + return Null; + + IConvertible convertible = value as IConvertible; + + if (convertible != null) + { + switch (convertible.GetTypeCode()) + { + case TypeCode.String: + return ToString(convertible.ToString(CultureInfo.InvariantCulture)); + case TypeCode.Char: + return ToString(convertible.ToChar(CultureInfo.InvariantCulture)); + case TypeCode.Boolean: + return ToString(convertible.ToBoolean(CultureInfo.InvariantCulture)); + case TypeCode.SByte: + return ToString(convertible.ToSByte(CultureInfo.InvariantCulture)); + case TypeCode.Int16: + return ToString(convertible.ToInt16(CultureInfo.InvariantCulture)); + case TypeCode.UInt16: + return ToString(convertible.ToUInt16(CultureInfo.InvariantCulture)); + case TypeCode.Int32: + return ToString(convertible.ToInt32(CultureInfo.InvariantCulture)); + case TypeCode.Byte: + return ToString(convertible.ToByte(CultureInfo.InvariantCulture)); + case TypeCode.UInt32: + return ToString(convertible.ToUInt32(CultureInfo.InvariantCulture)); + case TypeCode.Int64: + return ToString(convertible.ToInt64(CultureInfo.InvariantCulture)); + case TypeCode.UInt64: + return ToString(convertible.ToUInt64(CultureInfo.InvariantCulture)); + case TypeCode.Single: + return ToString(convertible.ToSingle(CultureInfo.InvariantCulture)); + case TypeCode.Double: + return ToString(convertible.ToDouble(CultureInfo.InvariantCulture)); + case TypeCode.DateTime: + return ToString(convertible.ToDateTime(CultureInfo.InvariantCulture)); + case TypeCode.Decimal: + return ToString(convertible.ToDecimal(CultureInfo.InvariantCulture)); + case TypeCode.DBNull: + return Null; + } + } +#if !PocketPC && !NET20 + else if (value is DateTimeOffset) + { + return ToString((DateTimeOffset)value); + } +#endif + + throw new ArgumentException("Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.".FormatWith(CultureInfo.InvariantCulture, value.GetType())); + } + + private static bool IsJsonPrimitiveTypeCode(TypeCode typeCode) + { + switch (typeCode) + { + case TypeCode.String: + case TypeCode.Char: + case TypeCode.Boolean: + case TypeCode.SByte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.Byte: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.DateTime: + case TypeCode.Decimal: + case TypeCode.DBNull: + return true; + default: + return false; + } + } + + internal static bool IsJsonPrimitiveType(Type type) + { + if (ReflectionUtils.IsNullableType(type)) + type = Nullable.GetUnderlyingType(type); + +#if !PocketPC && !NET20 + if (type == typeof(DateTimeOffset)) + return true; +#endif + if (type == typeof(byte[])) + return true; + + return IsJsonPrimitiveTypeCode(Type.GetTypeCode(type)); + } + + internal static bool IsJsonPrimitive(object value) + { + if (value == null) + return true; + + IConvertible convertible = value as IConvertible; + + if (convertible != null) + return IsJsonPrimitiveTypeCode(convertible.GetTypeCode()); + +#if !PocketPC && !NET20 + if (value is DateTimeOffset) + return true; +#endif + + if (value is byte[]) + return true; + + return false; + } + + /// + /// Serializes the specified object to a JSON string. + /// + /// The object to serialize. + /// A JSON string representation of the object. + public static string SerializeObject(object value) + { + return SerializeObject(value, Formatting.None, (JsonSerializerSettings)null); + } + + /// + /// Serializes the specified object to a JSON string. + /// + /// The object to serialize. + /// Indicates how the output is formatted. + /// + /// A JSON string representation of the object. + /// + public static string SerializeObject(object value, Formatting formatting) + { + return SerializeObject(value, formatting, (JsonSerializerSettings)null); + } + + /// + /// Serializes the specified object to a JSON string using a collection of . + /// + /// The object to serialize. + /// A collection converters used while serializing. + /// A JSON string representation of the object. + public static string SerializeObject(object value, params JsonConverter[] converters) + { + return SerializeObject(value, Formatting.None, converters); + } + + /// + /// Serializes the specified object to a JSON string using a collection of . + /// + /// The object to serialize. + /// Indicates how the output is formatted. + /// A collection converters used while serializing. + /// A JSON string representation of the object. + public static string SerializeObject(object value, Formatting formatting, params JsonConverter[] converters) + { + JsonSerializerSettings settings = (converters != null && converters.Length > 0) + ? new JsonSerializerSettings { Converters = converters } + : null; + + return SerializeObject(value, formatting, settings); + } + + /// + /// Serializes the specified object to a JSON string using a collection of . + /// + /// The object to serialize. + /// Indicates how the output is formatted. + /// The used to serialize the object. + /// If this is null, default serialization settings will be is used. + /// + /// A JSON string representation of the object. + /// + public static string SerializeObject(object value, Formatting formatting, JsonSerializerSettings settings) + { + JsonSerializer jsonSerializer = JsonSerializer.Create(settings); + + StringBuilder sb = new StringBuilder(128); + StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture); + using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.Formatting = formatting; + + jsonSerializer.Serialize(jsonWriter, value); + } + + return sw.ToString(); + } + + /// + /// Deserializes the JSON to a .NET object. + /// + /// The JSON to deserialize. + /// The deserialized object from the Json string. + public static object DeserializeObject(string value) + { + return DeserializeObject(value, null, (JsonSerializerSettings)null); + } + + /// + /// Deserializes the JSON to a .NET object. + /// + /// The JSON to deserialize. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be is used. + /// + /// The deserialized object from the JSON string. + public static object DeserializeObject(string value, JsonSerializerSettings settings) + { + return DeserializeObject(value, null, settings); + } + + /// + /// Deserializes the JSON to the specified .NET type. + /// + /// The JSON to deserialize. + /// The of object being deserialized. + /// The deserialized object from the Json string. + public static object DeserializeObject(string value, Type type) + { + return DeserializeObject(value, type, (JsonSerializerSettings)null); + } + + /// + /// Deserializes the JSON to the specified .NET type. + /// + /// The type of the object to deserialize to. + /// The JSON to deserialize. + /// The deserialized object from the Json string. + public static T DeserializeObject(string value) + { + return DeserializeObject(value, (JsonSerializerSettings)null); + } + + /// + /// Deserializes the JSON to the given anonymous type. + /// + /// + /// The anonymous type to deserialize to. This can't be specified + /// traditionally and must be infered from the anonymous type passed + /// as a parameter. + /// + /// The JSON to deserialize. + /// The anonymous type object. + /// The deserialized anonymous type from the JSON string. + public static T DeserializeAnonymousType(string value, T anonymousTypeObject) + { + return DeserializeObject(value); + } + + /// + /// Deserializes the JSON to the specified .NET type. + /// + /// The type of the object to deserialize to. + /// The JSON to deserialize. + /// Converters to use while deserializing. + /// The deserialized object from the JSON string. + public static T DeserializeObject(string value, params JsonConverter[] converters) + { + return (T)DeserializeObject(value, typeof(T), converters); + } + + /// + /// Deserializes the JSON to the specified .NET type. + /// + /// The type of the object to deserialize to. + /// The object to deserialize. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be is used. + /// + /// The deserialized object from the JSON string. + public static T DeserializeObject(string value, JsonSerializerSettings settings) + { + return (T)DeserializeObject(value, typeof(T), settings); + } + + /// + /// Deserializes the JSON to the specified .NET type. + /// + /// The JSON to deserialize. + /// The type of the object to deserialize. + /// Converters to use while deserializing. + /// The deserialized object from the JSON string. + public static object DeserializeObject(string value, Type type, params JsonConverter[] converters) + { + JsonSerializerSettings settings = (converters != null && converters.Length > 0) + ? new JsonSerializerSettings { Converters = converters } + : null; + + return DeserializeObject(value, type, settings); + } + + /// + /// Deserializes the JSON to the specified .NET type. + /// + /// The JSON to deserialize. + /// The type of the object to deserialize to. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be is used. + /// + /// The deserialized object from the JSON string. + public static object DeserializeObject(string value, Type type, JsonSerializerSettings settings) + { + StringReader sr = new StringReader(value); + JsonSerializer jsonSerializer = JsonSerializer.Create(settings); + + object deserializedValue; + + using (JsonReader jsonReader = new JsonTextReader(sr)) + { + deserializedValue = jsonSerializer.Deserialize(jsonReader, type); + + if (jsonReader.Read() && jsonReader.TokenType != JsonToken.Comment) + throw new JsonSerializationException("Additional text found in JSON string after finishing deserializing object."); + } + + return deserializedValue; + } + + /// + /// Populates the object with values from the JSON string. + /// + /// The JSON to populate values from. + /// The target object to populate values onto. + public static void PopulateObject(string value, object target) + { + PopulateObject(value, target, null); + } + + + /// + /// Populates the object with values from the JSON string. + /// + /// The JSON to populate values from. + /// The target object to populate values onto. + /// + /// The used to deserialize the object. + /// If this is null, default serialization settings will be is used. + /// + public static void PopulateObject(string value, object target, JsonSerializerSettings settings) + { + StringReader sr = new StringReader(value); + JsonSerializer jsonSerializer = JsonSerializer.Create(settings); + + using (JsonReader jsonReader = new JsonTextReader(sr)) + { + jsonSerializer.Populate(jsonReader, target); + + if (jsonReader.Read() && jsonReader.TokenType != JsonToken.Comment) + throw new JsonSerializationException("Additional text found in JSON string after finishing deserializing object."); + } + } + +#if !SILVERLIGHT + /// + /// Serializes the XML node to a JSON string. + /// + /// The node to serialize. + /// A JSON string of the XmlNode. + public static string SerializeXmlNode(XmlNode node) + { + return SerializeXmlNode(node, Formatting.None); + } + + /// + /// Serializes the XML node to a JSON string. + /// + /// The node to serialize. + /// Indicates how the output is formatted. + /// A JSON string of the XmlNode. + public static string SerializeXmlNode(XmlNode node, Formatting formatting) + { + XmlNodeConverter converter = new XmlNodeConverter(); + + return SerializeObject(node, formatting, converter); + } + + /// + /// Serializes the XML node to a JSON string. + /// + /// The node to serialize. + /// Indicates how the output is formatted. + /// Omits writing the root object. + /// A JSON string of the XmlNode. + public static string SerializeXmlNode(XmlNode node, Formatting formatting, bool omitRootObject) + { + XmlNodeConverter converter = new XmlNodeConverter { OmitRootObject = omitRootObject }; + + return SerializeObject(node, formatting, converter); + } + + /// + /// Deserializes the XmlNode from a JSON string. + /// + /// The JSON string. + /// The deserialized XmlNode + public static XmlDocument DeserializeXmlNode(string value) + { + return DeserializeXmlNode(value, null); + } + + /// + /// Deserializes the XmlNode from a JSON string nested in a root elment. + /// + /// The JSON string. + /// The name of the root element to append when deserializing. + /// The deserialized XmlNode + public static XmlDocument DeserializeXmlNode(string value, string deserializeRootElementName) + { + return DeserializeXmlNode(value, deserializeRootElementName, false); + } + + /// + /// Deserializes the XmlNode from a JSON string nested in a root elment. + /// + /// The JSON string. + /// The name of the root element to append when deserializing. + /// + /// A flag to indicate whether to write the Json.NET array attribute. + /// This attribute helps preserve arrays when converting the written XML back to JSON. + /// + /// The deserialized XmlNode + public static XmlDocument DeserializeXmlNode(string value, string deserializeRootElementName, bool writeArrayAttribute) + { + XmlNodeConverter converter = new XmlNodeConverter(); + converter.DeserializeRootElementName = deserializeRootElementName; + converter.WriteArrayAttribute = writeArrayAttribute; + + return (XmlDocument)DeserializeObject(value, typeof(XmlDocument), converter); + } +#endif + +#if !NET20 && (!SILVERLIGHT || WINDOWS_PHONE) + /// + /// Serializes the to a JSON string. + /// + /// The node to convert to JSON. + /// A JSON string of the XNode. + public static string SerializeXNode(XObject node) + { + return SerializeXNode(node, Formatting.None); + } + + /// + /// Serializes the to a JSON string. + /// + /// The node to convert to JSON. + /// Indicates how the output is formatted. + /// A JSON string of the XNode. + public static string SerializeXNode(XObject node, Formatting formatting) + { + return SerializeXNode(node, formatting, false); + } + + /// + /// Serializes the to a JSON string. + /// + /// The node to serialize. + /// Indicates how the output is formatted. + /// Omits writing the root object. + /// A JSON string of the XNode. + public static string SerializeXNode(XObject node, Formatting formatting, bool omitRootObject) + { + XmlNodeConverter converter = new XmlNodeConverter { OmitRootObject = omitRootObject }; + + return SerializeObject(node, formatting, converter); + } + + /// + /// Deserializes the from a JSON string. + /// + /// The JSON string. + /// The deserialized XNode + public static XDocument DeserializeXNode(string value) + { + return DeserializeXNode(value, null); + } + + /// + /// Deserializes the from a JSON string nested in a root elment. + /// + /// The JSON string. + /// The name of the root element to append when deserializing. + /// The deserialized XNode + public static XDocument DeserializeXNode(string value, string deserializeRootElementName) + { + return DeserializeXNode(value, deserializeRootElementName, false); + } + + /// + /// Deserializes the from a JSON string nested in a root elment. + /// + /// The JSON string. + /// The name of the root element to append when deserializing. + /// + /// A flag to indicate whether to write the Json.NET array attribute. + /// This attribute helps preserve arrays when converting the written XML back to JSON. + /// + /// The deserialized XNode + public static XDocument DeserializeXNode(string value, string deserializeRootElementName, bool writeArrayAttribute) + { + XmlNodeConverter converter = new XmlNodeConverter(); + converter.DeserializeRootElementName = deserializeRootElementName; + converter.WriteArrayAttribute = writeArrayAttribute; + + return (XDocument)DeserializeObject(value, typeof(XDocument), converter); + } +#endif + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConverter.cs new file mode 100644 index 0000000..dd619f2 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConverter.cs @@ -0,0 +1,93 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Schema; + +namespace Newtonsoft.Json +{ + /// + /// Converts an object to and from JSON. + /// + public abstract class JsonConverter + { + /// + /// Writes the JSON representation of the object. + /// + /// The to write to. + /// The value. + /// The calling serializer. + public abstract void WriteJson(JsonWriter writer, object value, JsonSerializer serializer); + + /// + /// Reads the JSON representation of the object. + /// + /// The to read from. + /// Type of the object. + /// The existing value of object being read. + /// The calling serializer. + /// The object value. + public abstract object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer); + + /// + /// Determines whether this instance can convert the specified object type. + /// + /// Type of the object. + /// + /// true if this instance can convert the specified object type; otherwise, false. + /// + public abstract bool CanConvert(Type objectType); + + /// + /// Gets the of the JSON produced by the JsonConverter. + /// + /// The of the JSON produced by the JsonConverter. + public virtual JsonSchema GetSchema() + { + return null; + } + + /// + /// Gets a value indicating whether this can read JSON. + /// + /// true if this can read JSON; otherwise, false. + public virtual bool CanRead + { + get { return true; } + } + + /// + /// Gets a value indicating whether this can write JSON. + /// + /// true if this can write JSON; otherwise, false. + public virtual bool CanWrite + { + get { return true; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConverterAttribute.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConverterAttribute.cs new file mode 100644 index 0000000..2b46f93 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConverterAttribute.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the to use the specified when serializing the member or class. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface | AttributeTargets.Enum, AllowMultiple = false)] + public sealed class JsonConverterAttribute : Attribute + { + private readonly Type _converterType; + + /// + /// Gets the type of the converter. + /// + /// The type of the converter. + public Type ConverterType + { + get { return _converterType; } + } + + /// + /// Initializes a new instance of the class. + /// + /// Type of the converter. + public JsonConverterAttribute(Type converterType) + { + if (converterType == null) + throw new ArgumentNullException("converterType"); + + _converterType = converterType; + } + + internal static JsonConverter CreateJsonConverterInstance(Type converterType) + { + try + { + return (JsonConverter)Activator.CreateInstance(converterType); + } + catch (Exception ex) + { + throw new Exception("Error creating {0}".FormatWith(CultureInfo.InvariantCulture, converterType), ex); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConverterCollection.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConverterCollection.cs new file mode 100644 index 0000000..50f70c5 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonConverterCollection.cs @@ -0,0 +1,39 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections.ObjectModel; + +namespace Newtonsoft.Json +{ + /// + /// Represents a collection of . + /// + public class JsonConverterCollection : Collection + { + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonIgnoreAttribute.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonIgnoreAttribute.cs new file mode 100644 index 0000000..e7511a0 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonIgnoreAttribute.cs @@ -0,0 +1,39 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the not to serialize the public field or public read/write property value. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] + public sealed class JsonIgnoreAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonObjectAttribute.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonObjectAttribute.cs new file mode 100644 index 0000000..588eefc --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonObjectAttribute.cs @@ -0,0 +1,73 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the how to serialize the object. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = false)] + public sealed class JsonObjectAttribute : JsonContainerAttribute + { + private MemberSerialization _memberSerialization = MemberSerialization.OptOut; + + /// + /// Gets or sets the member serialization. + /// + /// The member serialization. + public MemberSerialization MemberSerialization + { + get { return _memberSerialization; } + set { _memberSerialization = value; } + } + + /// + /// Initializes a new instance of the class. + /// + public JsonObjectAttribute() + { + } + + /// + /// Initializes a new instance of the class with the specified member serialization. + /// + /// The member serialization. + public JsonObjectAttribute(MemberSerialization memberSerialization) + { + MemberSerialization = memberSerialization; + } + + /// + /// Initializes a new instance of the class with the specified container Id. + /// + /// The container Id. + public JsonObjectAttribute(string id) + : base(id) + { + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonPropertyAttribute.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonPropertyAttribute.cs new file mode 100644 index 0000000..a85e567 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonPropertyAttribute.cs @@ -0,0 +1,110 @@ +using System; + +namespace Newtonsoft.Json +{ + /// + /// Instructs the to always serialize the member with the specified name. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] + public sealed class JsonPropertyAttribute : Attribute + { + // yuck. can't set nullable properties on an attribute in C# + // have to use this approach to get an unset default state + internal NullValueHandling? _nullValueHandling; + internal DefaultValueHandling? _defaultValueHandling; + internal ReferenceLoopHandling? _referenceLoopHandling; + internal ObjectCreationHandling? _objectCreationHandling; + internal TypeNameHandling? _typeNameHandling; + internal bool? _isReference; + + /// + /// Gets or sets the null value handling used when serializing this property. + /// + /// The null value handling. + public NullValueHandling NullValueHandling + { + get { return _nullValueHandling ?? default(NullValueHandling); } + set { _nullValueHandling = value; } + } + + /// + /// Gets or sets the default value handling used when serializing this property. + /// + /// The default value handling. + public DefaultValueHandling DefaultValueHandling + { + get { return _defaultValueHandling ?? default(DefaultValueHandling); } + set { _defaultValueHandling = value; } + } + + /// + /// Gets or sets the reference loop handling used when serializing this property. + /// + /// The reference loop handling. + public ReferenceLoopHandling ReferenceLoopHandling + { + get { return _referenceLoopHandling ?? default(ReferenceLoopHandling); } + set { _referenceLoopHandling = value; } + } + + /// + /// Gets or sets the object creation handling used when deserializing this property. + /// + /// The object creation handling. + public ObjectCreationHandling ObjectCreationHandling + { + get { return _objectCreationHandling ?? default(ObjectCreationHandling); } + set { _objectCreationHandling = value; } + } + + /// + /// Gets or sets the type name handling used when serializing this property. + /// + /// The type name handling. + public TypeNameHandling TypeNameHandling + { + get { return _typeNameHandling ?? default(TypeNameHandling); } + set { _typeNameHandling = value; } + } + + /// + /// Gets or sets whether this property's value is serialized as a reference. + /// + /// Whether this property's value is serialized as a reference. + public bool IsReference + { + get { return _isReference ?? default(bool); } + set { _isReference = value; } + } + + /// + /// Gets or sets the name of the property. + /// + /// The name of the property. + public string PropertyName { get; set; } + + /// + /// Gets or sets a value indicating whether this property is required. + /// + /// + /// A value indicating whether this property is required. + /// + public Required Required { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public JsonPropertyAttribute() + { + } + + /// + /// Initializes a new instance of the class with the specified name. + /// + /// Name of the property. + public JsonPropertyAttribute(string propertyName) + { + PropertyName = propertyName; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonReader.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonReader.cs new file mode 100644 index 0000000..f46197b --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonReader.cs @@ -0,0 +1,457 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Globalization; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json +{ + /// + /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + /// + public abstract class JsonReader : IDisposable + { + /// + /// Specifies the state of the reader. + /// + protected enum State + { + /// + /// The Read method has not been called. + /// + Start, + /// + /// The end of the file has been reached successfully. + /// + Complete, + /// + /// Reader is at a property. + /// + Property, + /// + /// Reader is at the start of an object. + /// + ObjectStart, + /// + /// Reader is in an object. + /// + Object, + /// + /// Reader is at the start of an array. + /// + ArrayStart, + /// + /// Reader is in an array. + /// + Array, + /// + /// The Close method has been called. + /// + Closed, + /// + /// Reader has just read a value. + /// + PostValue, + /// + /// Reader is at the start of a constructor. + /// + ConstructorStart, + /// + /// Reader in a constructor. + /// + Constructor, + /// + /// An error occurred that prevents the read operation from continuing. + /// + Error, + /// + /// The end of the file has been reached successfully. + /// + Finished + } + + // current Token data + private JsonToken _token; + private object _value; + private Type _valueType; + private char _quoteChar; + private State _currentState; + private JTokenType _currentTypeContext; + + /// + /// Gets the current reader state. + /// + /// The current reader state. + protected State CurrentState + { + get { return _currentState; } + } + + private int _top; + + private readonly List _stack; + + /// + /// Gets or sets a value indicating whether the underlying stream or + /// should be closed when the reader is closed. + /// + /// + /// true to close the underlying stream or when + /// the reader is closed; otherwise false. The default is true. + /// + public bool CloseInput { get; set; } + + /// + /// Gets the quotation mark character used to enclose the value of a string. + /// + public virtual char QuoteChar + { + get { return _quoteChar; } + protected internal set { _quoteChar = value; } + } + + /// + /// Gets the type of the current Json token. + /// + public virtual JsonToken TokenType + { + get { return _token; } + } + + /// + /// Gets the text value of the current Json token. + /// + public virtual object Value + { + get { return _value; } + } + + /// + /// Gets The Common Language Runtime (CLR) type for the current Json token. + /// + public virtual Type ValueType + { + get { return _valueType; } + } + + /// + /// Gets the depth of the current token in the JSON document. + /// + /// The depth of the current token in the JSON document. + public virtual int Depth + { + get + { + int depth = _top - 1; + if (IsStartToken(TokenType)) + return depth - 1; + else + return depth; + } + } + + /// + /// Initializes a new instance of the class with the specified . + /// + protected JsonReader() + { + _currentState = State.Start; + _stack = new List(); + + CloseInput = true; + + Push(JTokenType.None); + } + + private void Push(JTokenType value) + { + _stack.Add(value); + _top++; + _currentTypeContext = value; + } + + private JTokenType Pop() + { + JTokenType value = Peek(); + _stack.RemoveAt(_stack.Count - 1); + _top--; + _currentTypeContext = _stack[_top - 1]; + + return value; + } + + private JTokenType Peek() + { + return _currentTypeContext; + } + + /// + /// Reads the next JSON token from the stream. + /// + /// true if the next token was read successfully; false if there are no more tokens to read. + public abstract bool Read(); + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A or a null reference if the next JSON token is null. + public abstract byte[] ReadAsBytes(); + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public abstract decimal? ReadAsDecimal(); + +#if !NET20 + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public abstract DateTimeOffset? ReadAsDateTimeOffset(); +#endif + + /// + /// Skips the children of the current token. + /// + public void Skip() + { + if (IsStartToken(TokenType)) + { + int depth = Depth; + + while (Read() && (depth < Depth)) + { + } + } + } + + /// + /// Sets the current token. + /// + /// The new token. + protected void SetToken(JsonToken newToken) + { + SetToken(newToken, null); + } + + /// + /// Sets the current token and value. + /// + /// The new token. + /// The value. + protected virtual void SetToken(JsonToken newToken, object value) + { + _token = newToken; + + switch (newToken) + { + case JsonToken.StartObject: + _currentState = State.ObjectStart; + Push(JTokenType.Object); + break; + case JsonToken.StartArray: + _currentState = State.ArrayStart; + Push(JTokenType.Array); + break; + case JsonToken.StartConstructor: + _currentState = State.ConstructorStart; + Push(JTokenType.Constructor); + break; + case JsonToken.EndObject: + ValidateEnd(JsonToken.EndObject); + _currentState = State.PostValue; + break; + case JsonToken.EndArray: + ValidateEnd(JsonToken.EndArray); + _currentState = State.PostValue; + break; + case JsonToken.EndConstructor: + ValidateEnd(JsonToken.EndConstructor); + _currentState = State.PostValue; + break; + case JsonToken.PropertyName: + _currentState = State.Property; + Push(JTokenType.Property); + break; + case JsonToken.Undefined: + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.Boolean: + case JsonToken.Null: + case JsonToken.Date: + case JsonToken.String: + case JsonToken.Raw: + case JsonToken.Bytes: + _currentState = State.PostValue; + break; + } + + JTokenType current = Peek(); + if (current == JTokenType.Property && _currentState == State.PostValue) + Pop(); + + if (value != null) + { + _value = value; + _valueType = value.GetType(); + } + else + { + _value = null; + _valueType = null; + } + } + + private void ValidateEnd(JsonToken endToken) + { + JTokenType currentObject = Pop(); + + if (GetTypeForCloseToken(endToken) != currentObject) + throw new JsonReaderException("JsonToken {0} is not valid for closing JsonType {1}.".FormatWith(CultureInfo.InvariantCulture, endToken, currentObject)); + } + + /// + /// Sets the state based on current token type. + /// + protected void SetStateBasedOnCurrent() + { + JTokenType currentObject = Peek(); + + switch (currentObject) + { + case JTokenType.Object: + _currentState = State.Object; + break; + case JTokenType.Array: + _currentState = State.Array; + break; + case JTokenType.Constructor: + _currentState = State.Constructor; + break; + case JTokenType.None: + _currentState = State.Finished; + break; + default: + throw new JsonReaderException("While setting the reader state back to current object an unexpected JsonType was encountered: {0}".FormatWith(CultureInfo.InvariantCulture, currentObject)); + } + } + + internal static bool IsPrimitiveToken(JsonToken token) + { + switch (token) + { + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.String: + case JsonToken.Boolean: + case JsonToken.Undefined: + case JsonToken.Null: + case JsonToken.Date: + case JsonToken.Bytes: + return true; + default: + return false; + } + } + + internal static bool IsStartToken(JsonToken token) + { + switch (token) + { + case JsonToken.StartObject: + case JsonToken.StartArray: + case JsonToken.StartConstructor: + case JsonToken.PropertyName: + return true; + case JsonToken.None: + case JsonToken.Comment: + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.String: + case JsonToken.Boolean: + case JsonToken.Null: + case JsonToken.Undefined: + case JsonToken.EndObject: + case JsonToken.EndArray: + case JsonToken.EndConstructor: + case JsonToken.Date: + case JsonToken.Raw: + case JsonToken.Bytes: + return false; + default: + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("token", token, "Unexpected JsonToken value."); + } + } + + private JTokenType GetTypeForCloseToken(JsonToken token) + { + switch (token) + { + case JsonToken.EndObject: + return JTokenType.Object; + case JsonToken.EndArray: + return JTokenType.Array; + case JsonToken.EndConstructor: + return JTokenType.Constructor; + default: + throw new JsonReaderException("Not a valid close JsonToken: {0}".FormatWith(CultureInfo.InvariantCulture, token)); + } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + void IDisposable.Dispose() + { + Dispose(true); + } + + /// + /// Releases unmanaged and - optionally - managed resources + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (_currentState != State.Closed && disposing) + Close(); + } + + /// + /// Changes the to Closed. + /// + public virtual void Close() + { + _currentState = State.Closed; + _token = JsonToken.None; + _value = null; + _valueType = null; + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonReaderException.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonReaderException.cs new file mode 100644 index 0000000..a466768 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonReaderException.cs @@ -0,0 +1,83 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json +{ + /// + /// The exception thrown when an error occurs while reading Json text. + /// + public class JsonReaderException : Exception + { + /// + /// Gets the line number indicating where the error occurred. + /// + /// The line number indicating where the error occurred. + public int LineNumber { get; private set; } + + + /// + /// Gets the line position indicating where the error occurred. + /// + /// The line position indicating where the error occurred. + public int LinePosition { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public JsonReaderException() + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message. + /// + /// The error message that explains the reason for the exception. + public JsonReaderException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public JsonReaderException(string message, Exception innerException) + : base(message, innerException) + { + } + + internal JsonReaderException(string message, Exception innerException, int lineNumber, int linePosition) + : base(message, innerException) + { + LineNumber = lineNumber; + LinePosition = linePosition; + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonSerializationException.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonSerializationException.cs new file mode 100644 index 0000000..758caa4 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonSerializationException.cs @@ -0,0 +1,65 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// The exception thrown when an error occurs during Json serialization or deserialization. + /// + public class JsonSerializationException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public JsonSerializationException() + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message. + /// + /// The error message that explains the reason for the exception. + public JsonSerializationException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public JsonSerializationException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonSerializer.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonSerializer.cs new file mode 100644 index 0000000..4bb5389 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonSerializer.cs @@ -0,0 +1,484 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization.Formatters; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Utilities; +using System.Runtime.Serialization; +using ErrorEventArgs=Newtonsoft.Json.Serialization.ErrorEventArgs; + +namespace Newtonsoft.Json +{ + /// + /// Serializes and deserializes objects into and from the JSON format. + /// The enables you to control how objects are encoded into JSON. + /// + public class JsonSerializer + { + #region Properties + private TypeNameHandling _typeNameHandling; + private FormatterAssemblyStyle _typeNameAssemblyFormat; + private PreserveReferencesHandling _preserveReferencesHandling; + private ReferenceLoopHandling _referenceLoopHandling; + private MissingMemberHandling _missingMemberHandling; + private ObjectCreationHandling _objectCreationHandling; + private NullValueHandling _nullValueHandling; + private DefaultValueHandling _defaultValueHandling; + private ConstructorHandling _constructorHandling; + private JsonConverterCollection _converters; + private IContractResolver _contractResolver; + private IReferenceResolver _referenceResolver; + private SerializationBinder _binder; + private StreamingContext _context; + + /// + /// Occurs when the errors during serialization and deserialization. + /// + public virtual event EventHandler Error; + + /// + /// Gets or sets the used by the serializer when resolving references. + /// + public virtual IReferenceResolver ReferenceResolver + { + get + { + if (_referenceResolver == null) + _referenceResolver = new DefaultReferenceResolver(); + + return _referenceResolver; + } + set + { + if (value == null) + throw new ArgumentNullException("value", "Reference resolver cannot be null."); + + _referenceResolver = value; + } + } + + /// + /// Gets or sets the used by the serializer when resolving type names. + /// + public virtual SerializationBinder Binder + { + get + { + return _binder; + } + set + { + if (value == null) + throw new ArgumentNullException("value", "Serialization binder cannot be null."); + + _binder = value; + } + } + + /// + /// Gets or sets how type name writing and reading is handled by the serializer. + /// + public virtual TypeNameHandling TypeNameHandling + { + get { return _typeNameHandling; } + set + { + if (value < TypeNameHandling.None || value > TypeNameHandling.Auto) + throw new ArgumentOutOfRangeException("value"); + + _typeNameHandling = value; + } + } + + /// + /// Gets or sets how a type name assembly is written and resolved by the serializer. + /// + /// The type name assembly format. + public virtual FormatterAssemblyStyle TypeNameAssemblyFormat + { + get { return _typeNameAssemblyFormat; } + set + { + if (value < FormatterAssemblyStyle.Simple || value > FormatterAssemblyStyle.Full) + throw new ArgumentOutOfRangeException("value"); + + _typeNameAssemblyFormat = value; + } + } + + /// + /// Gets or sets how object references are preserved by the serializer. + /// + public virtual PreserveReferencesHandling PreserveReferencesHandling + { + get { return _preserveReferencesHandling; } + set + { + if (value < PreserveReferencesHandling.None || value > PreserveReferencesHandling.All) + throw new ArgumentOutOfRangeException("value"); + + _preserveReferencesHandling = value; + } + } + + /// + /// Get or set how reference loops (e.g. a class referencing itself) is handled. + /// + public virtual ReferenceLoopHandling ReferenceLoopHandling + { + get { return _referenceLoopHandling; } + set + { + if (value < ReferenceLoopHandling.Error || value > ReferenceLoopHandling.Serialize) + throw new ArgumentOutOfRangeException("value"); + + _referenceLoopHandling = value; + } + } + + /// + /// Get or set how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization. + /// + public virtual MissingMemberHandling MissingMemberHandling + { + get { return _missingMemberHandling; } + set + { + if (value < MissingMemberHandling.Ignore || value > MissingMemberHandling.Error) + throw new ArgumentOutOfRangeException("value"); + + _missingMemberHandling = value; + } + } + + /// + /// Get or set how null values are handled during serialization and deserialization. + /// + public virtual NullValueHandling NullValueHandling + { + get { return _nullValueHandling; } + set + { + if (value < NullValueHandling.Include || value > NullValueHandling.Ignore) + throw new ArgumentOutOfRangeException("value"); + + _nullValueHandling = value; + } + } + + /// + /// Get or set how null default are handled during serialization and deserialization. + /// + public virtual DefaultValueHandling DefaultValueHandling + { + get { return _defaultValueHandling; } + set + { + if (value < DefaultValueHandling.Include || value > DefaultValueHandling.Ignore) + throw new ArgumentOutOfRangeException("value"); + + _defaultValueHandling = value; + } + } + + /// + /// Gets or sets how objects are created during deserialization. + /// + /// The object creation handling. + public virtual ObjectCreationHandling ObjectCreationHandling + { + get { return _objectCreationHandling; } + set + { + if (value < ObjectCreationHandling.Auto || value > ObjectCreationHandling.Replace) + throw new ArgumentOutOfRangeException("value"); + + _objectCreationHandling = value; + } + } + + /// + /// Gets or sets how constructors are used during deserialization. + /// + /// The constructor handling. + public virtual ConstructorHandling ConstructorHandling + { + get { return _constructorHandling; } + set + { + if (value < ConstructorHandling.Default || value > ConstructorHandling.AllowNonPublicDefaultConstructor) + throw new ArgumentOutOfRangeException("value"); + + _constructorHandling = value; + } + } + + /// + /// Gets a collection that will be used during serialization. + /// + /// Collection that will be used during serialization. + public virtual JsonConverterCollection Converters + { + get + { + if (_converters == null) + _converters = new JsonConverterCollection(); + + return _converters; + } + } + + /// + /// Gets or sets the contract resolver used by the serializer when + /// serializing .NET objects to JSON and vice versa. + /// + public virtual IContractResolver ContractResolver + { + get + { + if (_contractResolver == null) + _contractResolver = DefaultContractResolver.Instance; + + return _contractResolver; + } + set { _contractResolver = value; } + } + + /// + /// Gets or sets the used by the serializer when invoking serialization callback methods. + /// + /// The context. + public virtual StreamingContext Context + { + get { return _context; } + set { _context = value; } + } + #endregion + + /// + /// Initializes a new instance of the class. + /// + public JsonSerializer() + { + _referenceLoopHandling = JsonSerializerSettings.DefaultReferenceLoopHandling; + _missingMemberHandling = JsonSerializerSettings.DefaultMissingMemberHandling; + _nullValueHandling = JsonSerializerSettings.DefaultNullValueHandling; + _defaultValueHandling = JsonSerializerSettings.DefaultDefaultValueHandling; + _objectCreationHandling = JsonSerializerSettings.DefaultObjectCreationHandling; + _preserveReferencesHandling = JsonSerializerSettings.DefaultPreserveReferencesHandling; + _constructorHandling = JsonSerializerSettings.DefaultConstructorHandling; + _typeNameHandling = JsonSerializerSettings.DefaultTypeNameHandling; + _context = JsonSerializerSettings.DefaultContext; + + _binder = DefaultSerializationBinder.Instance; + } + + /// + /// Creates a new instance using the specified . + /// + /// The settings to be applied to the . + /// A new instance using the specified . + public static JsonSerializer Create(JsonSerializerSettings settings) + { + JsonSerializer jsonSerializer = new JsonSerializer(); + + if (settings != null) + { + if (!CollectionUtils.IsNullOrEmpty(settings.Converters)) + jsonSerializer.Converters.AddRange(settings.Converters); + + jsonSerializer.TypeNameHandling = settings.TypeNameHandling; + jsonSerializer.TypeNameAssemblyFormat = settings.TypeNameAssemblyFormat; + jsonSerializer.PreserveReferencesHandling = settings.PreserveReferencesHandling; + jsonSerializer.ReferenceLoopHandling = settings.ReferenceLoopHandling; + jsonSerializer.MissingMemberHandling = settings.MissingMemberHandling; + jsonSerializer.ObjectCreationHandling = settings.ObjectCreationHandling; + jsonSerializer.NullValueHandling = settings.NullValueHandling; + jsonSerializer.DefaultValueHandling = settings.DefaultValueHandling; + jsonSerializer.ConstructorHandling = settings.ConstructorHandling; + jsonSerializer.Context = settings.Context; + + if (settings.Error != null) + jsonSerializer.Error += settings.Error; + + if (settings.ContractResolver != null) + jsonSerializer.ContractResolver = settings.ContractResolver; + if (settings.ReferenceResolver != null) + jsonSerializer.ReferenceResolver = settings.ReferenceResolver; + if (settings.Binder != null) + jsonSerializer.Binder = settings.Binder; + } + + return jsonSerializer; + } + + /// + /// Populates the JSON values onto the target object. + /// + /// The that contains the JSON structure to reader values from. + /// The target object to populate values onto. + public void Populate(TextReader reader, object target) + { + Populate(new JsonTextReader(reader), target); + } + + /// + /// Populates the JSON values onto the target object. + /// + /// The that contains the JSON structure to reader values from. + /// The target object to populate values onto. + public void Populate(JsonReader reader, object target) + { + PopulateInternal(reader, target); + } + + internal virtual void PopulateInternal(JsonReader reader, object target) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + ValidationUtils.ArgumentNotNull(target, "target"); + + JsonSerializerInternalReader serializerReader = new JsonSerializerInternalReader(this); + serializerReader.Populate(reader, target); + } + + /// + /// Deserializes the Json structure contained by the specified . + /// + /// The that contains the JSON structure to deserialize. + /// The being deserialized. + public object Deserialize(JsonReader reader) + { + return Deserialize(reader, null); + } + + /// + /// Deserializes the Json structure contained by the specified + /// into an instance of the specified type. + /// + /// The containing the object. + /// The of object being deserialized. + /// The instance of being deserialized. + public object Deserialize(TextReader reader, Type objectType) + { + return Deserialize(new JsonTextReader(reader), objectType); + } + + /// + /// Deserializes the Json structure contained by the specified + /// into an instance of the specified type. + /// + /// The containing the object. + /// The type of the object to deserialize. + /// The instance of being deserialized. + public T Deserialize(JsonReader reader) + { + return (T)Deserialize(reader, typeof(T)); + } + + /// + /// Deserializes the Json structure contained by the specified + /// into an instance of the specified type. + /// + /// The containing the object. + /// The of object being deserialized. + /// The instance of being deserialized. + public object Deserialize(JsonReader reader, Type objectType) + { + return DeserializeInternal(reader, objectType); + } + + internal virtual object DeserializeInternal(JsonReader reader, Type objectType) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + + JsonSerializerInternalReader serializerReader = new JsonSerializerInternalReader(this); + return serializerReader.Deserialize(reader, objectType); + } + + /// + /// Serializes the specified and writes the Json structure + /// to a Stream using the specified . + /// + /// The used to write the Json structure. + /// The to serialize. + public void Serialize(TextWriter textWriter, object value) + { + Serialize(new JsonTextWriter(textWriter), value); + } + + /// + /// Serializes the specified and writes the Json structure + /// to a Stream using the specified . + /// + /// The used to write the Json structure. + /// The to serialize. + public void Serialize(JsonWriter jsonWriter, object value) + { + SerializeInternal(jsonWriter, value); + } + + internal virtual void SerializeInternal(JsonWriter jsonWriter, object value) + { + ValidationUtils.ArgumentNotNull(jsonWriter, "jsonWriter"); + + JsonSerializerInternalWriter serializerWriter = new JsonSerializerInternalWriter(this); + serializerWriter.Serialize(jsonWriter, value); + } + + internal JsonConverter GetMatchingConverter(Type type) + { + return GetMatchingConverter(_converters, type); + } + + internal static JsonConverter GetMatchingConverter(IList converters, Type objectType) + { + ValidationUtils.ArgumentNotNull(objectType, "objectType"); + + if (converters != null) + { + for (int i = 0; i < converters.Count; i++) + { + JsonConverter converter = converters[i]; + + if (converter.CanConvert(objectType)) + return converter; + } + } + + return null; + } + + internal void OnError(ErrorEventArgs e) + { + EventHandler error = Error; + if (error != null) + error(this, e); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonSerializerSettings.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonSerializerSettings.cs new file mode 100644 index 0000000..278493d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonSerializerSettings.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Serialization.Formatters; +using System.Text; +using Newtonsoft.Json.Serialization; +using Newtonsoft.Json.Utilities; +using System.Runtime.Serialization; + +namespace Newtonsoft.Json +{ + /// + /// Specifies the settings on a object. + /// + public class JsonSerializerSettings + { + internal const ReferenceLoopHandling DefaultReferenceLoopHandling = ReferenceLoopHandling.Error; + internal const MissingMemberHandling DefaultMissingMemberHandling = MissingMemberHandling.Ignore; + internal const NullValueHandling DefaultNullValueHandling = NullValueHandling.Include; + internal const DefaultValueHandling DefaultDefaultValueHandling = DefaultValueHandling.Include; + internal const ObjectCreationHandling DefaultObjectCreationHandling = ObjectCreationHandling.Auto; + internal const PreserveReferencesHandling DefaultPreserveReferencesHandling = PreserveReferencesHandling.None; + internal const ConstructorHandling DefaultConstructorHandling = ConstructorHandling.Default; + internal const TypeNameHandling DefaultTypeNameHandling = TypeNameHandling.None; + internal const FormatterAssemblyStyle DefaultTypeNameAssemblyFormat = FormatterAssemblyStyle.Simple; + internal static readonly StreamingContext DefaultContext = new StreamingContext(); + + /// + /// Gets or sets how reference loops (e.g. a class referencing itself) is handled. + /// + /// Reference loop handling. + public ReferenceLoopHandling ReferenceLoopHandling { get; set; } + + /// + /// Gets or sets how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization. + /// + /// Missing member handling. + public MissingMemberHandling MissingMemberHandling { get; set; } + + /// + /// Gets or sets how objects are created during deserialization. + /// + /// The object creation handling. + public ObjectCreationHandling ObjectCreationHandling { get; set; } + + /// + /// Gets or sets how null values are handled during serialization and deserialization. + /// + /// Null value handling. + public NullValueHandling NullValueHandling { get; set; } + + /// + /// Gets or sets how null default are handled during serialization and deserialization. + /// + /// The default value handling. + public DefaultValueHandling DefaultValueHandling { get; set; } + + /// + /// Gets or sets a collection that will be used during serialization. + /// + /// The converters. + public IList Converters { get; set; } + + /// + /// Gets or sets how object references are preserved by the serializer. + /// + /// The preserve references handling. + public PreserveReferencesHandling PreserveReferencesHandling { get; set; } + + /// + /// Gets or sets how type name writing and reading is handled by the serializer. + /// + /// The type name handling. + public TypeNameHandling TypeNameHandling { get; set; } + + /// + /// Gets or sets how a type name assembly is written and resolved by the serializer. + /// + /// The type name assembly format. + public FormatterAssemblyStyle TypeNameAssemblyFormat { get; set; } + + /// + /// Gets or sets how constructors are used during deserialization. + /// + /// The constructor handling. + public ConstructorHandling ConstructorHandling { get; set; } + + /// + /// Gets or sets the contract resolver used by the serializer when + /// serializing .NET objects to JSON and vice versa. + /// + /// The contract resolver. + public IContractResolver ContractResolver { get; set; } + + /// + /// Gets or sets the used by the serializer when resolving references. + /// + /// The reference resolver. + public IReferenceResolver ReferenceResolver { get; set; } + + /// + /// Gets or sets the used by the serializer when resolving type names. + /// + /// The binder. + public SerializationBinder Binder { get; set; } + + /// + /// Gets or sets the error handler called during serialization and deserialization. + /// + /// The error handler called during serialization and deserialization. + public EventHandler Error { get; set; } + + /// + /// Gets or sets the used by the serializer when invoking serialization callback methods. + /// + /// The context. + public StreamingContext Context { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public JsonSerializerSettings() + { + ReferenceLoopHandling = DefaultReferenceLoopHandling; + MissingMemberHandling = DefaultMissingMemberHandling; + ObjectCreationHandling = DefaultObjectCreationHandling; + NullValueHandling = DefaultNullValueHandling; + DefaultValueHandling = DefaultDefaultValueHandling; + PreserveReferencesHandling = DefaultPreserveReferencesHandling; + TypeNameHandling = DefaultTypeNameHandling; + TypeNameAssemblyFormat = DefaultTypeNameAssemblyFormat; + Context = DefaultContext; + Converters = new List(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonTextReader.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonTextReader.cs new file mode 100644 index 0000000..b71d0c8 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonTextReader.cs @@ -0,0 +1,1052 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Xml; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json +{ + /// + /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + /// + public class JsonTextReader : JsonReader, IJsonLineInfo + { + private enum ReadType + { + Read, + ReadAsBytes, + ReadAsDecimal, +#if !NET20 + ReadAsDateTimeOffset +#endif + } + + private readonly TextReader _reader; + private readonly StringBuffer _buffer; + private char? _lastChar; + private int _currentLinePosition; + private int _currentLineNumber; + private bool _end; + private ReadType _readType; + + /// + /// Initializes a new instance of the class with the specified . + /// + /// The TextReader containing the XML data to read. + public JsonTextReader(TextReader reader) + { + if (reader == null) + throw new ArgumentNullException("reader"); + + _reader = reader; + _buffer = new StringBuffer(4096); + _currentLineNumber = 1; + } + + private void ParseString(char quote) + { + ReadStringIntoBuffer(quote); + + if (_readType == ReadType.ReadAsBytes) + { + byte[] data; + if (_buffer.Position == 0) + { + data = new byte[0]; + } + else + { + data = Convert.FromBase64CharArray(_buffer.GetInternalBuffer(), 0, _buffer.Position); + _buffer.Position = 0; + } + + SetToken(JsonToken.Bytes, data); + } + else + { + string text = _buffer.ToString(); + _buffer.Position = 0; + + if (text.StartsWith("/Date(", StringComparison.Ordinal) && text.EndsWith(")/", StringComparison.Ordinal)) + { + ParseDate(text); + } + else + { + SetToken(JsonToken.String, text); + QuoteChar = quote; + } + } + } + + private void ReadStringIntoBuffer(char quote) + { + while (true) + { + char currentChar = MoveNext(); + + switch (currentChar) + { + case '\0': + if (_end) + throw CreateJsonReaderException("Unterminated string. Expected delimiter: {0}. Line {1}, position {2}.", quote, _currentLineNumber, _currentLinePosition); + + _buffer.Append('\0'); + break; + case '\\': + if ((currentChar = MoveNext()) != '\0' || !_end) + { + switch (currentChar) + { + case 'b': + _buffer.Append('\b'); + break; + case 't': + _buffer.Append('\t'); + break; + case 'n': + _buffer.Append('\n'); + break; + case 'f': + _buffer.Append('\f'); + break; + case 'r': + _buffer.Append('\r'); + break; + case '\\': + _buffer.Append('\\'); + break; + case '"': + case '\'': + case '/': + _buffer.Append(currentChar); + break; + case 'u': + char[] hexValues = new char[4]; + for (int i = 0; i < hexValues.Length; i++) + { + if ((currentChar = MoveNext()) != '\0' || !_end) + hexValues[i] = currentChar; + else + throw CreateJsonReaderException("Unexpected end while parsing unicode character. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } + + char hexChar = Convert.ToChar(int.Parse(new string(hexValues), NumberStyles.HexNumber, NumberFormatInfo.InvariantInfo)); + _buffer.Append(hexChar); + break; + default: + throw CreateJsonReaderException("Bad JSON escape sequence: {0}. Line {1}, position {2}.", @"\" + currentChar, _currentLineNumber, _currentLinePosition); + } + } + else + { + throw CreateJsonReaderException("Unterminated string. Expected delimiter: {0}. Line {1}, position {2}.", quote, _currentLineNumber, _currentLinePosition); + } + break; + case '"': + case '\'': + if (currentChar == quote) + { + return; + } + else + { + _buffer.Append(currentChar); + } + break; + default: + _buffer.Append(currentChar); + break; + } + } + } + + private JsonReaderException CreateJsonReaderException(string format, params object[] args) + { + string message = format.FormatWith(CultureInfo.InvariantCulture, args); + return new JsonReaderException(message, null, _currentLineNumber, _currentLinePosition); + } + + private TimeSpan ReadOffset(string offsetText) + { + bool negative = (offsetText[0] == '-'); + + int hours = int.Parse(offsetText.Substring(1, 2), NumberStyles.Integer, CultureInfo.InvariantCulture); + int minutes = 0; + if (offsetText.Length >= 5) + minutes = int.Parse(offsetText.Substring(3, 2), NumberStyles.Integer, CultureInfo.InvariantCulture); + + TimeSpan offset = TimeSpan.FromHours(hours) + TimeSpan.FromMinutes(minutes); + if (negative) + offset = offset.Negate(); + + return offset; + } + + private void ParseDate(string text) + { + string value = text.Substring(6, text.Length - 8); + DateTimeKind kind = DateTimeKind.Utc; + + int index = value.IndexOf('+', 1); + + if (index == -1) + index = value.IndexOf('-', 1); + + TimeSpan offset = TimeSpan.Zero; + + if (index != -1) + { + kind = DateTimeKind.Local; + offset = ReadOffset(value.Substring(index)); + value = value.Substring(0, index); + } + + long javaScriptTicks = long.Parse(value, NumberStyles.Integer, CultureInfo.InvariantCulture); + + DateTime utcDateTime = JsonConvert.ConvertJavaScriptTicksToDateTime(javaScriptTicks); + +#if !NET20 + if (_readType == ReadType.ReadAsDateTimeOffset) + { + SetToken(JsonToken.Date, new DateTimeOffset(utcDateTime.Add(offset).Ticks, offset)); + } + else +#endif + { + DateTime dateTime; + + switch (kind) + { + case DateTimeKind.Unspecified: + dateTime = DateTime.SpecifyKind(utcDateTime.ToLocalTime(), DateTimeKind.Unspecified); + break; + case DateTimeKind.Local: + dateTime = utcDateTime.ToLocalTime(); + break; + default: + dateTime = utcDateTime; + break; + } + + SetToken(JsonToken.Date, dateTime); + } + } + + private const int LineFeedValue = StringUtils.LineFeed; + private const int CarriageReturnValue = StringUtils.CarriageReturn; + + private char MoveNext() + { + int value = _reader.Read(); + + switch (value) + { + case -1: + _end = true; + return '\0'; + case CarriageReturnValue: + if (_reader.Peek() == LineFeedValue) + _reader.Read(); + + _currentLineNumber++; + _currentLinePosition = 0; + break; + case LineFeedValue: + _currentLineNumber++; + _currentLinePosition = 0; + break; + default: + _currentLinePosition++; + break; + } + + return (char)value; + } + + private bool HasNext() + { + return (_reader.Peek() != -1); + } + + private int PeekNext() + { + return _reader.Peek(); + } + + /// + /// Reads the next JSON token from the stream. + /// + /// + /// true if the next token was read successfully; false if there are no more tokens to read. + /// + public override bool Read() + { + _readType = ReadType.Read; + return ReadInternal(); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// + /// A or a null reference if the next JSON token is null. + /// + public override byte[] ReadAsBytes() + { + _readType = ReadType.ReadAsBytes; + + do + { + if (!ReadInternal()) + throw CreateJsonReaderException("Unexpected end when reading bytes: Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } while (TokenType == JsonToken.Comment); + + if (TokenType == JsonToken.Null) + return null; + if (TokenType == JsonToken.Bytes) + return (byte[]) Value; + + throw CreateJsonReaderException("Unexpected token when reading bytes: {0}. Line {1}, position {2}.", TokenType, _currentLineNumber, _currentLinePosition); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override decimal? ReadAsDecimal() + { + _readType = ReadType.ReadAsDecimal; + + do + { + if (!ReadInternal()) + throw CreateJsonReaderException("Unexpected end when reading decimal: Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } while (TokenType == JsonToken.Comment); + + if (TokenType == JsonToken.Null) + return null; + if (TokenType == JsonToken.Float) + return (decimal?)Value; + + decimal d; + if (TokenType == JsonToken.String && decimal.TryParse((string)Value, NumberStyles.Number, CultureInfo.InvariantCulture, out d)) + { + SetToken(JsonToken.Float, d); + return d; + } + + throw CreateJsonReaderException("Unexpected token when reading decimal: {0}. Line {1}, position {2}.", TokenType, _currentLineNumber, _currentLinePosition); + } + +#if !NET20 + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override DateTimeOffset? ReadAsDateTimeOffset() + { + _readType = ReadType.ReadAsDateTimeOffset; + + do + { + if (!ReadInternal()) + throw CreateJsonReaderException("Unexpected end when reading date: Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } while (TokenType == JsonToken.Comment); + + if (TokenType == JsonToken.Null) + return null; + if (TokenType == JsonToken.Date) + return (DateTimeOffset)Value; + + throw CreateJsonReaderException("Unexpected token when reading date: {0}. Line {1}, position {2}.", TokenType, _currentLineNumber, _currentLinePosition); + } +#endif + + private bool ReadInternal() + { + while (true) + { + char currentChar; + if (_lastChar != null) + { + currentChar = _lastChar.Value; + _lastChar = null; + } + else + { + currentChar = MoveNext(); + } + + if (currentChar == '\0' && _end) + return false; + + switch (CurrentState) + { + case State.Start: + case State.Property: + case State.Array: + case State.ArrayStart: + case State.Constructor: + case State.ConstructorStart: + return ParseValue(currentChar); + case State.Complete: + break; + case State.Object: + case State.ObjectStart: + return ParseObject(currentChar); + case State.PostValue: + // returns true if it hits + // end of object or array + if (ParsePostValue(currentChar)) + return true; + break; + case State.Closed: + break; + case State.Error: + break; + default: + throw CreateJsonReaderException("Unexpected state: {0}. Line {1}, position {2}.", CurrentState, _currentLineNumber, _currentLinePosition); + } + } + } + + private bool ParsePostValue(char currentChar) + { + do + { + switch (currentChar) + { + case '}': + SetToken(JsonToken.EndObject); + return true; + case ']': + SetToken(JsonToken.EndArray); + return true; + case ')': + SetToken(JsonToken.EndConstructor); + return true; + case '/': + ParseComment(); + return true; + case ',': + // finished parsing + SetStateBasedOnCurrent(); + return false; + case ' ': + case StringUtils.Tab: + case StringUtils.LineFeed: + case StringUtils.CarriageReturn: + // eat + break; + default: + if (char.IsWhiteSpace(currentChar)) + { + // eat + } + else + { + throw CreateJsonReaderException("After parsing a value an unexpected character was encountered: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); + } + break; + } + } while ((currentChar = MoveNext()) != '\0' || !_end); + + return false; + } + + private bool ParseObject(char currentChar) + { + do + { + switch (currentChar) + { + case '}': + SetToken(JsonToken.EndObject); + return true; + case '/': + ParseComment(); + return true; + case ' ': + case StringUtils.Tab: + case StringUtils.LineFeed: + case StringUtils.CarriageReturn: + // eat + break; + default: + if (char.IsWhiteSpace(currentChar)) + { + // eat + } + else + { + return ParseProperty(currentChar); + } + break; + } + } while ((currentChar = MoveNext()) != '\0' || !_end); + + return false; + } + + private bool ParseProperty(char firstChar) + { + char currentChar = firstChar; + char quoteChar; + + if (ValidIdentifierChar(currentChar)) + { + quoteChar = '\0'; + currentChar = ParseUnquotedProperty(currentChar); + } + else if (currentChar == '"' || currentChar == '\'') + { + quoteChar = currentChar; + ReadStringIntoBuffer(quoteChar); + currentChar = MoveNext(); + } + else + { + throw CreateJsonReaderException("Invalid property identifier character: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); + } + + if (currentChar != ':') + { + currentChar = MoveNext(); + + // finished property. skip any whitespace and move to colon + EatWhitespace(currentChar, false, out currentChar); + + if (currentChar != ':') + throw CreateJsonReaderException("Invalid character after parsing property name. Expected ':' but got: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); + } + + SetToken(JsonToken.PropertyName, _buffer.ToString()); + QuoteChar = quoteChar; + _buffer.Position = 0; + + return true; + } + + private bool ValidIdentifierChar(char value) + { + return (char.IsLetterOrDigit(value) || value == '_' || value == '$'); + } + + private char ParseUnquotedProperty(char firstChar) + { + // parse unquoted property name until whitespace or colon + _buffer.Append(firstChar); + + char currentChar; + + while ((currentChar = MoveNext()) != '\0' || !_end) + { + if (char.IsWhiteSpace(currentChar) || currentChar == ':') + { + return currentChar; + } + else if (ValidIdentifierChar(currentChar)) + { + _buffer.Append(currentChar); + } + else + { + throw CreateJsonReaderException("Invalid JavaScript property identifier character: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); + } + } + + throw CreateJsonReaderException("Unexpected end when parsing unquoted property name. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } + + private bool ParseValue(char currentChar) + { + do + { + switch (currentChar) + { + case '"': + case '\'': + ParseString(currentChar); + return true; + case 't': + ParseTrue(); + return true; + case 'f': + ParseFalse(); + return true; + case 'n': + if (HasNext()) + { + char next = (char)PeekNext(); + + if (next == 'u') + ParseNull(); + else if (next == 'e') + ParseConstructor(); + else + throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); + } + else + { + throw CreateJsonReaderException("Unexpected end. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } + return true; + case 'N': + ParseNumberNaN(); + return true; + case 'I': + ParseNumberPositiveInfinity(); + return true; + case '-': + if (PeekNext() == 'I') + ParseNumberNegativeInfinity(); + else + ParseNumber(currentChar); + return true; + case '/': + ParseComment(); + return true; + case 'u': + ParseUndefined(); + return true; + case '{': + SetToken(JsonToken.StartObject); + return true; + case '[': + SetToken(JsonToken.StartArray); + return true; + case '}': + SetToken(JsonToken.EndObject); + return true; + case ']': + SetToken(JsonToken.EndArray); + return true; + case ',': + SetToken(JsonToken.Undefined); + return true; + case ')': + SetToken(JsonToken.EndConstructor); + return true; + case ' ': + case StringUtils.Tab: + case StringUtils.LineFeed: + case StringUtils.CarriageReturn: + // eat + break; + default: + if (char.IsWhiteSpace(currentChar)) + { + // eat + } + else if (char.IsNumber(currentChar) || currentChar == '-' || currentChar == '.') + { + ParseNumber(currentChar); + return true; + } + else + { + throw CreateJsonReaderException("Unexpected character encountered while parsing value: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); + } + break; + } + } while ((currentChar = MoveNext()) != '\0' || !_end); + + return false; + } + + private bool EatWhitespace(char initialChar, bool oneOrMore, out char finalChar) + { + bool whitespace = false; + char currentChar = initialChar; + while (currentChar == ' ' || char.IsWhiteSpace(currentChar)) + { + whitespace = true; + currentChar = MoveNext(); + } + + finalChar = currentChar; + + return (!oneOrMore || whitespace); + } + + private void ParseConstructor() + { + if (MatchValue('n', "new", true)) + { + char currentChar = MoveNext(); + + if (EatWhitespace(currentChar, true, out currentChar)) + { + while (char.IsLetter(currentChar)) + { + _buffer.Append(currentChar); + currentChar = MoveNext(); + } + + EatWhitespace(currentChar, false, out currentChar); + + if (currentChar != '(') + throw CreateJsonReaderException("Unexpected character while parsing constructor: {0}. Line {1}, position {2}.", currentChar, _currentLineNumber, _currentLinePosition); + + string constructorName = _buffer.ToString(); + _buffer.Position = 0; + + SetToken(JsonToken.StartConstructor, constructorName); + } + } + } + + private void ParseNumber(char firstChar) + { + char currentChar = firstChar; + + // parse until seperator character or end + bool end = false; + do + { + if (IsSeperator(currentChar)) + { + end = true; + _lastChar = currentChar; + } + else + { + _buffer.Append(currentChar); + } + + } while (!end && ((currentChar = MoveNext()) != '\0' || !_end)); + + string number = _buffer.ToString(); + object numberValue; + JsonToken numberType; + + bool nonBase10 = (firstChar == '0' && !number.StartsWith("0.", StringComparison.OrdinalIgnoreCase)); + + if (_readType == ReadType.ReadAsDecimal) + { + if (nonBase10) + { + // decimal.Parse doesn't support parsing hexadecimal values + long integer = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) + ? Convert.ToInt64(number, 16) + : Convert.ToInt64(number, 8); + + numberValue = Convert.ToDecimal(integer); + } + else + { + numberValue = decimal.Parse(number, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture); + } + + numberType = JsonToken.Float; + } + else + { + if (nonBase10) + { + numberValue = number.StartsWith("0x", StringComparison.OrdinalIgnoreCase) + ? Convert.ToInt64(number, 16) + : Convert.ToInt64(number, 8); + numberType = JsonToken.Integer; + } + else if (number.IndexOf(".", StringComparison.OrdinalIgnoreCase) != -1 || number.IndexOf("e", StringComparison.OrdinalIgnoreCase) != -1) + { + numberValue = Convert.ToDouble(number, CultureInfo.InvariantCulture); + numberType = JsonToken.Float; + } + else + { + try + { + numberValue = Convert.ToInt64(number, CultureInfo.InvariantCulture); + } + catch (OverflowException ex) + { + throw new JsonReaderException("JSON integer {0} is too large or small for an Int64.".FormatWith(CultureInfo.InvariantCulture, number), ex); + } + + numberType = JsonToken.Integer; + } + } + + _buffer.Position = 0; + + SetToken(numberType, numberValue); + } + + private void ParseComment() + { + // should have already parsed / character before reaching this method + + char currentChar = MoveNext(); + + if (currentChar == '*') + { + while ((currentChar = MoveNext()) != '\0' || !_end) + { + if (currentChar == '*') + { + if ((currentChar = MoveNext()) != '\0' || !_end) + { + if (currentChar == '/') + { + break; + } + else + { + _buffer.Append('*'); + _buffer.Append(currentChar); + } + } + } + else + { + _buffer.Append(currentChar); + } + } + } + else + { + throw CreateJsonReaderException("Error parsing comment. Expected: *. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } + + SetToken(JsonToken.Comment, _buffer.ToString()); + + _buffer.Position = 0; + } + + private bool MatchValue(char firstChar, string value) + { + char currentChar = firstChar; + + int i = 0; + do + { + if (currentChar != value[i]) + { + break; + } + i++; + } + while (i < value.Length && ((currentChar = MoveNext()) != '\0' || !_end)); + + return (i == value.Length); + } + + private bool MatchValue(char firstChar, string value, bool noTrailingNonSeperatorCharacters) + { + // will match value and then move to the next character, checking that it is a seperator character + bool match = MatchValue(firstChar, value); + + if (!noTrailingNonSeperatorCharacters) + { + return match; + } + else + { + int c = PeekNext(); + char next = (c != -1) ? (char) c : '\0'; + bool matchAndNoTrainingNonSeperatorCharacters = (match && (next == '\0' || IsSeperator(next))); + + return matchAndNoTrainingNonSeperatorCharacters; + } + } + + private bool IsSeperator(char c) + { + switch (c) + { + case '}': + case ']': + case ',': + return true; + case '/': + // check next character to see if start of a comment + return (HasNext() && PeekNext() == '*'); + case ')': + if (CurrentState == State.Constructor || CurrentState == State.ConstructorStart) + return true; + break; + case ' ': + case StringUtils.Tab: + case StringUtils.LineFeed: + case StringUtils.CarriageReturn: + return true; + default: + if (char.IsWhiteSpace(c)) + return true; + break; + } + + return false; + } + + private void ParseTrue() + { + // check characters equal 'true' + // and that it is followed by either a seperator character + // or the text ends + if (MatchValue('t', JsonConvert.True, true)) + { + SetToken(JsonToken.Boolean, true); + } + else + { + throw CreateJsonReaderException("Error parsing boolean value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } + } + + private void ParseNull() + { + if (MatchValue('n', JsonConvert.Null, true)) + { + SetToken(JsonToken.Null); + } + else + { + throw CreateJsonReaderException("Error parsing null value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } + } + + private void ParseUndefined() + { + if (MatchValue('u', JsonConvert.Undefined, true)) + { + SetToken(JsonToken.Undefined); + } + else + { + throw CreateJsonReaderException("Error parsing undefined value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } + } + + private void ParseFalse() + { + if (MatchValue('f', JsonConvert.False, true)) + { + SetToken(JsonToken.Boolean, false); + } + else + { + throw CreateJsonReaderException("Error parsing boolean value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } + } + + private void ParseNumberNegativeInfinity() + { + if (MatchValue('-', JsonConvert.NegativeInfinity, true)) + { + SetToken(JsonToken.Float, double.NegativeInfinity); + } + else + { + throw CreateJsonReaderException("Error parsing negative infinity value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } + } + + private void ParseNumberPositiveInfinity() + { + if (MatchValue('I', JsonConvert.PositiveInfinity, true)) + { + SetToken(JsonToken.Float, double.PositiveInfinity); + } + else + { + throw CreateJsonReaderException("Error parsing positive infinity value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } + } + + private void ParseNumberNaN() + { + if (MatchValue('N', JsonConvert.NaN, true)) + { + SetToken(JsonToken.Float, double.NaN); + } + else + { + throw CreateJsonReaderException("Error parsing NaN value. Line {0}, position {1}.", _currentLineNumber, _currentLinePosition); + } + } + + /// + /// Changes the state to closed. + /// + public override void Close() + { + base.Close(); + + if (CloseInput && _reader != null) + _reader.Close(); + + if (_buffer != null) + _buffer.Clear(); + } + + /// + /// Gets a value indicating whether the class can return line information. + /// + /// + /// true if LineNumber and LinePosition can be provided; otherwise, false. + /// + public bool HasLineInfo() + { + return true; + } + + /// + /// Gets the current line number. + /// + /// + /// The current line number or 0 if no line information is available (for example, HasLineInfo returns false). + /// + public int LineNumber + { + get + { + if (CurrentState == State.Start) + return 0; + + return _currentLineNumber; + } + } + + /// + /// Gets the current line position. + /// + /// + /// The current line position or 0 if no line information is available (for example, HasLineInfo returns false). + /// + public int LinePosition + { + get { return _currentLinePosition; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonTextWriter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonTextWriter.cs new file mode 100644 index 0000000..ea26daf --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonTextWriter.cs @@ -0,0 +1,490 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Xml; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json +{ + /// + /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + /// + public class JsonTextWriter : JsonWriter + { + private readonly TextWriter _writer; + private Base64Encoder _base64Encoder; + private char _indentChar; + private int _indentation; + private char _quoteChar; + private bool _quoteName; + + private Base64Encoder Base64Encoder + { + get + { + if (_base64Encoder == null) + _base64Encoder = new Base64Encoder(_writer); + + return _base64Encoder; + } + } + + /// + /// Gets or sets how many IndentChars to write for each level in the hierarchy when is set to Formatting.Indented. + /// + public int Indentation + { + get { return _indentation; } + set + { + if (value < 0) + throw new ArgumentException("Indentation value must be greater than 0."); + + _indentation = value; + } + } + + /// + /// Gets or sets which character to use to quote attribute values. + /// + public char QuoteChar + { + get { return _quoteChar; } + set + { + if (value != '"' && value != '\'') + throw new ArgumentException(@"Invalid JavaScript string quote character. Valid quote characters are ' and ""."); + + _quoteChar = value; + } + } + + /// + /// Gets or sets which character to use for indenting when is set to Formatting.Indented. + /// + public char IndentChar + { + get { return _indentChar; } + set { _indentChar = value; } + } + + /// + /// Gets or sets a value indicating whether object names will be surrounded with quotes. + /// + public bool QuoteName + { + get { return _quoteName; } + set { _quoteName = value; } + } + + /// + /// Creates an instance of the JsonWriter class using the specified . + /// + /// The TextWriter to write to. + public JsonTextWriter(TextWriter textWriter) + { + if (textWriter == null) + throw new ArgumentNullException("textWriter"); + + _writer = textWriter; + _quoteChar = '"'; + _quoteName = true; + _indentChar = ' '; + _indentation = 2; + } + + /// + /// Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + /// + public override void Flush() + { + _writer.Flush(); + } + + /// + /// Closes this stream and the underlying stream. + /// + public override void Close() + { + base.Close(); + + if (CloseOutput && _writer != null) + _writer.Close(); + } + + /// + /// Writes the beginning of a Json object. + /// + public override void WriteStartObject() + { + base.WriteStartObject(); + + _writer.Write("{"); + } + + /// + /// Writes the beginning of a Json array. + /// + public override void WriteStartArray() + { + base.WriteStartArray(); + + _writer.Write("["); + } + + /// + /// Writes the start of a constructor with the given name. + /// + /// The name of the constructor. + public override void WriteStartConstructor(string name) + { + base.WriteStartConstructor(name); + + _writer.Write("new "); + _writer.Write(name); + _writer.Write("("); + } + + /// + /// Writes the specified end token. + /// + /// The end token to write. + protected override void WriteEnd(JsonToken token) + { + switch (token) + { + case JsonToken.EndObject: + _writer.Write("}"); + break; + case JsonToken.EndArray: + _writer.Write("]"); + break; + case JsonToken.EndConstructor: + _writer.Write(")"); + break; + default: + throw new JsonWriterException("Invalid JsonToken: " + token); + } + } + + /// + /// Writes the property name of a name/value pair on a Json object. + /// + /// The name of the property. + public override void WritePropertyName(string name) + { + base.WritePropertyName(name); + + JavaScriptUtils.WriteEscapedJavaScriptString(_writer, name, _quoteChar, _quoteName); + + _writer.Write(':'); + } + + /// + /// Writes indent characters. + /// + protected override void WriteIndent() + { + if (Formatting == Formatting.Indented) + { + _writer.Write(Environment.NewLine); + + // levels of indentation multiplied by the indent count + int currentIndentCount = Top * _indentation; + + for (int i = 0; i < currentIndentCount; i++) + { + _writer.Write(_indentChar); + } + } + } + + /// + /// Writes the JSON value delimiter. + /// + protected override void WriteValueDelimiter() + { + _writer.Write(','); + } + + /// + /// Writes an indent space. + /// + protected override void WriteIndentSpace() + { + _writer.Write(' '); + } + + private void WriteValueInternal(string value, JsonToken token) + { + _writer.Write(value); + } + + #region WriteValue methods + /// + /// Writes a null value. + /// + public override void WriteNull() + { + base.WriteNull(); + WriteValueInternal(JsonConvert.Null, JsonToken.Null); + } + + /// + /// Writes an undefined value. + /// + public override void WriteUndefined() + { + base.WriteUndefined(); + WriteValueInternal(JsonConvert.Undefined, JsonToken.Undefined); + } + + /// + /// Writes raw JSON. + /// + /// The raw JSON to write. + public override void WriteRaw(string json) + { + base.WriteRaw(json); + + _writer.Write(json); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(string value) + { + base.WriteValue(value); + if (value == null) + WriteValueInternal(JsonConvert.Null, JsonToken.Null); + else + JavaScriptUtils.WriteEscapedJavaScriptString(_writer, value, _quoteChar, true); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(int value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(uint value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(long value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(ulong value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(float value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(double value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(bool value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Boolean); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(short value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(ushort value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(char value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(byte value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(sbyte value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(decimal value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTime value) + { + base.WriteValue(value); + JsonConvert.WriteDateTimeString(_writer, value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(byte[] value) + { + base.WriteValue(value); + + if (value != null) + { + _writer.Write(_quoteChar); + Base64Encoder.Encode(value, 0, value.Length); + Base64Encoder.Flush(); + _writer.Write(_quoteChar); + } + } + +#if !PocketPC && !NET20 + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTimeOffset value) + { + base.WriteValue(value); + WriteValueInternal(JsonConvert.ToString(value), JsonToken.Date); + } +#endif + #endregion + + /// + /// Writes out a comment /*...*/ containing the specified text. + /// + /// Text to place inside the comment. + public override void WriteComment(string text) + { + base.WriteComment(text); + + _writer.Write("/*"); + _writer.Write(text); + _writer.Write("*/"); + } + + /// + /// Writes out the given white space. + /// + /// The string of white space characters. + public override void WriteWhitespace(string ws) + { + base.WriteWhitespace(ws); + + _writer.Write(ws); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonToken.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonToken.cs new file mode 100644 index 0000000..9be0d19 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonToken.cs @@ -0,0 +1,110 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Specifies the type of Json token. + /// + public enum JsonToken + { + /// + /// This is returned by the if a method has not been called. + /// + None, + /// + /// An object start token. + /// + StartObject, + /// + /// An array start token. + /// + StartArray, + /// + /// A constructor start token. + /// + StartConstructor, + /// + /// An object property name. + /// + PropertyName, + /// + /// A comment. + /// + Comment, + /// + /// Raw JSON. + /// + Raw, + /// + /// An interger. + /// + Integer, + /// + /// A float. + /// + Float, + /// + /// A string. + /// + String, + /// + /// A boolean. + /// + Boolean, + /// + /// A null token. + /// + Null, + /// + /// An undefined token. + /// + Undefined, + /// + /// An object end token. + /// + EndObject, + /// + /// An array end token. + /// + EndArray, + /// + /// A constructor end token. + /// + EndConstructor, + /// + /// A Date. + /// + Date, + /// + /// Byte data. + /// + Bytes + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonValidatingReader.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonValidatingReader.cs new file mode 100644 index 0000000..ab75cbc --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonValidatingReader.cs @@ -0,0 +1,751 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using Newtonsoft.Json.Utilities; +using System.Globalization; +using System.Text.RegularExpressions; +using System.IO; + +namespace Newtonsoft.Json +{ + /// + /// Represents a reader that provides validation. + /// + public class JsonValidatingReader : JsonReader, IJsonLineInfo + { + private class SchemaScope + { + private readonly JTokenType _tokenType; + private readonly IList _schemas; + private readonly Dictionary _requiredProperties; + + public string CurrentPropertyName { get; set; } + public int ArrayItemCount { get; set; } + + public IList Schemas + { + get { return _schemas; } + } + + public Dictionary RequiredProperties + { + get { return _requiredProperties; } + } + + public JTokenType TokenType + { + get { return _tokenType; } + } + + public SchemaScope(JTokenType tokenType, IList schemas) + { + _tokenType = tokenType; + _schemas = schemas; + + _requiredProperties = schemas.SelectMany(GetRequiredProperties).Distinct().ToDictionary(p => p, p => false); + } + + private IEnumerable GetRequiredProperties(JsonSchemaModel schema) + { + if (schema == null || schema.Properties == null) + return Enumerable.Empty(); + + return schema.Properties.Where(p => p.Value.Required).Select(p => p.Key); + } + } + + private readonly JsonReader _reader; + private readonly Stack _stack; + private JsonSchema _schema; + private JsonSchemaModel _model; + private SchemaScope _currentScope; + + /// + /// Sets an event handler for receiving schema validation errors. + /// + public event ValidationEventHandler ValidationEventHandler; + + /// + /// Gets the text value of the current Json token. + /// + /// + public override object Value + { + get { return _reader.Value; } + } + + /// + /// Gets the depth of the current token in the JSON document. + /// + /// The depth of the current token in the JSON document. + public override int Depth + { + get { return _reader.Depth; } + } + + /// + /// Gets the quotation mark character used to enclose the value of a string. + /// + /// + public override char QuoteChar + { + get { return _reader.QuoteChar; } + protected internal set { } + } + + /// + /// Gets the type of the current Json token. + /// + /// + public override JsonToken TokenType + { + get { return _reader.TokenType; } + } + + /// + /// Gets The Common Language Runtime (CLR) type for the current Json token. + /// + /// + public override Type ValueType + { + get { return _reader.ValueType; } + } + + private void Push(SchemaScope scope) + { + _stack.Push(scope); + _currentScope = scope; + } + + private SchemaScope Pop() + { + SchemaScope poppedScope = _stack.Pop(); + _currentScope = (_stack.Count != 0) + ? _stack.Peek() + : null; + + return poppedScope; + } + + private IEnumerable CurrentSchemas + { + get { return _currentScope.Schemas; } + } + + private IEnumerable CurrentMemberSchemas + { + get + { + if (_currentScope == null) + return new List(new [] { _model }); + + if (_currentScope.Schemas == null || _currentScope.Schemas.Count == 0) + return Enumerable.Empty(); + + switch (_currentScope.TokenType) + { + case JTokenType.None: + return _currentScope.Schemas; + case JTokenType.Object: + { + if (_currentScope.CurrentPropertyName == null) + throw new Exception("CurrentPropertyName has not been set on scope."); + + IList schemas = new List(); + + foreach (JsonSchemaModel schema in CurrentSchemas) + { + JsonSchemaModel propertySchema; + if (schema.Properties != null && schema.Properties.TryGetValue(_currentScope.CurrentPropertyName, out propertySchema)) + { + schemas.Add(propertySchema); + } + if (schema.PatternProperties != null) + { + foreach (KeyValuePair patternProperty in schema.PatternProperties) + { + if (Regex.IsMatch(_currentScope.CurrentPropertyName, patternProperty.Key)) + { + schemas.Add(patternProperty.Value); + } + } + } + + if (schemas.Count == 0 && schema.AllowAdditionalProperties && schema.AdditionalProperties != null) + schemas.Add(schema.AdditionalProperties); + } + + return schemas; + } + case JTokenType.Array: + { + IList schemas = new List(); + + foreach (JsonSchemaModel schema in CurrentSchemas) + { + if (!CollectionUtils.IsNullOrEmpty(schema.Items)) + { + if (schema.Items.Count == 1) + schemas.Add(schema.Items[0]); + + if (schema.Items.Count > (_currentScope.ArrayItemCount - 1)) + schemas.Add(schema.Items[_currentScope.ArrayItemCount - 1]); + } + + if (schema.AllowAdditionalProperties && schema.AdditionalProperties != null) + schemas.Add(schema.AdditionalProperties); + } + + return schemas; + } + case JTokenType.Constructor: + return Enumerable.Empty(); + default: + throw new ArgumentOutOfRangeException("TokenType", "Unexpected token type: {0}".FormatWith(CultureInfo.InvariantCulture, _currentScope.TokenType)); + } + } + } + + private void RaiseError(string message, JsonSchemaModel schema) + { + IJsonLineInfo lineInfo = this; + + string exceptionMessage = (lineInfo.HasLineInfo()) + ? message + " Line {0}, position {1}.".FormatWith(CultureInfo.InvariantCulture, lineInfo.LineNumber, lineInfo.LinePosition) + : message; + + OnValidationEvent(new JsonSchemaException(exceptionMessage, null, lineInfo.LineNumber, lineInfo.LinePosition)); + } + + private void OnValidationEvent(JsonSchemaException exception) + { + ValidationEventHandler handler = ValidationEventHandler; + if (handler != null) + handler(this, new ValidationEventArgs(exception)); + else + throw exception; + } + + /// + /// Initializes a new instance of the class that + /// validates the content returned from the given . + /// + /// The to read from while validating. + public JsonValidatingReader(JsonReader reader) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + _reader = reader; + _stack = new Stack(); + } + + /// + /// Gets or sets the schema. + /// + /// The schema. + public JsonSchema Schema + { + get { return _schema; } + set + { + if (TokenType != JsonToken.None) + throw new Exception("Cannot change schema while validating JSON."); + + _schema = value; + _model = null; + } + } + + /// + /// Gets the used to construct this . + /// + /// The specified in the constructor. + public JsonReader Reader + { + get { return _reader; } + } + + private void ValidateInEnumAndNotDisallowed(JsonSchemaModel schema) + { + if (schema == null) + return; + + JToken value = new JValue(_reader.Value); + + if (schema.Enum != null) + { + StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); + value.WriteTo(new JsonTextWriter(sw)); + + if (!schema.Enum.ContainsValue(value, new JTokenEqualityComparer())) + RaiseError("Value {0} is not defined in enum.".FormatWith(CultureInfo.InvariantCulture, sw.ToString()), + schema); + } + + JsonSchemaType? currentNodeType = GetCurrentNodeSchemaType(); + if (currentNodeType != null) + { + if (JsonSchemaGenerator.HasFlag(schema.Disallow, currentNodeType.Value)) + RaiseError("Type {0} is disallowed.".FormatWith(CultureInfo.InvariantCulture, currentNodeType), schema); + } + } + + private JsonSchemaType? GetCurrentNodeSchemaType() + { + switch (_reader.TokenType) + { + case JsonToken.StartObject: + return JsonSchemaType.Object; + case JsonToken.StartArray: + return JsonSchemaType.Array; + case JsonToken.Integer: + return JsonSchemaType.Integer; + case JsonToken.Float: + return JsonSchemaType.Float; + case JsonToken.String: + return JsonSchemaType.String; + case JsonToken.Boolean: + return JsonSchemaType.Boolean; + case JsonToken.Null: + return JsonSchemaType.Null; + default: + return null; + } + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// + /// A or a null reference if the next JSON token is null. + /// + public override byte[] ReadAsBytes() + { + byte[] data = _reader.ReadAsBytes(); + + ValidateCurrentToken(); + return data; + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override decimal? ReadAsDecimal() + { + decimal? d = _reader.ReadAsDecimal(); + + ValidateCurrentToken(); + return d; + } + +#if !NET20 + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override DateTimeOffset? ReadAsDateTimeOffset() + { + DateTimeOffset? dateTimeOffset = _reader.ReadAsDateTimeOffset(); + + ValidateCurrentToken(); + return dateTimeOffset; + } +#endif + + /// + /// Reads the next JSON token from the stream. + /// + /// + /// true if the next token was read successfully; false if there are no more tokens to read. + /// + public override bool Read() + { + if (!_reader.Read()) + return false; + + if (_reader.TokenType == JsonToken.Comment) + return true; + + ValidateCurrentToken(); + return true; + } + + private void ValidateCurrentToken() + { + // first time validate has been called. build model + if (_model == null) + { + JsonSchemaModelBuilder builder = new JsonSchemaModelBuilder(); + _model = builder.Build(_schema); + } + + //ValidateValueToken(); + + switch (_reader.TokenType) + { + case JsonToken.StartObject: + ProcessValue(); + IList objectSchemas = CurrentMemberSchemas.Where(ValidateObject).ToList(); + Push(new SchemaScope(JTokenType.Object, objectSchemas)); + break; + case JsonToken.StartArray: + ProcessValue(); + IList arraySchemas = CurrentMemberSchemas.Where(ValidateArray).ToList(); + Push(new SchemaScope(JTokenType.Array, arraySchemas)); + break; + case JsonToken.StartConstructor: + Push(new SchemaScope(JTokenType.Constructor, null)); + break; + case JsonToken.PropertyName: + foreach (JsonSchemaModel schema in CurrentSchemas) + { + ValidatePropertyName(schema); + } + break; + case JsonToken.Raw: + break; + case JsonToken.Integer: + ProcessValue(); + foreach (JsonSchemaModel schema in CurrentMemberSchemas) + { + ValidateInteger(schema); + } + break; + case JsonToken.Float: + ProcessValue(); + foreach (JsonSchemaModel schema in CurrentMemberSchemas) + { + ValidateFloat(schema); + } + break; + case JsonToken.String: + ProcessValue(); + foreach (JsonSchemaModel schema in CurrentMemberSchemas) + { + ValidateString(schema); + } + break; + case JsonToken.Boolean: + ProcessValue(); + foreach (JsonSchemaModel schema in CurrentMemberSchemas) + { + ValidateBoolean(schema); + } + break; + case JsonToken.Null: + ProcessValue(); + foreach (JsonSchemaModel schema in CurrentMemberSchemas) + { + ValidateNull(schema); + } + break; + case JsonToken.Undefined: + break; + case JsonToken.EndObject: + foreach (JsonSchemaModel schema in CurrentSchemas) + { + ValidateEndObject(schema); + } + Pop(); + break; + case JsonToken.EndArray: + foreach (JsonSchemaModel schema in CurrentSchemas) + { + ValidateEndArray(schema); + } + Pop(); + break; + case JsonToken.EndConstructor: + Pop(); + break; + case JsonToken.Date: + break; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private void ValidateEndObject(JsonSchemaModel schema) + { + if (schema == null) + return; + + Dictionary requiredProperties = _currentScope.RequiredProperties; + + if (requiredProperties != null) + { + List unmatchedRequiredProperties = + requiredProperties.Where(kv => !kv.Value).Select(kv => kv.Key).ToList(); + + if (unmatchedRequiredProperties.Count > 0) + RaiseError("Required properties are missing from object: {0}.".FormatWith(CultureInfo.InvariantCulture, string.Join(", ", unmatchedRequiredProperties.ToArray())), schema); + } + } + + private void ValidateEndArray(JsonSchemaModel schema) + { + if (schema == null) + return; + + int arrayItemCount = _currentScope.ArrayItemCount; + + if (schema.MaximumItems != null && arrayItemCount > schema.MaximumItems) + RaiseError("Array item count {0} exceeds maximum count of {1}.".FormatWith(CultureInfo.InvariantCulture, arrayItemCount, schema.MaximumItems), schema); + + if (schema.MinimumItems != null && arrayItemCount < schema.MinimumItems) + RaiseError("Array item count {0} is less than minimum count of {1}.".FormatWith(CultureInfo.InvariantCulture, arrayItemCount, schema.MinimumItems), schema); + } + + private void ValidateNull(JsonSchemaModel schema) + { + if (schema == null) + return; + + if (!TestType(schema, JsonSchemaType.Null)) + return; + + ValidateInEnumAndNotDisallowed(schema); + } + + private void ValidateBoolean(JsonSchemaModel schema) + { + if (schema == null) + return; + + if (!TestType(schema, JsonSchemaType.Boolean)) + return; + + ValidateInEnumAndNotDisallowed(schema); + } + + private void ValidateString(JsonSchemaModel schema) + { + if (schema == null) + return; + + if (!TestType(schema, JsonSchemaType.String)) + return; + + ValidateInEnumAndNotDisallowed(schema); + + string value = _reader.Value.ToString(); + + if (schema.MaximumLength != null && value.Length > schema.MaximumLength) + RaiseError("String '{0}' exceeds maximum length of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.MaximumLength), schema); + + if (schema.MinimumLength != null && value.Length < schema.MinimumLength) + RaiseError("String '{0}' is less than minimum length of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.MinimumLength), schema); + + if (schema.Patterns != null) + { + foreach (string pattern in schema.Patterns) + { + if (!Regex.IsMatch(value, pattern)) + RaiseError("String '{0}' does not match regex pattern '{1}'.".FormatWith(CultureInfo.InvariantCulture, value, pattern), schema); + } + } + } + + private void ValidateInteger(JsonSchemaModel schema) + { + if (schema == null) + return; + + if (!TestType(schema, JsonSchemaType.Integer)) + return; + + ValidateInEnumAndNotDisallowed(schema); + + long value = Convert.ToInt64(_reader.Value, CultureInfo.InvariantCulture); + + if (schema.Maximum != null) + { + if (value > schema.Maximum) + RaiseError("Integer {0} exceeds maximum value of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.Maximum), schema); + if (schema.ExclusiveMaximum && value == schema.Maximum) + RaiseError("Integer {0} equals maximum value of {1} and exclusive maximum is true.".FormatWith(CultureInfo.InvariantCulture, value, schema.Maximum), schema); + } + + if (schema.Minimum != null) + { + if (value < schema.Minimum) + RaiseError("Integer {0} is less than minimum value of {1}.".FormatWith(CultureInfo.InvariantCulture, value, schema.Minimum), schema); + if (schema.ExclusiveMinimum && value == schema.Minimum) + RaiseError("Integer {0} equals minimum value of {1} and exclusive minimum is true.".FormatWith(CultureInfo.InvariantCulture, value, schema.Minimum), schema); + } + + if (schema.DivisibleBy != null && !IsZero(value % schema.DivisibleBy.Value)) + RaiseError("Integer {0} is not evenly divisible by {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.DivisibleBy), schema); + } + + private void ProcessValue() + { + if (_currentScope != null && _currentScope.TokenType == JTokenType.Array) + { + _currentScope.ArrayItemCount++; + + foreach (JsonSchemaModel currentSchema in CurrentSchemas) + { + if (currentSchema != null && currentSchema.Items != null && currentSchema.Items.Count > 1 && _currentScope.ArrayItemCount >= currentSchema.Items.Count) + RaiseError("Index {0} has not been defined and the schema does not allow additional items.".FormatWith(CultureInfo.InvariantCulture, _currentScope.ArrayItemCount), currentSchema); + } + } + } + + private void ValidateFloat(JsonSchemaModel schema) + { + if (schema == null) + return; + + if (!TestType(schema, JsonSchemaType.Float)) + return; + + ValidateInEnumAndNotDisallowed(schema); + + double value = Convert.ToDouble(_reader.Value, CultureInfo.InvariantCulture); + + if (schema.Maximum != null) + { + if (value > schema.Maximum) + RaiseError("Float {0} exceeds maximum value of {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Maximum), schema); + if (schema.ExclusiveMaximum && value == schema.Maximum) + RaiseError("Float {0} equals maximum value of {1} and exclusive maximum is true.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Maximum), schema); + } + + if (schema.Minimum != null) + { + if (value < schema.Minimum) + RaiseError("Float {0} is less than minimum value of {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Minimum), schema); + if (schema.ExclusiveMinimum && value == schema.Minimum) + RaiseError("Float {0} equals minimum value of {1} and exclusive minimum is true.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.Minimum), schema); + } + + if (schema.DivisibleBy != null && !IsZero(value % schema.DivisibleBy.Value)) + RaiseError("Float {0} is not evenly divisible by {1}.".FormatWith(CultureInfo.InvariantCulture, JsonConvert.ToString(value), schema.DivisibleBy), schema); + } + + private static bool IsZero(double value) + { + double epsilon = 2.2204460492503131e-016; + + return Math.Abs(value) < 10.0 * epsilon; + } + + private void ValidatePropertyName(JsonSchemaModel schema) + { + if (schema == null) + return; + + string propertyName = Convert.ToString(_reader.Value, CultureInfo.InvariantCulture); + + if (_currentScope.RequiredProperties.ContainsKey(propertyName)) + _currentScope.RequiredProperties[propertyName] = true; + + if (!schema.AllowAdditionalProperties) + { + bool propertyDefinied = IsPropertyDefinied(schema, propertyName); + + if (!propertyDefinied) + RaiseError("Property '{0}' has not been defined and the schema does not allow additional properties.".FormatWith(CultureInfo.InvariantCulture, propertyName), schema); + } + + _currentScope.CurrentPropertyName = propertyName; + } + + private bool IsPropertyDefinied(JsonSchemaModel schema, string propertyName) + { + if (schema.Properties != null && schema.Properties.ContainsKey(propertyName)) + return true; + + if (schema.PatternProperties != null) + { + foreach (string pattern in schema.PatternProperties.Keys) + { + if (Regex.IsMatch(propertyName, pattern)) + return true; + } + } + + return false; + } + + private bool ValidateArray(JsonSchemaModel schema) + { + if (schema == null) + return true; + + return (TestType(schema, JsonSchemaType.Array)); + } + + private bool ValidateObject(JsonSchemaModel schema) + { + if (schema == null) + return true; + + return (TestType(schema, JsonSchemaType.Object)); + } + + private bool TestType(JsonSchemaModel currentSchema, JsonSchemaType currentType) + { + if (!JsonSchemaGenerator.HasFlag(currentSchema.Type, currentType)) + { + RaiseError("Invalid type. Expected {0} but got {1}.".FormatWith(CultureInfo.InvariantCulture, currentSchema.Type, currentType), currentSchema); + return false; + } + + return true; + } + + bool IJsonLineInfo.HasLineInfo() + { + IJsonLineInfo lineInfo = _reader as IJsonLineInfo; + return (lineInfo != null) ? lineInfo.HasLineInfo() : false; + } + + int IJsonLineInfo.LineNumber + { + get + { + IJsonLineInfo lineInfo = _reader as IJsonLineInfo; + return (lineInfo != null) ? lineInfo.LineNumber : 0; + } + } + + int IJsonLineInfo.LinePosition + { + get + { + IJsonLineInfo lineInfo = _reader as IJsonLineInfo; + return (lineInfo != null) ? lineInfo.LinePosition : 0; + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonWriter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonWriter.cs new file mode 100644 index 0000000..f86a1cf --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonWriter.cs @@ -0,0 +1,1134 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; +using System.Xml; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; +using System.Globalization; + +namespace Newtonsoft.Json +{ + /// + /// Specifies the state of the . + /// + public enum WriteState + { + /// + /// An exception has been thrown, which has left the in an invalid state. + /// You may call the method to put the in the Closed state. + /// Any other method calls results in an being thrown. + /// + Error, + /// + /// The method has been called. + /// + Closed, + /// + /// An object is being written. + /// + Object, + /// + /// A array is being written. + /// + Array, + /// + /// A constructor is being written. + /// + Constructor, + /// + /// A property is being written. + /// + Property, + /// + /// A write method has not been called. + /// + Start + } + + /// + /// Specifies formatting options for the . + /// + public enum Formatting + { + /// + /// No special formatting is applied. This is the default. + /// + None, + /// + /// Causes child objects to be indented according to the and settings. + /// + Indented + } + + /// + /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + /// + public abstract class JsonWriter : IDisposable + { + private enum State + { + Start, + Property, + ObjectStart, + Object, + ArrayStart, + Array, + ConstructorStart, + Constructor, + Bytes, + Closed, + Error + } + + // array that gives a new state based on the current state an the token being written + private static readonly State[][] stateArray = new[] { +// Start PropertyName ObjectStart Object ArrayStart Array ConstructorStart Constructor Closed Error +// +/* None */new[]{ State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error }, +/* StartObject */new[]{ State.ObjectStart, State.ObjectStart, State.Error, State.Error, State.ObjectStart, State.ObjectStart, State.ObjectStart, State.ObjectStart, State.Error, State.Error }, +/* StartArray */new[]{ State.ArrayStart, State.ArrayStart, State.Error, State.Error, State.ArrayStart, State.ArrayStart, State.ArrayStart, State.ArrayStart, State.Error, State.Error }, +/* StartConstructor */new[]{ State.ConstructorStart, State.ConstructorStart, State.Error, State.Error, State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.ConstructorStart, State.Error, State.Error }, +/* StartProperty */new[]{ State.Property, State.Error, State.Property, State.Property, State.Error, State.Error, State.Error, State.Error, State.Error, State.Error }, +/* Comment */new[]{ State.Start, State.Property, State.ObjectStart, State.Object, State.ArrayStart, State.Array, State.Constructor, State.Constructor, State.Error, State.Error }, +/* Raw */new[]{ State.Start, State.Property, State.ObjectStart, State.Object, State.ArrayStart, State.Array, State.Constructor, State.Constructor, State.Error, State.Error }, +/* Value */new[]{ State.Start, State.Object, State.Error, State.Error, State.Array, State.Array, State.Constructor, State.Constructor, State.Error, State.Error }, + }; + + private int _top; + + private readonly List _stack; + private State _currentState; + private Formatting _formatting; + + /// + /// Gets or sets a value indicating whether the underlying stream or + /// should be closed when the writer is closed. + /// + /// + /// true to close the underlying stream or when + /// the writer is closed; otherwise false. The default is true. + /// + public bool CloseOutput { get; set; } + + /// + /// Gets the top. + /// + /// The top. + protected internal int Top + { + get { return _top; } + } + + /// + /// Gets the state of the writer. + /// + public WriteState WriteState + { + get + { + switch (_currentState) + { + case State.Error: + return WriteState.Error; + case State.Closed: + return WriteState.Closed; + case State.Object: + case State.ObjectStart: + return WriteState.Object; + case State.Array: + case State.ArrayStart: + return WriteState.Array; + case State.Constructor: + case State.ConstructorStart: + return WriteState.Constructor; + case State.Property: + return WriteState.Property; + case State.Start: + return WriteState.Start; + default: + throw new JsonWriterException("Invalid state: " + _currentState); + } + } + } + + /// + /// Indicates how the output is formatted. + /// + public Formatting Formatting + { + get { return _formatting; } + set { _formatting = value; } + } + + /// + /// Creates an instance of the JsonWriter class. + /// + protected JsonWriter() + { + _stack = new List(8); + _stack.Add(JTokenType.None); + _currentState = State.Start; + _formatting = Formatting.None; + + CloseOutput = true; + } + + private void Push(JTokenType value) + { + _top++; + if (_stack.Count <= _top) + _stack.Add(value); + else + _stack[_top] = value; + } + + private JTokenType Pop() + { + JTokenType value = Peek(); + _top--; + + return value; + } + + private JTokenType Peek() + { + return _stack[_top]; + } + + /// + /// Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + /// + public abstract void Flush(); + + /// + /// Closes this stream and the underlying stream. + /// + public virtual void Close() + { + AutoCompleteAll(); + } + + /// + /// Writes the beginning of a Json object. + /// + public virtual void WriteStartObject() + { + AutoComplete(JsonToken.StartObject); + Push(JTokenType.Object); + } + + /// + /// Writes the end of a Json object. + /// + public void WriteEndObject() + { + AutoCompleteClose(JsonToken.EndObject); + } + + /// + /// Writes the beginning of a Json array. + /// + public virtual void WriteStartArray() + { + AutoComplete(JsonToken.StartArray); + Push(JTokenType.Array); + } + + /// + /// Writes the end of an array. + /// + public void WriteEndArray() + { + AutoCompleteClose(JsonToken.EndArray); + } + + /// + /// Writes the start of a constructor with the given name. + /// + /// The name of the constructor. + public virtual void WriteStartConstructor(string name) + { + AutoComplete(JsonToken.StartConstructor); + Push(JTokenType.Constructor); + } + + /// + /// Writes the end constructor. + /// + public void WriteEndConstructor() + { + AutoCompleteClose(JsonToken.EndConstructor); + } + + /// + /// Writes the property name of a name/value pair on a Json object. + /// + /// The name of the property. + public virtual void WritePropertyName(string name) + { + AutoComplete(JsonToken.PropertyName); + } + + /// + /// Writes the end of the current Json object or array. + /// + public void WriteEnd() + { + WriteEnd(Peek()); + } + + /// + /// Writes the current token. + /// + /// The to read the token from. + public void WriteToken(JsonReader reader) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + + int initialDepth; + + if (reader.TokenType == JsonToken.None) + initialDepth = -1; + else if (!IsStartToken(reader.TokenType)) + initialDepth = reader.Depth + 1; + else + initialDepth = reader.Depth; + + WriteToken(reader, initialDepth); + } + + internal void WriteToken(JsonReader reader, int initialDepth) + { + do + { + switch (reader.TokenType) + { + case JsonToken.None: + // read to next + break; + case JsonToken.StartObject: + WriteStartObject(); + break; + case JsonToken.StartArray: + WriteStartArray(); + break; + case JsonToken.StartConstructor: + string constructorName = reader.Value.ToString(); + // write a JValue date when the constructor is for a date + if (string.Compare(constructorName, "Date", StringComparison.Ordinal) == 0) + WriteConstructorDate(reader); + else + WriteStartConstructor(reader.Value.ToString()); + break; + case JsonToken.PropertyName: + WritePropertyName(reader.Value.ToString()); + break; + case JsonToken.Comment: + WriteComment(reader.Value.ToString()); + break; + case JsonToken.Integer: + WriteValue((long)reader.Value); + break; + case JsonToken.Float: + WriteValue((double)reader.Value); + break; + case JsonToken.String: + WriteValue(reader.Value.ToString()); + break; + case JsonToken.Boolean: + WriteValue((bool)reader.Value); + break; + case JsonToken.Null: + WriteNull(); + break; + case JsonToken.Undefined: + WriteUndefined(); + break; + case JsonToken.EndObject: + WriteEndObject(); + break; + case JsonToken.EndArray: + WriteEndArray(); + break; + case JsonToken.EndConstructor: + WriteEndConstructor(); + break; + case JsonToken.Date: + WriteValue((DateTime)reader.Value); + break; + case JsonToken.Raw: + WriteRawValue((string)reader.Value); + break; + case JsonToken.Bytes: + WriteValue((byte[])reader.Value); + break; + default: + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("TokenType", reader.TokenType, "Unexpected token type."); + } + } + while ( + // stop if we have reached the end of the token being read + initialDepth - 1 < reader.Depth - (IsEndToken(reader.TokenType) ? 1 : 0) + && reader.Read()); + } + + private void WriteConstructorDate(JsonReader reader) + { + if (!reader.Read()) + throw new Exception("Unexpected end while reading date constructor."); + if (reader.TokenType != JsonToken.Integer) + throw new Exception("Unexpected token while reading date constructor. Expected Integer, got " + reader.TokenType); + + long ticks = (long)reader.Value; + DateTime date = JsonConvert.ConvertJavaScriptTicksToDateTime(ticks); + + if (!reader.Read()) + throw new Exception("Unexpected end while reading date constructor."); + if (reader.TokenType != JsonToken.EndConstructor) + throw new Exception("Unexpected token while reading date constructor. Expected EndConstructor, got " + reader.TokenType); + + WriteValue(date); + } + + private bool IsEndToken(JsonToken token) + { + switch (token) + { + case JsonToken.EndObject: + case JsonToken.EndArray: + case JsonToken.EndConstructor: + return true; + default: + return false; + } + } + + private bool IsStartToken(JsonToken token) + { + switch (token) + { + case JsonToken.StartObject: + case JsonToken.StartArray: + case JsonToken.StartConstructor: + return true; + default: + return false; + } + } + + private void WriteEnd(JTokenType type) + { + switch (type) + { + case JTokenType.Object: + WriteEndObject(); + break; + case JTokenType.Array: + WriteEndArray(); + break; + case JTokenType.Constructor: + WriteEndConstructor(); + break; + default: + throw new JsonWriterException("Unexpected type when writing end: " + type); + } + } + + private void AutoCompleteAll() + { + while (_top > 0) + { + WriteEnd(); + } + } + + private JTokenType GetTypeForCloseToken(JsonToken token) + { + switch (token) + { + case JsonToken.EndObject: + return JTokenType.Object; + case JsonToken.EndArray: + return JTokenType.Array; + case JsonToken.EndConstructor: + return JTokenType.Constructor; + default: + throw new JsonWriterException("No type for token: " + token); + } + } + + private JsonToken GetCloseTokenForType(JTokenType type) + { + switch (type) + { + case JTokenType.Object: + return JsonToken.EndObject; + case JTokenType.Array: + return JsonToken.EndArray; + case JTokenType.Constructor: + return JsonToken.EndConstructor; + default: + throw new JsonWriterException("No close token for type: " + type); + } + } + + private void AutoCompleteClose(JsonToken tokenBeingClosed) + { + // write closing symbol and calculate new state + + int levelsToComplete = 0; + + for (int i = 0; i < _top; i++) + { + int currentLevel = _top - i; + + if (_stack[currentLevel] == GetTypeForCloseToken(tokenBeingClosed)) + { + levelsToComplete = i + 1; + break; + } + } + + if (levelsToComplete == 0) + throw new JsonWriterException("No token to close."); + + for (int i = 0; i < levelsToComplete; i++) + { + JsonToken token = GetCloseTokenForType(Pop()); + + if (_currentState != State.ObjectStart && _currentState != State.ArrayStart) + WriteIndent(); + + WriteEnd(token); + } + + JTokenType currentLevelType = Peek(); + + switch (currentLevelType) + { + case JTokenType.Object: + _currentState = State.Object; + break; + case JTokenType.Array: + _currentState = State.Array; + break; + case JTokenType.Constructor: + _currentState = State.Array; + break; + case JTokenType.None: + _currentState = State.Start; + break; + default: + throw new JsonWriterException("Unknown JsonType: " + currentLevelType); + } + } + + /// + /// Writes the specified end token. + /// + /// The end token to write. + protected virtual void WriteEnd(JsonToken token) + { + } + + /// + /// Writes indent characters. + /// + protected virtual void WriteIndent() + { + } + + /// + /// Writes the JSON value delimiter. + /// + protected virtual void WriteValueDelimiter() + { + } + + /// + /// Writes an indent space. + /// + protected virtual void WriteIndentSpace() + { + } + + internal void AutoComplete(JsonToken tokenBeingWritten) + { + int token; + + switch (tokenBeingWritten) + { + default: + token = (int)tokenBeingWritten; + break; + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.String: + case JsonToken.Boolean: + case JsonToken.Null: + case JsonToken.Undefined: + case JsonToken.Date: + case JsonToken.Bytes: + // a value is being written + token = 7; + break; + } + + // gets new state based on the current state and what is being written + State newState = stateArray[token][(int)_currentState]; + + if (newState == State.Error) + throw new JsonWriterException("Token {0} in state {1} would result in an invalid JavaScript object.".FormatWith(CultureInfo.InvariantCulture, tokenBeingWritten.ToString(), _currentState.ToString())); + + if ((_currentState == State.Object || _currentState == State.Array || _currentState == State.Constructor) && tokenBeingWritten != JsonToken.Comment) + { + WriteValueDelimiter(); + } + else if (_currentState == State.Property) + { + if (_formatting == Formatting.Indented) + WriteIndentSpace(); + } + + WriteState writeState = WriteState; + + // don't indent a property when it is the first token to be written (i.e. at the start) + if ((tokenBeingWritten == JsonToken.PropertyName && writeState != WriteState.Start) || + writeState == WriteState.Array || writeState == WriteState.Constructor) + { + WriteIndent(); + } + + _currentState = newState; + } + + #region WriteValue methods + /// + /// Writes a null value. + /// + public virtual void WriteNull() + { + AutoComplete(JsonToken.Null); + } + + /// + /// Writes an undefined value. + /// + public virtual void WriteUndefined() + { + AutoComplete(JsonToken.Undefined); + } + + /// + /// Writes raw JSON without changing the writer's state. + /// + /// The raw JSON to write. + public virtual void WriteRaw(string json) + { + } + + /// + /// Writes raw JSON where a value is expected and updates the writer's state. + /// + /// The raw JSON to write. + public virtual void WriteRawValue(string json) + { + // hack. want writer to change state as if a value had been written + AutoComplete(JsonToken.Undefined); + WriteRaw(json); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(string value) + { + AutoComplete(JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(int value) + { + AutoComplete(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(uint value) + { + AutoComplete(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(long value) + { + AutoComplete(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(ulong value) + { + AutoComplete(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(float value) + { + AutoComplete(JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(double value) + { + AutoComplete(JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(bool value) + { + AutoComplete(JsonToken.Boolean); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(short value) + { + AutoComplete(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(ushort value) + { + AutoComplete(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(char value) + { + AutoComplete(JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(byte value) + { + AutoComplete(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(sbyte value) + { + AutoComplete(JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(decimal value) + { + AutoComplete(JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(DateTime value) + { + AutoComplete(JsonToken.Date); + } + +#if !PocketPC && !NET20 + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(DateTimeOffset value) + { + AutoComplete(JsonToken.Date); + } +#endif + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(int? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(uint? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(long? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(ulong? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(float? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(double? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(bool? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(short? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(ushort? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(char? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(byte? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public virtual void WriteValue(sbyte? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(decimal? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(DateTime? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } + +#if !PocketPC && !NET20 + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(DateTimeOffset? value) + { + if (value == null) + WriteNull(); + else + WriteValue(value.Value); + } +#endif + + /// + /// Writes a value. + /// + /// The value to write. + public virtual void WriteValue(byte[] value) + { + if (value == null) + WriteNull(); + else + AutoComplete(JsonToken.Bytes); + } + + /// + /// Writes a value. + /// An error will raised if the value cannot be written as a single JSON token. + /// + /// The value to write. + public virtual void WriteValue(object value) + { + if (value == null) + { + WriteNull(); + return; + } + else if (value is IConvertible) + { + IConvertible convertible = value as IConvertible; + + switch (convertible.GetTypeCode()) + { + case TypeCode.String: + WriteValue(convertible.ToString(CultureInfo.InvariantCulture)); + return; + case TypeCode.Char: + WriteValue(convertible.ToChar(CultureInfo.InvariantCulture)); + return; + case TypeCode.Boolean: + WriteValue(convertible.ToBoolean(CultureInfo.InvariantCulture)); + return; + case TypeCode.SByte: + WriteValue(convertible.ToSByte(CultureInfo.InvariantCulture)); + return; + case TypeCode.Int16: + WriteValue(convertible.ToInt16(CultureInfo.InvariantCulture)); + return; + case TypeCode.UInt16: + WriteValue(convertible.ToUInt16(CultureInfo.InvariantCulture)); + return; + case TypeCode.Int32: + WriteValue(convertible.ToInt32(CultureInfo.InvariantCulture)); + return; + case TypeCode.Byte: + WriteValue(convertible.ToByte(CultureInfo.InvariantCulture)); + return; + case TypeCode.UInt32: + WriteValue(convertible.ToUInt32(CultureInfo.InvariantCulture)); + return; + case TypeCode.Int64: + WriteValue(convertible.ToInt64(CultureInfo.InvariantCulture)); + return; + case TypeCode.UInt64: + WriteValue(convertible.ToUInt64(CultureInfo.InvariantCulture)); + return; + case TypeCode.Single: + WriteValue(convertible.ToSingle(CultureInfo.InvariantCulture)); + return; + case TypeCode.Double: + WriteValue(convertible.ToDouble(CultureInfo.InvariantCulture)); + return; + case TypeCode.DateTime: + WriteValue(convertible.ToDateTime(CultureInfo.InvariantCulture)); + return; + case TypeCode.Decimal: + WriteValue(convertible.ToDecimal(CultureInfo.InvariantCulture)); + return; + case TypeCode.DBNull: + WriteNull(); + return; + } + } +#if !PocketPC && !NET20 + else if (value is DateTimeOffset) + { + WriteValue((DateTimeOffset)value); + return; + } +#endif + else if (value is byte[]) + { + WriteValue((byte[])value); + return; + } + + throw new ArgumentException("Unsupported type: {0}. Use the JsonSerializer class to get the object's JSON representation.".FormatWith(CultureInfo.InvariantCulture, value.GetType())); + } + #endregion + + /// + /// Writes out a comment /*...*/ containing the specified text. + /// + /// Text to place inside the comment. + public virtual void WriteComment(string text) + { + AutoComplete(JsonToken.Comment); + } + + /// + /// Writes out the given white space. + /// + /// The string of white space characters. + public virtual void WriteWhitespace(string ws) + { + if (ws != null) + { + if (!StringUtils.IsWhiteSpace(ws)) + throw new JsonWriterException("Only white space characters should be used."); + } + } + + + void IDisposable.Dispose() + { + Dispose(true); + } + + private void Dispose(bool disposing) + { + if (WriteState != WriteState.Closed) + Close(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonWriterException.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonWriterException.cs new file mode 100644 index 0000000..a906f2c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/JsonWriterException.cs @@ -0,0 +1,65 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// The exception thrown when an error occurs while reading Json text. + /// + public class JsonWriterException : Exception + { + /// + /// Initializes a new instance of the class. + /// + public JsonWriterException() + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message. + /// + /// The error message that explains the reason for the exception. + public JsonWriterException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public JsonWriterException(string message, Exception innerException) + : base(message, innerException) + { + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs new file mode 100644 index 0000000..df63d36 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/ComponentModel/JPropertyDescriptor.cs @@ -0,0 +1,173 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !SILVERLIGHT +using System; +using System.ComponentModel; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq.ComponentModel +{ + /// + /// Represents a view of a . + /// + public class JPropertyDescriptor : PropertyDescriptor + { + private readonly Type _propertyType; + + /// + /// Initializes a new instance of the class. + /// + /// The name. + /// Type of the property. + public JPropertyDescriptor(string name, Type propertyType) + : base(name, null) + { + ValidationUtils.ArgumentNotNull(name, "name"); + ValidationUtils.ArgumentNotNull(propertyType, "propertyType"); + + _propertyType = propertyType; + } + + private static JObject CastInstance(object instance) + { + return (JObject)instance; + } + + /// + /// When overridden in a derived class, returns whether resetting an object changes its value. + /// + /// + /// true if resetting the component changes its value; otherwise, false. + /// + /// The component to test for reset capability. + /// + public override bool CanResetValue(object component) + { + return false; + } + + /// + /// When overridden in a derived class, gets the current value of the property on a component. + /// + /// + /// The value of a property for a given component. + /// + /// The component with the property for which to retrieve the value. + /// + public override object GetValue(object component) + { + JToken token = CastInstance(component)[Name]; + + return token; + } + + /// + /// When overridden in a derived class, resets the value for this property of the component to the default value. + /// + /// The component with the property value that is to be reset to the default value. + /// + public override void ResetValue(object component) + { + } + + /// + /// When overridden in a derived class, sets the value of the component to a different value. + /// + /// The component with the property value that is to be set. + /// The new value. + /// + public override void SetValue(object component, object value) + { + JToken token = (value is JToken) ? (JToken) value : new JValue(value); + + CastInstance(component)[Name] = token; + } + + /// + /// When overridden in a derived class, determines a value indicating whether the value of this property needs to be persisted. + /// + /// + /// true if the property should be persisted; otherwise, false. + /// + /// The component with the property to be examined for persistence. + /// + public override bool ShouldSerializeValue(object component) + { + return false; + } + + /// + /// When overridden in a derived class, gets the type of the component this property is bound to. + /// + /// + /// A that represents the type of component this property is bound to. When the or methods are invoked, the object specified might be an instance of this type. + /// + public override Type ComponentType + { + get { return typeof(JObject); } + } + + /// + /// When overridden in a derived class, gets a value indicating whether this property is read-only. + /// + /// + /// true if the property is read-only; otherwise, false. + /// + public override bool IsReadOnly + { + get { return false; } + } + + /// + /// When overridden in a derived class, gets the type of the property. + /// + /// + /// A that represents the type of the property. + /// + public override Type PropertyType + { + get { return _propertyType; } + } + + /// + /// Gets the hash code for the name of the member. + /// + /// + /// + /// The hash code for the name of the member. + /// + protected override int NameHashCode + { + get + { + // override property to fix up an error in its documentation + int nameHashCode = base.NameHashCode; + return nameHashCode; + } + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/Extensions.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/Extensions.cs new file mode 100644 index 0000000..0b737b2 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/Extensions.cs @@ -0,0 +1,316 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; +using System.Collections; +using System.Globalization; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Contains the LINQ to JSON extension methods. + /// + public static class Extensions + { + /// + /// Returns a collection of tokens that contains the ancestors of every token in the source collection. + /// + /// The type of the objects in source, constrained to . + /// An of that contains the source collection. + /// An of that contains the ancestors of every node in the source collection. + public static IJEnumerable Ancestors(this IEnumerable source) where T : JToken + { + ValidationUtils.ArgumentNotNull(source, "source"); + + return source.SelectMany(j => j.Ancestors()).AsJEnumerable(); + } + + //TODO + //public static IEnumerable AncestorsAndSelf(this IEnumerable source) where T : JObject + //{ + // ValidationUtils.ArgumentNotNull(source, "source"); + + // return source.SelectMany(j => j.AncestorsAndSelf()); + //} + + /// + /// Returns a collection of tokens that contains the descendants of every token in the source collection. + /// + /// The type of the objects in source, constrained to . + /// An of that contains the source collection. + /// An of that contains the descendants of every node in the source collection. + public static IJEnumerable Descendants(this IEnumerable source) where T : JContainer + { + ValidationUtils.ArgumentNotNull(source, "source"); + + return source.SelectMany(j => j.Descendants()).AsJEnumerable(); + } + + //TODO + //public static IEnumerable DescendantsAndSelf(this IEnumerable source) where T : JContainer + //{ + // ValidationUtils.ArgumentNotNull(source, "source"); + + // return source.SelectMany(j => j.DescendantsAndSelf()); + //} + + /// + /// Returns a collection of child properties of every object in the source collection. + /// + /// An of that contains the source collection. + /// An of that contains the properties of every object in the source collection. + public static IJEnumerable Properties(this IEnumerable source) + { + ValidationUtils.ArgumentNotNull(source, "source"); + + return source.SelectMany(d => d.Properties()).AsJEnumerable(); + } + + /// + /// Returns a collection of child values of every object in the source collection with the given key. + /// + /// An of that contains the source collection. + /// The token key. + /// An of that contains the values of every node in the source collection with the given key. + public static IJEnumerable Values(this IEnumerable source, object key) + { + return Values(source, key).AsJEnumerable(); + } + + /// + /// Returns a collection of child values of every object in the source collection. + /// + /// An of that contains the source collection. + /// An of that contains the values of every node in the source collection. + public static IJEnumerable Values(this IEnumerable source) + { + return source.Values(null); + } + + /// + /// Returns a collection of converted child values of every object in the source collection with the given key. + /// + /// The type to convert the values to. + /// An of that contains the source collection. + /// The token key. + /// An that contains the converted values of every node in the source collection with the given key. + public static IEnumerable Values(this IEnumerable source, object key) + { + return Values(source, key); + } + + /// + /// Returns a collection of converted child values of every object in the source collection. + /// + /// The type to convert the values to. + /// An of that contains the source collection. + /// An that contains the converted values of every node in the source collection. + public static IEnumerable Values(this IEnumerable source) + { + return Values(source, null); + } + + /// + /// Converts the value. + /// + /// The type to convert the value to. + /// A cast as a of . + /// A converted value. + public static U Value(this IEnumerable value) + { + return value.Value(); + } + + /// + /// Converts the value. + /// + /// The source collection type. + /// The type to convert the value to. + /// A cast as a of . + /// A converted value. + public static U Value(this IEnumerable value) where T : JToken + { + ValidationUtils.ArgumentNotNull(value, "source"); + + JToken token = value as JToken; + if (token == null) + throw new ArgumentException("Source value must be a JToken."); + + return token.Convert(); + } + + + internal static IEnumerable Values(this IEnumerable source, object key) where T : JToken + { + ValidationUtils.ArgumentNotNull(source, "source"); + + foreach (JToken token in source) + { + if (key == null) + { + if (token is JValue) + { + yield return Convert((JValue)token); + } + else + { + foreach (JToken t in token.Children()) + { + yield return t.Convert(); ; + } + } + } + else + { + JToken value = token[key]; + if (value != null) + yield return value.Convert(); + } + } + + yield break; + } + + //TODO + //public static IEnumerable InDocumentOrder(this IEnumerable source) where T : JObject; + + //public static IEnumerable Children(this IEnumerable source) where T : JToken + //{ + // ValidationUtils.ArgumentNotNull(source, "source"); + + // return source.SelectMany(c => c.Children()); + //} + + /// + /// Returns a collection of child tokens of every array in the source collection. + /// + /// The source collection type. + /// An of that contains the source collection. + /// An of that contains the values of every node in the source collection. + public static IJEnumerable Children(this IEnumerable source) where T : JToken + { + return Children(source).AsJEnumerable(); + } + + /// + /// Returns a collection of converted child tokens of every array in the source collection. + /// + /// An of that contains the source collection. + /// The type to convert the values to. + /// The source collection type. + /// An that contains the converted values of every node in the source collection. + public static IEnumerable Children(this IEnumerable source) where T : JToken + { + ValidationUtils.ArgumentNotNull(source, "source"); + + return source.SelectMany(c => c.Children()).Convert(); + } + + internal static IEnumerable Convert(this IEnumerable source) where T : JToken + { + ValidationUtils.ArgumentNotNull(source, "source"); + + bool cast = typeof(JToken).IsAssignableFrom(typeof(U)); + + foreach (JToken token in source) + { + yield return Convert(token, cast); + } + } + + internal static U Convert(this T token) where T : JToken + { + bool cast = typeof(JToken).IsAssignableFrom(typeof(U)); + + return Convert(token, cast); + } + + internal static U Convert(this T token, bool cast) where T : JToken + { + if (cast) + { + // HACK + return (U)(object)token; + } + else + { + if (token == null) + return default(U); + + JValue value = token as JValue; + if (value == null) + throw new InvalidCastException("Cannot cast {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, token.GetType(), typeof(T))); + + if (value.Value is U) + return (U)value.Value; + + Type targetType = typeof(U); + + if (ReflectionUtils.IsNullableType(targetType)) + { + if (value.Value == null) + return default(U); + + targetType = Nullable.GetUnderlyingType(targetType); + } + + return (U)System.Convert.ChangeType(value.Value, targetType, CultureInfo.InvariantCulture); + } + } + + //TODO + //public static void Remove(this IEnumerable source) where T : JContainer; + + /// + /// Returns the input typed as . + /// + /// An of that contains the source collection. + /// The input typed as . + public static IJEnumerable AsJEnumerable(this IEnumerable source) + { + return source.AsJEnumerable(); + } + + /// + /// Returns the input typed as . + /// + /// The source collection type. + /// An of that contains the source collection. + /// The input typed as . + public static IJEnumerable AsJEnumerable(this IEnumerable source) where T : JToken + { + if (source == null) + return null; + else if (source is IJEnumerable) + return (IJEnumerable)source; + else + return new JEnumerable(source); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/IJEnumerable.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/IJEnumerable.cs new file mode 100644 index 0000000..09cdb95 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/IJEnumerable.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a collection of objects. + /// + /// The type of token + public interface IJEnumerable< +#if !(NET20 || NET35 || SILVERLIGHT) + out +#endif + T> : IEnumerable where T : JToken + { + /// + /// Gets the with the specified key. + /// + /// + IJEnumerable this[object key] { get; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JArray.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JArray.cs new file mode 100644 index 0000000..3eea72c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JArray.cs @@ -0,0 +1,324 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; +using System.IO; +using System.Globalization; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a JSON array. + /// + public class JArray : JContainer, IList + { + /// + /// Gets the node type for this . + /// + /// The type. + public override JTokenType Type + { + get { return JTokenType.Array; } + } + + /// + /// Initializes a new instance of the class. + /// + public JArray() + { + } + + /// + /// Initializes a new instance of the class from another object. + /// + /// A object to copy from. + public JArray(JArray other) + : base(other) + { + } + + /// + /// Initializes a new instance of the class with the specified content. + /// + /// The contents of the array. + public JArray(params object[] content) + : this((object)content) + { + } + + /// + /// Initializes a new instance of the class with the specified content. + /// + /// The contents of the array. + public JArray(object content) + { + Add(content); + } + + internal override bool DeepEquals(JToken node) + { + JArray t = node as JArray; + return (t != null && ContentsEqual(t)); + } + + internal override JToken CloneToken() + { + return new JArray(this); + } + + /// + /// Loads an from a . + /// + /// A that will be read for the content of the . + /// A that contains the JSON that was read from the specified . + public static new JArray Load(JsonReader reader) + { + if (reader.TokenType == JsonToken.None) + { + if (!reader.Read()) + throw new Exception("Error reading JArray from JsonReader."); + } + if (reader.TokenType != JsonToken.StartArray) + throw new Exception("Error reading JArray from JsonReader. Current JsonReader item is not an array: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + JArray a = new JArray(); + a.SetLineInfo(reader as IJsonLineInfo); + + a.ReadTokenFrom(reader); + + return a; + } + + /// + /// Load a from a string that contains JSON. + /// + /// A that contains JSON. + /// A populated from the string that contains JSON. + public static new JArray Parse(string json) + { + JsonReader jsonReader = new JsonTextReader(new StringReader(json)); + + return Load(jsonReader); + } + + /// + /// Creates a from an object. + /// + /// The object that will be used to create . + /// A with the values of the specified object + public static new JArray FromObject(object o) + { + return FromObject(o, new JsonSerializer()); + } + + /// + /// Creates a from an object. + /// + /// The object that will be used to create . + /// The that will be used to read the object. + /// A with the values of the specified object + public static new JArray FromObject(object o, JsonSerializer jsonSerializer) + { + JToken token = FromObjectInternal(o, jsonSerializer); + + if (token.Type != JTokenType.Array) + throw new ArgumentException("Object serialized to {0}. JArray instance expected.".FormatWith(CultureInfo.InvariantCulture, token.Type)); + + return (JArray)token; + } + + /// + /// Writes this token to a . + /// + /// A into which this method will write. + /// A collection of which will be used when writing the token. + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) + { + writer.WriteStartArray(); + + foreach (JToken token in Children()) + { + token.WriteTo(writer, converters); + } + + writer.WriteEndArray(); + } + + /// + /// Gets the with the specified key. + /// + /// The with the specified key. + public override JToken this[object key] + { + get + { + ValidationUtils.ArgumentNotNull(key, "o"); + + if (!(key is int)) + throw new ArgumentException("Accessed JArray values with invalid key value: {0}. Array position index expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); + + return GetItem((int)key); + } + set + { + ValidationUtils.ArgumentNotNull(key, "o"); + + if (!(key is int)) + throw new ArgumentException("Set JArray values with invalid key value: {0}. Array position index expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); + + SetItem((int)key, value); + } + } + + /// + /// Gets or sets the at the specified index. + /// + /// + public JToken this[int index] + { + get { return GetItem(index); } + set { SetItem(index, value); } + } + + #region IList Members + + /// + /// Determines the index of a specific item in the . + /// + /// The object to locate in the . + /// + /// The index of if found in the list; otherwise, -1. + /// + public int IndexOf(JToken item) + { + return IndexOfItem(item); + } + + /// + /// Inserts an item to the at the specified index. + /// + /// The zero-based index at which should be inserted. + /// The object to insert into the . + /// + /// is not a valid index in the . + /// The is read-only. + public void Insert(int index, JToken item) + { + InsertItem(index, item); + } + + /// + /// Removes the item at the specified index. + /// + /// The zero-based index of the item to remove. + /// + /// is not a valid index in the . + /// The is read-only. + public void RemoveAt(int index) + { + RemoveItemAt(index); + } + + #endregion + + #region ICollection Members + + /// + /// Adds an item to the . + /// + /// The object to add to the . + /// The is read-only. + public void Add(JToken item) + { + Add((object)item); + } + + /// + /// Removes all items from the . + /// + /// The is read-only. + public void Clear() + { + ClearItems(); + } + + /// + /// Determines whether the contains a specific value. + /// + /// The object to locate in the . + /// + /// true if is found in the ; otherwise, false. + /// + public bool Contains(JToken item) + { + return ContainsItem(item); + } + + void ICollection.CopyTo(JToken[] array, int arrayIndex) + { + CopyItemsTo(array, arrayIndex); + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + public int Count + { + get { return CountItems(); } + } + + bool ICollection.IsReadOnly + { + get { return false; } + } + + /// + /// Removes the first occurrence of a specific object from the . + /// + /// The object to remove from the . + /// + /// true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the original . + /// + /// The is read-only. + public bool Remove(JToken item) + { + return RemoveItem(item); + } + + #endregion + + internal override int GetDeepHashCode() + { + return ContentsHashCode(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JConstructor.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JConstructor.cs new file mode 100644 index 0000000..b75d605 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JConstructor.cs @@ -0,0 +1,193 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a JSON constructor. + /// + public class JConstructor : JContainer + { + private string _name; + + /// + /// Gets or sets the name of this constructor. + /// + /// The constructor name. + public string Name + { + get { return _name; } + set { _name = value; } + } + + /// + /// Gets the node type for this . + /// + /// The type. + public override JTokenType Type + { + get { return JTokenType.Constructor; } + } + + /// + /// Initializes a new instance of the class. + /// + public JConstructor() + { + } + + /// + /// Initializes a new instance of the class from another object. + /// + /// A object to copy from. + public JConstructor(JConstructor other) + : base(other) + { + _name = other.Name; + } + + /// + /// Initializes a new instance of the class with the specified name and content. + /// + /// The constructor name. + /// The contents of the constructor. + public JConstructor(string name, params object[] content) + : this(name, (object)content) + { + } + + /// + /// Initializes a new instance of the class with the specified name and content. + /// + /// The constructor name. + /// The contents of the constructor. + public JConstructor(string name, object content) + : this(name) + { + Add(content); + } + + /// + /// Initializes a new instance of the class with the specified name. + /// + /// The constructor name. + public JConstructor(string name) + { + ValidationUtils.ArgumentNotNullOrEmpty(name, "name"); + + _name = name; + } + + internal override bool DeepEquals(JToken node) + { + JConstructor c = node as JConstructor; + return (c != null && _name == c.Name && ContentsEqual(c)); + } + + internal override JToken CloneToken() + { + return new JConstructor(this); + } + + /// + /// Writes this token to a . + /// + /// A into which this method will write. + /// A collection of which will be used when writing the token. + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) + { + writer.WriteStartConstructor(_name); + + foreach (JToken token in Children()) + { + token.WriteTo(writer, converters); + } + + writer.WriteEndConstructor(); + } + + /// + /// Gets the with the specified key. + /// + /// The with the specified key. + public override JToken this[object key] + { + get + { + ValidationUtils.ArgumentNotNull(key, "o"); + + if (!(key is int)) + throw new ArgumentException("Accessed JConstructor values with invalid key value: {0}. Argument position index expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); + + return GetItem((int)key); + } + set + { + ValidationUtils.ArgumentNotNull(key, "o"); + + if (!(key is int)) + throw new ArgumentException("Set JConstructor values with invalid key value: {0}. Argument position index expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); + + SetItem((int)key, value); + } + } + + internal override int GetDeepHashCode() + { + return _name.GetHashCode() ^ ContentsHashCode(); + } + + /// + /// Loads an from a . + /// + /// A that will be read for the content of the . + /// A that contains the JSON that was read from the specified . + public static new JConstructor Load(JsonReader reader) + { + if (reader.TokenType == JsonToken.None) + { + if (!reader.Read()) + throw new Exception("Error reading JConstructor from JsonReader."); + } + + if (reader.TokenType != JsonToken.StartConstructor) + throw new Exception("Error reading JConstructor from JsonReader. Current JsonReader item is not a constructor: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + + JConstructor c = new JConstructor((string)reader.Value); + c.SetLineInfo(reader as IJsonLineInfo); + + c.ReadTokenFrom(reader); + + return c; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JContainer.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JContainer.cs new file mode 100644 index 0000000..954df2a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JContainer.cs @@ -0,0 +1,1069 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Newtonsoft.Json.Utilities; +using System.Collections; +using System.Diagnostics; +using System.Globalization; +using System.ComponentModel; +using System.Collections.Specialized; +#if !SILVERLIGHT +using Newtonsoft.Json.Linq.ComponentModel; +#endif + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a token that can contain other tokens. + /// + public abstract class JContainer : JToken, IList +#if !SILVERLIGHT + , ITypedList, IBindingList +#else + , IList, INotifyCollectionChanged +#endif +#if !(SILVERLIGHT || NET20 || NET35) + , INotifyCollectionChanged +#endif + { +#if !SILVERLIGHT + /// + /// Occurs when the list changes or an item in the list changes. + /// + public event ListChangedEventHandler ListChanged; + + /// + /// Occurs before an item is added to the collection. + /// + public event AddingNewEventHandler AddingNew; +#endif +#if SILVERLIGHT || !(NET20 || NET35) + /// + /// Occurs when the items list of the collection has changed, or the collection is reset. + /// + public event NotifyCollectionChangedEventHandler CollectionChanged; +#endif + + private JToken _content; + private object _syncRoot; + private bool _busy; + + internal JToken Content + { + get { return _content; } + set { _content = value; } + } + + internal JContainer() + { + } + + internal JContainer(JContainer other) + { + ValidationUtils.ArgumentNotNull(other, "c"); + + JToken content = other.Last; + if (content != null) + { + do + { + content = content._next; + Add(content.CloneToken()); + } + while (content != other.Last); + } + } + + internal void CheckReentrancy() + { + if (_busy) + throw new InvalidOperationException("Cannot change {0} during a collection change event.".FormatWith(CultureInfo.InvariantCulture, GetType())); + } + + #if !SILVERLIGHT + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnAddingNew(AddingNewEventArgs e) + { + AddingNewEventHandler handler = AddingNew; + if (handler != null) + handler(this, e); + } + + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnListChanged(ListChangedEventArgs e) + { + ListChangedEventHandler handler = ListChanged; + + if (handler != null) + { + _busy = true; + try + { + handler(this, e); + } + finally + { + _busy = false; + } + } + } +#endif +#if SILVERLIGHT || !(NET20 || NET35) + /// + /// Raises the event. + /// + /// The instance containing the event data. + protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) + { + NotifyCollectionChangedEventHandler handler = CollectionChanged; + + if (handler != null) + { + _busy = true; + try + { + handler(this, e); + } + finally + { + _busy = false; + } + } + } +#endif + + /// + /// Gets a value indicating whether this token has childen tokens. + /// + /// + /// true if this token has child values; otherwise, false. + /// + public override bool HasValues + { + get { return (_content != null); } + } + + internal bool ContentsEqual(JContainer container) + { + JToken t1 = First; + JToken t2 = container.First; + + if (t1 == t2) + return true; + + do + { + if (t1 == null && t2 == null) + return true; + + if (t1 != null && t2 != null && t1.DeepEquals(t2)) + { + t1 = (t1 != Last) ? t1.Next : null; + t2 = (t2 != container.Last) ? t2.Next : null; + } + else + { + return false; + } + } + while (true); + } + + /// + /// Get the first child token of this token. + /// + /// + /// A containing the first child token of the . + /// + public override JToken First + { + get + { + if (Last == null) + return null; + + return Last._next; + } + } + + /// + /// Get the last child token of this token. + /// + /// + /// A containing the last child token of the . + /// + public override JToken Last + { + [DebuggerStepThrough] + get { return _content; } + } + + /// + /// Returns a collection of the child tokens of this token, in document order. + /// + /// + /// An of containing the child tokens of this , in document order. + /// + public override JEnumerable Children() + { + return new JEnumerable(ChildrenInternal()); + } + + internal IEnumerable ChildrenInternal() + { + JToken first = First; + JToken current = first; + if (current == null) + yield break; + + do + { + yield return current; + } + while ((current = current.Next) != null); + } + + /// + /// Returns a collection of the child values of this token, in document order. + /// + /// The type to convert the values to. + /// + /// A containing the child values of this , in document order. + /// + public override IEnumerable Values() + { + return Children().Convert(); + } + + /// + /// Returns a collection of the descendant tokens for this token in document order. + /// + /// An containing the descendant tokens of the . + public IEnumerable Descendants() + { + foreach (JToken o in Children()) + { + yield return o; + JContainer c = o as JContainer; + if (c != null) + { + foreach (JToken d in c.Descendants()) + { + yield return d; + } + } + } + } + + internal bool IsMultiContent(object content) + { + return (content is IEnumerable && !(content is string) && !(content is JToken) && !(content is byte[])); + } + + internal virtual void AddItem(bool isLast, JToken previous, JToken item) + { + CheckReentrancy(); + + ValidateToken(item, null); + + item = EnsureParentToken(item); + + JToken next = (previous != null) ? previous._next : item; + + item.Parent = this; + item.Next = next; + + if (previous != null) + previous.Next = item; + + if (isLast || previous == null) + _content = item; + +#if !SILVERLIGHT + if (ListChanged != null) + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemAdded, IndexOfItem(item))); +#endif +#if SILVERLIGHT || !(NET20 || NET35) + if (CollectionChanged != null) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, IndexOfItem(item))); +#endif + } + + internal JToken EnsureParentToken(JToken item) + { + if (item.Parent != null) + { + item = item.CloneToken(); + } + else + { + // check whether attempting to add a token to itself + JContainer parent = this; + while (parent.Parent != null) + { + parent = parent.Parent; + } + if (item == parent) + { + item = item.CloneToken(); + } + } + return item; + } + + internal void AddInternal(bool isLast, JToken previous, object content) + { + if (IsMultiContent(content)) + { + IEnumerable enumerable = (IEnumerable) content; + + JToken multiPrevious = previous; + foreach (object c in enumerable) + { + AddInternal(isLast, multiPrevious, c); + multiPrevious = (multiPrevious != null) ? multiPrevious._next : Last; + } + } + else + { + JToken item = CreateFromContent(content); + + AddItem(isLast, previous, item); + } + } + + internal int IndexOfItem(JToken item) + { + int index = 0; + foreach (JToken token in Children()) + { + if (token == item) + return index; + + index++; + } + + return -1; + } + + internal virtual void InsertItem(int index, JToken item) + { + if (index == 0) + { + AddFirst(item); + } + else + { + JToken token = GetItem(index); + AddInternal(false, token.Previous, item); + } + } + + internal virtual void RemoveItemAt(int index) + { + if (index < 0) + throw new ArgumentOutOfRangeException("index", "index is less than 0."); + + CheckReentrancy(); + + int currentIndex = 0; + foreach (JToken token in Children()) + { + if (index == currentIndex) + { + token.Remove(); + +#if !SILVERLIGHT + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, index)); +#endif +#if SILVERLIGHT || !(NET20 || NET35) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, token, index)); +#endif + + return; + } + + currentIndex++; + } + + throw new ArgumentOutOfRangeException("index", "index is equal to or greater than Count."); + } + + internal virtual bool RemoveItem(JToken item) + { + if (item == null || item.Parent != this) + return false; + + CheckReentrancy(); + + JToken content = _content; + + int itemIndex = 0; + while (content._next != item) + { + itemIndex++; + content = content._next; + } + if (content == item) + { + // token is containers last child + _content = null; + } + else + { + if (_content == item) + { + _content = content; + } + content._next = item._next; + } + item.Parent = null; + item.Next = null; + +#if !SILVERLIGHT + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemDeleted, itemIndex)); +#endif +#if SILVERLIGHT || !(NET20 || NET35) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, itemIndex)); +#endif + + return true; + } + + internal virtual JToken GetItem(int index) + { + return Children().ElementAt(index); + } + + internal virtual void SetItem(int index, JToken item) + { + CheckReentrancy(); + + JToken token = GetItem(index); + token.Replace(item); + +#if !SILVERLIGHT + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, index)); +#endif +#if SILVERLIGHT || !(NET20 || NET35) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, item, token, index)); +#endif + } + + internal virtual void ClearItems() + { + CheckReentrancy(); + + while (_content != null) + { + JToken o = _content; + + JToken next = o._next; + if (o != _content || next != o._next) + throw new InvalidOperationException("This operation was corrupted by external code."); + + if (next != o) + o._next = next._next; + else + _content = null; + + next.Parent = null; + next._next = null; + } + +#if !SILVERLIGHT + OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1)); +#endif +#if SILVERLIGHT || !(NET20 || NET35) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); +#endif + } + + internal virtual void ReplaceItem(JToken existing, JToken replacement) + { + if (existing == null || existing.Parent != this) + return; + + if (IsTokenUnchanged(existing, replacement)) + return; + + CheckReentrancy(); + + replacement = EnsureParentToken(replacement); + + ValidateToken(replacement, existing); + + JToken content = _content; + + int itemIndex = 0; + while (content._next != existing) + { + itemIndex++; + content = content._next; + } + + if (content == existing) + { + // token is containers last child + _content = replacement; + replacement._next = replacement; + } + else + { + if (_content == existing) + { + _content = replacement; + } + content._next = replacement; + replacement._next = existing._next; + } + + replacement.Parent = this; + + existing.Parent = null; + existing.Next = null; + +#if !SILVERLIGHT + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, itemIndex)); +#endif +#if SILVERLIGHT || !(NET20 || NET35) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, replacement, existing, itemIndex)); +#endif + } + + internal virtual bool ContainsItem(JToken item) + { + return (IndexOfItem(item) != -1); + } + + internal virtual void CopyItemsTo(Array array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException("array"); + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException("arrayIndex", "arrayIndex is less than 0."); + if (arrayIndex >= array.Length) + throw new ArgumentException("arrayIndex is equal to or greater than the length of array."); + if (CountItems() > array.Length - arrayIndex) + throw new ArgumentException("The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array."); + + int index = 0; + foreach (JToken token in Children()) + { + array.SetValue(token, arrayIndex + index); + index++; + } + } + + internal virtual int CountItems() + { + return Children().Count(); + } + + internal static bool IsTokenUnchanged(JToken currentValue, JToken newValue) + { + JValue v1 = currentValue as JValue; + if (v1 != null) + { + // null will get turned into a JValue of type null + if (v1.Type == JTokenType.Null && newValue == null) + return true; + + return v1.Equals(newValue); + } + + return false; + } + + internal virtual void ValidateToken(JToken o, JToken existing) + { + ValidationUtils.ArgumentNotNull(o, "o"); + + if (o.Type == JTokenType.Property) + throw new ArgumentException("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType())); + } + + /// + /// Adds the specified content as children of this . + /// + /// The content to be added. + public void Add(object content) + { + AddInternal(true, Last, content); + } + + /// + /// Adds the specified content as the first children of this . + /// + /// The content to be added. + public void AddFirst(object content) + { + AddInternal(false, Last, content); + } + + internal JToken CreateFromContent(object content) + { + if (content is JToken) + return (JToken)content; + + return new JValue(content); + } + + /// + /// Creates an that can be used to add tokens to the . + /// + /// An that is ready to have content written to it. + public JsonWriter CreateWriter() + { + return new JTokenWriter(this); + } + + /// + /// Replaces the children nodes of this token with the specified content. + /// + /// The content. + public void ReplaceAll(object content) + { + ClearItems(); + Add(content); + } + + /// + /// Removes the child nodes from this token. + /// + public void RemoveAll() + { + ClearItems(); + } + + internal void ReadTokenFrom(JsonReader r) + { + int startDepth = r.Depth; + + if (!r.Read()) + throw new Exception("Error reading {0} from JsonReader.".FormatWith(CultureInfo.InvariantCulture, GetType().Name)); + + ReadContentFrom(r); + + int endDepth = r.Depth; + + if (endDepth > startDepth) + throw new Exception("Unexpected end of content while loading {0}.".FormatWith(CultureInfo.InvariantCulture, GetType().Name)); + } + + internal void ReadContentFrom(JsonReader r) + { + ValidationUtils.ArgumentNotNull(r, "r"); + IJsonLineInfo lineInfo = r as IJsonLineInfo; + + JContainer parent = this; + + do + { + if (parent is JProperty && ((JProperty)parent).Value != null) + { + if (parent == this) + return; + + parent = parent.Parent; + } + + switch (r.TokenType) + { + case JsonToken.None: + // new reader. move to actual content + break; + case JsonToken.StartArray: + JArray a = new JArray(); + a.SetLineInfo(lineInfo); + parent.Add(a); + parent = a; + break; + + case JsonToken.EndArray: + if (parent == this) + return; + + parent = parent.Parent; + break; + case JsonToken.StartObject: + JObject o = new JObject(); + o.SetLineInfo(lineInfo); + parent.Add(o); + parent = o; + break; + case JsonToken.EndObject: + if (parent == this) + return; + + parent = parent.Parent; + break; + case JsonToken.StartConstructor: + JConstructor constructor = new JConstructor(r.Value.ToString()); + constructor.SetLineInfo(constructor); + parent.Add(constructor); + parent = constructor; + break; + case JsonToken.EndConstructor: + if (parent == this) + return; + + parent = parent.Parent; + break; + case JsonToken.String: + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.Date: + case JsonToken.Boolean: + case JsonToken.Bytes: + JValue v = new JValue(r.Value); + v.SetLineInfo(lineInfo); + parent.Add(v); + break; + case JsonToken.Comment: + v = JValue.CreateComment(r.Value.ToString()); + v.SetLineInfo(lineInfo); + parent.Add(v); + break; + case JsonToken.Null: + v = new JValue(null, JTokenType.Null); + v.SetLineInfo(lineInfo); + parent.Add(v); + break; + case JsonToken.Undefined: + v = new JValue(null, JTokenType.Undefined); + v.SetLineInfo(lineInfo); + parent.Add(v); + break; + case JsonToken.PropertyName: + string propertyName = r.Value.ToString(); + JProperty property = new JProperty(propertyName); + property.SetLineInfo(lineInfo); + JObject parentObject = (JObject) parent; + // handle multiple properties with the same name in JSON + JProperty existingPropertyWithName = parentObject.Property(propertyName); + if (existingPropertyWithName == null) + parent.Add(property); + else + existingPropertyWithName.Replace(property); + parent = property; + break; + default: + throw new InvalidOperationException("The JsonReader should not be on a token of type {0}.".FormatWith(CultureInfo.InvariantCulture, r.TokenType)); + } + } + while (r.Read()); + } + + internal int ContentsHashCode() + { + int hashCode = 0; + foreach (JToken item in Children()) + { + hashCode ^= item.GetDeepHashCode(); + } + return hashCode; + } + +#if !SILVERLIGHT + string ITypedList.GetListName(PropertyDescriptor[] listAccessors) + { + return string.Empty; + } + + PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors) + { + ICustomTypeDescriptor d = First as ICustomTypeDescriptor; + if (d != null) + return d.GetProperties(); + + return null; + } +#endif + + #region IList Members + + int IList.IndexOf(JToken item) + { + return IndexOfItem(item); + } + + void IList.Insert(int index, JToken item) + { + InsertItem(index, item); + } + + void IList.RemoveAt(int index) + { + RemoveItemAt(index); + } + + JToken IList.this[int index] + { + get { return GetItem(index); } + set { SetItem(index, value); } + } + + #endregion + + #region ICollection Members + + void ICollection.Add(JToken item) + { + Add(item); + } + + void ICollection.Clear() + { + ClearItems(); + } + + bool ICollection.Contains(JToken item) + { + return ContainsItem(item); + } + + void ICollection.CopyTo(JToken[] array, int arrayIndex) + { + CopyItemsTo(array, arrayIndex); + } + + int ICollection.Count + { + get { return CountItems(); } + } + + bool ICollection.IsReadOnly + { + get { return false; } + } + + bool ICollection.Remove(JToken item) + { + return RemoveItem(item); + } + + #endregion + + private JToken EnsureValue(object value) + { + if (value == null) + return null; + + if (value is JToken) + return (JToken) value; + + throw new ArgumentException("Argument is not a JToken."); + } + + #region IList Members + + int IList.Add(object value) + { + Add(EnsureValue(value)); + return CountItems() - 1; + } + + void IList.Clear() + { + ClearItems(); + } + + bool IList.Contains(object value) + { + return ContainsItem(EnsureValue(value)); + } + + int IList.IndexOf(object value) + { + return IndexOfItem(EnsureValue(value)); + } + + void IList.Insert(int index, object value) + { + InsertItem(index, EnsureValue(value)); + } + + bool IList.IsFixedSize + { + get { return false; } + } + + bool IList.IsReadOnly + { + get { return false; } + } + + void IList.Remove(object value) + { + RemoveItem(EnsureValue(value)); + } + + void IList.RemoveAt(int index) + { + RemoveItemAt(index); + } + + object IList.this[int index] + { + get { return GetItem(index); } + set { SetItem(index, EnsureValue(value)); } + } + + #endregion + + #region ICollection Members + + void ICollection.CopyTo(Array array, int index) + { + CopyItemsTo(array, index); + } + + int ICollection.Count + { + get { return CountItems(); } + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + Interlocked.CompareExchange(ref _syncRoot, new object(), null); + + return _syncRoot; + } + + } + + #endregion + + #region IBindingList Members + +#if !SILVERLIGHT + void IBindingList.AddIndex(PropertyDescriptor property) + { + } + + object IBindingList.AddNew() + { + AddingNewEventArgs args = new AddingNewEventArgs(); + OnAddingNew(args); + + if (args.NewObject == null) + throw new Exception("Could not determine new value to add to '{0}'.".FormatWith(CultureInfo.InvariantCulture, GetType())); + + if (!(args.NewObject is JToken)) + throw new Exception("New item to be added to collection must be compatible with {0}.".FormatWith(CultureInfo.InvariantCulture, typeof (JToken))); + + JToken newItem = (JToken)args.NewObject; + Add(newItem); + + return newItem; + } + + bool IBindingList.AllowEdit + { + get { return true; } + } + + bool IBindingList.AllowNew + { + get { return true; } + } + + bool IBindingList.AllowRemove + { + get { return true; } + } + + void IBindingList.ApplySort(PropertyDescriptor property, ListSortDirection direction) + { + throw new NotSupportedException(); + } + + int IBindingList.Find(PropertyDescriptor property, object key) + { + throw new NotSupportedException(); + } + + bool IBindingList.IsSorted + { + get { return false; } + } + + void IBindingList.RemoveIndex(PropertyDescriptor property) + { + } + + void IBindingList.RemoveSort() + { + throw new NotSupportedException(); + } + + ListSortDirection IBindingList.SortDirection + { + get { return ListSortDirection.Ascending; } + } + + PropertyDescriptor IBindingList.SortProperty + { + get { return null; } + } + + bool IBindingList.SupportsChangeNotification + { + get { return true; } + } + + bool IBindingList.SupportsSearching + { + get { return false; } + } + + bool IBindingList.SupportsSorting + { + get { return false; } + } +#endif + + #endregion + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JEnumerable.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JEnumerable.cs new file mode 100644 index 0000000..44ca165 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JEnumerable.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; +using System.Collections; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a collection of objects. + /// + /// The type of token + public struct JEnumerable : IJEnumerable where T : JToken + { + /// + /// An empty collection of objects. + /// + public static readonly JEnumerable Empty = new JEnumerable(Enumerable.Empty()); + + private IEnumerable _enumerable; + + /// + /// Initializes a new instance of the struct. + /// + /// The enumerable. + public JEnumerable(IEnumerable enumerable) + { + ValidationUtils.ArgumentNotNull(enumerable, "enumerable"); + + _enumerable = enumerable; + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + return _enumerable.GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + /// + /// Gets the with the specified key. + /// + /// + public IJEnumerable this[object key] + { + get { return new JEnumerable(Extensions.Values(_enumerable, key)); } + } + + /// + /// Determines whether the specified is equal to this instance. + /// + /// The to compare with this instance. + /// + /// true if the specified is equal to this instance; otherwise, false. + /// + public override bool Equals(object obj) + { + if (obj is JEnumerable) + return _enumerable.Equals(((JEnumerable)obj)._enumerable); + + return false; + } + + /// + /// Returns a hash code for this instance. + /// + /// + /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. + /// + public override int GetHashCode() + { + return _enumerable.GetHashCode(); + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JObject.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JObject.cs new file mode 100644 index 0000000..1691351 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JObject.cs @@ -0,0 +1,698 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +#if !(NET35 || NET20 || WINDOWS_PHONE) +using System.Dynamic; +using System.Linq.Expressions; +#endif +using System.Linq; +using System.IO; +using Newtonsoft.Json.Utilities; +using System.Globalization; +#if !PocketPC && !SILVERLIGHT +using Newtonsoft.Json.Linq.ComponentModel; +#endif + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a JSON object. + /// + public class JObject : JContainer, IDictionary, INotifyPropertyChanged +#if !(PocketPC || SILVERLIGHT) + , ICustomTypeDescriptor +#endif +#if !(PocketPC || SILVERLIGHT || NET20) + , INotifyPropertyChanging +#endif + { + /// + /// Occurs when a property value changes. + /// + public event PropertyChangedEventHandler PropertyChanged; + +#if !(PocketPC || SILVERLIGHT || NET20) + /// + /// Occurs when a property value is changing. + /// + public event PropertyChangingEventHandler PropertyChanging; +#endif + + /// + /// Initializes a new instance of the class. + /// + public JObject() + { + } + + /// + /// Initializes a new instance of the class from another object. + /// + /// A object to copy from. + public JObject(JObject other) + : base(other) + { + } + + /// + /// Initializes a new instance of the class with the specified content. + /// + /// The contents of the object. + public JObject(params object[] content) + : this((object)content) + { + } + + /// + /// Initializes a new instance of the class with the specified content. + /// + /// The contents of the object. + public JObject(object content) + { + Add(content); + } + + internal override bool DeepEquals(JToken node) + { + JObject t = node as JObject; + return (t != null && ContentsEqual(t)); + } + + internal override void ValidateToken(JToken o, JToken existing) + { + ValidationUtils.ArgumentNotNull(o, "o"); + + if (o.Type != JTokenType.Property) + throw new ArgumentException("Can not add {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, o.GetType(), GetType())); + + // looping over all properties every time isn't good + // need to think about performance here + JProperty property = (JProperty)o; + foreach (JProperty childProperty in Children()) + { + if (childProperty != existing && string.Equals(childProperty.Name, property.Name, StringComparison.Ordinal)) + throw new ArgumentException("Can not add property {0} to {1}. Property with the same name already exists on object.".FormatWith(CultureInfo.InvariantCulture, property.Name, GetType())); + } + } + + internal void InternalPropertyChanged(JProperty childProperty) + { + OnPropertyChanged(childProperty.Name); +#if !SILVERLIGHT + OnListChanged(new ListChangedEventArgs(ListChangedType.ItemChanged, IndexOfItem(childProperty))); +#endif +#if SILVERLIGHT || !(NET20 || NET35) + OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, childProperty, childProperty, IndexOfItem(childProperty))); +#endif + } + + internal void InternalPropertyChanging(JProperty childProperty) + { +#if !PocketPC && !SILVERLIGHT && !NET20 + OnPropertyChanging(childProperty.Name); +#endif + } + + internal override JToken CloneToken() + { + return new JObject(this); + } + + /// + /// Gets the node type for this . + /// + /// The type. + public override JTokenType Type + { + get { return JTokenType.Object; } + } + + /// + /// Gets an of this object's properties. + /// + /// An of this object's properties. + public IEnumerable Properties() + { + return Children().Cast(); + } + + /// + /// Gets a the specified name. + /// + /// The property name. + /// A with the specified name or null. + public JProperty Property(string name) + { + return Properties() + .Where(p => string.Equals(p.Name, name, StringComparison.Ordinal)) + .SingleOrDefault(); + } + + /// + /// Gets an of this object's property values. + /// + /// An of this object's property values. + public JEnumerable PropertyValues() + { + return new JEnumerable(Properties().Select(p => p.Value)); + } + + /// + /// Gets the with the specified key. + /// + /// The with the specified key. + public override JToken this[object key] + { + get + { + ValidationUtils.ArgumentNotNull(key, "o"); + + string propertyName = key as string; + if (propertyName == null) + throw new ArgumentException("Accessed JObject values with invalid key value: {0}. Object property name expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); + + return this[propertyName]; + } + set + { + ValidationUtils.ArgumentNotNull(key, "o"); + + string propertyName = key as string; + if (propertyName == null) + throw new ArgumentException("Set JObject values with invalid key value: {0}. Object property name expected.".FormatWith(CultureInfo.InvariantCulture, MiscellaneousUtils.ToString(key))); + + this[propertyName] = value; + } + } + + /// + /// Gets or sets the with the specified property name. + /// + /// + public JToken this[string propertyName] + { + get + { + ValidationUtils.ArgumentNotNull(propertyName, "propertyName"); + + JProperty property = Property(propertyName); + + return (property != null) ? property.Value : null; + } + set + { + JProperty property = Property(propertyName); + if (property != null) + { + property.Value = value; + } + else + { +#if !PocketPC && !SILVERLIGHT && !NET20 + OnPropertyChanging(propertyName); +#endif + Add(new JProperty(propertyName, value)); + OnPropertyChanged(propertyName); + } + } + } + + /// + /// Loads an from a . + /// + /// A that will be read for the content of the . + /// A that contains the JSON that was read from the specified . + public static new JObject Load(JsonReader reader) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + + if (reader.TokenType == JsonToken.None) + { + if (!reader.Read()) + throw new Exception("Error reading JObject from JsonReader."); + } + + if (reader.TokenType != JsonToken.StartObject) + throw new Exception( + "Error reading JObject from JsonReader. Current JsonReader item is not an object: {0}".FormatWith( + CultureInfo.InvariantCulture, reader.TokenType)); + + JObject o = new JObject(); + o.SetLineInfo(reader as IJsonLineInfo); + + o.ReadTokenFrom(reader); + + return o; + } + + /// + /// Load a from a string that contains JSON. + /// + /// A that contains JSON. + /// A populated from the string that contains JSON. + public static new JObject Parse(string json) + { + JsonReader jsonReader = new JsonTextReader(new StringReader(json)); + + return Load(jsonReader); + } + + /// + /// Creates a from an object. + /// + /// The object that will be used to create . + /// A with the values of the specified object + public static new JObject FromObject(object o) + { + return FromObject(o, new JsonSerializer()); + } + + /// + /// Creates a from an object. + /// + /// The object that will be used to create . + /// The that will be used to read the object. + /// A with the values of the specified object + public static new JObject FromObject(object o, JsonSerializer jsonSerializer) + { + JToken token = FromObjectInternal(o, jsonSerializer); + + if (token != null && token.Type != JTokenType.Object) + throw new ArgumentException("Object serialized to {0}. JObject instance expected.".FormatWith(CultureInfo.InvariantCulture, token.Type)); + + return (JObject)token; + } + + /// + /// Writes this token to a . + /// + /// A into which this method will write. + /// A collection of which will be used when writing the token. + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) + { + writer.WriteStartObject(); + + foreach (JProperty property in ChildrenInternal()) + { + property.WriteTo(writer, converters); + } + + writer.WriteEndObject(); + } + + #region IDictionary Members + /// + /// Adds the specified property name. + /// + /// Name of the property. + /// The value. + public void Add(string propertyName, JToken value) + { + Add(new JProperty(propertyName, value)); + } + + bool IDictionary.ContainsKey(string key) + { + return (Property(key) != null); + } + + ICollection IDictionary.Keys + { + get { throw new NotImplementedException(); } + } + + /// + /// Removes the property with the specified name. + /// + /// Name of the property. + /// true if item was successfully removed; otherwise, false. + public bool Remove(string propertyName) + { + JProperty property = Property(propertyName); + if (property == null) + return false; + + property.Remove(); + return true; + } + + /// + /// Tries the get value. + /// + /// Name of the property. + /// The value. + /// true if a value was successfully retrieved; otherwise, false. + public bool TryGetValue(string propertyName, out JToken value) + { + JProperty property = Property(propertyName); + if (property == null) + { + value = null; + return false; + } + + value = property.Value; + return true; + } + + ICollection IDictionary.Values + { + get { throw new NotImplementedException(); } + } + + #endregion + + #region ICollection> Members + + void ICollection>.Add(KeyValuePair item) + { + Add(new JProperty(item.Key, item.Value)); + } + + void ICollection>.Clear() + { + RemoveAll(); + } + + bool ICollection>.Contains(KeyValuePair item) + { + JProperty property = Property(item.Key); + if (property == null) + return false; + + return (property.Value == item.Value); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException("array"); + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException("arrayIndex", "arrayIndex is less than 0."); + if (arrayIndex >= array.Length) + throw new ArgumentException("arrayIndex is equal to or greater than the length of array."); + if (Count > array.Length - arrayIndex) + throw new ArgumentException("The number of elements in the source JObject is greater than the available space from arrayIndex to the end of the destination array."); + + int index = 0; + foreach (JProperty property in Properties()) + { + array[arrayIndex + index] = new KeyValuePair(property.Name, property.Value); + index++; + } + } + + /// + /// Gets the number of elements contained in the . + /// + /// + /// The number of elements contained in the . + public int Count + { + get { return Children().Count(); } + } + + bool ICollection>.IsReadOnly + { + get { return false; } + } + + bool ICollection>.Remove(KeyValuePair item) + { + if (!((ICollection>)this).Contains(item)) + return false; + + ((IDictionary)this).Remove(item.Key); + return true; + } + + #endregion + + internal override int GetDeepHashCode() + { + return ContentsHashCode(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// A that can be used to iterate through the collection. + /// + public IEnumerator> GetEnumerator() + { + foreach (JProperty property in Properties()) + { + yield return new KeyValuePair(property.Name, property.Value); + } + } + + /// + /// Raises the event with the provided arguments. + /// + /// Name of the property. + protected virtual void OnPropertyChanged(string propertyName) + { + if (PropertyChanged != null) + PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); + } + +#if !PocketPC && !SILVERLIGHT && !NET20 + /// + /// Raises the event with the provided arguments. + /// + /// Name of the property. + protected virtual void OnPropertyChanging(string propertyName) + { + if (PropertyChanging != null) + PropertyChanging(this, new PropertyChangingEventArgs(propertyName)); + } +#endif + +#if !PocketPC && !SILVERLIGHT + // include custom type descriptor on JObject rather than use a provider because the properties are specific to a type + #region ICustomTypeDescriptor + /// + /// Returns the properties for this instance of a component. + /// + /// + /// A that represents the properties for this component instance. + /// + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() + { + return ((ICustomTypeDescriptor) this).GetProperties(null); + } + + private static Type GetTokenPropertyType(JToken token) + { + if (token is JValue) + { + JValue v = (JValue)token; + return (v.Value != null) ? v.Value.GetType() : typeof(object); + } + + return token.GetType(); + } + + /// + /// Returns the properties for this instance of a component using the attribute array as a filter. + /// + /// An array of type that is used as a filter. + /// + /// A that represents the filtered properties for this component instance. + /// + PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) + { + PropertyDescriptorCollection descriptors = new PropertyDescriptorCollection(null); + + foreach (KeyValuePair propertyValue in this) + { + descriptors.Add(new JPropertyDescriptor(propertyValue.Key, GetTokenPropertyType(propertyValue.Value))); + } + + return descriptors; + } + + /// + /// Returns a collection of custom attributes for this instance of a component. + /// + /// + /// An containing the attributes for this object. + /// + AttributeCollection ICustomTypeDescriptor.GetAttributes() + { + return AttributeCollection.Empty; + } + + /// + /// Returns the class name of this instance of a component. + /// + /// + /// The class name of the object, or null if the class does not have a name. + /// + string ICustomTypeDescriptor.GetClassName() + { + return null; + } + + /// + /// Returns the name of this instance of a component. + /// + /// + /// The name of the object, or null if the object does not have a name. + /// + string ICustomTypeDescriptor.GetComponentName() + { + return null; + } + + /// + /// Returns a type converter for this instance of a component. + /// + /// + /// A that is the converter for this object, or null if there is no for this object. + /// + TypeConverter ICustomTypeDescriptor.GetConverter() + { + return new TypeConverter(); + } + + /// + /// Returns the default event for this instance of a component. + /// + /// + /// An that represents the default event for this object, or null if this object does not have events. + /// + EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() + { + return null; + } + + /// + /// Returns the default property for this instance of a component. + /// + /// + /// A that represents the default property for this object, or null if this object does not have properties. + /// + PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() + { + return null; + } + + /// + /// Returns an editor of the specified type for this instance of a component. + /// + /// A that represents the editor for this object. + /// + /// An of the specified type that is the editor for this object, or null if the editor cannot be found. + /// + object ICustomTypeDescriptor.GetEditor(Type editorBaseType) + { + return null; + } + + /// + /// Returns the events for this instance of a component using the specified attribute array as a filter. + /// + /// An array of type that is used as a filter. + /// + /// An that represents the filtered events for this component instance. + /// + EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) + { + return EventDescriptorCollection.Empty; + } + + /// + /// Returns the events for this instance of a component. + /// + /// + /// An that represents the events for this component instance. + /// + EventDescriptorCollection ICustomTypeDescriptor.GetEvents() + { + return EventDescriptorCollection.Empty; + } + + /// + /// Returns an object that contains the property described by the specified property descriptor. + /// + /// A that represents the property whose owner is to be found. + /// + /// An that represents the owner of the specified property. + /// + object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) + { + return null; + } + #endregion +#endif + +#if !(NET35 || NET20 || WINDOWS_PHONE) + /// + /// Returns the responsible for binding operations performed on this object. + /// + /// The expression tree representation of the runtime value. + /// + /// The to bind this object. + /// + protected override DynamicMetaObject GetMetaObject(Expression parameter) + { + return new DynamicProxyMetaObject(parameter, this, new JObjectDynamicProxy(), true); + } + + private class JObjectDynamicProxy : DynamicProxy + { + public override bool TryGetMember(JObject instance, GetMemberBinder binder, out object result) + { + // result can be null + result = instance[binder.Name]; + return true; + } + + public override bool TrySetMember(JObject instance, SetMemberBinder binder, object value) + { + JToken v = value as JToken; + + // this can throw an error if value isn't a valid for a JValue + if (v == null) + v = new JValue(value); + + instance[binder.Name] = v; + return true; + } + + public override IEnumerable GetDynamicMemberNames(JObject instance) + { + return instance.Properties().Select(p => p.Name); + } + } +#endif + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JPath.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JPath.cs new file mode 100644 index 0000000..fe67f76 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JPath.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq +{ + internal class JPath + { + private readonly string _expression; + public List Parts { get; private set; } + + private int _currentIndex; + + public JPath(string expression) + { + ValidationUtils.ArgumentNotNull(expression, "expression"); + _expression = expression; + Parts = new List(); + + ParseMain(); + } + + private void ParseMain() + { + int currentPartStartIndex = _currentIndex; + bool followingIndexer = false; + + while (_currentIndex < _expression.Length) + { + char currentChar = _expression[_currentIndex]; + + switch (currentChar) + { + case '[': + case '(': + if (_currentIndex > currentPartStartIndex) + { + string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex); + Parts.Add(member); + } + + ParseIndexer(currentChar); + currentPartStartIndex = _currentIndex + 1; + followingIndexer = true; + break; + case ']': + case ')': + throw new Exception("Unexpected character while parsing path: " + currentChar); + case '.': + if (_currentIndex > currentPartStartIndex) + { + string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex); + Parts.Add(member); + } + currentPartStartIndex = _currentIndex + 1; + followingIndexer = false; + break; + default: + if (followingIndexer) + throw new Exception("Unexpected character following indexer: " + currentChar); + break; + } + + _currentIndex++; + } + + if (_currentIndex > currentPartStartIndex) + { + string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex); + Parts.Add(member); + } + } + + private void ParseIndexer(char indexerOpenChar) + { + _currentIndex++; + + char indexerCloseChar = (indexerOpenChar == '[') ? ']' : ')'; + int indexerStart = _currentIndex; + int indexerLength = 0; + bool indexerClosed = false; + + while (_currentIndex < _expression.Length) + { + char currentCharacter = _expression[_currentIndex]; + if (char.IsDigit(currentCharacter)) + { + indexerLength++; + } + else if (currentCharacter == indexerCloseChar) + { + indexerClosed = true; + break; + } + else + { + throw new Exception("Unexpected character while parsing path indexer: " + currentCharacter); + } + + _currentIndex++; + } + + if (!indexerClosed) + throw new Exception("Path ended with open indexer. Expected " + indexerCloseChar); + + if (indexerLength == 0) + throw new Exception("Empty path indexer."); + + string indexer = _expression.Substring(indexerStart, indexerLength); + Parts.Add(Convert.ToInt32(indexer, CultureInfo.InvariantCulture)); + } + + internal JToken Evaluate(JToken root, bool errorWhenNoMatch) + { + JToken current = root; + + foreach (object part in Parts) + { + string propertyName = part as string; + if (propertyName != null) + { + JObject o = current as JObject; + if (o != null) + { + current = o[propertyName]; + + if (current == null && errorWhenNoMatch) + throw new Exception("Property '{0}' does not exist on JObject.".FormatWith(CultureInfo.InvariantCulture, propertyName)); + } + else + { + if (errorWhenNoMatch) + throw new Exception("Property '{0}' not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, propertyName, current.GetType().Name)); + + return null; + } + } + else + { + int index = (int) part; + + JArray a = current as JArray; + + if (a != null) + { + if (a.Count <= index) + { + if (errorWhenNoMatch) + throw new IndexOutOfRangeException("Index {0} outside the bounds of JArray.".FormatWith(CultureInfo.InvariantCulture, index)); + + return null; + } + + current = a[index]; + } + else + { + if (errorWhenNoMatch) + throw new Exception("Index {0} not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, index, current.GetType().Name)); + + return null; + } + } + } + + return current; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JProperty.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JProperty.cs new file mode 100644 index 0000000..4b17f91 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JProperty.cs @@ -0,0 +1,268 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; +using System.Diagnostics; +using System.Globalization; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a JSON property. + /// + public class JProperty : JContainer + { + private readonly string _name; + + /// + /// Gets the property name. + /// + /// The property name. + public string Name + { + [DebuggerStepThrough] + get { return _name; } + } + + /// + /// Gets or sets the property value. + /// + /// The property value. + public JToken Value + { + [DebuggerStepThrough] + get { return Content; } + set + { + CheckReentrancy(); + + JToken newValue = value ?? new JValue((object) null); + + if (Content == null) + { + newValue = EnsureParentToken(newValue); + + Content = newValue; + Content.Parent = this; + Content.Next = Content; + } + else + { + Content.Replace(newValue); + } + } + } + + internal override void ReplaceItem(JToken existing, JToken replacement) + { + if (IsTokenUnchanged(existing, replacement)) + return; + + if (Parent != null) + ((JObject)Parent).InternalPropertyChanging(this); + + base.ReplaceItem(existing, replacement); + + if (Parent != null) + ((JObject)Parent).InternalPropertyChanged(this); + } + + /// + /// Initializes a new instance of the class from another object. + /// + /// A object to copy from. + public JProperty(JProperty other) + : base(other) + { + _name = other.Name; + } + + internal override void AddItem(bool isLast, JToken previous, JToken item) + { + if (Value != null) + throw new Exception("{0} cannot have multiple values.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + + Value = item; + } + + internal override JToken GetItem(int index) + { + if (index != 0) + throw new ArgumentOutOfRangeException(); + + return Value; + } + + internal override void SetItem(int index, JToken item) + { + if (index != 0) + throw new ArgumentOutOfRangeException(); + + Value = item; + } + + internal override bool RemoveItem(JToken item) + { + throw new Exception("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + } + + internal override void RemoveItemAt(int index) + { + throw new Exception("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + } + + internal override void InsertItem(int index, JToken item) + { + throw new Exception("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + } + + internal override bool ContainsItem(JToken item) + { + return (Value == item); + } + + internal override void ClearItems() + { + throw new Exception("Cannot add or remove items from {0}.".FormatWith(CultureInfo.InvariantCulture, typeof(JProperty))); + } + + /// + /// Returns a collection of the child tokens of this token, in document order. + /// + /// + /// An of containing the child tokens of this , in document order. + /// + public override JEnumerable Children() + { + return new JEnumerable(GetValueEnumerable()); + } + + private IEnumerable GetValueEnumerable() + { + yield return Value; + } + + internal override bool DeepEquals(JToken node) + { + JProperty t = node as JProperty; + return (t != null && _name == t.Name && ContentsEqual(t)); + } + + internal override JToken CloneToken() + { + return new JProperty(this); + } + + /// + /// Gets the node type for this . + /// + /// The type. + public override JTokenType Type + { + [DebuggerStepThrough] + get { return JTokenType.Property; } + } + + internal JProperty(string name) + { + // called from JTokenWriter + ValidationUtils.ArgumentNotNull(name, "name"); + + _name = name; + } + + /// + /// Initializes a new instance of the class. + /// + /// The property name. + /// The property content. + public JProperty(string name, params object[] content) + : this(name, (object)content) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The property name. + /// The property content. + public JProperty(string name, object content) + { + ValidationUtils.ArgumentNotNull(name, "name"); + + _name = name; + + Value = IsMultiContent(content) + ? new JArray(content) + : CreateFromContent(content); + } + + /// + /// Writes this token to a . + /// + /// A into which this method will write. + /// A collection of which will be used when writing the token. + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) + { + writer.WritePropertyName(_name); + Value.WriteTo(writer, converters); + } + + internal override int GetDeepHashCode() + { + return _name.GetHashCode() ^ ((Value != null) ? Value.GetDeepHashCode() : 0); + } + + /// + /// Loads an from a . + /// + /// A that will be read for the content of the . + /// A that contains the JSON that was read from the specified . + public static new JProperty Load(JsonReader reader) + { + if (reader.TokenType == JsonToken.None) + { + if (!reader.Read()) + throw new Exception("Error reading JProperty from JsonReader."); + } + if (reader.TokenType != JsonToken.PropertyName) + throw new Exception( + "Error reading JProperty from JsonReader. Current JsonReader item is not a property: {0}".FormatWith( + CultureInfo.InvariantCulture, reader.TokenType)); + + JProperty p = new JProperty((string)reader.Value); + p.SetLineInfo(reader as IJsonLineInfo); + + p.ReadTokenFrom(reader); + + return p; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JRaw.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JRaw.cs new file mode 100644 index 0000000..401100a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JRaw.cs @@ -0,0 +1,54 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a raw JSON string. + /// + public class JRaw : JValue + { + /// + /// Initializes a new instance of the class from another object. + /// + /// A object to copy from. + public JRaw(JRaw other) + : base(other) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The raw json. + public JRaw(object rawJson) + : base(rawJson, JTokenType.Raw) + { + } + + /// + /// Creates an instance of with the content of the reader's current token. + /// + /// The reader. + /// An instance of with the content of the reader's current token. + public static JRaw Create(JsonReader reader) + { + using (StringWriter sw = new StringWriter(CultureInfo.InvariantCulture)) + using (JsonTextWriter jsonWriter = new JsonTextWriter(sw)) + { + jsonWriter.WriteToken(reader); + + return new JRaw(sw.ToString()); + } + } + + internal override JToken CloneToken() + { + return new JRaw(this); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JToken.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JToken.cs new file mode 100644 index 0000000..5a09ce5 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JToken.cs @@ -0,0 +1,1347 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +#if !(NET35 || NET20 || WINDOWS_PHONE) +using System.Dynamic; +using System.Linq.Expressions; +#endif +using System.Linq; +using System.IO; +using Newtonsoft.Json.Utilities; +using System.Diagnostics; +using System.Globalization; +using System.Collections; +using System.ComponentModel; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents an abstract JSON token. + /// + public abstract class JToken : IJEnumerable, IJsonLineInfo +#if !SILVERLIGHT +, ICloneable +#endif +#if !(NET35 || NET20 || WINDOWS_PHONE) +, IDynamicMetaObjectProvider +#endif + { + private JContainer _parent; + internal JToken _next; + private static JTokenEqualityComparer _equalityComparer; + + private int? _lineNumber; + private int? _linePosition; + + /// + /// Gets a comparer that can compare two tokens for value equality. + /// + /// A that can compare two nodes for value equality. + public static JTokenEqualityComparer EqualityComparer + { + get + { + if (_equalityComparer == null) + _equalityComparer = new JTokenEqualityComparer(); + + return _equalityComparer; + } + } + + /// + /// Gets or sets the parent. + /// + /// The parent. + public JContainer Parent + { + [DebuggerStepThrough] + get { return _parent; } + internal set { _parent = value; } + } + + /// + /// Gets the root of this . + /// + /// The root of this . + public JToken Root + { + get + { + JContainer parent = Parent; + if (parent == null) + return this; + + while (parent.Parent != null) + { + parent = parent.Parent; + } + + return parent; + } + } + + internal abstract JToken CloneToken(); + internal abstract bool DeepEquals(JToken node); + + /// + /// Gets the node type for this . + /// + /// The type. + public abstract JTokenType Type { get; } + + /// + /// Gets a value indicating whether this token has childen tokens. + /// + /// + /// true if this token has child values; otherwise, false. + /// + public abstract bool HasValues { get; } + + /// + /// Compares the values of two tokens, including the values of all descendant tokens. + /// + /// The first to compare. + /// The second to compare. + /// true if the tokens are equal; otherwise false. + public static bool DeepEquals(JToken t1, JToken t2) + { + return (t1 == t2 || (t1 != null && t2 != null && t1.DeepEquals(t2))); + } + + /// + /// Gets the next sibling token of this node. + /// + /// The that contains the next sibling token. + public JToken Next + { + get + { + if (_parent != null && _next != _parent.First) + return _next; + + return null; + } + internal set { _next = value; } + } + + /// + /// Gets the previous sibling token of this node. + /// + /// The that contains the previous sibling token. + public JToken Previous + { + get + { + if (_parent == null) + return null; + + JToken parentNext = _parent.Content._next; + JToken parentNextBefore = null; + while (parentNext != this) + { + parentNextBefore = parentNext; + parentNext = parentNext.Next; + } + return parentNextBefore; + } + } + + internal JToken() + { + } + + /// + /// Adds the specified content immediately after this token. + /// + /// A content object that contains simple content or a collection of content objects to be added after this token. + public void AddAfterSelf(object content) + { + if (_parent == null) + throw new InvalidOperationException("The parent is missing."); + + _parent.AddInternal((Next == null), this, content); + } + + /// + /// Adds the specified content immediately before this token. + /// + /// A content object that contains simple content or a collection of content objects to be added before this token. + public void AddBeforeSelf(object content) + { + if (_parent == null) + throw new InvalidOperationException("The parent is missing."); + + JToken previous = Previous; + if (previous == null) + previous = _parent.Last; + + _parent.AddInternal(false, previous, content); + } + + /// + /// Returns a collection of the ancestor tokens of this token. + /// + /// A collection of the ancestor tokens of this token. + public IEnumerable Ancestors() + { + for (JToken parent = Parent; parent != null; parent = parent.Parent) + { + yield return parent; + } + } + + /// + /// Returns a collection of the sibling tokens after this token, in document order. + /// + /// A collection of the sibling tokens after this tokens, in document order. + public IEnumerable AfterSelf() + { + if (Parent == null) + yield break; + + for (JToken o = Next; o != null; o = o.Next) + yield return o; + } + + /// + /// Returns a collection of the sibling tokens before this token, in document order. + /// + /// A collection of the sibling tokens before this token, in document order. + public IEnumerable BeforeSelf() + { + for (JToken o = Parent.First; o != this; o = o.Next) + yield return o; + } + + /// + /// Gets the with the specified key. + /// + /// The with the specified key. + public virtual JToken this[object key] + { + get { throw new InvalidOperationException("Cannot access child value on {0}.".FormatWith(CultureInfo.InvariantCulture, GetType())); } + set { throw new InvalidOperationException("Cannot set child value on {0}.".FormatWith(CultureInfo.InvariantCulture, GetType())); } + } + + /// + /// Gets the with the specified key converted to the specified type. + /// + /// The type to convert the token to. + /// The token key. + /// The converted token value. + public virtual T Value(object key) + { + JToken token = this[key]; + + return Extensions.Convert(token); + } + + /// + /// Get the first child token of this token. + /// + /// A containing the first child token of the . + public virtual JToken First + { + get { throw new InvalidOperationException("Cannot access child value on {0}.".FormatWith(CultureInfo.InvariantCulture, GetType())); } + } + + /// + /// Get the last child token of this token. + /// + /// A containing the last child token of the . + public virtual JToken Last + { + get { throw new InvalidOperationException("Cannot access child value on {0}.".FormatWith(CultureInfo.InvariantCulture, GetType())); } + } + + /// + /// Returns a collection of the child tokens of this token, in document order. + /// + /// An of containing the child tokens of this , in document order. + public virtual JEnumerable Children() + { + throw new InvalidOperationException("Cannot access child value on {0}.".FormatWith(CultureInfo.InvariantCulture, GetType())); + } + + /// + /// Returns a collection of the child tokens of this token, in document order, filtered by the specified type. + /// + /// The type to filter the child tokens on. + /// A containing the child tokens of this , in document order. + public JEnumerable Children() where T : JToken + { + return new JEnumerable(Children().OfType()); + } + + /// + /// Returns a collection of the child values of this token, in document order. + /// + /// The type to convert the values to. + /// A containing the child values of this , in document order. + public virtual IEnumerable Values() + { + throw new InvalidOperationException("Cannot access child value on {0}.".FormatWith(CultureInfo.InvariantCulture, GetType())); + } + + /// + /// Removes this token from its parent. + /// + public void Remove() + { + if (_parent == null) + throw new InvalidOperationException("The parent is missing."); + + _parent.RemoveItem(this); + } + + /// + /// Replaces this token with the specified token. + /// + /// The value. + public void Replace(JToken value) + { + if (_parent == null) + throw new InvalidOperationException("The parent is missing."); + + _parent.ReplaceItem(this, value); + } + + /// + /// Writes this token to a . + /// + /// A into which this method will write. + /// A collection of which will be used when writing the token. + public abstract void WriteTo(JsonWriter writer, params JsonConverter[] converters); + + /// + /// Returns the indented JSON for this token. + /// + /// + /// The indented JSON for this token. + /// + public override string ToString() + { + return ToString(Formatting.Indented); + } + + /// + /// Returns the JSON for this token using the given formatting and converters. + /// + /// Indicates how the output is formatted. + /// A collection of which will be used when writing the token. + /// The JSON for this token using the given formatting and converters. + public string ToString(Formatting formatting, params JsonConverter[] converters) + { + using (StringWriter sw = new StringWriter(CultureInfo.InvariantCulture)) + { + JsonTextWriter jw = new JsonTextWriter(sw); + jw.Formatting = formatting; + + WriteTo(jw, converters); + + return sw.ToString(); + } + } + + private static JValue EnsureValue(JToken value) + { + if (value == null) + throw new ArgumentNullException("value"); + + if (value is JProperty) + value = ((JProperty)value).Value; + + JValue v = value as JValue; + + return v; + } + + private static string GetType(JToken token) + { + ValidationUtils.ArgumentNotNull(token, "token"); + + if (token is JProperty) + token = ((JProperty)token).Value; + + return token.Type.ToString(); + } + + private static bool IsNullable(JToken o) + { + return (o.Type == JTokenType.Undefined || o.Type == JTokenType.Null); + } + + private static bool ValidateFloat(JToken o, bool nullable) + { + return (o.Type == JTokenType.Float || o.Type == JTokenType.Integer || (nullable && IsNullable(o))); + } + + private static bool ValidateInteger(JToken o, bool nullable) + { + return (o.Type == JTokenType.Integer || (nullable && IsNullable(o))); + } + + private static bool ValidateDate(JToken o, bool nullable) + { + return (o.Type == JTokenType.Date || (nullable && IsNullable(o))); + } + + private static bool ValidateBoolean(JToken o, bool nullable) + { + return (o.Type == JTokenType.Boolean || (nullable && IsNullable(o))); + } + + private static bool ValidateString(JToken o) + { + return (o.Type == JTokenType.String || o.Type == JTokenType.Comment || o.Type == JTokenType.Raw || IsNullable(o)); + } + + private static bool ValidateBytes(JToken o) + { + return (o.Type == JTokenType.Bytes || IsNullable(o)); + } + + #region Cast from operators + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator bool(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateBoolean(v, false)) + throw new ArgumentException("Can not convert {0} to Boolean.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (bool)v.Value; + } + +#if !PocketPC && !NET20 + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator DateTimeOffset(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateDate(v, false)) + throw new ArgumentException("Can not convert {0} to DateTimeOffset.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (DateTimeOffset)v.Value; + } +#endif + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator bool?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateBoolean(v, true)) + throw new ArgumentException("Can not convert {0} to Boolean.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (bool?)v.Value; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator long(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, false)) + throw new ArgumentException("Can not convert {0} to Int64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (long)v.Value; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator DateTime?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateDate(v, true)) + throw new ArgumentException("Can not convert {0} to DateTime.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (DateTime?)v.Value; + } + +#if !PocketPC && !NET20 + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator DateTimeOffset?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateDate(v, true)) + throw new ArgumentException("Can not convert {0} to DateTimeOffset.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (DateTimeOffset?)v.Value; + } +#endif + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator decimal?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateFloat(v, true)) + throw new ArgumentException("Can not convert {0} to Decimal.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (v.Value != null) ? (decimal?)Convert.ToDecimal(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator double?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateFloat(v, true)) + throw new ArgumentException("Can not convert {0} to Double.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (double?)v.Value; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator int(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, false)) + throw new ArgumentException("Can not convert {0} to Int32.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return Convert.ToInt32(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator short(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, false)) + throw new ArgumentException("Can not convert {0} to Int16.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return Convert.ToInt16(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator ushort(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, false)) + throw new ArgumentException("Can not convert {0} to UInt16.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return Convert.ToUInt16(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator int?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, true)) + throw new ArgumentException("Can not convert {0} to Int32.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (v.Value != null) ? (int?)Convert.ToInt32(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator short?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, true)) + throw new ArgumentException("Can not convert {0} to Int16.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (v.Value != null) ? (short?)Convert.ToInt16(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator ushort?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, true)) + throw new ArgumentException("Can not convert {0} to UInt16.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (v.Value != null) ? (ushort?)Convert.ToInt16(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator DateTime(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateDate(v, false)) + throw new ArgumentException("Can not convert {0} to DateTime.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (DateTime)v.Value; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator long?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, true)) + throw new ArgumentException("Can not convert {0} to Int64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (long?)v.Value; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator float?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateFloat(v, true)) + throw new ArgumentException("Can not convert {0} to Single.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (v.Value != null) ? (float?)Convert.ToSingle(v.Value, CultureInfo.InvariantCulture) : null; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator decimal(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateFloat(v, false)) + throw new ArgumentException("Can not convert {0} to Decimal.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return Convert.ToDecimal(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator uint?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, true)) + throw new ArgumentException("Can not convert {0} to UInt32.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (uint?)v.Value; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator ulong?(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, true)) + throw new ArgumentException("Can not convert {0} to UInt64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (ulong?)v.Value; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator double(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateFloat(v, false)) + throw new ArgumentException("Can not convert {0} to Double.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (double)v.Value; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator float(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateFloat(v, false)) + throw new ArgumentException("Can not convert {0} to Single.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return Convert.ToSingle(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator string(JToken value) + { + if (value == null) + return null; + + JValue v = EnsureValue(value); + if (v == null || !ValidateString(v)) + throw new ArgumentException("Can not convert {0} to String.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (string)v.Value; + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator uint(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, false)) + throw new ArgumentException("Can not convert {0} to UInt32.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return Convert.ToUInt32(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + [CLSCompliant(false)] + public static explicit operator ulong(JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateInteger(v, false)) + throw new ArgumentException("Can not convert {0} to UInt64.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return Convert.ToUInt64(v.Value, CultureInfo.InvariantCulture); + } + + /// + /// Performs an explicit conversion from to . + /// + /// The value. + /// The result of the conversion. + public static explicit operator byte[](JToken value) + { + JValue v = EnsureValue(value); + if (v == null || !ValidateBytes(v)) + throw new ArgumentException("Can not convert {0} to byte array.".FormatWith(CultureInfo.InvariantCulture, GetType(value))); + + return (byte[])v.Value; + } + #endregion + + #region Cast to operators + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(bool value) + { + return new JValue(value); + } + +#if !PocketPC && !NET20 + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(DateTimeOffset value) + { + return new JValue(value); + } +#endif + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(bool? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(long value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(DateTime? value) + { + return new JValue(value); + } + +#if !PocketPC && !NET20 + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(DateTimeOffset? value) + { + return new JValue(value); + } +#endif + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(decimal? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(double? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(short value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(ushort value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(int value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(int? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(DateTime value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(long? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(float? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(decimal value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(short? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(ushort? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(uint? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(ulong? value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(double value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(float value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(string value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(uint value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + [CLSCompliant(false)] + public static implicit operator JToken(ulong value) + { + return new JValue(value); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The value to create a from. + /// The initialized with the specified value. + public static implicit operator JToken(byte[] value) + { + return new JValue(value); + } + #endregion + + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)this).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return Children().GetEnumerator(); + } + + internal abstract int GetDeepHashCode(); + + IJEnumerable IJEnumerable.this[object key] + { + get { return this[key]; } + } + + /// + /// Creates an for this token. + /// + /// An that can be used to read this token and its descendants. + public JsonReader CreateReader() + { + return new JTokenReader(this); + } + + internal static JToken FromObjectInternal(object o, JsonSerializer jsonSerializer) + { + ValidationUtils.ArgumentNotNull(o, "o"); + ValidationUtils.ArgumentNotNull(jsonSerializer, "jsonSerializer"); + + JToken token; + using (JTokenWriter jsonWriter = new JTokenWriter()) + { + jsonSerializer.Serialize(jsonWriter, o); + token = jsonWriter.Token; + } + + return token; + } + + /// + /// Creates a from an object. + /// + /// The object that will be used to create . + /// A with the value of the specified object + public static JToken FromObject(object o) + { + return FromObjectInternal(o, new JsonSerializer()); + } + + /// + /// Creates a from an object using the specified . + /// + /// The object that will be used to create . + /// The that will be used when reading the object. + /// A with the value of the specified object + public static JToken FromObject(object o, JsonSerializer jsonSerializer) + { + return FromObjectInternal(o, jsonSerializer); + } + + /// + /// Creates a from a . + /// + /// An positioned at the token to read into this . + /// + /// An that contains the token and its descendant tokens + /// that were read from the reader. The runtime type of the token is determined + /// by the token type of the first token encountered in the reader. + /// + public static JToken ReadFrom(JsonReader reader) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + + if (reader.TokenType == JsonToken.None) + { + if (!reader.Read()) + throw new Exception("Error reading JToken from JsonReader."); + } + + if (reader.TokenType == JsonToken.StartObject) + return JObject.Load(reader); + + if (reader.TokenType == JsonToken.StartArray) + return JArray.Load(reader); + + if (reader.TokenType == JsonToken.PropertyName) + return JProperty.Load(reader); + + if (reader.TokenType == JsonToken.StartConstructor) + return JConstructor.Load(reader); + + // hack. change to look at TokenType rather than using value + if (!JsonReader.IsStartToken(reader.TokenType)) + return new JValue(reader.Value); + + // TODO: loading constructor and parameters? + throw new Exception("Error reading JToken from JsonReader. Unexpected token: {0}".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + + /// + /// Load a from a string that contains JSON. + /// + /// A that contains JSON. + /// A populated from the string that contains JSON. + public static JToken Parse(string json) + { + JsonReader jsonReader = new JsonTextReader(new StringReader(json)); + + return Load(jsonReader); + } + + /// + /// Creates a from a . + /// + /// An positioned at the token to read into this . + /// + /// An that contains the token and its descendant tokens + /// that were read from the reader. The runtime type of the token is determined + /// by the token type of the first token encountered in the reader. + /// + public static JToken Load(JsonReader reader) + { + return ReadFrom(reader); + } + + internal void SetLineInfo(IJsonLineInfo lineInfo) + { + if (lineInfo == null || !lineInfo.HasLineInfo()) + return; + + SetLineInfo(lineInfo.LineNumber, lineInfo.LinePosition); + } + + internal void SetLineInfo(int lineNumber, int linePosition) + { + _lineNumber = lineNumber; + _linePosition = linePosition; + } + + bool IJsonLineInfo.HasLineInfo() + { + return (_lineNumber != null && _linePosition != null); + } + + int IJsonLineInfo.LineNumber + { + get { return _lineNumber ?? 0; } + } + + int IJsonLineInfo.LinePosition + { + get { return _linePosition ?? 0; } + } + + /// + /// Selects the token that matches the object path. + /// + /// + /// The object path from the current to the + /// to be returned. This must be a string of property names or array indexes separated + /// by periods, such as Tables[0].DefaultView[0].Price in C# or + /// Tables(0).DefaultView(0).Price in Visual Basic. + /// + /// The that matches the object path or a null reference if no matching token is found. + public JToken SelectToken(string path) + { + return SelectToken(path, false); + } + + /// + /// Selects the token that matches the object path. + /// + /// + /// The object path from the current to the + /// to be returned. This must be a string of property names or array indexes separated + /// by periods, such as Tables[0].DefaultView[0].Price in C# or + /// Tables(0).DefaultView(0).Price in Visual Basic. + /// + /// A flag to indicate whether an error should be thrown if no token is found. + /// The that matches the object path. + public JToken SelectToken(string path, bool errorWhenNoMatch) + { + JPath p = new JPath(path); + return p.Evaluate(this, errorWhenNoMatch); + } + +#if !(NET35 || NET20 || WINDOWS_PHONE) + /// + /// Returns the responsible for binding operations performed on this object. + /// + /// The expression tree representation of the runtime value. + /// + /// The to bind this object. + /// + protected virtual DynamicMetaObject GetMetaObject(Expression parameter) + { + return new DynamicProxyMetaObject(parameter, this, new DynamicProxy(), true); + } + + /// + /// Returns the responsible for binding operations performed on this object. + /// + /// The expression tree representation of the runtime value. + /// + /// The to bind this object. + /// + DynamicMetaObject IDynamicMetaObjectProvider.GetMetaObject(Expression parameter) + { + return GetMetaObject(parameter); + } +#endif + +#if !SILVERLIGHT + object ICloneable.Clone() + { + return DeepClone(); + } +#endif + + /// + /// Creates a new instance of the . All child tokens are recursively cloned. + /// + /// A new instance of the . + public JToken DeepClone() + { + return CloneToken(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JTokenEqualityComparer.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JTokenEqualityComparer.cs new file mode 100644 index 0000000..93acd9a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JTokenEqualityComparer.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Compares tokens to determine whether they are equal. + /// + public class JTokenEqualityComparer : IEqualityComparer + { + /// + /// Determines whether the specified objects are equal. + /// + /// The first object of type to compare. + /// The second object of type to compare. + /// + /// true if the specified objects are equal; otherwise, false. + /// + public bool Equals(JToken x, JToken y) + { + return JToken.DeepEquals(x, y); + } + + /// + /// Returns a hash code for the specified object. + /// + /// The for which a hash code is to be returned. + /// A hash code for the specified object. + /// The type of is a reference type and is null. + public int GetHashCode(JToken obj) + { + if (obj == null) + return 0; + + return obj.GetDeepHashCode(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JTokenReader.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JTokenReader.cs new file mode 100644 index 0000000..e09cdb8 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JTokenReader.cs @@ -0,0 +1,289 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a reader that provides fast, non-cached, forward-only access to serialized Json data. + /// + public class JTokenReader : JsonReader, IJsonLineInfo + { + private readonly JToken _root; + private JToken _parent; + private JToken _current; + + /// + /// Initializes a new instance of the class. + /// + /// The token to read from. + public JTokenReader(JToken token) + { + ValidationUtils.ArgumentNotNull(token, "token"); + + _root = token; + _current = token; + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// + /// A or a null reference if the next JSON token is null. + /// + public override byte[] ReadAsBytes() + { + Read(); + + // attempt to convert possible base 64 string to bytes + if (TokenType == JsonToken.String) + { + string s = (string) Value; + byte[] data = (s.Length == 0) ? new byte[0] : Convert.FromBase64String(s); + SetToken(JsonToken.Bytes, data); + } + + if (TokenType == JsonToken.Null) + return null; + if (TokenType == JsonToken.Bytes) + return (byte[])Value; + + throw new JsonReaderException("Error reading bytes. Expected bytes but got {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); + } + + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override decimal? ReadAsDecimal() + { + Read(); + + if (TokenType == JsonToken.Null) + return null; + if (TokenType == JsonToken.Integer || TokenType == JsonToken.Float) + { + SetToken(JsonToken.Float, Convert.ToDecimal(Value, CultureInfo.InvariantCulture)); + return (decimal) Value; + } + + throw new JsonReaderException("Error reading decimal. Expected a number but got {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); + } + +#if !NET20 + /// + /// Reads the next JSON token from the stream as a . + /// + /// A . + public override DateTimeOffset? ReadAsDateTimeOffset() + { + Read(); + + if (TokenType == JsonToken.Null) + return null; + if (TokenType == JsonToken.Date) + { + SetToken(JsonToken.Date, new DateTimeOffset((DateTime)Value)); + return (DateTimeOffset)Value; + } + + throw new JsonReaderException("Error reading date. Expected bytes but got {0}.".FormatWith(CultureInfo.InvariantCulture, TokenType)); + } +#endif + + /// + /// Reads the next JSON token from the stream. + /// + /// + /// true if the next token was read successfully; false if there are no more tokens to read. + /// + public override bool Read() + { + if (CurrentState != State.Start) + { + JContainer container = _current as JContainer; + if (container != null && _parent != container) + return ReadInto(container); + else + return ReadOver(_current); + } + + SetToken(_current); + return true; + } + + private bool ReadOver(JToken t) + { + if (t == _root) + return ReadToEnd(); + + JToken next = t.Next; + if ((next == null || next == t) || t == t.Parent.Last) + { + if (t.Parent == null) + return ReadToEnd(); + + return SetEnd(t.Parent); + } + else + { + _current = next; + SetToken(_current); + return true; + } + } + + private bool ReadToEnd() + { + //CurrentState = State.Finished; + return false; + } + + private bool IsEndElement + { + get { return (_current == _parent); } + } + + private JsonToken? GetEndToken(JContainer c) + { + switch (c.Type) + { + case JTokenType.Object: + return JsonToken.EndObject; + case JTokenType.Array: + return JsonToken.EndArray; + case JTokenType.Constructor: + return JsonToken.EndConstructor; + case JTokenType.Property: + return null; + default: + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("Type", c.Type, "Unexpected JContainer type."); + } + } + + private bool ReadInto(JContainer c) + { + JToken firstChild = c.First; + if (firstChild == null) + { + return SetEnd(c); + } + else + { + SetToken(firstChild); + _current = firstChild; + _parent = c; + return true; + } + } + + private bool SetEnd(JContainer c) + { + JsonToken? endToken = GetEndToken(c); + if (endToken != null) + { + SetToken(endToken.Value); + _current = c; + _parent = c; + return true; + } + else + { + return ReadOver(c); + } + } + + private void SetToken(JToken token) + { + switch (token.Type) + { + case JTokenType.Object: + SetToken(JsonToken.StartObject); + break; + case JTokenType.Array: + SetToken(JsonToken.StartArray); + break; + case JTokenType.Constructor: + SetToken(JsonToken.StartConstructor); + break; + case JTokenType.Property: + SetToken(JsonToken.PropertyName, ((JProperty)token).Name); + break; + case JTokenType.Comment: + SetToken(JsonToken.Comment, ((JValue)token).Value); + break; + case JTokenType.Integer: + SetToken(JsonToken.Integer, ((JValue)token).Value); + break; + case JTokenType.Float: + SetToken(JsonToken.Float, ((JValue)token).Value); + break; + case JTokenType.String: + SetToken(JsonToken.String, ((JValue)token).Value); + break; + case JTokenType.Boolean: + SetToken(JsonToken.Boolean, ((JValue)token).Value); + break; + case JTokenType.Null: + SetToken(JsonToken.Null, ((JValue)token).Value); + break; + case JTokenType.Undefined: + SetToken(JsonToken.Undefined, ((JValue)token).Value); + break; + case JTokenType.Date: + SetToken(JsonToken.Date, ((JValue)token).Value); + break; + case JTokenType.Raw: + SetToken(JsonToken.Raw, ((JValue)token).Value); + break; + case JTokenType.Bytes: + SetToken(JsonToken.Bytes, ((JValue)token).Value); + break; + default: + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("Type", token.Type, "Unexpected JTokenType."); + } + } + + bool IJsonLineInfo.HasLineInfo() + { + if (CurrentState == State.Start) + return false; + + IJsonLineInfo info = IsEndElement ? null : _current; + return (info != null && info.HasLineInfo()); + } + + int IJsonLineInfo.LineNumber + { + get + { + if (CurrentState == State.Start) + return 0; + + IJsonLineInfo info = IsEndElement ? null : _current; + if (info != null) + return info.LineNumber; + + return 0; + } + } + + int IJsonLineInfo.LinePosition + { + get + { + if (CurrentState == State.Start) + return 0; + + IJsonLineInfo info = IsEndElement ? null : _current; + if (info != null) + return info.LinePosition; + + return 0; + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JTokenType.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JTokenType.cs new file mode 100644 index 0000000..8150ed0 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JTokenType.cs @@ -0,0 +1,94 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Linq +{ + /// + /// Specifies the type of token. + /// + public enum JTokenType + { + /// + /// No token type has been set. + /// + None, + /// + /// A JSON object. + /// + Object, + /// + /// A JSON array. + /// + Array, + /// + /// A JSON constructor. + /// + Constructor, + /// + /// A JSON object property. + /// + Property, + /// + /// A comment. + /// + Comment, + /// + /// An integer value. + /// + Integer, + /// + /// A float value. + /// + Float, + /// + /// A string value. + /// + String, + /// + /// A boolean value. + /// + Boolean, + /// + /// A null value. + /// + Null, + /// + /// An undefined value. + /// + Undefined, + /// + /// A date value. + /// + Date, + /// + /// A raw JSON value. + /// + Raw, + /// + /// A collection of bytes value. + /// + Bytes + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JTokenWriter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JTokenWriter.cs new file mode 100644 index 0000000..7044137 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JTokenWriter.cs @@ -0,0 +1,373 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a writer that provides a fast, non-cached, forward-only way of generating Json data. + /// + public class JTokenWriter : JsonWriter + { + private JContainer _token; + private JContainer _parent; + // used when writer is writing single value and the value has no containing parent + private JValue _value; + + /// + /// Gets the token being writen. + /// + /// The token being writen. + public JToken Token + { + get + { + if (_token != null) + return _token; + + return _value; + } + } + + /// + /// Initializes a new instance of the class writing to the given . + /// + /// The container being written to. + public JTokenWriter(JContainer container) + { + ValidationUtils.ArgumentNotNull(container, "container"); + + _token = container; + _parent = container; + } + + /// + /// Initializes a new instance of the class. + /// + public JTokenWriter() + { + } + + /// + /// Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream. + /// + public override void Flush() + { + } + + /// + /// Closes this stream and the underlying stream. + /// + public override void Close() + { + base.Close(); + } + + /// + /// Writes the beginning of a Json object. + /// + public override void WriteStartObject() + { + base.WriteStartObject(); + + AddParent(new JObject()); + } + + private void AddParent(JContainer container) + { + if (_parent == null) + _token = container; + else + _parent.Add(container); + + _parent = container; + } + + private void RemoveParent() + { + _parent = _parent.Parent; + + if (_parent != null && _parent.Type == JTokenType.Property) + _parent = _parent.Parent; + } + + /// + /// Writes the beginning of a Json array. + /// + public override void WriteStartArray() + { + base.WriteStartArray(); + + AddParent(new JArray()); + } + + /// + /// Writes the start of a constructor with the given name. + /// + /// The name of the constructor. + public override void WriteStartConstructor(string name) + { + base.WriteStartConstructor(name); + + AddParent(new JConstructor(name)); + } + + /// + /// Writes the end. + /// + /// The token. + protected override void WriteEnd(JsonToken token) + { + RemoveParent(); + } + + /// + /// Writes the property name of a name/value pair on a Json object. + /// + /// The name of the property. + public override void WritePropertyName(string name) + { + base.WritePropertyName(name); + + AddParent(new JProperty(name)); + } + + private void AddValue(object value, JsonToken token) + { + AddValue(new JValue(value), token); + } + + internal void AddValue(JValue value, JsonToken token) + { + if (_parent != null) + { + _parent.Add(value); + + if (_parent.Type == JTokenType.Property) + _parent = _parent.Parent; + } + else + { + _value = value; + } + } + + #region WriteValue methods + /// + /// Writes a null value. + /// + public override void WriteNull() + { + base.WriteNull(); + AddValue(null, JsonToken.Null); + } + + /// + /// Writes an undefined value. + /// + public override void WriteUndefined() + { + base.WriteUndefined(); + AddValue(null, JsonToken.Undefined); + } + + /// + /// Writes raw JSON. + /// + /// The raw JSON to write. + public override void WriteRaw(string json) + { + base.WriteRaw(json); + AddValue(new JRaw(json), JsonToken.Raw); + } + + /// + /// Writes out a comment /*...*/ containing the specified text. + /// + /// Text to place inside the comment. + public override void WriteComment(string text) + { + base.WriteComment(text); + AddValue(JValue.CreateComment(text), JsonToken.Comment); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(string value) + { + base.WriteValue(value); + AddValue(value ?? string.Empty, JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(int value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(uint value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(long value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(ulong value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(float value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(double value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(bool value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Boolean); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(short value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(ushort value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(char value) + { + base.WriteValue(value); + AddValue(value.ToString(), JsonToken.String); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(byte value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + [CLSCompliant(false)] + public override void WriteValue(sbyte value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Integer); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(decimal value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Float); + } + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTime value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Date); + } + +#if !PocketPC && !NET20 + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(DateTimeOffset value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Date); + } +#endif + + /// + /// Writes a value. + /// + /// The value to write. + public override void WriteValue(byte[] value) + { + base.WriteValue(value); + AddValue(value, JsonToken.Bytes); + } + #endregion + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JValue.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JValue.cs new file mode 100644 index 0000000..a1b4681 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Linq/JValue.cs @@ -0,0 +1,738 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; +using System.Globalization; +using System.ComponentModel; +#if !(NET35 || NET20 || WINDOWS_PHONE) +using System.Dynamic; +using System.Linq.Expressions; +#endif + +namespace Newtonsoft.Json.Linq +{ + /// + /// Represents a value in JSON (string, integer, date, etc). + /// + public class JValue : JToken, IEquatable, IFormattable, IComparable, IComparable + { + private JTokenType _valueType; + private object _value; + + internal JValue(object value, JTokenType type) + { + _value = value; + _valueType = type; + } + + /// + /// Initializes a new instance of the class from another object. + /// + /// A object to copy from. + public JValue(JValue other) + : this(other.Value, other.Type) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(long value) + : this(value, JTokenType.Integer) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + [CLSCompliant(false)] + public JValue(ulong value) + : this(value, JTokenType.Integer) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(double value) + : this(value, JTokenType.Float) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(DateTime value) + : this(value, JTokenType.Date) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(bool value) + : this(value, JTokenType.Boolean) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(string value) + : this(value, JTokenType.String) + { + } + + /// + /// Initializes a new instance of the class with the given value. + /// + /// The value. + public JValue(object value) + : this(value, GetValueType(null, value)) + { + } + + internal override bool DeepEquals(JToken node) + { + JValue other = node as JValue; + if (other == null) + return false; + + return ValuesEquals(this, other); + } + + /// + /// Gets a value indicating whether this token has childen tokens. + /// + /// + /// true if this token has child values; otherwise, false. + /// + public override bool HasValues + { + get { return false; } + } + + private static int Compare(JTokenType valueType, object objA, object objB) + { + if (objA == null && objB == null) + return 0; + if (objA != null && objB == null) + return 1; + if (objA == null && objB != null) + return -1; + + switch (valueType) + { + case JTokenType.Integer: + if (objA is ulong || objB is ulong || objA is decimal || objB is decimal) + return Convert.ToDecimal(objA, CultureInfo.InvariantCulture).CompareTo(Convert.ToDecimal(objB, CultureInfo.InvariantCulture)); + else if (objA is float || objB is float || objA is double || objB is double) + return CompareFloat(objA, objB); + else + return Convert.ToInt64(objA, CultureInfo.InvariantCulture).CompareTo(Convert.ToInt64(objB, CultureInfo.InvariantCulture)); + case JTokenType.Float: + return CompareFloat(objA, objB); + case JTokenType.Comment: + case JTokenType.String: + case JTokenType.Raw: + string s1 = Convert.ToString(objA, CultureInfo.InvariantCulture); + string s2 = Convert.ToString(objB, CultureInfo.InvariantCulture); + + return s1.CompareTo(s2); + case JTokenType.Boolean: + bool b1 = Convert.ToBoolean(objA, CultureInfo.InvariantCulture); + bool b2 = Convert.ToBoolean(objB, CultureInfo.InvariantCulture); + + return b1.CompareTo(b2); + case JTokenType.Date: +#if !NET20 + if (objA is DateTime) + { +#endif + DateTime date1 = Convert.ToDateTime(objA, CultureInfo.InvariantCulture); + DateTime date2 = Convert.ToDateTime(objB, CultureInfo.InvariantCulture); + + return date1.CompareTo(date2); +#if !NET20 + } + else + { + if (!(objB is DateTimeOffset)) + throw new ArgumentException("Object must be of type DateTimeOffset."); + + DateTimeOffset date1 = (DateTimeOffset)objA; + DateTimeOffset date2 = (DateTimeOffset)objB; + + return date1.CompareTo(date2); + } +#endif + case JTokenType.Bytes: + if (!(objB is byte[])) + throw new ArgumentException("Object must be of type byte[]."); + + byte[] bytes1 = objA as byte[]; + byte[] bytes2 = objB as byte[]; + if (bytes1 == null) + return -1; + if (bytes2 == null) + return 1; + + return MiscellaneousUtils.ByteArrayCompare(bytes1, bytes2); + default: + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("valueType", valueType, "Unexpected value type: {0}".FormatWith(CultureInfo.InvariantCulture, valueType)); + } + } + + private static int CompareFloat(object objA, object objB) + { + double d1 = Convert.ToDouble(objA, CultureInfo.InvariantCulture); + double d2 = Convert.ToDouble(objB, CultureInfo.InvariantCulture); + + // take into account possible floating point errors + if (MathUtils.ApproxEquals(d1, d2)) + return 0; + + return d1.CompareTo(d2); + } + +#if !(NET35 || NET20 || WINDOWS_PHONE) + private static bool Operation(ExpressionType operation, object objA, object objB, out object result) + { + if (objA is string || objB is string) + { + if (operation == ExpressionType.Add || operation == ExpressionType.AddAssign) + { + result = ((objA != null) ? objA.ToString() : null) + ((objB != null) ? objB.ToString() : null); + return true; + } + } + + if (objA is ulong || objB is ulong || objA is decimal || objB is decimal) + { + if (objA == null || objB == null) + { + result = null; + return true; + } + + decimal d1 = Convert.ToDecimal(objA, CultureInfo.InvariantCulture); + decimal d2 = Convert.ToDecimal(objB, CultureInfo.InvariantCulture); + + switch (operation) + { + case ExpressionType.Add: + case ExpressionType.AddAssign: + result = d1 + d2; + return true; + case ExpressionType.Subtract: + case ExpressionType.SubtractAssign: + result = d1 - d2; + return true; + case ExpressionType.Multiply: + case ExpressionType.MultiplyAssign: + result = d1 * d2; + return true; + case ExpressionType.Divide: + case ExpressionType.DivideAssign: + result = d1 / d2; + return true; + } + } + else if (objA is float || objB is float || objA is double || objB is double) + { + if (objA == null || objB == null) + { + result = null; + return true; + } + + double d1 = Convert.ToDouble(objA, CultureInfo.InvariantCulture); + double d2 = Convert.ToDouble(objB, CultureInfo.InvariantCulture); + + switch (operation) + { + case ExpressionType.Add: + case ExpressionType.AddAssign: + result = d1 + d2; + return true; + case ExpressionType.Subtract: + case ExpressionType.SubtractAssign: + result = d1 - d2; + return true; + case ExpressionType.Multiply: + case ExpressionType.MultiplyAssign: + result = d1 * d2; + return true; + case ExpressionType.Divide: + case ExpressionType.DivideAssign: + result = d1 / d2; + return true; + } + } + else if (objA is int || objA is uint || objA is long || objA is short || objA is ushort || objA is sbyte || objA is byte || + objB is int || objB is uint || objB is long || objB is short || objB is ushort || objB is sbyte || objB is byte) + { + if (objA == null || objB == null) + { + result = null; + return true; + } + + long l1 = Convert.ToInt64(objA, CultureInfo.InvariantCulture); + long l2 = Convert.ToInt64(objB, CultureInfo.InvariantCulture); + + switch (operation) + { + case ExpressionType.Add: + case ExpressionType.AddAssign: + result = l1 + l2; + return true; + case ExpressionType.Subtract: + case ExpressionType.SubtractAssign: + result = l1 - l2; + return true; + case ExpressionType.Multiply: + case ExpressionType.MultiplyAssign: + result = l1 * l2; + return true; + case ExpressionType.Divide: + case ExpressionType.DivideAssign: + result = l1 / l2; + return true; + } + } + + result = null; + return false; + } +#endif + + internal override JToken CloneToken() + { + return new JValue(this); + } + + /// + /// Creates a comment with the given value. + /// + /// The value. + /// A comment with the given value. + public static JValue CreateComment(string value) + { + return new JValue(value, JTokenType.Comment); + } + + /// + /// Creates a string with the given value. + /// + /// The value. + /// A string with the given value. + public static JValue CreateString(string value) + { + return new JValue(value, JTokenType.String); + } + + private static JTokenType GetValueType(JTokenType? current, object value) + { + if (value == null) + return JTokenType.Null; + else if (value == DBNull.Value) + return JTokenType.Null; + else if (value is string) + return GetStringValueType(current); + else if (value is long || value is int || value is short || value is sbyte + || value is ulong || value is uint || value is ushort || value is byte) + return JTokenType.Integer; + else if (value is Enum) + return JTokenType.Integer; + else if (value is double || value is float || value is decimal) + return JTokenType.Float; + else if (value is DateTime) + return JTokenType.Date; +#if !PocketPC && !NET20 + else if (value is DateTimeOffset) + return JTokenType.Date; +#endif + else if (value is byte[]) + return JTokenType.Bytes; + else if (value is bool) + return JTokenType.Boolean; + + throw new ArgumentException("Could not determine JSON object type for type {0}.".FormatWith(CultureInfo.InvariantCulture, value.GetType())); + } + + private static JTokenType GetStringValueType(JTokenType? current) + { + if (current == null) + return JTokenType.String; + + switch (current.Value) + { + case JTokenType.Comment: + case JTokenType.String: + case JTokenType.Raw: + return current.Value; + default: + return JTokenType.String; + } + } + + /// + /// Gets the node type for this . + /// + /// The type. + public override JTokenType Type + { + get { return _valueType; } + } + + /// + /// Gets or sets the underlying token value. + /// + /// The underlying token value. + public object Value + { + get { return _value; } + set + { + Type currentType = (_value != null) ? _value.GetType() : null; + Type newType = (value != null) ? value.GetType() : null; + + if (currentType != newType) + _valueType = GetValueType(_valueType, value); + + _value = value; + } + } + + /// + /// Writes this token to a . + /// + /// A into which this method will write. + /// A collection of which will be used when writing the token. + public override void WriteTo(JsonWriter writer, params JsonConverter[] converters) + { + switch (_valueType) + { + case JTokenType.Comment: + writer.WriteComment(_value.ToString()); + return; + case JTokenType.Raw: + writer.WriteRawValue((_value != null) ? _value.ToString() : null); + return; + case JTokenType.Null: + writer.WriteNull(); + return; + case JTokenType.Undefined: + writer.WriteUndefined(); + return; + } + + JsonConverter matchingConverter; + if (_value != null && ((matchingConverter = JsonSerializer.GetMatchingConverter(converters, _value.GetType())) != null)) + { + matchingConverter.WriteJson(writer, _value, new JsonSerializer()); + return; + } + + switch (_valueType) + { + case JTokenType.Integer: + writer.WriteValue(Convert.ToInt64(_value, CultureInfo.InvariantCulture)); + return; + case JTokenType.Float: + writer.WriteValue(Convert.ToDouble(_value, CultureInfo.InvariantCulture)); + return; + case JTokenType.String: + writer.WriteValue((_value != null) ? _value.ToString() : null); + return; + case JTokenType.Boolean: + writer.WriteValue(Convert.ToBoolean(_value, CultureInfo.InvariantCulture)); + return; + case JTokenType.Date: +#if !PocketPC && !NET20 + if (_value is DateTimeOffset) + writer.WriteValue((DateTimeOffset)_value); + else +#endif + writer.WriteValue(Convert.ToDateTime(_value, CultureInfo.InvariantCulture)); ; + return; + case JTokenType.Bytes: + writer.WriteValue((byte[])_value); + return; + } + + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("TokenType", _valueType, "Unexpected token type."); + } + + internal override int GetDeepHashCode() + { + int valueHashCode = (_value != null) ? _value.GetHashCode() : 0; + + return _valueType.GetHashCode() ^ valueHashCode; + } + + private static bool ValuesEquals(JValue v1, JValue v2) + { + return (v1 == v2 || (v1._valueType == v2._valueType && Compare(v1._valueType, v1._value, v2._value) == 0)); + } + + /// + /// Indicates whether the current object is equal to another object of the same type. + /// + /// + /// true if the current object is equal to the parameter; otherwise, false. + /// + /// An object to compare with this object. + public bool Equals(JValue other) + { + if (other == null) + return false; + + return ValuesEquals(this, other); + } + + /// + /// Determines whether the specified is equal to the current . + /// + /// The to compare with the current . + /// + /// true if the specified is equal to the current ; otherwise, false. + /// + /// + /// The parameter is null. + /// + public override bool Equals(object obj) + { + if (obj == null) + return false; + + JValue otherValue = obj as JValue; + if (otherValue != null) + return Equals(otherValue); + + return base.Equals(obj); + } + + /// + /// Serves as a hash function for a particular type. + /// + /// + /// A hash code for the current . + /// + public override int GetHashCode() + { + if (_value == null) + return 0; + + return _value.GetHashCode(); + } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + if (_value == null) + return string.Empty; + + return _value.ToString(); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// + /// A that represents this instance. + /// + public string ToString(string format) + { + return ToString(format, CultureInfo.CurrentCulture); + } + + /// + /// Returns a that represents this instance. + /// + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(IFormatProvider formatProvider) + { + return ToString(null, formatProvider); + } + + /// + /// Returns a that represents this instance. + /// + /// The format. + /// The format provider. + /// + /// A that represents this instance. + /// + public string ToString(string format, IFormatProvider formatProvider) + { + if (_value == null) + return string.Empty; + + IFormattable formattable = _value as IFormattable; + if (formattable != null) + return formattable.ToString(format, formatProvider); + else + return _value.ToString(); + } + +#if !(NET35 || NET20 || WINDOWS_PHONE) + /// + /// Returns the responsible for binding operations performed on this object. + /// + /// The expression tree representation of the runtime value. + /// + /// The to bind this object. + /// + protected override DynamicMetaObject GetMetaObject(Expression parameter) + { + return new DynamicProxyMetaObject(parameter, this, new JValueDynamicProxy(), true); + } + + private class JValueDynamicProxy : DynamicProxy + { + public override bool TryConvert(JValue instance, ConvertBinder binder, out object result) + { + if (binder.Type == typeof(JValue)) + { + result = instance; + return true; + } + + object value = instance.Value; + + if (value == null) + { + result = null; + return ReflectionUtils.IsNullable(binder.Type); + } + + result = ConvertUtils.Convert(instance.Value, CultureInfo.InvariantCulture, binder.Type); + return true; + } + + public override bool TryBinaryOperation(JValue instance, BinaryOperationBinder binder, object arg, out object result) + { + object compareValue = (arg is JValue) ? ((JValue) arg).Value : arg; + + switch (binder.Operation) + { + case ExpressionType.Equal: + result = (Compare(instance.Type, instance.Value, compareValue) == 0); + return true; + case ExpressionType.NotEqual: + result = (Compare(instance.Type, instance.Value, compareValue) != 0); + return true; + case ExpressionType.GreaterThan: + result = (Compare(instance.Type, instance.Value, compareValue) > 0); + return true; + case ExpressionType.GreaterThanOrEqual: + result = (Compare(instance.Type, instance.Value, compareValue) >= 0); + return true; + case ExpressionType.LessThan: + result = (Compare(instance.Type, instance.Value, compareValue) < 0); + return true; + case ExpressionType.LessThanOrEqual: + result = (Compare(instance.Type, instance.Value, compareValue) <= 0); + return true; + case ExpressionType.Add: + case ExpressionType.AddAssign: + case ExpressionType.Subtract: + case ExpressionType.SubtractAssign: + case ExpressionType.Multiply: + case ExpressionType.MultiplyAssign: + case ExpressionType.Divide: + case ExpressionType.DivideAssign: + if (Operation(binder.Operation, instance.Value, compareValue, out result)) + { + result = new JValue(result); + return true; + } + break; + } + + result = null; + return false; + } + } +#endif + + int IComparable.CompareTo(object obj) + { + if (obj == null) + return 1; + + object otherValue = (obj is JValue) ? ((JValue) obj).Value : obj; + + return Compare(_valueType, _value, otherValue); + } + + /// + /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. + /// + /// An object to compare with this instance. + /// + /// A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings: + /// Value + /// Meaning + /// Less than zero + /// This instance is less than . + /// Zero + /// This instance is equal to . + /// Greater than zero + /// This instance is greater than . + /// + /// + /// is not the same type as this instance. + /// + public int CompareTo(JValue obj) + { + if (obj == null) + return 1; + + return Compare(_valueType, _value, obj._value); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/MemberSerialization.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/MemberSerialization.cs new file mode 100644 index 0000000..00aeb2c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/MemberSerialization.cs @@ -0,0 +1,47 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Specifies the member serialization options for the . + /// + public enum MemberSerialization + { + /// + /// All members are serialized by default. Members can be excluded using the . + /// + OptOut, + /// + /// Only members must be marked with the are serialized. + /// + OptIn + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/MissingMemberHandling.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/MissingMemberHandling.cs new file mode 100644 index 0000000..d61e1e6 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/MissingMemberHandling.cs @@ -0,0 +1,46 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Specifies missing member handling options for the . + /// + public enum MissingMemberHandling + { + /// + /// Ignore a missing member and do not attempt to deserialize it. + /// + Ignore = 0, + /// + /// Throw a when a missing member is encountered during deserialization. + /// + Error = 1 + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj new file mode 100644 index 0000000..fd6fb43 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.Net20.csproj @@ -0,0 +1,252 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D} + Library + Properties + Newtonsoft.Json + Newtonsoft.Json.Net20 + false + + + + + + + + + + + + + 3.5 + + + false + v2.0 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + true + full + false + bin\Debug\Net20\ + TRACE;DEBUG;NET20 + prompt + 4 + bin\Debug\Net20\Newtonsoft.Json.Net20.xml + true + + + Newtonsoft.Json.ruleset + + + pdbonly + true + bin\Release\Net20\ + TRACE;NET20 + prompt + 4 + bin\Release\Net20\Newtonsoft.Json.Net20.xml + AllRules.ruleset + + + + False + ..\Lib\LinqBridge.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + false + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj new file mode 100644 index 0000000..b61ef8a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.Net35.csproj @@ -0,0 +1,267 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D} + Library + Properties + Newtonsoft.Json.Net35 + Newtonsoft.Json.Net35 + false + + + + + + + + + + + + + 3.5 + + + false + v3.5 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + Client + + + true + full + false + bin\Debug\Net35\ + TRACE;DEBUG;CODE_ANALYSIS;NET35 + prompt + 4 + bin\Debug\Net35\Newtonsoft.Json.Net35.xml + true + + + Newtonsoft.Json.ruleset + + + pdbonly + true + bin\Release\Net35\ + TRACE;NET35 + prompt + 4 + bin\Release\Net35\Newtonsoft.Json.Net35.xml + ..\..\..\..\trunk\Src\Newtonsoft.Json\Newtonsoft.Json.ruleset + false + + + + + 3.5 + + + + 3.0 + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj new file mode 100644 index 0000000..6c5379d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.Silverlight.csproj @@ -0,0 +1,267 @@ + + + + v3.5 + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {DC3C6F3D-2CA1-4278-9B79-63770FB3EA2D} + {A1591282-1198-4647-A2B1-27E5FF5F6F3B};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Newtonsoft.Json + Newtonsoft.Json.Silverlight + v4.0 + false + true + false + + + 3.5 + + + Silverlight + $(TargetFrameworkVersion) + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + + false + + + true + full + false + Bin\Debug\Silverlight\ + DEBUG;TRACE;SILVERLIGHT + true + true + prompt + 4 + Bin\Debug\Silverlight\Newtonsoft.Json.Silverlight.xml + Newtonsoft.Json.ruleset + true + + + pdbonly + true + Bin\Release\Silverlight\ + TRACE;SILVERLIGHT + true + true + prompt + 4 + Bin\Release\Silverlight\Newtonsoft.Json.Silverlight.xml + AllRules.ruleset + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj new file mode 100644 index 0000000..939848d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.WindowsPhone.csproj @@ -0,0 +1,215 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {7A7F70AB-5C07-47ED-BDD2-ECC14DBACA5E} + {C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} + Library + Properties + Newtonsoft.Json.WindowsPhone + Newtonsoft.Json.WindowsPhone + v4.0 + $(TargetFrameworkVersion) + WindowsPhone + Silverlight + false + true + true + + + true + full + false + Bin\Debug\WindowsPhone\ + DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE + true + true + prompt + 4 + + + + + pdbonly + true + Bin\Release\WindowsPhone\ + TRACE;SILVERLIGHT;WINDOWS_PHONE + true + true + prompt + 4 + Bin\Release\WindowsPhone\Newtonsoft.Json.WindowsPhone.xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.csproj b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.csproj new file mode 100644 index 0000000..a69324b --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.csproj @@ -0,0 +1,266 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D} + Library + Properties + Newtonsoft.Json + Newtonsoft.Json + false + + + + + + + + + + + + + 3.5 + + + false + v4.0 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + Client + + + true + full + false + bin\Debug\Net\ + DEBUG;TRACE + prompt + 4 + bin\Debug\Net\Newtonsoft.Json.xml + true + + + Newtonsoft.Json.ruleset + + + pdbonly + true + bin\Release\Net\ + TRACE + prompt + 4 + bin\Release\Net\Newtonsoft.Json.xml + AllRules.ruleset + + + + + 3.5 + + + + 3.0 + + + + 3.5 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 2.0 %28x86%29 + true + + + False + .NET Framework 3.0 %28x86%29 + false + + + False + .NET Framework 3.5 + false + + + False + .NET Framework 3.5 SP1 + false + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.ruleset b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.ruleset new file mode 100644 index 0000000..d80dec1 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Newtonsoft.Json.ruleset @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/NullValueHandling.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/NullValueHandling.cs new file mode 100644 index 0000000..09fb8c6 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/NullValueHandling.cs @@ -0,0 +1,47 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Specifies null value handling options for the . + /// + public enum NullValueHandling + { + /// + /// Include null values when serializing and deserializing objects. + /// + Include = 0, + /// + /// Ignore null values when serializing and deserializing objects. + /// + Ignore = 1 + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/ObjectCreationHandling.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/ObjectCreationHandling.cs new file mode 100644 index 0000000..dc22666 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/ObjectCreationHandling.cs @@ -0,0 +1,51 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Specifies how object creation is handled by the . + /// + public enum ObjectCreationHandling + { + /// + /// Reuse existing objects, create new objects when needed. + /// + Auto = 0, + /// + /// Only reuse existing objects. + /// + Reuse = 1, + /// + /// Always create new objects. + /// + Replace = 2 + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/PreserveReferencesHandling.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/PreserveReferencesHandling.cs new file mode 100644 index 0000000..f74d15c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/PreserveReferencesHandling.cs @@ -0,0 +1,55 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Specifies reference handling options for the . + /// + [Flags] + public enum PreserveReferencesHandling + { + /// + /// Do not preserve references when serializing types. + /// + None = 0, + /// + /// Preserve references when serializing into a JSON object structure. + /// + Objects = 1, + /// + /// Preserve references when serializing into a JSON array structure. + /// + Arrays = 2, + /// + /// Preserve references when serializing. + /// + All = Objects | Arrays + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..14c7126 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Properties/AssemblyInfo.cs @@ -0,0 +1,116 @@ +#region License +// Copyright 2006 James Newton-King +// http://www.newtonsoft.com +// +// This work is licensed under the Creative Commons Attribution 2.5 License +// http://creativecommons.org/licenses/by/2.5/ +// +// You are free: +// * to copy, distribute, display, and perform the work +// * to make derivative works +// * to make commercial use of the work +// +// Under the following conditions: +// * You must attribute the work in the manner specified by the author or licensor: +// - If you find this component useful a link to http://www.newtonsoft.com would be appreciated. +// * For any reuse or distribution, you must make clear to others the license terms of this work. +// * Any of these conditions can be waived if you get permission from the copyright holder. +#endregion + +using System; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Security; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +#if WINDOWS_PHONE +[assembly: AssemblyTitle("Newtonsoft Json.NET Windows Phone")] +#elif SILVERLIGHT +[assembly: AssemblyTitle("Newtonsoft Json.NET Silverlight")] +#elif PocketPC +[assembly: AssemblyTitle("Newtonsoft Json.NET Compact")] +#elif NET20 +[assembly: AssemblyTitle("Newtonsoft Json.NET .NET 2.0")] +[assembly: AllowPartiallyTrustedCallers] +#elif NET35 +[assembly: AssemblyTitle("Newtonsoft Json.NET .NET 3.5")] +[assembly: AllowPartiallyTrustedCallers] +#else +[assembly: AssemblyTitle("Newtonsoft Json.NET")] +[assembly: AllowPartiallyTrustedCallers] +#endif + +#if !SIGNED + +#if WINDOWS_PHONE +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests.WindowsPhone")] +#elif SILVERLIGHT +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests.Silverlight")] +#elif PocketPC +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests.Compact")] +#elif NET20 +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests.Net20")] +#elif NET35 +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests.Net35")] +#else +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests")] +#endif + +#else + +#if WINDOWS_PHONE +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests.WindowsPhone, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f561df277c6c0b497d629032b410cdcf286e537c054724f7ffa0164345f62b3e642029d7a80cc351918955328c4adc8a048823ef90b0cf38ea7db0d729caf2b633c3babe08b0310198c1081995c19029bc675193744eab9d7345b8a67258ec17d112cebdbbb2a281487dceeafb9d83aa930f32103fbe1d2911425bc5744002c7")] +#elif SILVERLIGHT +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests.Silverlight, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f561df277c6c0b497d629032b410cdcf286e537c054724f7ffa0164345f62b3e642029d7a80cc351918955328c4adc8a048823ef90b0cf38ea7db0d729caf2b633c3babe08b0310198c1081995c19029bc675193744eab9d7345b8a67258ec17d112cebdbbb2a281487dceeafb9d83aa930f32103fbe1d2911425bc5744002c7")] +#elif PocketPC +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests.Compact, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f561df277c6c0b497d629032b410cdcf286e537c054724f7ffa0164345f62b3e642029d7a80cc351918955328c4adc8a048823ef90b0cf38ea7db0d729caf2b633c3babe08b0310198c1081995c19029bc675193744eab9d7345b8a67258ec17d112cebdbbb2a281487dceeafb9d83aa930f32103fbe1d2911425bc5744002c7")] +#elif NET20 +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests.Net20, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f561df277c6c0b497d629032b410cdcf286e537c054724f7ffa0164345f62b3e642029d7a80cc351918955328c4adc8a048823ef90b0cf38ea7db0d729caf2b633c3babe08b0310198c1081995c19029bc675193744eab9d7345b8a67258ec17d112cebdbbb2a281487dceeafb9d83aa930f32103fbe1d2911425bc5744002c7")] +#elif NET35 +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests.Net35, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f561df277c6c0b497d629032b410cdcf286e537c054724f7ffa0164345f62b3e642029d7a80cc351918955328c4adc8a048823ef90b0cf38ea7db0d729caf2b633c3babe08b0310198c1081995c19029bc675193744eab9d7345b8a67258ec17d112cebdbbb2a281487dceeafb9d83aa930f32103fbe1d2911425bc5744002c7")] +#else +[assembly: InternalsVisibleTo("Newtonsoft.Json.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f561df277c6c0b497d629032b410cdcf286e537c054724f7ffa0164345f62b3e642029d7a80cc351918955328c4adc8a048823ef90b0cf38ea7db0d729caf2b633c3babe08b0310198c1081995c19029bc675193744eab9d7345b8a67258ec17d112cebdbbb2a281487dceeafb9d83aa930f32103fbe1d2911425bc5744002c7")] +#endif + +#endif + + +[assembly: InternalsVisibleTo("Newtonsoft.Json.Dynamic, PublicKey=0024000004800000940000000602000000240000525341310004000001000100cbd8d53b9d7de30f1f1278f636ec462cf9c254991291e66ebb157a885638a517887633b898ccbcf0d5c5ff7be85a6abe9e765d0ac7cd33c68dac67e7e64530e8222101109f154ab14a941c490ac155cd1d4fcba0fabb49016b4ef28593b015cab5937da31172f03f67d09edda404b88a60023f062ae71d0b2e4438b74cc11dc9")] + + + +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Newtonsoft")] +[assembly: AssemblyProduct("Newtonsoft Json.NET")] +[assembly: AssemblyCopyright("Copyright © Newtonsoft 2008")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM componenets. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9ca358aa-317b-4925-8ada-4a29e943a363")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("4.0.2.0")] +#if !PocketPC +[assembly: AssemblyFileVersion("4.0.2.13623")] +#endif + +[assembly: CLSCompliant(true)] diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/ReferenceLoopHandling.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/ReferenceLoopHandling.cs new file mode 100644 index 0000000..01d03b1 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/ReferenceLoopHandling.cs @@ -0,0 +1,50 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Specifies reference loop handling options for the . + /// + public enum ReferenceLoopHandling + { + /// + /// Throw a when a loop is encountered. + /// + Error = 0, + /// + /// Ignore loop references and do not serialize. + /// + Ignore = 1, + /// + /// Serialize loop references. + /// + Serialize = 2 + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Required.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Required.cs new file mode 100644 index 0000000..1db6bfa --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Required.cs @@ -0,0 +1,21 @@ +namespace Newtonsoft.Json +{ + /// + /// Indicating whether a property is required. + /// + public enum Required + { + /// + /// The property is not required. The default state. + /// + Default, + /// + /// The property must be defined in JSON but can be a null value. + /// + AllowNull, + /// + /// The property must be defined in JSON and cannot be a null value. + /// + Always + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/Extensions.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/Extensions.cs new file mode 100644 index 0000000..4784b43 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/Extensions.cs @@ -0,0 +1,88 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Schema +{ + /// + /// Contains the JSON schema extension methods. + /// + public static class Extensions + { + /// + /// Determines whether the is valid. + /// + /// The source to test. + /// The schema to test with. + /// + /// true if the specified is valid; otherwise, false. + /// + public static bool IsValid(this JToken source, JsonSchema schema) + { + bool valid = true; + source.Validate(schema, (sender, args) => { valid = false; }); + return valid; + } + + /// + /// Validates the specified . + /// + /// The source to test. + /// The schema to test with. + public static void Validate(this JToken source, JsonSchema schema) + { + source.Validate(schema, null); + } + + /// + /// Validates the specified . + /// + /// The source to test. + /// The schema to test with. + /// The validation event handler. + public static void Validate(this JToken source, JsonSchema schema, ValidationEventHandler validationEventHandler) + { + ValidationUtils.ArgumentNotNull(source, "source"); + ValidationUtils.ArgumentNotNull(schema, "schema"); + + using (JsonValidatingReader reader = new JsonValidatingReader(source.CreateReader())) + { + reader.Schema = schema; + if (validationEventHandler != null) + reader.ValidationEventHandler += validationEventHandler; + + while (reader.Read()) + { + } + } + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchema.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchema.cs new file mode 100644 index 0000000..d016538 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchema.cs @@ -0,0 +1,299 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Schema +{ + /// + /// An in-memory representation of a JSON Schema. + /// + public class JsonSchema + { + /// + /// Gets or sets the id. + /// + public string Id { get; set; } + /// + /// Gets or sets the title. + /// + public string Title { get; set; } + /// + /// Gets or sets whether the object is required. + /// + public bool? Required { get; set; } + /// + /// Gets or sets whether the object is read only. + /// + public bool? ReadOnly { get; set; } + /// + /// Gets or sets whether the object is visible to users. + /// + public bool? Hidden { get; set; } + /// + /// Gets or sets whether the object is transient. + /// + public bool? Transient { get; set; } + /// + /// Gets or sets the description of the object. + /// + public string Description { get; set; } + /// + /// Gets or sets the types of values allowed by the object. + /// + /// The type. + public JsonSchemaType? Type { get; set; } + /// + /// Gets or sets the pattern. + /// + /// The pattern. + public string Pattern { get; set; } + /// + /// Gets or sets the minimum length. + /// + /// The minimum length. + public int? MinimumLength { get; set; } + /// + /// Gets or sets the maximum length. + /// + /// The maximum length. + public int? MaximumLength { get; set; } + /// + /// Gets or sets a number that the value should be divisble by. + /// + /// A number that the value should be divisble by. + public double? DivisibleBy { get; set; } + /// + /// Gets or sets the minimum. + /// + /// The minimum. + public double? Minimum { get; set; } + /// + /// Gets or sets the maximum. + /// + /// The maximum. + public double? Maximum { get; set; } + /// + /// Gets or sets a flag indicating whether the value can not equal the number defined by the "minimum" attribute. + /// + /// A flag indicating whether the value can not equal the number defined by the "minimum" attribute. + public bool? ExclusiveMinimum { get; set; } + /// + /// Gets or sets a flag indicating whether the value can not equal the number defined by the "maximum" attribute. + /// + /// A flag indicating whether the value can not equal the number defined by the "maximum" attribute. + public bool? ExclusiveMaximum { get; set; } + /// + /// Gets or sets the minimum number of items. + /// + /// The minimum number of items. + public int? MinimumItems { get; set; } + /// + /// Gets or sets the maximum number of items. + /// + /// The maximum number of items. + public int? MaximumItems { get; set; } + /// + /// Gets or sets the of items. + /// + /// The of items. + public IList Items { get; set; } + /// + /// Gets or sets the of properties. + /// + /// The of properties. + public IDictionary Properties { get; set; } + /// + /// Gets or sets the of additional properties. + /// + /// The of additional properties. + public JsonSchema AdditionalProperties { get; set; } + /// + /// Gets or sets the pattern properties. + /// + /// The pattern properties. + public IDictionary PatternProperties { get; set; } + /// + /// Gets or sets a value indicating whether additional properties are allowed. + /// + /// + /// true if additional properties are allowed; otherwise, false. + /// + public bool AllowAdditionalProperties { get; set; } + /// + /// Gets or sets the required property if this property is present. + /// + /// The required property if this property is present. + public string Requires { get; set; } + /// + /// Gets or sets the identity. + /// + /// The identity. + public IList Identity { get; set; } + /// + /// Gets or sets the a collection of valid enum values allowed. + /// + /// A collection of valid enum values allowed. + public IList Enum { get; set; } + /// + /// Gets or sets a collection of options. + /// + /// A collection of options. + public IDictionary Options { get; set; } + /// + /// Gets or sets disallowed types. + /// + /// The disallow types. + public JsonSchemaType? Disallow { get; set; } + /// + /// Gets or sets the default value. + /// + /// The default value. + public JToken Default { get; set; } + /// + /// Gets or sets the extend . + /// + /// The extended . + public JsonSchema Extends { get; set; } + /// + /// Gets or sets the format. + /// + /// The format. + public string Format { get; set; } + + private readonly string _internalId = Guid.NewGuid().ToString("N"); + + internal string InternalId + { + get { return _internalId; } + } + + /// + /// Initializes a new instance of the class. + /// + public JsonSchema() + { + AllowAdditionalProperties = true; + } + + /// + /// Reads a from the specified . + /// + /// The containing the JSON Schema to read. + /// The object representing the JSON Schema. + public static JsonSchema Read(JsonReader reader) + { + return Read(reader, new JsonSchemaResolver()); + } + + /// + /// Reads a from the specified . + /// + /// The containing the JSON Schema to read. + /// The to use when resolving schema references. + /// The object representing the JSON Schema. + public static JsonSchema Read(JsonReader reader, JsonSchemaResolver resolver) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + ValidationUtils.ArgumentNotNull(resolver, "resolver"); + + JsonSchemaBuilder builder = new JsonSchemaBuilder(resolver); + return builder.Parse(reader); + } + + /// + /// Load a from a string that contains schema JSON. + /// + /// A that contains JSON. + /// A populated from the string that contains JSON. + public static JsonSchema Parse(string json) + { + return Parse(json, new JsonSchemaResolver()); + } + + /// + /// Parses the specified json. + /// + /// The json. + /// The resolver. + /// A populated from the string that contains JSON. + public static JsonSchema Parse(string json, JsonSchemaResolver resolver) + { + ValidationUtils.ArgumentNotNull(json, "json"); + + JsonReader reader = new JsonTextReader(new StringReader(json)); + + return Read(reader, resolver); + } + + /// + /// Writes this schema to a . + /// + /// A into which this method will write. + public void WriteTo(JsonWriter writer) + { + WriteTo(writer, new JsonSchemaResolver()); + } + + /// + /// Writes this schema to a using the specified . + /// + /// A into which this method will write. + /// The resolver used. + public void WriteTo(JsonWriter writer, JsonSchemaResolver resolver) + { + ValidationUtils.ArgumentNotNull(writer, "writer"); + ValidationUtils.ArgumentNotNull(resolver, "resolver"); + + JsonSchemaWriter schemaWriter = new JsonSchemaWriter(writer, resolver); + schemaWriter.WriteSchema(this); + } + + /// + /// Returns a that represents the current . + /// + /// + /// A that represents the current . + /// + public override string ToString() + { + StringWriter writer = new StringWriter(CultureInfo.InvariantCulture); + JsonTextWriter jsonWriter = new JsonTextWriter(writer); + jsonWriter.Formatting = Formatting.Indented; + + WriteTo(jsonWriter); + + return writer.ToString(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaBuilder.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaBuilder.cs new file mode 100644 index 0000000..702ec1e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaBuilder.cs @@ -0,0 +1,423 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Globalization; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Schema +{ + internal class JsonSchemaBuilder + { + private JsonReader _reader; + private readonly IList _stack; + private readonly JsonSchemaResolver _resolver; + private JsonSchema _currentSchema; + + private void Push(JsonSchema value) + { + _currentSchema = value; + _stack.Add(value); + _resolver.LoadedSchemas.Add(value); + } + + private JsonSchema Pop() + { + JsonSchema poppedSchema = _currentSchema; + _stack.RemoveAt(_stack.Count - 1); + _currentSchema = _stack.LastOrDefault(); + + return poppedSchema; + } + + private JsonSchema CurrentSchema + { + get { return _currentSchema; } + } + + public JsonSchemaBuilder(JsonSchemaResolver resolver) + { + _stack = new List(); + _resolver = resolver; + } + + internal JsonSchema Parse(JsonReader reader) + { + _reader = reader; + + if (reader.TokenType == JsonToken.None) + _reader.Read(); + + return BuildSchema(); + } + + private JsonSchema BuildSchema() + { + if (_reader.TokenType != JsonToken.StartObject) + throw new Exception("Expected StartObject while parsing schema object, got {0}.".FormatWith(CultureInfo.InvariantCulture, _reader.TokenType)); + + _reader.Read(); + // empty schema object + if (_reader.TokenType == JsonToken.EndObject) + { + Push(new JsonSchema()); + return Pop(); + } + + string propertyName = Convert.ToString(_reader.Value, CultureInfo.InvariantCulture); + _reader.Read(); + + // schema reference + if (propertyName == JsonSchemaConstants.ReferencePropertyName) + { + string id = (string)_reader.Value; + _reader.Read(); + JsonSchema referencedSchema = _resolver.GetSchema(id); + if (referencedSchema == null) + throw new Exception("Could not resolve schema reference for Id '{0}'.".FormatWith(CultureInfo.InvariantCulture, id)); + + return referencedSchema; + } + + // regular ol' schema object + Push(new JsonSchema()); + + ProcessSchemaProperty(propertyName); + + while (_reader.Read() && _reader.TokenType != JsonToken.EndObject) + { + propertyName = Convert.ToString(_reader.Value, CultureInfo.InvariantCulture); + _reader.Read(); + + ProcessSchemaProperty(propertyName); + } + + return Pop(); + } + + private void ProcessSchemaProperty(string propertyName) + { + switch (propertyName) + { + case JsonSchemaConstants.TypePropertyName: + CurrentSchema.Type = ProcessType(); + break; + case JsonSchemaConstants.IdPropertyName: + CurrentSchema.Id = (string) _reader.Value; + break; + case JsonSchemaConstants.TitlePropertyName: + CurrentSchema.Title = (string) _reader.Value; + break; + case JsonSchemaConstants.DescriptionPropertyName: + CurrentSchema.Description = (string)_reader.Value; + break; + case JsonSchemaConstants.PropertiesPropertyName: + ProcessProperties(); + break; + case JsonSchemaConstants.ItemsPropertyName: + ProcessItems(); + break; + case JsonSchemaConstants.AdditionalPropertiesPropertyName: + ProcessAdditionalProperties(); + break; + case JsonSchemaConstants.PatternPropertiesPropertyName: + ProcessPatternProperties(); + break; + case JsonSchemaConstants.RequiredPropertyName: + CurrentSchema.Required = (bool)_reader.Value; + break; + case JsonSchemaConstants.RequiresPropertyName: + CurrentSchema.Requires = (string) _reader.Value; + break; + case JsonSchemaConstants.IdentityPropertyName: + ProcessIdentity(); + break; + case JsonSchemaConstants.MinimumPropertyName: + CurrentSchema.Minimum = Convert.ToDouble(_reader.Value, CultureInfo.InvariantCulture); + break; + case JsonSchemaConstants.MaximumPropertyName: + CurrentSchema.Maximum = Convert.ToDouble(_reader.Value, CultureInfo.InvariantCulture); + break; + case JsonSchemaConstants.ExclusiveMinimumPropertyName: + CurrentSchema.ExclusiveMinimum = (bool)_reader.Value; + break; + case JsonSchemaConstants.ExclusiveMaximumPropertyName: + CurrentSchema.ExclusiveMaximum = (bool)_reader.Value; + break; + case JsonSchemaConstants.MaximumLengthPropertyName: + CurrentSchema.MaximumLength = Convert.ToInt32(_reader.Value, CultureInfo.InvariantCulture); + break; + case JsonSchemaConstants.MinimumLengthPropertyName: + CurrentSchema.MinimumLength = Convert.ToInt32(_reader.Value, CultureInfo.InvariantCulture); + break; + case JsonSchemaConstants.MaximumItemsPropertyName: + CurrentSchema.MaximumItems = Convert.ToInt32(_reader.Value, CultureInfo.InvariantCulture); + break; + case JsonSchemaConstants.MinimumItemsPropertyName: + CurrentSchema.MinimumItems = Convert.ToInt32(_reader.Value, CultureInfo.InvariantCulture); + break; + case JsonSchemaConstants.DivisibleByPropertyName: + CurrentSchema.DivisibleBy = Convert.ToDouble(_reader.Value, CultureInfo.InvariantCulture); + break; + case JsonSchemaConstants.DisallowPropertyName: + CurrentSchema.Disallow = ProcessType(); + break; + case JsonSchemaConstants.DefaultPropertyName: + ProcessDefault(); + break; + case JsonSchemaConstants.HiddenPropertyName: + CurrentSchema.Hidden = (bool) _reader.Value; + break; + case JsonSchemaConstants.ReadOnlyPropertyName: + CurrentSchema.ReadOnly = (bool) _reader.Value; + break; + case JsonSchemaConstants.FormatPropertyName: + CurrentSchema.Format = (string) _reader.Value; + break; + case JsonSchemaConstants.PatternPropertyName: + CurrentSchema.Pattern = (string) _reader.Value; + break; + case JsonSchemaConstants.OptionsPropertyName: + ProcessOptions(); + break; + case JsonSchemaConstants.EnumPropertyName: + ProcessEnum(); + break; + case JsonSchemaConstants.ExtendsPropertyName: + ProcessExtends(); + break; + default: + _reader.Skip(); + break; + } + } + + private void ProcessExtends() + { + CurrentSchema.Extends = BuildSchema(); + } + + private void ProcessEnum() + { + if (_reader.TokenType != JsonToken.StartArray) + throw new Exception("Expected StartArray token while parsing enum values, got {0}.".FormatWith(CultureInfo.InvariantCulture, _reader.TokenType)); + + CurrentSchema.Enum = new List(); + + while (_reader.Read() && _reader.TokenType != JsonToken.EndArray) + { + JToken value = JToken.ReadFrom(_reader); + CurrentSchema.Enum.Add(value); + } + } + + private void ProcessOptions() + { + CurrentSchema.Options = new Dictionary(new JTokenEqualityComparer()); + + switch (_reader.TokenType) + { + case JsonToken.StartArray: + while (_reader.Read() && _reader.TokenType != JsonToken.EndArray) + { + if (_reader.TokenType != JsonToken.StartObject) + throw new Exception("Expect object token, got {0}.".FormatWith(CultureInfo.InvariantCulture, _reader.TokenType)); + + string label = null; + JToken value = null; + + while (_reader.Read() && _reader.TokenType != JsonToken.EndObject) + { + string propertyName = Convert.ToString(_reader.Value, CultureInfo.InvariantCulture); + _reader.Read(); + + switch (propertyName) + { + case JsonSchemaConstants.OptionValuePropertyName: + value = JToken.ReadFrom(_reader); + break; + case JsonSchemaConstants.OptionLabelPropertyName: + label = (string) _reader.Value; + break; + default: + throw new Exception("Unexpected property in JSON schema option: {0}.".FormatWith(CultureInfo.InvariantCulture, propertyName)); + } + } + + if (value == null) + throw new Exception("No value specified for JSON schema option."); + + if (CurrentSchema.Options.ContainsKey(value)) + throw new Exception("Duplicate value in JSON schema option collection: {0}".FormatWith(CultureInfo.InvariantCulture, value)); + + CurrentSchema.Options.Add(value, label); + } + break; + default: + throw new Exception("Expected array token, got {0}.".FormatWith(CultureInfo.InvariantCulture, _reader.TokenType)); + } + } + + private void ProcessDefault() + { + CurrentSchema.Default = JToken.ReadFrom(_reader); + } + + private void ProcessIdentity() + { + CurrentSchema.Identity = new List(); + + switch (_reader.TokenType) + { + case JsonToken.String: + CurrentSchema.Identity.Add(_reader.Value.ToString()); + break; + case JsonToken.StartArray: + while (_reader.Read() && _reader.TokenType != JsonToken.EndArray) + { + if (_reader.TokenType != JsonToken.String) + throw new Exception("Exception JSON property name string token, got {0}.".FormatWith(CultureInfo.InvariantCulture, _reader.TokenType)); + + CurrentSchema.Identity.Add(_reader.Value.ToString()); + } + break; + default: + throw new Exception("Expected array or JSON property name string token, got {0}.".FormatWith(CultureInfo.InvariantCulture, _reader.TokenType)); + } + } + + private void ProcessAdditionalProperties() + { + if (_reader.TokenType == JsonToken.Boolean) + CurrentSchema.AllowAdditionalProperties = (bool)_reader.Value; + else + CurrentSchema.AdditionalProperties = BuildSchema(); + } + + private void ProcessPatternProperties() + { + Dictionary patternProperties = new Dictionary(); + + if (_reader.TokenType != JsonToken.StartObject) + throw new Exception("Expected start object token."); + + while (_reader.Read() && _reader.TokenType != JsonToken.EndObject) + { + string propertyName = Convert.ToString(_reader.Value, CultureInfo.InvariantCulture); + _reader.Read(); + + if (patternProperties.ContainsKey(propertyName)) + throw new Exception("Property {0} has already been defined in schema.".FormatWith(CultureInfo.InvariantCulture, propertyName)); + + patternProperties.Add(propertyName, BuildSchema()); + } + + CurrentSchema.PatternProperties = patternProperties; + } + + private void ProcessItems() + { + CurrentSchema.Items = new List(); + + switch (_reader.TokenType) + { + case JsonToken.StartObject: + CurrentSchema.Items.Add(BuildSchema()); + break; + case JsonToken.StartArray: + while (_reader.Read() && _reader.TokenType != JsonToken.EndArray) + { + CurrentSchema.Items.Add(BuildSchema()); + } + break; + default: + throw new Exception("Expected array or JSON schema object token, got {0}.".FormatWith(CultureInfo.InvariantCulture, _reader.TokenType)); + } + } + + private void ProcessProperties() + { + IDictionary properties = new Dictionary(); + + if (_reader.TokenType != JsonToken.StartObject) + throw new Exception("Expected StartObject token while parsing schema properties, got {0}.".FormatWith(CultureInfo.InvariantCulture, _reader.TokenType)); + + while (_reader.Read() && _reader.TokenType != JsonToken.EndObject) + { + string propertyName = Convert.ToString(_reader.Value, CultureInfo.InvariantCulture); + _reader.Read(); + + if (properties.ContainsKey(propertyName)) + throw new Exception("Property {0} has already been defined in schema.".FormatWith(CultureInfo.InvariantCulture, propertyName)); + + properties.Add(propertyName, BuildSchema()); + } + + CurrentSchema.Properties = properties; + } + + private JsonSchemaType? ProcessType() + { + switch (_reader.TokenType) + { + case JsonToken.String: + return MapType(_reader.Value.ToString()); + case JsonToken.StartArray: + // ensure type is in blank state before ORing values + JsonSchemaType? type = JsonSchemaType.None; + + while (_reader.Read() && _reader.TokenType != JsonToken.EndArray) + { + if (_reader.TokenType != JsonToken.String) + throw new Exception("Exception JSON schema type string token, got {0}.".FormatWith(CultureInfo.InvariantCulture, _reader.TokenType)); + + type = type | MapType(_reader.Value.ToString()); + } + + return type; + default: + throw new Exception("Expected array or JSON schema type string token, got {0}.".FormatWith(CultureInfo.InvariantCulture, _reader.TokenType)); + } + } + + internal static JsonSchemaType MapType(string type) + { + JsonSchemaType mappedType; + if (!JsonSchemaConstants.JsonSchemaTypeMapping.TryGetValue(type, out mappedType)) + throw new Exception("Invalid JSON schema type: {0}".FormatWith(CultureInfo.InvariantCulture, type)); + + return mappedType; + } + + internal static string MapType(JsonSchemaType type) + { + return JsonSchemaConstants.JsonSchemaTypeMapping.Single(kv => kv.Value == type).Key; + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaConstants.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaConstants.cs new file mode 100644 index 0000000..b9f58ff --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaConstants.cs @@ -0,0 +1,83 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Schema +{ + internal static class JsonSchemaConstants + { + public const string TypePropertyName = "type"; + public const string PropertiesPropertyName = "properties"; + public const string ItemsPropertyName = "items"; + public const string RequiredPropertyName = "required"; + public const string PatternPropertiesPropertyName = "patternProperties"; + public const string AdditionalPropertiesPropertyName = "additionalProperties"; + public const string RequiresPropertyName = "requires"; + public const string IdentityPropertyName = "identity"; + public const string MinimumPropertyName = "minimum"; + public const string MaximumPropertyName = "maximum"; + public const string ExclusiveMinimumPropertyName = "exclusiveMinimum"; + public const string ExclusiveMaximumPropertyName = "exclusiveMaximum"; + public const string MinimumItemsPropertyName = "minItems"; + public const string MaximumItemsPropertyName = "maxItems"; + public const string PatternPropertyName = "pattern"; + public const string MaximumLengthPropertyName = "maxLength"; + public const string MinimumLengthPropertyName = "minLength"; + public const string EnumPropertyName = "enum"; + public const string OptionsPropertyName = "options"; + public const string ReadOnlyPropertyName = "readonly"; + public const string TitlePropertyName = "title"; + public const string DescriptionPropertyName = "description"; + public const string FormatPropertyName = "format"; + public const string DefaultPropertyName = "default"; + public const string TransientPropertyName = "transient"; + public const string DivisibleByPropertyName = "divisibleBy"; + public const string HiddenPropertyName = "hidden"; + public const string DisallowPropertyName = "disallow"; + public const string ExtendsPropertyName = "extends"; + public const string IdPropertyName = "id"; + + public const string OptionValuePropertyName = "value"; + public const string OptionLabelPropertyName = "label"; + + public const string ReferencePropertyName = "$ref"; + + public static readonly IDictionary JsonSchemaTypeMapping = new Dictionary() + { + {"string", JsonSchemaType.String}, + {"object", JsonSchemaType.Object}, + {"integer", JsonSchemaType.Integer}, + {"number", JsonSchemaType.Float}, + {"null", JsonSchemaType.Null}, + {"boolean", JsonSchemaType.Boolean}, + {"array", JsonSchemaType.Array}, + {"any", JsonSchemaType.Any} + }; + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaException.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaException.cs new file mode 100644 index 0000000..8a3872f --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaException.cs @@ -0,0 +1,83 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Schema +{ + /// + /// Returns detailed information about the schema exception. + /// + public class JsonSchemaException : Exception + { + /// + /// Gets the line number indicating where the error occurred. + /// + /// The line number indicating where the error occurred. + public int LineNumber { get; private set; } + + + /// + /// Gets the line position indicating where the error occurred. + /// + /// The line position indicating where the error occurred. + public int LinePosition { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + public JsonSchemaException() + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message. + /// + /// The error message that explains the reason for the exception. + public JsonSchemaException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class + /// with a specified error message and a reference to the inner exception that is the cause of this exception. + /// + /// The error message that explains the reason for the exception. + /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. + public JsonSchemaException(string message, Exception innerException) + : base(message, innerException) + { + } + + internal JsonSchemaException(string message, Exception innerException, int lineNumber, int linePosition) + : base(message, innerException) + { + LineNumber = lineNumber; + LinePosition = linePosition; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs new file mode 100644 index 0000000..3ead320 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaGenerator.cs @@ -0,0 +1,442 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Linq; +using System.Globalization; +using System.ComponentModel; +using System.Collections.Generic; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Schema +{ + /// + /// Generates a from a specified . + /// + public class JsonSchemaGenerator + { + /// + /// Gets or sets how undefined schemas are handled by the serializer. + /// + public UndefinedSchemaIdHandling UndefinedSchemaIdHandling { get; set; } + + private IContractResolver _contractResolver; + /// + /// Gets or sets the contract resolver. + /// + /// The contract resolver. + public IContractResolver ContractResolver + { + get + { + if (_contractResolver == null) + return DefaultContractResolver.Instance; + + return _contractResolver; + } + set { _contractResolver = value; } + } + + private class TypeSchema + { + public Type Type { get; private set; } + public JsonSchema Schema { get; private set;} + + public TypeSchema(Type type, JsonSchema schema) + { + ValidationUtils.ArgumentNotNull(type, "type"); + ValidationUtils.ArgumentNotNull(schema, "schema"); + + Type = type; + Schema = schema; + } + } + + private JsonSchemaResolver _resolver; + private IList _stack = new List(); + private JsonSchema _currentSchema; + + private JsonSchema CurrentSchema + { + get { return _currentSchema; } + } + + private void Push(TypeSchema typeSchema) + { + _currentSchema = typeSchema.Schema; + _stack.Add(typeSchema); + _resolver.LoadedSchemas.Add(typeSchema.Schema); + } + + private TypeSchema Pop() + { + TypeSchema popped = _stack[_stack.Count - 1]; + _stack.RemoveAt(_stack.Count - 1); + TypeSchema newValue = _stack.LastOrDefault(); + if (newValue != null) + { + _currentSchema = newValue.Schema; + } + else + { + _currentSchema = null; + } + + return popped; + } + + /// + /// Generate a from the specified type. + /// + /// The type to generate a from. + /// A generated from the specified type. + public JsonSchema Generate(Type type) + { + return Generate(type, new JsonSchemaResolver(), false); + } + + /// + /// Generate a from the specified type. + /// + /// The type to generate a from. + /// The used to resolve schema references. + /// A generated from the specified type. + public JsonSchema Generate(Type type, JsonSchemaResolver resolver) + { + return Generate(type, resolver, false); + } + + /// + /// Generate a from the specified type. + /// + /// The type to generate a from. + /// Specify whether the generated root will be nullable. + /// A generated from the specified type. + public JsonSchema Generate(Type type, bool rootSchemaNullable) + { + return Generate(type, new JsonSchemaResolver(), rootSchemaNullable); + } + + /// + /// Generate a from the specified type. + /// + /// The type to generate a from. + /// The used to resolve schema references. + /// Specify whether the generated root will be nullable. + /// A generated from the specified type. + public JsonSchema Generate(Type type, JsonSchemaResolver resolver, bool rootSchemaNullable) + { + ValidationUtils.ArgumentNotNull(type, "type"); + ValidationUtils.ArgumentNotNull(resolver, "resolver"); + + _resolver = resolver; + + return GenerateInternal(type, (!rootSchemaNullable) ? Required.Always : Required.Default, false); + } + + private string GetTitle(Type type) + { + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(type); + + if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Title)) + return containerAttribute.Title; + + return null; + } + + private string GetDescription(Type type) + { + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(type); + + if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Description)) + return containerAttribute.Description; + +#if !PocketPC + DescriptionAttribute descriptionAttribute = ReflectionUtils.GetAttribute(type); + if (descriptionAttribute != null) + return descriptionAttribute.Description; +#endif + + return null; + } + + private string GetTypeId(Type type, bool explicitOnly) + { + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(type); + + if (containerAttribute != null && !string.IsNullOrEmpty(containerAttribute.Id)) + return containerAttribute.Id; + + if (explicitOnly) + return null; + + switch (UndefinedSchemaIdHandling) + { + case UndefinedSchemaIdHandling.UseTypeName: + return type.FullName; + case UndefinedSchemaIdHandling.UseAssemblyQualifiedName: + return type.AssemblyQualifiedName; + default: + return null; + } + } + + private JsonSchema GenerateInternal(Type type, Required valueRequired, bool required) + { + ValidationUtils.ArgumentNotNull(type, "type"); + + string resolvedId = GetTypeId(type, false); + string explicitId = GetTypeId(type, true); + + if (!string.IsNullOrEmpty(resolvedId)) + { + JsonSchema resolvedSchema = _resolver.GetSchema(resolvedId); + if (resolvedSchema != null) + { + // resolved schema is not null but referencing member allows nulls + // change resolved schema to allow nulls. hacky but what are ya gonna do? + if (valueRequired != Required.Always && !HasFlag(resolvedSchema.Type, JsonSchemaType.Null)) + resolvedSchema.Type |= JsonSchemaType.Null; + if (required && resolvedSchema.Required != true) + resolvedSchema.Required = true; + + return resolvedSchema; + } + } + + // test for unresolved circular reference + if (_stack.Any(tc => tc.Type == type)) + { + throw new Exception("Unresolved circular reference for type '{0}'. Explicitly define an Id for the type using a JsonObject/JsonArray attribute or automatically generate a type Id using the UndefinedSchemaIdHandling property.".FormatWith(CultureInfo.InvariantCulture, type)); + } + + JsonContract contract = ContractResolver.ResolveContract(type); + JsonConverter converter; + if ((converter = contract.Converter) != null || (converter = contract.InternalConverter) != null) + { + JsonSchema converterSchema = converter.GetSchema(); + if (converterSchema != null) + return converterSchema; + } + + Push(new TypeSchema(type, new JsonSchema())); + + if (explicitId != null) + CurrentSchema.Id = explicitId; + + if (required) + CurrentSchema.Required = true; + CurrentSchema.Title = GetTitle(type); + CurrentSchema.Description = GetDescription(type); + + if (converter != null) + { + // todo: Add GetSchema to JsonConverter and use here? + CurrentSchema.Type = JsonSchemaType.Any; + } + else if (contract is JsonDictionaryContract) + { + CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); + + Type keyType; + Type valueType; + ReflectionUtils.GetDictionaryKeyValueTypes(type, out keyType, out valueType); + + if (keyType != null) + { + // can be converted to a string + if (typeof (IConvertible).IsAssignableFrom(keyType)) + { + CurrentSchema.AdditionalProperties = GenerateInternal(valueType, Required.Default, false); + } + } + } + else if (contract is JsonArrayContract) + { + CurrentSchema.Type = AddNullType(JsonSchemaType.Array, valueRequired); + + CurrentSchema.Id = GetTypeId(type, false); + + JsonArrayAttribute arrayAttribute = JsonTypeReflector.GetJsonContainerAttribute(type) as JsonArrayAttribute; + bool allowNullItem = (arrayAttribute != null) ? arrayAttribute.AllowNullItems : true; + + Type collectionItemType = ReflectionUtils.GetCollectionItemType(type); + if (collectionItemType != null) + { + CurrentSchema.Items = new List(); + CurrentSchema.Items.Add(GenerateInternal(collectionItemType, (!allowNullItem) ? Required.Always : Required.Default, false)); + } + } + else if (contract is JsonPrimitiveContract) + { + CurrentSchema.Type = GetJsonSchemaType(type, valueRequired); + + if (CurrentSchema.Type == JsonSchemaType.Integer && type.IsEnum && !type.IsDefined(typeof(FlagsAttribute), true)) + { + CurrentSchema.Enum = new List(); + CurrentSchema.Options = new Dictionary(); + + EnumValues enumValues = EnumUtils.GetNamesAndValues(type); + foreach (EnumValue enumValue in enumValues) + { + JToken value = JToken.FromObject(enumValue.Value); + + CurrentSchema.Enum.Add(value); + CurrentSchema.Options.Add(value, enumValue.Name); + } + } + } + else if (contract is JsonObjectContract) + { + CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); + CurrentSchema.Id = GetTypeId(type, false); + GenerateObjectSchema(type, (JsonObjectContract)contract); + } +#if !SILVERLIGHT && !PocketPC + else if (contract is JsonISerializableContract) + { + CurrentSchema.Type = AddNullType(JsonSchemaType.Object, valueRequired); + CurrentSchema.Id = GetTypeId(type, false); + GenerateISerializableContract(type, (JsonISerializableContract) contract); + } +#endif + else if (contract is JsonStringContract) + { + JsonSchemaType schemaType = (!ReflectionUtils.IsNullable(contract.UnderlyingType)) + ? JsonSchemaType.String + : AddNullType(JsonSchemaType.String, valueRequired); + + CurrentSchema.Type = schemaType; + } + else if (contract is JsonLinqContract) + { + CurrentSchema.Type = JsonSchemaType.Any; + } + else + { + throw new Exception("Unexpected contract type: {0}".FormatWith(CultureInfo.InvariantCulture, contract)); + } + + return Pop().Schema; + } + + private JsonSchemaType AddNullType(JsonSchemaType type, Required valueRequired) + { + if (valueRequired != Required.Always) + return type | JsonSchemaType.Null; + + return type; + } + + private void GenerateObjectSchema(Type type, JsonObjectContract contract) + { + CurrentSchema.Properties = new Dictionary(); + foreach (JsonProperty property in contract.Properties) + { + if (!property.Ignored) + { + bool optional = property.NullValueHandling == NullValueHandling.Ignore || + property.DefaultValueHandling == DefaultValueHandling.Ignore || + property.ShouldSerialize != null || + property.GetIsSpecified != null; + + JsonSchema propertySchema = GenerateInternal(property.PropertyType, property.Required, !optional); + + if (property.DefaultValue != null) + propertySchema.Default = JToken.FromObject(property.DefaultValue); + + CurrentSchema.Properties.Add(property.PropertyName, propertySchema); + } + } + + if (type.IsSealed) + CurrentSchema.AllowAdditionalProperties = false; + } + +#if !SILVERLIGHT && !PocketPC + private void GenerateISerializableContract(Type type, JsonISerializableContract contract) + { + CurrentSchema.AllowAdditionalProperties = true; + } +#endif + + internal static bool HasFlag(JsonSchemaType? value, JsonSchemaType flag) + { + // default value is Any + if (value == null) + return true; + + return ((value & flag) == flag); + } + + private JsonSchemaType GetJsonSchemaType(Type type, Required valueRequired) + { + JsonSchemaType schemaType = JsonSchemaType.None; + if (valueRequired != Required.Always && ReflectionUtils.IsNullable(type)) + { + schemaType = JsonSchemaType.Null; + if (ReflectionUtils.IsNullableType(type)) + type = Nullable.GetUnderlyingType(type); + } + + TypeCode typeCode = Type.GetTypeCode(type); + + switch (typeCode) + { + case TypeCode.Empty: + case TypeCode.Object: + return schemaType | JsonSchemaType.String; + case TypeCode.DBNull: + return schemaType | JsonSchemaType.Null; + case TypeCode.Boolean: + return schemaType | JsonSchemaType.Boolean; + case TypeCode.Char: + return schemaType | JsonSchemaType.String; + case TypeCode.SByte: + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + return schemaType | JsonSchemaType.Integer; + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return schemaType | JsonSchemaType.Float; + // convert to string? + case TypeCode.DateTime: + return schemaType | JsonSchemaType.String; + case TypeCode.String: + return schemaType | JsonSchemaType.String; + default: + throw new Exception("Unexpected type code '{0}' for type '{1}'.".FormatWith(CultureInfo.InvariantCulture, typeCode, type)); + } + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaModel.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaModel.cs new file mode 100644 index 0000000..82926d5 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaModel.cs @@ -0,0 +1,113 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Schema +{ + internal class JsonSchemaModel + { + public bool Required { get; set; } + public JsonSchemaType Type { get; set; } + public int? MinimumLength { get; set; } + public int? MaximumLength { get; set; } + public double? DivisibleBy { get; set; } + public double? Minimum { get; set; } + public double? Maximum { get; set; } + public bool ExclusiveMinimum { get; set; } + public bool ExclusiveMaximum { get; set; } + public int? MinimumItems { get; set; } + public int? MaximumItems { get; set; } + public IList Patterns { get; set; } + public IList Items { get; set; } + public IDictionary Properties { get; set; } + public IDictionary PatternProperties { get; set; } + public JsonSchemaModel AdditionalProperties { get; set; } + public bool AllowAdditionalProperties { get; set; } + public IList Enum { get; set; } + public JsonSchemaType Disallow { get; set; } + + public JsonSchemaModel() + { + Type = JsonSchemaType.Any; + AllowAdditionalProperties = true; + Required = false; + } + + public static JsonSchemaModel Create(IList schemata) + { + JsonSchemaModel model = new JsonSchemaModel(); + + foreach (JsonSchema schema in schemata) + { + Combine(model, schema); + } + + return model; + } + + private static void Combine(JsonSchemaModel model, JsonSchema schema) + { + // Version 3 of the Draft JSON Schema has the default value of Not Required + model.Required = model.Required || (schema.Required ?? false); + model.Type = model.Type & (schema.Type ?? JsonSchemaType.Any); + + model.MinimumLength = MathUtils.Max(model.MinimumLength, schema.MinimumLength); + model.MaximumLength = MathUtils.Min(model.MaximumLength, schema.MaximumLength); + + // not sure what is the best way to combine divisibleBy + model.DivisibleBy = MathUtils.Max(model.DivisibleBy, schema.DivisibleBy); + + model.Minimum = MathUtils.Max(model.Minimum, schema.Minimum); + model.Maximum = MathUtils.Max(model.Maximum, schema.Maximum); + model.ExclusiveMinimum = model.ExclusiveMinimum || (schema.ExclusiveMinimum ?? false); + model.ExclusiveMaximum = model.ExclusiveMaximum || (schema.ExclusiveMaximum ?? false); + + model.MinimumItems = MathUtils.Max(model.MinimumItems, schema.MinimumItems); + model.MaximumItems = MathUtils.Min(model.MaximumItems, schema.MaximumItems); + model.AllowAdditionalProperties = model.AllowAdditionalProperties && schema.AllowAdditionalProperties; + if (schema.Enum != null) + { + if (model.Enum == null) + model.Enum = new List(); + + model.Enum.AddRangeDistinct(schema.Enum, new JTokenEqualityComparer()); + } + model.Disallow = model.Disallow | (schema.Disallow ?? JsonSchemaType.None); + + if (schema.Pattern != null) + { + if (model.Patterns == null) + model.Patterns = new List(); + + model.Patterns.AddDistinct(schema.Pattern); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaModelBuilder.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaModelBuilder.cs new file mode 100644 index 0000000..b040248 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaModelBuilder.cs @@ -0,0 +1,173 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Schema +{ + internal class JsonSchemaModelBuilder + { + private JsonSchemaNodeCollection _nodes = new JsonSchemaNodeCollection(); + private Dictionary _nodeModels = new Dictionary(); + private JsonSchemaNode _node ; + + public JsonSchemaModel Build(JsonSchema schema) + { + _nodes = new JsonSchemaNodeCollection(); + _node = AddSchema(null, schema); + + _nodeModels = new Dictionary(); + JsonSchemaModel model = BuildNodeModel(_node); + + return model; + } + + public JsonSchemaNode AddSchema(JsonSchemaNode existingNode, JsonSchema schema) + { + string newId; + if (existingNode != null) + { + if (existingNode.Schemas.Contains(schema)) + return existingNode; + + newId = JsonSchemaNode.GetId(existingNode.Schemas.Union(new[] { schema })); + } + else + { + newId = JsonSchemaNode.GetId(new[] { schema }); + } + + if (_nodes.Contains(newId)) + return _nodes[newId]; + + JsonSchemaNode currentNode = (existingNode != null) + ? existingNode.Combine(schema) + : new JsonSchemaNode(schema); + + _nodes.Add(currentNode); + + AddProperties(schema.Properties, currentNode.Properties); + + AddProperties(schema.PatternProperties, currentNode.PatternProperties); + + if (schema.Items != null) + { + for (int i = 0; i < schema.Items.Count; i++) + { + AddItem(currentNode, i, schema.Items[i]); + } + } + + if (schema.AdditionalProperties != null) + AddAdditionalProperties(currentNode, schema.AdditionalProperties); + + if (schema.Extends != null) + currentNode = AddSchema(currentNode, schema.Extends); + + return currentNode; + } + + public void AddProperties(IDictionary source, IDictionary target) + { + if (source != null) + { + foreach (KeyValuePair property in source) + { + AddProperty(target, property.Key, property.Value); + } + } + } + + public void AddProperty(IDictionary target, string propertyName, JsonSchema schema) + { + JsonSchemaNode propertyNode; + target.TryGetValue(propertyName, out propertyNode); + + target[propertyName] = AddSchema(propertyNode, schema); + } + + public void AddItem(JsonSchemaNode parentNode, int index, JsonSchema schema) + { + JsonSchemaNode existingItemNode = (parentNode.Items.Count > index) + ? parentNode.Items[index] + : null; + + JsonSchemaNode newItemNode = AddSchema(existingItemNode, schema); + + if (!(parentNode.Items.Count > index)) + { + parentNode.Items.Add(newItemNode); + } + else + { + parentNode.Items[index] = newItemNode; + } + } + + public void AddAdditionalProperties(JsonSchemaNode parentNode, JsonSchema schema) + { + parentNode.AdditionalProperties = AddSchema(parentNode.AdditionalProperties, schema); + } + + private JsonSchemaModel BuildNodeModel(JsonSchemaNode node) + { + JsonSchemaModel model; + if (_nodeModels.TryGetValue(node, out model)) + return model; + + model = JsonSchemaModel.Create(node.Schemas); + _nodeModels[node] = model; + + foreach (KeyValuePair property in node.Properties) + { + if (model.Properties == null) + model.Properties = new Dictionary(); + + model.Properties[property.Key] = BuildNodeModel(property.Value); + } + foreach (KeyValuePair property in node.PatternProperties) + { + if (model.PatternProperties == null) + model.PatternProperties = new Dictionary(); + + model.PatternProperties[property.Key] = BuildNodeModel(property.Value); + } + for (int i = 0; i < node.Items.Count; i++) + { + if (model.Items == null) + model.Items = new List(); + + model.Items.Add(BuildNodeModel(node.Items[i])); + } + if (node.AdditionalProperties != null) + model.AdditionalProperties = BuildNodeModel(node.AdditionalProperties); + + return model; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaNode.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaNode.cs new file mode 100644 index 0000000..093f2b3 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaNode.cs @@ -0,0 +1,73 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace Newtonsoft.Json.Schema +{ + internal class JsonSchemaNode + { + public string Id { get; private set; } + public ReadOnlyCollection Schemas { get; private set; } + public Dictionary Properties { get; private set; } + public Dictionary PatternProperties { get; private set; } + public List Items { get; private set; } + public JsonSchemaNode AdditionalProperties { get; set; } + + public JsonSchemaNode(JsonSchema schema) + { + Schemas = new ReadOnlyCollection(new []{ schema }); + Properties = new Dictionary(); + PatternProperties = new Dictionary(); + Items = new List(); + + Id = GetId(Schemas); + } + + private JsonSchemaNode(JsonSchemaNode source, JsonSchema schema) + { + Schemas = new ReadOnlyCollection(source.Schemas.Union(new[] { schema }).ToList()); + Properties = new Dictionary(source.Properties); + PatternProperties = new Dictionary(source.PatternProperties); + Items = new List(source.Items); + AdditionalProperties = source.AdditionalProperties; + + Id = GetId(Schemas); + } + + public JsonSchemaNode Combine(JsonSchema schema) + { + return new JsonSchemaNode(this, schema); + } + + public static string GetId(IEnumerable schemata) + { + return string.Join("-", schemata.Select(s => s.InternalId).OrderBy(id => id, StringComparer.Ordinal).ToArray()); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaNodeCollection.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaNodeCollection.cs new file mode 100644 index 0000000..5f2ef01 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaNodeCollection.cs @@ -0,0 +1,37 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.Collections.ObjectModel; + +namespace Newtonsoft.Json.Schema +{ + internal class JsonSchemaNodeCollection : KeyedCollection + { + protected override string GetKeyForItem(JsonSchemaNode item) + { + return item.Id; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaResolver.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaResolver.cs new file mode 100644 index 0000000..6b50a6c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaResolver.cs @@ -0,0 +1,66 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Schema +{ + /// + /// Resolves from an id. + /// + public class JsonSchemaResolver + { + /// + /// Gets or sets the loaded schemas. + /// + /// The loaded schemas. + public IList LoadedSchemas { get; protected set; } + + /// + /// Initializes a new instance of the class. + /// + public JsonSchemaResolver() + { + LoadedSchemas = new List(); + } + + /// + /// Gets a for the specified id. + /// + /// The id. + /// A for the specified id. + public virtual JsonSchema GetSchema(string id) + { + JsonSchema schema = LoadedSchemas.SingleOrDefault(s => s.Id == id); + return schema; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaType.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaType.cs new file mode 100644 index 0000000..a58805a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaType.cs @@ -0,0 +1,76 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Schema +{ + /// + /// The value types allowed by the . + /// + [Flags] + public enum JsonSchemaType + { + /// + /// No type specified. + /// + None = 0, + /// + /// String type. + /// + String = 1, + /// + /// Float type. + /// + Float = 2, + /// + /// Integer type. + /// + Integer = 4, + /// + /// Boolean type. + /// + Boolean = 8, + /// + /// Object type. + /// + Object = 16, + /// + /// Array type. + /// + Array = 32, + /// + /// Null type. + /// + Null = 64, + /// + /// Any type. + /// + Any = String | Float | Integer | Boolean | Object | Array | Null + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaWriter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaWriter.cs new file mode 100644 index 0000000..1584fc4 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/JsonSchemaWriter.cs @@ -0,0 +1,221 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Schema +{ + internal class JsonSchemaWriter + { + private readonly JsonWriter _writer; + private readonly JsonSchemaResolver _resolver; + + public JsonSchemaWriter(JsonWriter writer, JsonSchemaResolver resolver) + { + ValidationUtils.ArgumentNotNull(writer, "writer"); + _writer = writer; + _resolver = resolver; + } + + private void ReferenceOrWriteSchema(JsonSchema schema) + { + if (schema.Id != null && _resolver.GetSchema(schema.Id) != null) + { + _writer.WriteStartObject(); + _writer.WritePropertyName(JsonSchemaConstants.ReferencePropertyName); + _writer.WriteValue(schema.Id); + _writer.WriteEndObject(); + } + else + { + WriteSchema(schema); + } + } + + public void WriteSchema(JsonSchema schema) + { + ValidationUtils.ArgumentNotNull(schema, "schema"); + + if (!_resolver.LoadedSchemas.Contains(schema)) + _resolver.LoadedSchemas.Add(schema); + + _writer.WriteStartObject(); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.IdPropertyName, schema.Id); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.TitlePropertyName, schema.Title); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.DescriptionPropertyName, schema.Description); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.RequiredPropertyName, schema.Required); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.ReadOnlyPropertyName, schema.ReadOnly); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.HiddenPropertyName, schema.Hidden); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.TransientPropertyName, schema.Transient); + if (schema.Type != null) + WriteType(JsonSchemaConstants.TypePropertyName, _writer, schema.Type.Value); + if (!schema.AllowAdditionalProperties) + { + _writer.WritePropertyName(JsonSchemaConstants.AdditionalPropertiesPropertyName); + _writer.WriteValue(schema.AllowAdditionalProperties); + } + else + { + if (schema.AdditionalProperties != null) + { + _writer.WritePropertyName(JsonSchemaConstants.AdditionalPropertiesPropertyName); + ReferenceOrWriteSchema(schema.AdditionalProperties); + } + } + WriteSchemaDictionaryIfNotNull(_writer, JsonSchemaConstants.PropertiesPropertyName, schema.Properties); + WriteSchemaDictionaryIfNotNull(_writer, JsonSchemaConstants.PatternPropertiesPropertyName, schema.PatternProperties); + WriteItems(schema); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.MinimumPropertyName, schema.Minimum); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.MaximumPropertyName, schema.Maximum); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.ExclusiveMinimumPropertyName, schema.ExclusiveMinimum); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.ExclusiveMaximumPropertyName, schema.ExclusiveMaximum); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.MinimumLengthPropertyName, schema.MinimumLength); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.MaximumLengthPropertyName, schema.MaximumLength); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.MinimumItemsPropertyName, schema.MinimumItems); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.MaximumItemsPropertyName, schema.MaximumItems); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.DivisibleByPropertyName, schema.DivisibleBy); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.FormatPropertyName, schema.Format); + WritePropertyIfNotNull(_writer, JsonSchemaConstants.PatternPropertyName, schema.Pattern); + if (schema.Enum != null) + { + _writer.WritePropertyName(JsonSchemaConstants.EnumPropertyName); + _writer.WriteStartArray(); + foreach (JToken token in schema.Enum) + { + token.WriteTo(_writer); + } + _writer.WriteEndArray(); + } + if (schema.Default != null) + { + _writer.WritePropertyName(JsonSchemaConstants.DefaultPropertyName); + schema.Default.WriteTo(_writer); + } + if (schema.Options != null) + { + _writer.WritePropertyName(JsonSchemaConstants.OptionsPropertyName); + _writer.WriteStartArray(); + foreach (KeyValuePair option in schema.Options) + { + _writer.WriteStartObject(); + _writer.WritePropertyName(JsonSchemaConstants.OptionValuePropertyName); + option.Key.WriteTo(_writer); + if (option.Value != null) + { + _writer.WritePropertyName(JsonSchemaConstants.OptionLabelPropertyName); + _writer.WriteValue(option.Value); + } + _writer.WriteEndObject(); + } + _writer.WriteEndArray(); + } + if (schema.Disallow != null) + WriteType(JsonSchemaConstants.DisallowPropertyName, _writer, schema.Disallow.Value); + if (schema.Extends != null) + { + _writer.WritePropertyName(JsonSchemaConstants.ExtendsPropertyName); + ReferenceOrWriteSchema(schema.Extends); + } + _writer.WriteEndObject(); + } + + private void WriteSchemaDictionaryIfNotNull(JsonWriter writer, string propertyName, IDictionary properties) + { + if (properties != null) + { + writer.WritePropertyName(propertyName); + writer.WriteStartObject(); + foreach (KeyValuePair property in properties) + { + writer.WritePropertyName(property.Key); + ReferenceOrWriteSchema(property.Value); + } + writer.WriteEndObject(); + } + } + + private void WriteItems(JsonSchema schema) + { + if (CollectionUtils.IsNullOrEmpty(schema.Items)) + return; + + _writer.WritePropertyName(JsonSchemaConstants.ItemsPropertyName); + + if (schema.Items.Count == 1) + { + ReferenceOrWriteSchema(schema.Items[0]); + return; + } + + _writer.WriteStartArray(); + foreach (JsonSchema itemSchema in schema.Items) + { + ReferenceOrWriteSchema(itemSchema); + } + _writer.WriteEndArray(); + } + + private void WriteType(string propertyName, JsonWriter writer, JsonSchemaType type) + { + IList types; + if (System.Enum.IsDefined(typeof(JsonSchemaType), type)) + types = new List { type }; + else + types = EnumUtils.GetFlagsValues(type).Where(v => v != JsonSchemaType.None).ToList(); + + if (types.Count == 0) + return; + + writer.WritePropertyName(propertyName); + + if (types.Count == 1) + { + writer.WriteValue(JsonSchemaBuilder.MapType(types[0])); + return; + } + + writer.WriteStartArray(); + foreach (JsonSchemaType jsonSchemaType in types) + { + writer.WriteValue(JsonSchemaBuilder.MapType(jsonSchemaType)); + } + writer.WriteEndArray(); + } + + private void WritePropertyIfNotNull(JsonWriter writer, string propertyName, object value) + { + if (value != null) + { + writer.WritePropertyName(propertyName); + writer.WriteValue(value); + } + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/UndefinedSchemaIdHandling.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/UndefinedSchemaIdHandling.cs new file mode 100644 index 0000000..34bbbee --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/UndefinedSchemaIdHandling.cs @@ -0,0 +1,46 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Schema +{ + /// + /// Specifies undefined schema Id handling options for the . + /// + public enum UndefinedSchemaIdHandling + { + /// + /// Do not infer a schema Id. + /// + None = 0, + /// + /// Use the .NET type name as the schema Id. + /// + UseTypeName = 1, + /// + /// Use the assembly qualified .NET type name as the schema Id. + /// + UseAssemblyQualifiedName = 2, + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/ValidationEventArgs.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/ValidationEventArgs.cs new file mode 100644 index 0000000..d56fb9f --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/ValidationEventArgs.cs @@ -0,0 +1,62 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Schema +{ + /// + /// Returns detailed information related to the . + /// + public class ValidationEventArgs : EventArgs + { + private readonly JsonSchemaException _ex; + + internal ValidationEventArgs(JsonSchemaException ex) + { + ValidationUtils.ArgumentNotNull(ex, "ex"); + _ex = ex; + } + + /// + /// Gets the associated with the validation event. + /// + /// The JsonSchemaException associated with the validation event. + public JsonSchemaException Exception + { + get { return _ex; } + } + + /// + /// Gets the text description corresponding to the validation event. + /// + /// The text description. + public string Message + { + get { return _ex.Message; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/ValidationEventHandler.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/ValidationEventHandler.cs new file mode 100644 index 0000000..95d213d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Schema/ValidationEventHandler.cs @@ -0,0 +1,32 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Schema +{ + /// + /// Represents the callback method that will handle JSON schema validation events and the . + /// + public delegate void ValidationEventHandler(object sender, ValidationEventArgs e); +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/CachedAttributeGetter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/CachedAttributeGetter.cs new file mode 100644 index 0000000..552920e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/CachedAttributeGetter.cs @@ -0,0 +1,44 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + internal static class CachedAttributeGetter where T : Attribute + { + private static readonly ThreadSafeStore TypeAttributeCache = new ThreadSafeStore(JsonTypeReflector.GetAttribute); + + public static T GetAttribute(ICustomAttributeProvider type) + { + return TypeAttributeCache.Get(type); + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs new file mode 100644 index 0000000..34784c8 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/CamelCasePropertyNamesContractResolver.cs @@ -0,0 +1,55 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Resolves member mappings for a type, camel casing property names. + /// + public class CamelCasePropertyNamesContractResolver : DefaultContractResolver + { + /// + /// Initializes a new instance of the class. + /// + public CamelCasePropertyNamesContractResolver() + : base(true) + { + } + + /// + /// Resolves the name of the property. + /// + /// Name of the property. + /// The property name camel cased. + protected override string ResolvePropertyName(string propertyName) + { + // lower case the first letter of the passed in name + return StringUtils.ToCamelCase(propertyName); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs new file mode 100644 index 0000000..51498ac --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/DefaultContractResolver.cs @@ -0,0 +1,788 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +#if !(NET35 || NET20 || WINDOWS_PHONE) +using System.Dynamic; +#endif +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security.Permissions; +using System.Xml.Serialization; +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; +using System.Runtime.CompilerServices; + +namespace Newtonsoft.Json.Serialization +{ + internal struct ResolverContractKey : IEquatable + { + private readonly Type _resolverType; + private readonly Type _contractType; + + public ResolverContractKey(Type resolverType, Type contractType) + { + _resolverType = resolverType; + _contractType = contractType; + } + + public override int GetHashCode() + { + return _resolverType.GetHashCode() ^ _contractType.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (!(obj is ResolverContractKey)) + return false; + + return Equals((ResolverContractKey)obj); + } + + public bool Equals(ResolverContractKey other) + { + return (_resolverType == other._resolverType && _contractType == other._contractType); + } + } + + /// + /// Used by to resolves a for a given . + /// + public class DefaultContractResolver : IContractResolver + { + internal static readonly IContractResolver Instance = new DefaultContractResolver(true); + private static readonly IList BuiltInConverters = new List + { +#if !PocketPC && !SILVERLIGHT && !NET20 + new EntityKeyMemberConverter(), +#endif +#if !(NET35 || NET20 || WINDOWS_PHONE) + new ExpandoObjectConverter(), +#endif + new BinaryConverter(), + new KeyValuePairConverter(), +#if !SILVERLIGHT || WINDOWS_PHONE + new XmlNodeConverter(), +#endif +#if !SILVERLIGHT + new DataSetConverter(), + new DataTableConverter(), +#endif + new BsonObjectIdConverter() + }; + + private static Dictionary _sharedContractCache; + private static readonly object _typeContractCacheLock = new object(); + + private Dictionary _instanceContractCache; + private readonly bool _sharedCache; + + /// + /// Gets a value indicating whether members are being get and set using dynamic code generation. + /// This value is determined by the runtime permissions available. + /// + /// + /// true if using dynamic code generation; otherwise, false. + /// + public bool DynamicCodeGeneration + { + get { return JsonTypeReflector.DynamicCodeGeneration; } + } + + /// + /// Gets or sets the default members search flags. + /// + /// The default members search flags. + public BindingFlags DefaultMembersSearchFlags { get; set; } + + /// + /// Gets or sets a value indicating whether compiler generated members should be serialized. + /// + /// + /// true if serialized compiler generated members; otherwise, false. + /// + public bool SerializeCompilerGeneratedMembers { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public DefaultContractResolver() + : this(false) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// If set to true the will use a cached shared with other resolvers of the same type. + /// Sharing the cache will significantly performance because expensive reflection will only happen once but could cause unexpected + /// behavior if different instances of the resolver are suppose to produce different results. When set to false it is highly + /// recommended to reuse instances with the . + /// + public DefaultContractResolver(bool shareCache) + { + DefaultMembersSearchFlags = BindingFlags.Public | BindingFlags.Instance; + _sharedCache = shareCache; + } + + private Dictionary GetCache() + { + if (_sharedCache) + return _sharedContractCache; + else + return _instanceContractCache; + } + + private void UpdateCache(Dictionary cache) + { + if (_sharedCache) + _sharedContractCache = cache; + else + _instanceContractCache = cache; + } + + /// + /// Resolves the contract for a given type. + /// + /// The type to resolve a contract for. + /// The contract for a given type. + public virtual JsonContract ResolveContract(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + JsonContract contract; + ResolverContractKey key = new ResolverContractKey(GetType(), type); + Dictionary cache = GetCache(); + if (cache == null || !cache.TryGetValue(key, out contract)) + { + contract = CreateContract(type); + + // avoid the possibility of modifying the cache dictionary while another thread is accessing it + lock (_typeContractCacheLock) + { + cache = GetCache(); + Dictionary updatedCache = + (cache != null) + ? new Dictionary(cache) + : new Dictionary(); + updatedCache[key] = contract; + + UpdateCache(updatedCache); + } + } + + return contract; + } + + /// + /// Gets the serializable members for the type. + /// + /// The type to get serializable members for. + /// The serializable members for the type. + protected virtual List GetSerializableMembers(Type objectType) + { +#if !PocketPC && !NET20 + DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(objectType); +#endif + + List defaultMembers = ReflectionUtils.GetFieldsAndProperties(objectType, DefaultMembersSearchFlags) + .Where(m => !ReflectionUtils.IsIndexedProperty(m)).ToList(); + List allMembers = ReflectionUtils.GetFieldsAndProperties(objectType, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static) + .Where(m => !ReflectionUtils.IsIndexedProperty(m)).ToList(); + + List serializableMembers = new List(); + foreach (MemberInfo member in allMembers) + { + // exclude members that are compiler generated if set + if (SerializeCompilerGeneratedMembers || !member.IsDefined(typeof(CompilerGeneratedAttribute), true)) + { + if (defaultMembers.Contains(member)) + { + // add all members that are found by default member search + serializableMembers.Add(member); + } + else + { + // add members that are explicitly marked with JsonProperty/DataMember attribute + if (JsonTypeReflector.GetAttribute(member) != null) + serializableMembers.Add(member); +#if !PocketPC && !NET20 + else if (dataContractAttribute != null && JsonTypeReflector.GetAttribute(member) != null) + serializableMembers.Add(member); +#endif + } + } + } + +#if !PocketPC && !SILVERLIGHT && !NET20 + Type match; + // don't include EntityKey on entities objects... this is a bit hacky + if (objectType.AssignableToTypeName("System.Data.Objects.DataClasses.EntityObject", out match)) + serializableMembers = serializableMembers.Where(ShouldSerializeEntityMember).ToList(); +#endif + + return serializableMembers; + } + +#if !PocketPC && !SILVERLIGHT && !NET20 + private bool ShouldSerializeEntityMember(MemberInfo memberInfo) + { + PropertyInfo propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + { + if (propertyInfo.PropertyType.IsGenericType && propertyInfo.PropertyType.GetGenericTypeDefinition().FullName == "System.Data.Objects.DataClasses.EntityReference`1") + return false; + } + + return true; + } +#endif + + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonObjectContract CreateObjectContract(Type objectType) + { + JsonObjectContract contract = new JsonObjectContract(objectType); + InitializeContract(contract); + + contract.MemberSerialization = JsonTypeReflector.GetObjectMemberSerialization(objectType); + contract.Properties.AddRange(CreateProperties(contract.UnderlyingType, contract.MemberSerialization)); + if (objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Any(c => c.IsDefined(typeof(JsonConstructorAttribute), true))) + contract.OverrideConstructor = GetAttributeConstructor(objectType); + else if (contract.DefaultCreator == null || contract.DefaultCreatorNonPublic) + contract.ParametrizedConstructor = GetParametrizedConstructor(objectType); + + return contract; + } + + private ConstructorInfo GetAttributeConstructor(Type objectType) + { + IList markedConstructors = objectType.GetConstructors(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Where(c => c.IsDefined(typeof(JsonConstructorAttribute), true)).ToList(); + + if (markedConstructors.Count > 1) + throw new Exception("Multiple constructors with the JsonConstructorAttribute."); + else if (markedConstructors.Count == 1) + return markedConstructors[0]; + + return null; + } + + private ConstructorInfo GetParametrizedConstructor(Type objectType) + { + IList constructors = objectType.GetConstructors(BindingFlags.Public | BindingFlags.Instance); + + if (constructors.Count == 1) + return constructors[0]; + else + return null; + } + + /// + /// Resolves the default for the contract. + /// + /// Type of the object. + /// + protected virtual JsonConverter ResolveContractConverter(Type objectType) + { + return JsonTypeReflector.GetJsonConverter(objectType, objectType); + } + + private Func GetDefaultCreator(Type createdType) + { + return JsonTypeReflector.ReflectionDelegateFactory.CreateDefaultConstructor(createdType); + } + +#if !PocketPC && !NET20 + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Runtime.Serialization.DataContractAttribute.#get_IsReference()")] +#endif + private void InitializeContract(JsonContract contract) + { + JsonContainerAttribute containerAttribute = JsonTypeReflector.GetJsonContainerAttribute(contract.UnderlyingType); + if (containerAttribute != null) + { + contract.IsReference = containerAttribute._isReference; + } +#if !PocketPC && !NET20 + else + { + DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(contract.UnderlyingType); + // doesn't have a null value + if (dataContractAttribute != null && dataContractAttribute.IsReference) + contract.IsReference = true; + } +#endif + + contract.Converter = ResolveContractConverter(contract.UnderlyingType); + + // then see whether object is compadible with any of the built in converters + contract.InternalConverter = JsonSerializer.GetMatchingConverter(BuiltInConverters, contract.UnderlyingType); + + if (ReflectionUtils.HasDefaultConstructor(contract.CreatedType, true) + || contract.CreatedType.IsValueType) + { + contract.DefaultCreator = GetDefaultCreator(contract.CreatedType); + + contract.DefaultCreatorNonPublic = (!contract.CreatedType.IsValueType && + ReflectionUtils.GetDefaultConstructor(contract.CreatedType) == null); + } + + foreach (MethodInfo method in contract.UnderlyingType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) + { + // compact framework errors when getting parameters for a generic method + // lame, but generic methods should not be callbacks anyway + if (method.ContainsGenericParameters) + continue; + + Type prevAttributeType = null; + ParameterInfo[] parameters = method.GetParameters(); + +#if !PocketPC + if (IsValidCallback(method, parameters, typeof(OnSerializingAttribute), contract.OnSerializing, ref prevAttributeType)) + { + contract.OnSerializing = method; + } + if (IsValidCallback(method, parameters, typeof(OnSerializedAttribute), contract.OnSerialized, ref prevAttributeType)) + { + contract.OnSerialized = method; + } + if (IsValidCallback(method, parameters, typeof(OnDeserializingAttribute), contract.OnDeserializing, ref prevAttributeType)) + { + contract.OnDeserializing = method; + } + if (IsValidCallback(method, parameters, typeof(OnDeserializedAttribute), contract.OnDeserialized, ref prevAttributeType)) + { + contract.OnDeserialized = method; + } +#endif + if (IsValidCallback(method, parameters, typeof(OnErrorAttribute), contract.OnError, ref prevAttributeType)) + { + contract.OnError = method; + } + } + } + + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonDictionaryContract CreateDictionaryContract(Type objectType) + { + JsonDictionaryContract contract = new JsonDictionaryContract(objectType); + InitializeContract(contract); + + contract.PropertyNameResolver = ResolvePropertyName; + + return contract; + } + + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonArrayContract CreateArrayContract(Type objectType) + { + JsonArrayContract contract = new JsonArrayContract(objectType); + InitializeContract(contract); + + return contract; + } + + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonPrimitiveContract CreatePrimitiveContract(Type objectType) + { + JsonPrimitiveContract contract = new JsonPrimitiveContract(objectType); + InitializeContract(contract); + + return contract; + } + + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonLinqContract CreateLinqContract(Type objectType) + { + JsonLinqContract contract = new JsonLinqContract(objectType); + InitializeContract(contract); + + return contract; + } + +#if !SILVERLIGHT && !PocketPC + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonISerializableContract CreateISerializableContract(Type objectType) + { + JsonISerializableContract contract = new JsonISerializableContract(objectType); + InitializeContract(contract); + + ConstructorInfo constructorInfo = objectType.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, new[] { typeof(SerializationInfo), typeof(StreamingContext) }, null); + if (constructorInfo != null) + { + MethodCall methodCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(constructorInfo); + + contract.ISerializableCreator = (args => methodCall(null, args)); + } + + return contract; + } +#endif + +#if !(NET35 || NET20 || WINDOWS_PHONE) + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonDynamicContract CreateDynamicContract(Type objectType) + { + JsonDynamicContract contract = new JsonDynamicContract(objectType); + InitializeContract(contract); + + contract.PropertyNameResolver = ResolvePropertyName; + contract.Properties.AddRange(CreateProperties(objectType, MemberSerialization.OptOut)); + + return contract; + } +#endif + + /// + /// Creates a for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonStringContract CreateStringContract(Type objectType) + { + JsonStringContract contract = new JsonStringContract(objectType); + InitializeContract(contract); + + return contract; + } + + /// + /// Determines which contract type is created for the given type. + /// + /// Type of the object. + /// A for the given type. + protected virtual JsonContract CreateContract(Type objectType) + { + Type t = ReflectionUtils.EnsureNotNullableType(objectType); + + if (JsonConvert.IsJsonPrimitiveType(t)) + return CreatePrimitiveContract(t); + + if (JsonTypeReflector.GetJsonObjectAttribute(t) != null) + return CreateObjectContract(t); + + if (JsonTypeReflector.GetJsonArrayAttribute(t) != null) + return CreateArrayContract(t); + + if (t == typeof(JToken) || t.IsSubclassOf(typeof(JToken))) + return CreateLinqContract(t); + + if (CollectionUtils.IsDictionaryType(t)) + return CreateDictionaryContract(t); + + if (typeof(IEnumerable).IsAssignableFrom(t)) + return CreateArrayContract(t); + + if (CanConvertToString(t)) + return CreateStringContract(t); + +#if !SILVERLIGHT && !PocketPC + if (typeof(ISerializable).IsAssignableFrom(t)) + return CreateISerializableContract(t); +#endif + +#if !(NET35 || NET20 || WINDOWS_PHONE) + if (typeof(IDynamicMetaObjectProvider).IsAssignableFrom(t)) + return CreateDynamicContract(t); +#endif + + return CreateObjectContract(t); + } + + internal static bool CanConvertToString(Type type) + { +#if !PocketPC + TypeConverter converter = ConvertUtils.GetConverter(type); + + // use the objectType's TypeConverter if it has one and can convert to a string + if (converter != null +#if !SILVERLIGHT + && !(converter is ComponentConverter) + && !(converter is ReferenceConverter) +#endif + && converter.GetType() != typeof(TypeConverter)) + { + if (converter.CanConvertTo(typeof(string))) + return true; + } +#endif + + if (type == typeof(Type) || type.IsSubclassOf(typeof(Type))) + return true; + +#if SILVERLIGHT || PocketPC + if (type == typeof(Guid) || type == typeof(Uri) || type == typeof(TimeSpan)) + return true; +#endif + + return false; + } + + private static bool IsValidCallback(MethodInfo method, ParameterInfo[] parameters, Type attributeType, MethodInfo currentCallback, ref Type prevAttributeType) + { + if (!method.IsDefined(attributeType, false)) + return false; + + if (currentCallback != null) + throw new Exception("Invalid attribute. Both '{0}' and '{1}' in type '{2}' have '{3}'.".FormatWith(CultureInfo.InvariantCulture, method, currentCallback, GetClrTypeFullName(method.DeclaringType), attributeType)); + + if (prevAttributeType != null) + throw new Exception("Invalid Callback. Method '{3}' in type '{2}' has both '{0}' and '{1}'.".FormatWith(CultureInfo.InvariantCulture, prevAttributeType, attributeType, GetClrTypeFullName(method.DeclaringType), method)); + + if (method.IsVirtual) + throw new Exception("Virtual Method '{0}' of type '{1}' cannot be marked with '{2}' attribute.".FormatWith(CultureInfo.InvariantCulture, method, GetClrTypeFullName(method.DeclaringType), attributeType)); + + if (method.ReturnType != typeof(void)) + throw new Exception("Serialization Callback '{1}' in type '{0}' must return void.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method)); + + if (attributeType == typeof(OnErrorAttribute)) + { + if (parameters == null || parameters.Length != 2 || parameters[0].ParameterType != typeof(StreamingContext) || parameters[1].ParameterType != typeof(ErrorContext)) + throw new Exception("Serialization Error Callback '{1}' in type '{0}' must have two parameters of type '{2}' and '{3}'.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method, typeof(StreamingContext), typeof(ErrorContext))); + } + else + { + if (parameters == null || parameters.Length != 1 || parameters[0].ParameterType != typeof(StreamingContext)) + throw new Exception("Serialization Callback '{1}' in type '{0}' must have a single parameter of type '{2}'.".FormatWith(CultureInfo.InvariantCulture, GetClrTypeFullName(method.DeclaringType), method, typeof(StreamingContext))); + } + + prevAttributeType = attributeType; + + return true; + } + + internal static string GetClrTypeFullName(Type type) + { + if (type.IsGenericTypeDefinition || !type.ContainsGenericParameters) + return type.FullName; + + return string.Format(CultureInfo.InvariantCulture, "{0}.{1}", new object[] { type.Namespace, type.Name }); + } + + /// + /// Creates properties for the given . + /// + /// The type to create properties for. + /// /// The member serialization mode for the type. + /// Properties for the given . + protected virtual IList CreateProperties(Type type, MemberSerialization memberSerialization) + { + List members = GetSerializableMembers(type); + if (members == null) + throw new JsonSerializationException("Null collection of seralizable members returned."); + + JsonPropertyCollection properties = new JsonPropertyCollection(type); + + foreach (MemberInfo member in members) + { + JsonProperty property = CreateProperty(member, memberSerialization); + + if (property != null) + properties.AddProperty(property); + } + + return properties; + } + + /// + /// Creates the used by the serializer to get and set values from a member. + /// + /// The member. + /// The used by the serializer to get and set values from a member. + protected virtual IValueProvider CreateMemberValueProvider(MemberInfo member) + { +#if !PocketPC && !SILVERLIGHT + if (DynamicCodeGeneration) + return new DynamicValueProvider(member); +#endif + + return new ReflectionValueProvider(member); + } + + /// + /// Creates a for the given . + /// + /// The member's parent . + /// The member to create a for. + /// A created for the given . + protected virtual JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) + { + JsonProperty property = new JsonProperty(); + property.PropertyType = ReflectionUtils.GetMemberUnderlyingType(member); + property.ValueProvider = CreateMemberValueProvider(member); + + // resolve converter for property + // the class type might have a converter but the property converter takes presidence + property.Converter = JsonTypeReflector.GetJsonConverter(member, property.PropertyType); + +#if !PocketPC && !NET20 + DataContractAttribute dataContractAttribute = JsonTypeReflector.GetDataContractAttribute(member.DeclaringType); + + DataMemberAttribute dataMemberAttribute; + if (dataContractAttribute != null) + dataMemberAttribute = JsonTypeReflector.GetAttribute(member); + else + dataMemberAttribute = null; +#endif + + JsonPropertyAttribute propertyAttribute = JsonTypeReflector.GetAttribute(member); + bool hasIgnoreAttribute = (JsonTypeReflector.GetAttribute(member) != null); + + string mappedName; + if (propertyAttribute != null && propertyAttribute.PropertyName != null) + mappedName = propertyAttribute.PropertyName; +#if !PocketPC && !NET20 + else if (dataMemberAttribute != null && dataMemberAttribute.Name != null) + mappedName = dataMemberAttribute.Name; +#endif + else + mappedName = member.Name; + + property.PropertyName = ResolvePropertyName(mappedName); + + if (propertyAttribute != null) + property.Required = propertyAttribute.Required; +#if !PocketPC && !NET20 + else if (dataMemberAttribute != null) + property.Required = (dataMemberAttribute.IsRequired) ? Required.AllowNull : Required.Default; +#endif + else + property.Required = Required.Default; + + property.Ignored = (hasIgnoreAttribute || + (memberSerialization == MemberSerialization.OptIn + && propertyAttribute == null +#if !PocketPC && !NET20 + && dataMemberAttribute == null +#endif +)); + + bool allowNonPublicAccess = false; + if ((DefaultMembersSearchFlags & BindingFlags.NonPublic) == BindingFlags.NonPublic) + allowNonPublicAccess = true; + if (propertyAttribute != null) + allowNonPublicAccess = true; +#if !PocketPC && !NET20 + if (dataMemberAttribute != null) + allowNonPublicAccess = true; +#endif + + property.Readable = ReflectionUtils.CanReadMemberValue(member, allowNonPublicAccess); + property.Writable = ReflectionUtils.CanSetMemberValue(member, allowNonPublicAccess); + + property.MemberConverter = JsonTypeReflector.GetJsonConverter(member, ReflectionUtils.GetMemberUnderlyingType(member)); + + DefaultValueAttribute defaultValueAttribute = JsonTypeReflector.GetAttribute(member); + property.DefaultValue = (defaultValueAttribute != null) ? defaultValueAttribute.Value : null; + + property.NullValueHandling = (propertyAttribute != null) ? propertyAttribute._nullValueHandling : null; + property.DefaultValueHandling = (propertyAttribute != null) ? propertyAttribute._defaultValueHandling : null; + property.ReferenceLoopHandling = (propertyAttribute != null) ? propertyAttribute._referenceLoopHandling : null; + property.ObjectCreationHandling = (propertyAttribute != null) ? propertyAttribute._objectCreationHandling : null; + property.TypeNameHandling = (propertyAttribute != null) ? propertyAttribute._typeNameHandling : null; + property.IsReference = (propertyAttribute != null) ? propertyAttribute._isReference : null; + + property.ShouldSerialize = CreateShouldSerializeTest(member); + + SetIsSpecifiedActions(property, member); + + return property; + } + + private Predicate CreateShouldSerializeTest(MemberInfo member) + { + MethodInfo shouldSerializeMethod = member.DeclaringType.GetMethod(JsonTypeReflector.ShouldSerializePrefix + member.Name, new Type[0]); + + if (shouldSerializeMethod == null || shouldSerializeMethod.ReturnType != typeof(bool)) + return null; + + MethodCall shouldSerializeCall = + JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(shouldSerializeMethod); + + return o => (bool)shouldSerializeCall(o); + } + + private void SetIsSpecifiedActions(JsonProperty property, MemberInfo member) + { + MemberInfo specifiedMember = member.DeclaringType.GetProperty(member.Name + JsonTypeReflector.SpecifiedPostfix); + if (specifiedMember == null) + specifiedMember = member.DeclaringType.GetField(member.Name + JsonTypeReflector.SpecifiedPostfix); + + if (specifiedMember == null || ReflectionUtils.GetMemberUnderlyingType(specifiedMember) != typeof(bool)) + { + return; + } + + Func specifiedPropertyGet = JsonTypeReflector.ReflectionDelegateFactory.CreateGet(specifiedMember); + + property.GetIsSpecified = o => (bool)specifiedPropertyGet(o); + property.SetIsSpecified = JsonTypeReflector.ReflectionDelegateFactory.CreateSet(specifiedMember); + } + + /// + /// Resolves the name of the property. + /// + /// Name of the property. + /// Name of the property. + protected virtual string ResolvePropertyName(string propertyName) + { + return propertyName; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/DefaultReferenceResolver.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/DefaultReferenceResolver.cs new file mode 100644 index 0000000..cb8dbe1 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/DefaultReferenceResolver.cs @@ -0,0 +1,87 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Serialization +{ + internal class DefaultReferenceResolver : IReferenceResolver + { + private int _referenceCount; + + private BidirectionalDictionary GetMappings(object context) + { + JsonSerializerInternalBase internalSerializer; + + if (context is JsonSerializerInternalBase) + internalSerializer = (JsonSerializerInternalBase) context; + else if (context is JsonSerializerProxy) + internalSerializer = ((JsonSerializerProxy) context).GetInternalSerializer(); + else + throw new Exception("The DefaultReferenceResolver can only be used internally."); + + return internalSerializer.DefaultReferenceMappings; + } + + public object ResolveReference(object context, string reference) + { + object value; + GetMappings(context).TryGetByFirst(reference, out value); + return value; + } + + public string GetReference(object context, object value) + { + var mappings = GetMappings(context); + + string reference; + if (!mappings.TryGetBySecond(value, out reference)) + { + _referenceCount++; + reference = _referenceCount.ToString(CultureInfo.InvariantCulture); + mappings.Add(reference, value); + } + + return reference; + } + + public void AddReference(object context, string reference, object value) + { + GetMappings(context).Add(reference, value); + } + + public bool IsReferenced(object context, object value) + { + string reference; + return GetMappings(context).TryGetBySecond(value, out reference); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs new file mode 100644 index 0000000..3a5c772 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/DefaultSerializationBinder.cs @@ -0,0 +1,120 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Runtime.Serialization; +using System.Reflection; +using System.Globalization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// The default serialization binder used when resolving and loading classes from type names. + /// + public class DefaultSerializationBinder : SerializationBinder + { + internal static readonly DefaultSerializationBinder Instance = new DefaultSerializationBinder(); + + private readonly ThreadSafeStore _typeCache = new ThreadSafeStore(GetTypeFromTypeNameKey); + + private static Type GetTypeFromTypeNameKey(TypeNameKey typeNameKey) + { + string assemblyName = typeNameKey.AssemblyName; + string typeName = typeNameKey.TypeName; + + if (assemblyName != null) + { + Assembly assembly; + +#if !SILVERLIGHT && !PocketPC + // look, I don't like using obsolete methods as much as you do but this is the only way + // Assembly.Load won't check the GAC for a partial name +#pragma warning disable 618,612 + assembly = Assembly.LoadWithPartialName(assemblyName); +#pragma warning restore 618,612 +#else + assembly = Assembly.Load(assemblyName); +#endif + + if (assembly == null) + throw new JsonSerializationException("Could not load assembly '{0}'.".FormatWith(CultureInfo.InvariantCulture, assemblyName)); + + Type type = assembly.GetType(typeName); + if (type == null) + throw new JsonSerializationException("Could not find type '{0}' in assembly '{1}'.".FormatWith(CultureInfo.InvariantCulture, typeName, assembly.FullName)); + + return type; + } + else + { + return Type.GetType(typeName); + } + } + + internal struct TypeNameKey : IEquatable + { + internal readonly string AssemblyName; + internal readonly string TypeName; + + public TypeNameKey(string assemblyName, string typeName) + { + AssemblyName = assemblyName; + TypeName = typeName; + } + + public override int GetHashCode() + { + return ((AssemblyName != null) ? AssemblyName.GetHashCode() : 0) ^ ((TypeName != null) ? TypeName.GetHashCode() : 0); + } + + public override bool Equals(object obj) + { + if (!(obj is TypeNameKey)) + return false; + + return Equals((TypeNameKey)obj); + } + + public bool Equals(TypeNameKey other) + { + return (AssemblyName == other.AssemblyName && TypeName == other.TypeName); + } + } + + /// + /// When overridden in a derived class, controls the binding of a serialized object to a type. + /// + /// Specifies the name of the serialized object. + /// Specifies the name of the serialized object. + /// + /// The type of the object the formatter creates a new instance of. + /// + public override Type BindToType(string assemblyName, string typeName) + { + return _typeCache.Get(new TypeNameKey(assemblyName, typeName)); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/DynamicValueProvider.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/DynamicValueProvider.cs new file mode 100644 index 0000000..e998bad --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/DynamicValueProvider.cs @@ -0,0 +1,111 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC && !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Get and set values for a using dynamic methods. + /// + public class DynamicValueProvider : IValueProvider + { + private readonly MemberInfo _memberInfo; + private Func _getter; + private Action _setter; + + /// + /// Initializes a new instance of the class. + /// + /// The member info. + public DynamicValueProvider(MemberInfo memberInfo) + { + ValidationUtils.ArgumentNotNull(memberInfo, "memberInfo"); + _memberInfo = memberInfo; + } + + /// + /// Sets the value. + /// + /// The target to set the value on. + /// The value to set on the target. + public void SetValue(object target, object value) + { + try + { + if (_setter == null) + _setter = DynamicReflectionDelegateFactory.Instance.CreateSet(_memberInfo); + +#if DEBUG + // dynamic method doesn't check whether the type is 'legal' to set + // add this check for unit tests + if (value == null) + { + if (!ReflectionUtils.IsNullable(ReflectionUtils.GetMemberUnderlyingType(_memberInfo))) + throw new Exception("Incompatible value. Cannot set {0} to null.".FormatWith(CultureInfo.InvariantCulture, _memberInfo)); + } + else if (!ReflectionUtils.GetMemberUnderlyingType(_memberInfo).IsAssignableFrom(value.GetType())) + { + throw new Exception("Incompatible value. Cannot set {0} to type {1}.".FormatWith(CultureInfo.InvariantCulture, _memberInfo, value.GetType())); + } +#endif + + _setter(target, value); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error setting value to '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex); + } + } + + /// + /// Gets the value. + /// + /// The target to get the value from. + /// The value. + public object GetValue(object target) + { + try + { + if (_getter == null) + _getter = DynamicReflectionDelegateFactory.Instance.CreateGet(_memberInfo); + + return _getter(target); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error getting value from '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex); + } + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/ErrorContext.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/ErrorContext.cs new file mode 100644 index 0000000..a89535f --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/ErrorContext.cs @@ -0,0 +1,66 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Provides information surrounding an error. + /// + public class ErrorContext + { + internal ErrorContext(object originalObject, object member, Exception error) + { + OriginalObject = originalObject; + Member = member; + Error = error; + } + + /// + /// Gets or sets the error. + /// + /// The error. + public Exception Error { get; private set; } + /// + /// Gets the original object that caused the error. + /// + /// The original object that caused the error. + public object OriginalObject { get; private set; } + /// + /// Gets the member that caused the error. + /// + /// The member that caused the error. + public object Member { get; private set; } + /// + /// Gets or sets a value indicating whether this is handled. + /// + /// true if handled; otherwise, false. + public bool Handled { get; set; } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/ErrorEventArgs.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/ErrorEventArgs.cs new file mode 100644 index 0000000..0c38b12 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/ErrorEventArgs.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Provides data for the Error event. + /// + public class ErrorEventArgs : EventArgs + { + /// + /// Gets the current object the error event is being raised against. + /// + /// The current object the error event is being raised against. + public object CurrentObject { get; private set; } + /// + /// Gets the error context. + /// + /// The error context. + public ErrorContext ErrorContext { get; private set; } + + /// + /// Initializes a new instance of the class. + /// + /// The current object. + /// The error context. + public ErrorEventArgs(object currentObject, ErrorContext errorContext) + { + CurrentObject = currentObject; + ErrorContext = errorContext; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/IContractResolver.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/IContractResolver.cs new file mode 100644 index 0000000..19d9671 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/IContractResolver.cs @@ -0,0 +1,45 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Used by to resolves a for a given . + /// + public interface IContractResolver + { + /// + /// Resolves the contract for a given type. + /// + /// The type to resolve a contract for. + /// The contract for a given type. + JsonContract ResolveContract(Type type); + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/IReferenceResolver.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/IReferenceResolver.cs new file mode 100644 index 0000000..bd96b19 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/IReferenceResolver.cs @@ -0,0 +1,64 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Used to resolve references when serializing and deserializing JSON by the . + /// + public interface IReferenceResolver + { + /// + /// Resolves a reference to its object. + /// + /// The serialization context. + /// The reference to resolve. + /// The object that + object ResolveReference(object context, string reference); + /// + /// Gets the reference for the sepecified object. + /// + /// The serialization context. + /// The object to get a reference for. + /// The reference to the object. + string GetReference(object context, object value); + /// + /// Determines whether the specified object is referenced. + /// + /// The serialization context. + /// The object to test for a reference. + /// + /// true if the specified object is referenced; otherwise, false. + /// + bool IsReferenced(object context, object value); + /// + /// Adds a reference to the specified object. + /// + /// The serialization context. + /// The reference. + /// The object to reference. + void AddReference(object context, string reference, object value); + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/IValueProvider.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/IValueProvider.cs new file mode 100644 index 0000000..9f75f7f --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/IValueProvider.cs @@ -0,0 +1,47 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Provides methods to get and set values. + /// + public interface IValueProvider + { + /// + /// Sets the value. + /// + /// The target to set the value on. + /// The value to set on the target. + void SetValue(object target, object value); + + /// + /// Gets the value. + /// + /// The target to get the value from. + /// The value. + object GetValue(object target); + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs new file mode 100644 index 0000000..d196aea --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonArrayContract.cs @@ -0,0 +1,129 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Newtonsoft.Json.Utilities; +using System.Collections; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonArrayContract : JsonContract + { + internal Type CollectionItemType { get; private set; } + + private readonly bool _isCollectionItemTypeNullableType; + private readonly Type _genericCollectionDefinitionType; + private Type _genericWrapperType; + private MethodCall _genericWrapperCreator; + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonArrayContract(Type underlyingType) + : base(underlyingType) + { + if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(ICollection<>), out _genericCollectionDefinitionType)) + { + CollectionItemType = _genericCollectionDefinitionType.GetGenericArguments()[0]; + } + else + { + CollectionItemType = ReflectionUtils.GetCollectionItemType(UnderlyingType); + } + + if (CollectionItemType != null) + _isCollectionItemTypeNullableType = ReflectionUtils.IsNullableType(CollectionItemType); + + if (IsTypeGenericCollectionInterface(UnderlyingType)) + { + CreatedType = ReflectionUtils.MakeGenericType(typeof(List<>), CollectionItemType); + } + } + + internal IWrappedCollection CreateWrapper(object list) + { + if ((list is IList && (CollectionItemType == null || !_isCollectionItemTypeNullableType)) + || UnderlyingType.IsArray) + return new CollectionWrapper((IList)list); + + if (_genericCollectionDefinitionType != null) + { + EnsureGenericWrapperCreator(); + return (IWrappedCollection) _genericWrapperCreator(null, list); + } + else + { + IList values = ((IEnumerable) list).Cast().ToList(); + + if (CollectionItemType != null) + { + Array array = Array.CreateInstance(CollectionItemType, values.Count); + for (int i = 0; i < values.Count; i++) + { + array.SetValue(values[i], i); + } + + values = array; + } + + return new CollectionWrapper(values); + } + } + + private void EnsureGenericWrapperCreator() + { + if (_genericWrapperType == null) + { + _genericWrapperType = ReflectionUtils.MakeGenericType(typeof (CollectionWrapper<>), CollectionItemType); + + Type constructorArgument = (ReflectionUtils.InheritsGenericDefinition(_genericCollectionDefinitionType, typeof (List<>))) + ? ReflectionUtils.MakeGenericType(typeof (ICollection<>), CollectionItemType) + : _genericCollectionDefinitionType; + + ConstructorInfo genericWrapperConstructor = _genericWrapperType.GetConstructor(new[] { constructorArgument }); + _genericWrapperCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(genericWrapperConstructor); + } + } + + private bool IsTypeGenericCollectionInterface(Type type) + { + if (!type.IsGenericType) + return false; + + Type genericDefinition = type.GetGenericTypeDefinition(); + + return (genericDefinition == typeof(IList<>) + || genericDefinition == typeof(ICollection<>) + || genericDefinition == typeof(IEnumerable<>)); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonContract.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonContract.cs new file mode 100644 index 0000000..6f20dbb --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonContract.cs @@ -0,0 +1,153 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Reflection; +using System.Runtime.Serialization; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public abstract class JsonContract + { + /// + /// Gets the underlying type for the contract. + /// + /// The underlying type for the contract. + public Type UnderlyingType { get; private set; } + + /// + /// Gets or sets the type created during deserialization. + /// + /// The type created during deserialization. + public Type CreatedType { get; set; } + + /// + /// Gets or sets whether this type contract is serialized as a reference. + /// + /// Whether this type contract is serialized as a reference. + public bool? IsReference { get; set; } + + /// + /// Gets or sets the default for this contract. + /// + /// The converter. + public JsonConverter Converter { get; set; } + + // internally specified JsonConverter's to override default behavour + // checked for after passed in converters and attribute specified converters + internal JsonConverter InternalConverter { get; set; } + +#if !PocketPC + /// + /// Gets or sets the method called immediately after deserialization of the object. + /// + /// The method called immediately after deserialization of the object. + public MethodInfo OnDeserialized { get; set; } + /// + /// Gets or sets the method called during deserialization of the object. + /// + /// The method called during deserialization of the object. + public MethodInfo OnDeserializing { get; set; } + /// + /// Gets or sets the method called after serialization of the object graph. + /// + /// The method called after serialization of the object graph. + public MethodInfo OnSerialized { get; set; } + /// + /// Gets or sets the method called before serialization of the object. + /// + /// The method called before serialization of the object. + public MethodInfo OnSerializing { get; set; } +#endif + + /// + /// Gets or sets the default creator method used to create the object. + /// + /// The default creator method used to create the object. + public Func DefaultCreator { get; set; } + + /// + /// Gets or sets a value indicating whether [default creator non public]. + /// + /// true if the default object creator is non-public; otherwise, false. + public bool DefaultCreatorNonPublic { get; set; } + + /// + /// Gets or sets the method called when an error is thrown during the serialization of the object. + /// + /// The method called when an error is thrown during the serialization of the object. + public MethodInfo OnError { get; set; } + + internal void InvokeOnSerializing(object o, StreamingContext context) + { +#if !PocketPC + if (OnSerializing != null) + OnSerializing.Invoke(o, new object[] { context }); +#endif + } + + internal void InvokeOnSerialized(object o, StreamingContext context) + { +#if !PocketPC + if (OnSerialized != null) + OnSerialized.Invoke(o, new object[] { context }); +#endif + } + + internal void InvokeOnDeserializing(object o, StreamingContext context) + { +#if !PocketPC + if (OnDeserializing != null) + OnDeserializing.Invoke(o, new object[] { context }); +#endif + } + + internal void InvokeOnDeserialized(object o, StreamingContext context) + { +#if !PocketPC + if (OnDeserialized != null) + OnDeserialized.Invoke(o, new object[] { context }); +#endif + } + + internal void InvokeOnError(object o, StreamingContext context, ErrorContext errorContext) + { + if (OnError != null) + OnError.Invoke(o, new object[] { context, errorContext }); + } + + internal JsonContract(Type underlyingType) + { + ValidationUtils.ArgumentNotNull(underlyingType, "underlyingType"); + + UnderlyingType = underlyingType; + CreatedType = underlyingType; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs new file mode 100644 index 0000000..55b757d --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonDictionaryContract.cs @@ -0,0 +1,106 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Reflection; +using Newtonsoft.Json.Utilities; +using System.Collections; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonDictionaryContract : JsonContract + { + /// + /// Gets or sets the property name resolver. + /// + /// The property name resolver. + public Func PropertyNameResolver { get; set; } + + internal Type DictionaryKeyType { get; private set; } + internal Type DictionaryValueType { get; private set; } + + private readonly Type _genericCollectionDefinitionType; + private Type _genericWrapperType; + private MethodCall _genericWrapperCreator; + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonDictionaryContract(Type underlyingType) + : base(underlyingType) + { + Type keyType; + Type valueType; + if (ReflectionUtils.ImplementsGenericDefinition(underlyingType, typeof(IDictionary<,>), out _genericCollectionDefinitionType)) + { + keyType = _genericCollectionDefinitionType.GetGenericArguments()[0]; + valueType = _genericCollectionDefinitionType.GetGenericArguments()[1]; + } + else + { + ReflectionUtils.GetDictionaryKeyValueTypes(UnderlyingType, out keyType, out valueType); + } + + DictionaryKeyType = keyType; + DictionaryValueType = valueType; + + if (IsTypeGenericDictionaryInterface(UnderlyingType)) + { + CreatedType = ReflectionUtils.MakeGenericType(typeof(Dictionary<,>), keyType, valueType); + } + } + + internal IWrappedDictionary CreateWrapper(object dictionary) + { + if (dictionary is IDictionary) + return new DictionaryWrapper((IDictionary)dictionary); + + if (_genericWrapperType == null) + { + _genericWrapperType = ReflectionUtils.MakeGenericType(typeof(DictionaryWrapper<,>), DictionaryKeyType, DictionaryValueType); + + ConstructorInfo genericWrapperConstructor = _genericWrapperType.GetConstructor(new[] { _genericCollectionDefinitionType }); + _genericWrapperCreator = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(genericWrapperConstructor); + } + + return (IWrappedDictionary)_genericWrapperCreator(null, dictionary); + } + + private bool IsTypeGenericDictionaryInterface(Type type) + { + if (!type.IsGenericType) + return false; + + Type genericDefinition = type.GetGenericTypeDefinition(); + + return (genericDefinition == typeof(IDictionary<,>)); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs new file mode 100644 index 0000000..8d61f6a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonDynamicContract.cs @@ -0,0 +1,63 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !(NET35 || NET20 || WINDOWS_PHONE) +using System; +using System.Collections.Generic; +using System.Reflection; +using Newtonsoft.Json.Utilities; +using System.Collections; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonDynamicContract : JsonContract + { + /// + /// Gets the object's properties. + /// + /// The object's properties. + public JsonPropertyCollection Properties { get; private set; } + + /// + /// Gets or sets the property name resolver. + /// + /// The property name resolver. + public Func PropertyNameResolver { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonDynamicContract(Type underlyingType) + : base(underlyingType) + { + Properties = new JsonPropertyCollection(UnderlyingType); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonFormatterConverter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonFormatterConverter.cs new file mode 100644 index 0000000..09f2bea --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonFormatterConverter.cs @@ -0,0 +1,154 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !SILVERLIGHT && !PocketPC +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Runtime.Serialization; +using System.Text; +using Newtonsoft.Json.Utilities; +using Newtonsoft.Json.Linq; + +namespace Newtonsoft.Json.Serialization +{ + internal class JsonFormatterConverter : IFormatterConverter + { + private readonly JsonSerializer _serializer; + + public JsonFormatterConverter(JsonSerializer serializer) + { + ValidationUtils.ArgumentNotNull(serializer, "serializer"); + + _serializer = serializer; + } + + private T GetTokenValue(object value) + { + ValidationUtils.ArgumentNotNull(value, "value"); + + JValue v = (JValue)value; + return (T)System.Convert.ChangeType(v.Value, typeof(T), CultureInfo.InvariantCulture); + } + + public object Convert(object value, Type type) + { + ValidationUtils.ArgumentNotNull(value, "value"); + + JToken token = value as JToken; + if (token == null) + throw new ArgumentException("Value is not a JToken.", "value"); + + return _serializer.Deserialize(token.CreateReader(), type); + } + + public object Convert(object value, TypeCode typeCode) + { + ValidationUtils.ArgumentNotNull(value, "value"); + + if (value is JValue) + value = ((JValue) value).Value; + + return System.Convert.ChangeType(value, typeCode, CultureInfo.InvariantCulture); + } + + public bool ToBoolean(object value) + { + return GetTokenValue(value); + } + + public byte ToByte(object value) + { + return GetTokenValue(value); + } + + public char ToChar(object value) + { + return GetTokenValue(value); + } + + public DateTime ToDateTime(object value) + { + return GetTokenValue(value); + } + + public decimal ToDecimal(object value) + { + return GetTokenValue(value); + } + + public double ToDouble(object value) + { + return GetTokenValue(value); + } + + public short ToInt16(object value) + { + return GetTokenValue(value); + } + + public int ToInt32(object value) + { + return GetTokenValue(value); + } + + public long ToInt64(object value) + { + return GetTokenValue(value); + } + + public sbyte ToSByte(object value) + { + return GetTokenValue(value); + } + + public float ToSingle(object value) + { + return GetTokenValue(value); + } + + public string ToString(object value) + { + return GetTokenValue(value); + } + + public ushort ToUInt16(object value) + { + return GetTokenValue(value); + } + + public uint ToUInt32(object value) + { + return GetTokenValue(value); + } + + public ulong ToUInt64(object value) + { + return GetTokenValue(value); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs new file mode 100644 index 0000000..b943d60 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonISerializableContract.cs @@ -0,0 +1,56 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !SILVERLIGHT && !PocketPC +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonISerializableContract : JsonContract + { + /// + /// Gets or sets the ISerializable object constructor. + /// + /// The ISerializable object constructor. + public ObjectConstructor ISerializableCreator { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonISerializableContract(Type underlyingType) + : base(underlyingType) + { + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonLinqContract.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonLinqContract.cs new file mode 100644 index 0000000..212c445 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonLinqContract.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonLinqContract : JsonContract + { + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonLinqContract(Type underlyingType) + : base(underlyingType) + { + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs new file mode 100644 index 0000000..61687a9 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonObjectContract.cs @@ -0,0 +1,72 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Reflection; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonObjectContract : JsonContract + { + /// + /// Gets or sets the object member serialization. + /// + /// The member object serialization. + public MemberSerialization MemberSerialization { get; set; } + + /// + /// Gets the object's properties. + /// + /// The object's properties. + public JsonPropertyCollection Properties { get; private set; } + + /// + /// Gets or sets the override constructor used to create the object. + /// This is set when a constructor is marked up using the + /// JsonConstructor attribute. + /// + /// The override constructor. + public ConstructorInfo OverrideConstructor { get; set; } + + /// + /// Gets or sets the parametrized constructor used to create the object. + /// + /// The parametrized constructor. + public ConstructorInfo ParametrizedConstructor { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonObjectContract(Type underlyingType) + : base(underlyingType) + { + Properties = new JsonPropertyCollection(UnderlyingType); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonPrimitiveContract.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonPrimitiveContract.cs new file mode 100644 index 0000000..335c090 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonPrimitiveContract.cs @@ -0,0 +1,44 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonPrimitiveContract : JsonContract + { + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonPrimitiveContract(Type underlyingType) + : base(underlyingType) + { + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonProperty.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonProperty.cs new file mode 100644 index 0000000..f1b9a71 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonProperty.cs @@ -0,0 +1,163 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Maps a JSON property to a .NET member. + /// + public class JsonProperty + { + /// + /// Gets the name of the property. + /// + /// The name of the property. + public string PropertyName { get; set; } + + /// + /// Gets the that will get and set the during serialization. + /// + /// The that will get and set the during serialization. + public IValueProvider ValueProvider { get; set; } + + /// + /// Gets or sets the type of the property. + /// + /// The type of the property. + public Type PropertyType { get; set; } + + /// + /// Gets or sets the for the property. + /// If set this converter takes presidence over the contract converter for the property type. + /// + /// The converter. + public JsonConverter Converter { get; set; } + + /// + /// Gets a value indicating whether this is ignored. + /// + /// true if ignored; otherwise, false. + public bool Ignored { get; set; } + + /// + /// Gets a value indicating whether this is readable. + /// + /// true if readable; otherwise, false. + public bool Readable { get; set; } + + /// + /// Gets a value indicating whether this is writable. + /// + /// true if writable; otherwise, false. + public bool Writable { get; set; } + + /// + /// Gets the member converter. + /// + /// The member converter. + public JsonConverter MemberConverter { get; set; } + + /// + /// Gets the default value. + /// + /// The default value. + public object DefaultValue { get; set; } + + /// + /// Gets a value indicating whether this is required. + /// + /// A value indicating whether this is required. + public Required Required { get; set; } + + /// + /// Gets a value indicating whether this property preserves object references. + /// + /// + /// true if this instance is reference; otherwise, false. + /// + public bool? IsReference { get; set; } + + /// + /// Gets the property null value handling. + /// + /// The null value handling. + public NullValueHandling? NullValueHandling { get; set; } + + /// + /// Gets the property default value handling. + /// + /// The default value handling. + public DefaultValueHandling? DefaultValueHandling { get; set; } + + /// + /// Gets the property reference loop handling. + /// + /// The reference loop handling. + public ReferenceLoopHandling? ReferenceLoopHandling { get; set; } + + /// + /// Gets the property object creation handling. + /// + /// The object creation handling. + public ObjectCreationHandling? ObjectCreationHandling { get; set; } + + /// + /// Gets or sets the type name handling. + /// + /// The type name handling. + public TypeNameHandling? TypeNameHandling { get; set; } + + /// + /// Gets or sets a predicate used to determine whether the property should be serialize. + /// + /// A predicate used to determine whether the property should be serialize. + public Predicate ShouldSerialize { get; set; } + + /// + /// Gets or sets a predicate used to determine whether the property should be serialized. + /// + /// A predicate used to determine whether the property should be serialized. + public Predicate GetIsSpecified { get; set; } + + /// + /// Gets or sets an action used to set whether the property has been deserialized. + /// + /// An action used to set whether the property has been deserialized. + public Action SetIsSpecified { get; set; } + + /// + /// Returns a that represents this instance. + /// + /// + /// A that represents this instance. + /// + public override string ToString() + { + return PropertyName; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonPropertyCollection.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonPropertyCollection.cs new file mode 100644 index 0000000..78e5fdf --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonPropertyCollection.cs @@ -0,0 +1,122 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections.ObjectModel; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// A collection of objects. + /// + public class JsonPropertyCollection : KeyedCollection + { + private readonly Type _type; + + /// + /// Initializes a new instance of the class. + /// + /// The type. + public JsonPropertyCollection(Type type) + { + ValidationUtils.ArgumentNotNull(type, "type"); + _type = type; + } + + /// + /// When implemented in a derived class, extracts the key from the specified element. + /// + /// The element from which to extract the key. + /// The key for the specified element. + protected override string GetKeyForItem(JsonProperty item) + { + return item.PropertyName; + } + + /// + /// Adds a object. + /// + /// The property to add to the collection. + public void AddProperty(JsonProperty property) + { + if (Contains(property.PropertyName)) + { + // don't overwrite existing property with ignored property + if (property.Ignored) + return; + + JsonProperty existingProperty = this[property.PropertyName]; + + if (!existingProperty.Ignored) + throw new JsonSerializationException( + "A member with the name '{0}' already exists on '{1}'. Use the JsonPropertyAttribute to specify another name.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName, _type)); + + // remove ignored property so it can be replaced in collection + Remove(existingProperty); + } + + Add(property); + } + + /// + /// Gets the closest matching object. + /// First attempts to get an exact case match of propertyName and then + /// a case insensitive match. + /// + /// Name of the property. + /// A matching property if found. + public JsonProperty GetClosestMatchProperty(string propertyName) + { + JsonProperty property = GetProperty(propertyName, StringComparison.Ordinal); + if (property == null) + property = GetProperty(propertyName, StringComparison.OrdinalIgnoreCase); + + return property; + } + + /// + /// Gets a property by property name. + /// + /// The name of the property to get. + /// Type property name string comparison. + /// A matching property if found. + public JsonProperty GetProperty(string propertyName, StringComparison comparisonType) + { + foreach (JsonProperty property in this) + { + if (string.Equals(propertyName, property.PropertyName, comparisonType)) + { + return property; + } + } + + return null; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs new file mode 100644 index 0000000..5f09aa6 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalBase.cs @@ -0,0 +1,112 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Net; +using System.Runtime.CompilerServices; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + internal abstract class JsonSerializerInternalBase + { + private class ReferenceEqualsEqualityComparer : IEqualityComparer + { + bool IEqualityComparer.Equals(object x, object y) + { + return ReferenceEquals(x, y); + } + + int IEqualityComparer.GetHashCode(object obj) + { +#if !PocketPC + // put objects in a bucket based on their reference + return RuntimeHelpers.GetHashCode(obj); +#else + // put all objects in the same bucket so ReferenceEquals is called on all + return -1; +#endif + } + } + + private ErrorContext _currentErrorContext; + private BidirectionalDictionary _mappings; + + internal JsonSerializer Serializer { get; private set; } + + protected JsonSerializerInternalBase(JsonSerializer serializer) + { + ValidationUtils.ArgumentNotNull(serializer, "serializer"); + + Serializer = serializer; + } + + internal BidirectionalDictionary DefaultReferenceMappings + { + get + { + // override equality comparer for object key dictionary + // object will be modified as it deserializes and might have mutable hashcode + if (_mappings == null) + _mappings = new BidirectionalDictionary( + EqualityComparer.Default, + new ReferenceEqualsEqualityComparer()); + + return _mappings; + } + } + + protected ErrorContext GetErrorContext(object currentObject, object member, Exception error) + { + if (_currentErrorContext == null) + _currentErrorContext = new ErrorContext(currentObject, member, error); + + if (_currentErrorContext.Error != error) + throw new InvalidOperationException("Current error context error is different to requested error."); + + return _currentErrorContext; + } + + protected void ClearErrorContext() + { + if (_currentErrorContext == null) + throw new InvalidOperationException("Could not clear error context. Error context is already null."); + + _currentErrorContext = null; + } + + protected bool IsErrorHandled(object currentObject, JsonContract contract, object keyValue, Exception ex) + { + ErrorContext errorContext = GetErrorContext(currentObject, keyValue, ex); + contract.InvokeOnError(currentObject, Serializer.Context, errorContext); + + if (!errorContext.Handled) + Serializer.OnError(new ErrorEventArgs(currentObject, errorContext)); + + return errorContext.Handled; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs new file mode 100644 index 0000000..63dde38 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalReader.cs @@ -0,0 +1,1082 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +#if !(NET35 || NET20 || WINDOWS_PHONE) +using System.Dynamic; +#endif +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ + internal class JsonSerializerInternalReader : JsonSerializerInternalBase + { + private JsonSerializerProxy _internalSerializer; +#if !SILVERLIGHT && !PocketPC + private JsonFormatterConverter _formatterConverter; +#endif + + public JsonSerializerInternalReader(JsonSerializer serializer) : base(serializer) + { + } + + public void Populate(JsonReader reader, object target) + { + ValidationUtils.ArgumentNotNull(target, "target"); + + Type objectType = target.GetType(); + + JsonContract contract = Serializer.ContractResolver.ResolveContract(objectType); + + if (reader.TokenType == JsonToken.None) + reader.Read(); + + if (reader.TokenType == JsonToken.StartArray) + { + if (contract is JsonArrayContract) + PopulateList(CollectionUtils.CreateCollectionWrapper(target), reader, null, (JsonArrayContract)contract); + else + throw new JsonSerializationException("Cannot populate JSON array onto type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); + } + else if (reader.TokenType == JsonToken.StartObject) + { + CheckedRead(reader); + + string id = null; + if (reader.TokenType == JsonToken.PropertyName && string.Equals(reader.Value.ToString(), JsonTypeReflector.IdPropertyName, StringComparison.Ordinal)) + { + CheckedRead(reader); + id = reader.Value.ToString(); + CheckedRead(reader); + } + + if (contract is JsonDictionaryContract) + PopulateDictionary(CollectionUtils.CreateDictionaryWrapper(target), reader, (JsonDictionaryContract) contract, id); + else if (contract is JsonObjectContract) + PopulateObject(target, reader, (JsonObjectContract) contract, id); + else + throw new JsonSerializationException("Cannot populate JSON object onto type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); + } + else + { + throw new JsonSerializationException("Unexpected initial token '{0}' when populating object. Expected JSON object or array.".FormatWith(CultureInfo.InvariantCulture, reader.TokenType)); + } + } + + private JsonContract GetContractSafe(Type type) + { + if (type == null) + return null; + + return Serializer.ContractResolver.ResolveContract(type); + } + + private JsonContract GetContractSafe(Type type, object value) + { + if (value == null) + return GetContractSafe(type); + + return Serializer.ContractResolver.ResolveContract(value.GetType()); + } + + public object Deserialize(JsonReader reader, Type objectType) + { + if (reader == null) + throw new ArgumentNullException("reader"); + + if (reader.TokenType == JsonToken.None && !ReadForType(reader, objectType, null)) + return null; + + return CreateValueNonProperty(reader, objectType, GetContractSafe(objectType)); + } + + private JsonSerializerProxy GetInternalSerializer() + { + if (_internalSerializer == null) + _internalSerializer = new JsonSerializerProxy(this); + + return _internalSerializer; + } + +#if !SILVERLIGHT && !PocketPC + private JsonFormatterConverter GetFormatterConverter() + { + if (_formatterConverter == null) + _formatterConverter = new JsonFormatterConverter(GetInternalSerializer()); + + return _formatterConverter; + } +#endif + + private JToken CreateJToken(JsonReader reader, JsonContract contract) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + + if (contract != null && contract.UnderlyingType == typeof(JRaw)) + { + return JRaw.Create(reader); + } + else + { + JToken token; + using (JTokenWriter writer = new JTokenWriter()) + { + writer.WriteToken(reader); + token = writer.Token; + } + + return token; + } + } + + private JToken CreateJObject(JsonReader reader) + { + ValidationUtils.ArgumentNotNull(reader, "reader"); + + // this is needed because we've already read inside the object, looking for special properties + JToken token; + using (JTokenWriter writer = new JTokenWriter()) + { + writer.WriteStartObject(); + + if (reader.TokenType == JsonToken.PropertyName) + writer.WriteToken(reader, reader.Depth - 1); + else + writer.WriteEndObject(); + + token = writer.Token; + } + + return token; + } + + private object CreateValueProperty(JsonReader reader, JsonProperty property, object target, bool gottenCurrentValue, object currentValue) + { + JsonContract contract = GetContractSafe(property.PropertyType, currentValue); + Type objectType = property.PropertyType; + + JsonConverter converter = GetConverter(contract, property.MemberConverter); + + if (converter != null && converter.CanRead) + { + if (!gottenCurrentValue && target != null && property.Readable) + currentValue = property.ValueProvider.GetValue(target); + + return converter.ReadJson(reader, objectType, currentValue, GetInternalSerializer()); + } + + return CreateValueInternal(reader, objectType, contract, property, currentValue); + } + + private object CreateValueNonProperty(JsonReader reader, Type objectType, JsonContract contract) + { + JsonConverter converter = GetConverter(contract, null); + + if (converter != null && converter.CanRead) + return converter.ReadJson(reader, objectType, null, GetInternalSerializer()); + + return CreateValueInternal(reader, objectType, contract, null, null); + } + + private object CreateValueInternal(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, object existingValue) + { + if (contract is JsonLinqContract) + return CreateJToken(reader, contract); + + do + { + switch (reader.TokenType) + { + // populate a typed object or generic dictionary/array + // depending upon whether an objectType was supplied + case JsonToken.StartObject: + return CreateObject(reader, objectType, contract, member, existingValue); + case JsonToken.StartArray: + return CreateList(reader, objectType, contract, member, existingValue, null); + case JsonToken.Integer: + case JsonToken.Float: + case JsonToken.Boolean: + case JsonToken.Date: + case JsonToken.Bytes: + return EnsureType(reader.Value, CultureInfo.InvariantCulture, objectType); + case JsonToken.String: + // convert empty string to null automatically for nullable types + if (string.IsNullOrEmpty((string)reader.Value) && + objectType != null && + ReflectionUtils.IsNullableType(objectType)) + return null; + + // string that needs to be returned as a byte array should be base 64 decoded + if (objectType == typeof(byte[])) + return Convert.FromBase64String((string)reader.Value); + + return EnsureType(reader.Value, CultureInfo.InvariantCulture, objectType); + case JsonToken.StartConstructor: + case JsonToken.EndConstructor: + string constructorName = reader.Value.ToString(); + + return constructorName; + case JsonToken.Null: + case JsonToken.Undefined: + if (objectType == typeof (DBNull)) + return DBNull.Value; + + return EnsureType(reader.Value, CultureInfo.InvariantCulture, objectType); + case JsonToken.Raw: + return new JRaw((string)reader.Value); + case JsonToken.Comment: + // ignore + break; + default: + throw new JsonSerializationException("Unexpected token while deserializing object: " + reader.TokenType); + } + } while (reader.Read()); + + throw new JsonSerializationException("Unexpected end when deserializing object."); + } + + private JsonConverter GetConverter(JsonContract contract, JsonConverter memberConverter) + { + JsonConverter converter = null; + if (memberConverter != null) + { + // member attribute converter + converter = memberConverter; + } + else if (contract != null) + { + JsonConverter matchingConverter; + if (contract.Converter != null) + // class attribute converter + converter = contract.Converter; + else if ((matchingConverter = Serializer.GetMatchingConverter(contract.UnderlyingType)) != null) + // passed in converters + converter = matchingConverter; + else if (contract.InternalConverter != null) + // internally specified converter + converter = contract.InternalConverter; + } + return converter; + } + + private object CreateObject(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, object existingValue) + { + CheckedRead(reader); + + string id = null; + + if (reader.TokenType == JsonToken.PropertyName) + { + bool specialProperty; + + do + { + string propertyName = reader.Value.ToString(); + + if (string.Equals(propertyName, JsonTypeReflector.RefPropertyName, StringComparison.Ordinal)) + { + CheckedRead(reader); + if (reader.TokenType != JsonToken.String) + throw new JsonSerializationException("JSON reference {0} property must have a string value.".FormatWith(CultureInfo.InvariantCulture, JsonTypeReflector.RefPropertyName)); + + string reference = reader.Value.ToString(); + + CheckedRead(reader); + if (reader.TokenType == JsonToken.PropertyName) + throw new JsonSerializationException("Additional content found in JSON reference object. A JSON reference object should only have a {0} property.".FormatWith(CultureInfo.InvariantCulture, JsonTypeReflector.RefPropertyName)); + + return Serializer.ReferenceResolver.ResolveReference(this, reference); + } + else if (string.Equals(propertyName, JsonTypeReflector.TypePropertyName, StringComparison.Ordinal)) + { + CheckedRead(reader); + string qualifiedTypeName = reader.Value.ToString(); + + CheckedRead(reader); + + if ((((member != null) ? member.TypeNameHandling : null) ?? Serializer.TypeNameHandling) != TypeNameHandling.None) + { + string typeName; + string assemblyName; + ReflectionUtils.SplitFullyQualifiedTypeName(qualifiedTypeName, out typeName, out assemblyName); + + Type specifiedType; + try + { + specifiedType = Serializer.Binder.BindToType(assemblyName, typeName); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error resolving type specified in JSON '{0}'.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName), ex); + } + + if (specifiedType == null) + throw new JsonSerializationException("Type specified in JSON '{0}' was not resolved.".FormatWith(CultureInfo.InvariantCulture, qualifiedTypeName)); + + if (objectType != null && !objectType.IsAssignableFrom(specifiedType)) + throw new JsonSerializationException("Type specified in JSON '{0}' is not compatible with '{1}'.".FormatWith(CultureInfo.InvariantCulture, specifiedType.AssemblyQualifiedName, objectType.AssemblyQualifiedName)); + + objectType = specifiedType; + contract = GetContractSafe(specifiedType); + } + specialProperty = true; + } + else if (string.Equals(propertyName, JsonTypeReflector.IdPropertyName, StringComparison.Ordinal)) + { + CheckedRead(reader); + + id = reader.Value.ToString(); + CheckedRead(reader); + specialProperty = true; + } + else if (string.Equals(propertyName, JsonTypeReflector.ArrayValuesPropertyName, StringComparison.Ordinal)) + { + CheckedRead(reader); + object list = CreateList(reader, objectType, contract, member, existingValue, id); + CheckedRead(reader); + return list; + } + else + { + specialProperty = false; + } + } while (specialProperty + && reader.TokenType == JsonToken.PropertyName); + } + + if (!HasDefinedType(objectType)) + return CreateJObject(reader); + + if (contract == null) + throw new JsonSerializationException("Could not resolve type '{0}' to a JsonContract.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + JsonDictionaryContract dictionaryContract = contract as JsonDictionaryContract; + if (dictionaryContract != null) + { + if (existingValue == null) + return CreateAndPopulateDictionary(reader, dictionaryContract, id); + + return PopulateDictionary(dictionaryContract.CreateWrapper(existingValue), reader, dictionaryContract, id); + } + + JsonObjectContract objectContract = contract as JsonObjectContract; + if (objectContract != null) + { + if (existingValue == null) + return CreateAndPopulateObject(reader, objectContract, id); + + return PopulateObject(existingValue, reader, objectContract, id); + } + +#if !SILVERLIGHT && !PocketPC + JsonISerializableContract serializableContract = contract as JsonISerializableContract; + if (serializableContract != null) + { + return CreateISerializable(reader, serializableContract, id); + } +#endif + +#if !(NET35 || NET20 || WINDOWS_PHONE) + JsonDynamicContract dynamicContract = contract as JsonDynamicContract; + if (dynamicContract != null) + { + return CreateDynamic(reader, dynamicContract, id); + } +#endif + + throw new JsonSerializationException("Cannot deserialize JSON object into type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); + } + + private JsonArrayContract EnsureArrayContract(Type objectType, JsonContract contract) + { + if (contract == null) + throw new JsonSerializationException("Could not resolve type '{0}' to a JsonContract.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + JsonArrayContract arrayContract = contract as JsonArrayContract; + if (arrayContract == null) + throw new JsonSerializationException("Cannot deserialize JSON array into type '{0}'.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + return arrayContract; + } + + private void CheckedRead(JsonReader reader) + { + if (!reader.Read()) + throw new JsonSerializationException("Unexpected end when deserializing object."); + } + + private object CreateList(JsonReader reader, Type objectType, JsonContract contract, JsonProperty member, object existingValue, string reference) + { + object value; + if (HasDefinedType(objectType)) + { + JsonArrayContract arrayContract = EnsureArrayContract(objectType, contract); + + if (existingValue == null) + value = CreateAndPopulateList(reader, reference, arrayContract); + else + value = PopulateList(arrayContract.CreateWrapper(existingValue), reader, reference, arrayContract); + } + else + { + value = CreateJToken(reader, contract); + } + return value; + } + + private bool HasDefinedType(Type type) + { + return (type != null && type != typeof (object) && !typeof(JToken).IsAssignableFrom(type) +#if !(NET35 || NET20 || WINDOWS_PHONE) + && type != typeof(IDynamicMetaObjectProvider) +#endif + ); + } + + private object EnsureType(object value, CultureInfo culture, Type targetType) + { + if (targetType == null) + return value; + + Type valueType = ReflectionUtils.GetObjectType(value); + + // type of value and type of target don't match + // attempt to convert value's type to target's type + if (valueType != targetType) + { + try + { + return ConvertUtils.ConvertOrCast(value, culture, targetType); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error converting value {0} to type '{1}'.".FormatWith(CultureInfo.InvariantCulture, FormatValueForPrint(value), targetType), ex); + } + } + + return value; + } + + private string FormatValueForPrint(object value) + { + if (value == null) + return "{null}"; + + if (value is string) + return @"""" + value + @""""; + + return value.ToString(); + } + + private void SetPropertyValue(JsonProperty property, JsonReader reader, object target) + { + if (property.Ignored) + { + reader.Skip(); + return; + } + + object currentValue = null; + bool useExistingValue = false; + bool gottenCurrentValue = false; + + ObjectCreationHandling objectCreationHandling = + property.ObjectCreationHandling.GetValueOrDefault(Serializer.ObjectCreationHandling); + + if ((objectCreationHandling == ObjectCreationHandling.Auto || objectCreationHandling == ObjectCreationHandling.Reuse) + && (reader.TokenType == JsonToken.StartArray || reader.TokenType == JsonToken.StartObject) + && property.Readable) + { + currentValue = property.ValueProvider.GetValue(target); + gottenCurrentValue = true; + + useExistingValue = (currentValue != null + && !property.PropertyType.IsArray + && !ReflectionUtils.InheritsGenericDefinition(property.PropertyType, typeof(ReadOnlyCollection<>)) + && !property.PropertyType.IsValueType); + } + + if (!property.Writable && !useExistingValue) + { + reader.Skip(); + return; + } + + // test tokentype here because null might not be convertable to some types, e.g. ignoring null when applied to DateTime + if (property.NullValueHandling.GetValueOrDefault(Serializer.NullValueHandling) == NullValueHandling.Ignore && reader.TokenType == JsonToken.Null) + { + reader.Skip(); + return; + } + + // test tokentype here because default value might not be convertable to actual type, e.g. default of "" for DateTime + if (property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling) == DefaultValueHandling.Ignore + && JsonReader.IsPrimitiveToken(reader.TokenType) + && Equals(reader.Value, property.DefaultValue)) + { + reader.Skip(); + return; + } + + object existingValue = (useExistingValue) ? currentValue : null; + object value = CreateValueProperty(reader, property, target, gottenCurrentValue, existingValue); + + // always set the value if useExistingValue is false, + // otherwise also set it if CreateValue returns a new value compared to the currentValue + // this could happen because of a JsonConverter against the type + if ((!useExistingValue || value != currentValue) + && ShouldSetPropertyValue(property, value)) + { + property.ValueProvider.SetValue(target, value); + + if (property.SetIsSpecified != null) + property.SetIsSpecified(target, true); + } + } + + private bool ShouldSetPropertyValue(JsonProperty property, object value) + { + if (property.NullValueHandling.GetValueOrDefault(Serializer.NullValueHandling) == NullValueHandling.Ignore && value == null) + return false; + + if (property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling) == DefaultValueHandling.Ignore && MiscellaneousUtils.ValueEquals(value, property.DefaultValue)) + return false; + + if (!property.Writable) + return false; + + return true; + } + + private object CreateAndPopulateDictionary(JsonReader reader, JsonDictionaryContract contract, string id) + { + object dictionary; + + if (contract.DefaultCreator != null && + (!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) + dictionary = contract.DefaultCreator(); + else + throw new JsonSerializationException("Unable to find a default constructor to use for type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + IWrappedDictionary dictionaryWrapper = contract.CreateWrapper(dictionary); + + PopulateDictionary(dictionaryWrapper, reader, contract, id); + + return dictionaryWrapper.UnderlyingDictionary; + } + + private object PopulateDictionary(IWrappedDictionary dictionary, JsonReader reader, JsonDictionaryContract contract, string id) + { + if (id != null) + Serializer.ReferenceResolver.AddReference(this, id, dictionary.UnderlyingDictionary); + + contract.InvokeOnDeserializing(dictionary.UnderlyingDictionary, Serializer.Context); + + int initialDepth = reader.Depth; + + do + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + object keyValue; + try + { + keyValue = EnsureType(reader.Value, CultureInfo.InvariantCulture, contract.DictionaryKeyType); + } + catch (Exception ex) + { + throw new JsonSerializationException("Could not convert string '{0}' to dictionary key type '{1}'. Create a TypeConverter to convert from the string to the key type object.".FormatWith(CultureInfo.InvariantCulture, reader.Value, contract.DictionaryKeyType), ex); + } + + if (!ReadForType(reader, contract.DictionaryValueType, null)) + throw new JsonSerializationException("Unexpected end when deserializing object."); + + try + { + dictionary[keyValue] = CreateValueNonProperty(reader, contract.DictionaryValueType, GetContractSafe(contract.DictionaryValueType)); + } + catch (Exception ex) + { + if (IsErrorHandled(dictionary, contract, keyValue, ex)) + HandleError(reader, initialDepth); + else + throw; + } + break; + case JsonToken.Comment: + break; + case JsonToken.EndObject: + contract.InvokeOnDeserialized(dictionary.UnderlyingDictionary, Serializer.Context); + + return dictionary.UnderlyingDictionary; + default: + throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); + } + } while (reader.Read()); + + throw new JsonSerializationException("Unexpected end when deserializing object."); + } + + private object CreateAndPopulateList(JsonReader reader, string reference, JsonArrayContract contract) + { + return CollectionUtils.CreateAndPopulateList(contract.CreatedType, (l, isTemporaryListReference) => + { + if (reference != null && isTemporaryListReference) + throw new JsonSerializationException("Cannot preserve reference to array or readonly list: {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + +#if !PocketPC + if (contract.OnSerializing != null && isTemporaryListReference) + throw new JsonSerializationException("Cannot call OnSerializing on an array or readonly list: {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); +#endif + if (contract.OnError != null && isTemporaryListReference) + throw new JsonSerializationException("Cannot call OnError on an array or readonly list: {0}".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + PopulateList(contract.CreateWrapper(l), reader, reference, contract); + }); + } + + private bool ReadForTypeArrayHack(JsonReader reader, Type t) + { + // this is a nasty hack because calling ReadAsDecimal for example will error when we hit the end of the array + // need to think of a better way of doing this + try + { + return ReadForType(reader, t, null); + } + catch (JsonReaderException) + { + if (reader.TokenType == JsonToken.EndArray) + return true; + + throw; + } + } + + private object PopulateList(IWrappedCollection wrappedList, JsonReader reader, string reference, JsonArrayContract contract) + { + object list = wrappedList.UnderlyingCollection; + + if (reference != null) + Serializer.ReferenceResolver.AddReference(this, reference, list); + + contract.InvokeOnDeserializing(list, Serializer.Context); + + int initialDepth = reader.Depth; + + while (ReadForTypeArrayHack(reader, contract.CollectionItemType)) + { + switch (reader.TokenType) + { + case JsonToken.EndArray: + contract.InvokeOnDeserialized(list, Serializer.Context); + + return wrappedList.UnderlyingCollection; + case JsonToken.Comment: + break; + default: + try + { + object value = CreateValueNonProperty(reader, contract.CollectionItemType, GetContractSafe(contract.CollectionItemType)); + + wrappedList.Add(value); + } + catch (Exception ex) + { + if (IsErrorHandled(list, contract, wrappedList.Count, ex)) + HandleError(reader, initialDepth); + else + throw; + } + break; + } + } + + throw new JsonSerializationException("Unexpected end when deserializing array."); + } + +#if !SILVERLIGHT && !PocketPC + private object CreateISerializable(JsonReader reader, JsonISerializableContract contract, string id) + { + Type objectType = contract.UnderlyingType; + + SerializationInfo serializationInfo = new SerializationInfo(contract.UnderlyingType, GetFormatterConverter()); + + bool exit = false; + do + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string memberName = reader.Value.ToString(); + if (!reader.Read()) + throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + + serializationInfo.AddValue(memberName, JToken.ReadFrom(reader)); + break; + case JsonToken.Comment: + break; + case JsonToken.EndObject: + exit = true; + break; + default: + throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); + } + } while (!exit && reader.Read()); + + if (contract.ISerializableCreator == null) + throw new JsonSerializationException("ISerializable type '{0}' does not have a valid constructor.".FormatWith(CultureInfo.InvariantCulture, objectType)); + + object createdObject = contract.ISerializableCreator(serializationInfo, Serializer.Context); + + if (id != null) + Serializer.ReferenceResolver.AddReference(this, id, createdObject); + + // these are together because OnDeserializing takes an object but for an ISerializable the object is full created in the constructor + contract.InvokeOnDeserializing(createdObject, Serializer.Context); + contract.InvokeOnDeserialized(createdObject, Serializer.Context); + + return createdObject; + } +#endif + +#if !(NET35 || NET20 || WINDOWS_PHONE) + private object CreateDynamic(JsonReader reader, JsonDynamicContract contract, string id) + { + IDynamicMetaObjectProvider newObject = null; + + if (contract.UnderlyingType.IsInterface || contract.UnderlyingType.IsAbstract) + throw new JsonSerializationException("Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantated.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + if (contract.DefaultCreator != null && + (!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) + newObject = (IDynamicMetaObjectProvider)contract.DefaultCreator(); + else + throw new JsonSerializationException("Unable to find a default constructor to use for type {0}.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + if (id != null) + Serializer.ReferenceResolver.AddReference(this, id, newObject); + + contract.InvokeOnDeserializing(newObject, Serializer.Context); + + bool exit = false; + do + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string memberName = reader.Value.ToString(); + if (!reader.Read()) + throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + + // first attempt to find a settable property, otherwise fall back to a dynamic set without type + JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName); + if (property != null && property.Writable && !property.Ignored) + { + SetPropertyValue(property, reader, newObject); + } + else + { + Type t = (JsonReader.IsPrimitiveToken(reader.TokenType)) ? reader.ValueType : typeof (IDynamicMetaObjectProvider); + + object value = CreateValueNonProperty(reader, t, GetContractSafe(t, null)); + + newObject.TrySetMember(memberName, value); + } + break; + case JsonToken.EndObject: + exit = true; + break; + default: + throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); + } + } while (!exit && reader.Read()); + + contract.InvokeOnDeserialized(newObject, Serializer.Context); + + return newObject; + } +#endif + + private object CreateAndPopulateObject(JsonReader reader, JsonObjectContract contract, string id) + { + object newObject = null; + + if (contract.UnderlyingType.IsInterface || contract.UnderlyingType.IsAbstract) + throw new JsonSerializationException("Could not create an instance of type {0}. Type is an interface or abstract class and cannot be instantated.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + if (contract.OverrideConstructor != null) + { + if (contract.OverrideConstructor.GetParameters().Length > 0) + return CreateObjectFromNonDefaultConstructor(reader, contract, contract.OverrideConstructor, id); + + newObject = contract.OverrideConstructor.Invoke(null); + } + else if (contract.DefaultCreator != null && + (!contract.DefaultCreatorNonPublic || Serializer.ConstructorHandling == ConstructorHandling.AllowNonPublicDefaultConstructor)) + { + newObject = contract.DefaultCreator(); + } + else if (contract.ParametrizedConstructor != null) + { + return CreateObjectFromNonDefaultConstructor(reader, contract, contract.ParametrizedConstructor, id); + } + + if (newObject == null) + throw new JsonSerializationException("Unable to find a constructor to use for type {0}. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute.".FormatWith(CultureInfo.InvariantCulture, contract.UnderlyingType)); + + PopulateObject(newObject, reader, contract, id); + return newObject; + } + + private object CreateObjectFromNonDefaultConstructor(JsonReader reader, JsonObjectContract contract, ConstructorInfo constructorInfo, string id) + { + ValidationUtils.ArgumentNotNull(constructorInfo, "constructorInfo"); + + Type objectType = contract.UnderlyingType; + + IDictionary propertyValues = new Dictionary(); + + bool exit = false; + do + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string memberName = reader.Value.ToString(); + + // attempt exact case match first + // then try match ignoring case + JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName); + + if (property != null) + { + if (!ReadForType(reader, property.PropertyType, property.Converter)) + throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + + if (!property.Ignored) + propertyValues[property] = CreateValueProperty(reader, property, null, true, null); + else + reader.Skip(); + } + else + { + if (!reader.Read()) + throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + + if (Serializer.MissingMemberHandling == MissingMemberHandling.Error) + throw new JsonSerializationException("Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, objectType.Name)); + + reader.Skip(); + } + break; + case JsonToken.Comment: + break; + case JsonToken.EndObject: + exit = true; + break; + default: + throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); + } + } while (!exit && reader.Read()); + + IDictionary constructorParameters = constructorInfo.GetParameters().ToDictionary(p => p, p => (object)null); + IDictionary remainingPropertyValues = new Dictionary(); + + foreach (KeyValuePair propertyValue in propertyValues) + { + ParameterInfo matchingConstructorParameter = constructorParameters.ForgivingCaseSensitiveFind(kv => kv.Key.Name, propertyValue.Key.PropertyName).Key; + if (matchingConstructorParameter != null) + constructorParameters[matchingConstructorParameter] = propertyValue.Value; + else + remainingPropertyValues.Add(propertyValue); + } + + object createdObject = constructorInfo.Invoke(constructorParameters.Values.ToArray()); + + if (id != null) + Serializer.ReferenceResolver.AddReference(this, id, createdObject); + + contract.InvokeOnDeserializing(createdObject, Serializer.Context); + + // go through unused values and set the newly created object's properties + foreach (KeyValuePair remainingPropertyValue in remainingPropertyValues) + { + JsonProperty property = remainingPropertyValue.Key; + object value = remainingPropertyValue.Value; + + if (ShouldSetPropertyValue(remainingPropertyValue.Key, remainingPropertyValue.Value)) + property.ValueProvider.SetValue(createdObject, value); + } + + contract.InvokeOnDeserialized(createdObject, Serializer.Context); + return createdObject; + } + + private bool ReadForType(JsonReader reader, Type t, JsonConverter propertyConverter) + { + // don't read properties with converters as a specific value + // the value might be a string which will then get converted which will error if read as date for example + bool hasConverter = (GetConverter(GetContractSafe(t), propertyConverter) != null); + + if (hasConverter) + return reader.Read(); + + if (t == typeof(byte[])) + { + reader.ReadAsBytes(); + return true; + } + else if ((t == typeof(decimal) || t == typeof(decimal?))) + { + reader.ReadAsDecimal(); + return true; + } +#if !NET20 + else if ((t == typeof(DateTimeOffset) || t == typeof(DateTimeOffset?))) + { + reader.ReadAsDateTimeOffset(); + return true; + } +#endif + + do + { + if (!reader.Read()) + return false; + } while (reader.TokenType == JsonToken.Comment); + + return true; + } + + private object PopulateObject(object newObject, JsonReader reader, JsonObjectContract contract, string id) + { + contract.InvokeOnDeserializing(newObject, Serializer.Context); + + Dictionary requiredProperties = + contract.Properties.Where(m => m.Required != Required.Default).ToDictionary(m => m, m => RequiredValue.None); + + if (id != null) + Serializer.ReferenceResolver.AddReference(this, id, newObject); + + int initialDepth = reader.Depth; + + do + { + switch (reader.TokenType) + { + case JsonToken.PropertyName: + string memberName = reader.Value.ToString(); + + // attempt exact case match first + // then try match ignoring case + JsonProperty property = contract.Properties.GetClosestMatchProperty(memberName); + + if (property == null) + { + if (Serializer.MissingMemberHandling == MissingMemberHandling.Error) + throw new JsonSerializationException("Could not find member '{0}' on object of type '{1}'".FormatWith(CultureInfo.InvariantCulture, memberName, contract.UnderlyingType.Name)); + + reader.Skip(); + continue; + } + + if (!ReadForType(reader, property.PropertyType, property.Converter)) + throw new JsonSerializationException("Unexpected end when setting {0}'s value.".FormatWith(CultureInfo.InvariantCulture, memberName)); + + SetRequiredProperty(reader, property, requiredProperties); + + try + { + SetPropertyValue(property, reader, newObject); + } + catch (Exception ex) + { + if (IsErrorHandled(newObject, contract, memberName, ex)) + HandleError(reader, initialDepth); + else + throw; + } + break; + case JsonToken.EndObject: + foreach (KeyValuePair requiredProperty in requiredProperties) + { + if (requiredProperty.Value == RequiredValue.None) + throw new JsonSerializationException("Required property '{0}' not found in JSON.".FormatWith(CultureInfo.InvariantCulture, requiredProperty.Key.PropertyName)); + if (requiredProperty.Key.Required == Required.Always && requiredProperty.Value == RequiredValue.Null) + throw new JsonSerializationException("Required property '{0}' expects a value but got null.".FormatWith(CultureInfo.InvariantCulture, requiredProperty.Key.PropertyName)); + } + + contract.InvokeOnDeserialized(newObject, Serializer.Context); + return newObject; + case JsonToken.Comment: + // ignore + break; + default: + throw new JsonSerializationException("Unexpected token when deserializing object: " + reader.TokenType); + } + } while (reader.Read()); + + throw new JsonSerializationException("Unexpected end when deserializing object."); + } + + private void SetRequiredProperty(JsonReader reader, JsonProperty property, Dictionary requiredProperties) + { + if (property != null) + { + requiredProperties[property] = (reader.TokenType == JsonToken.Null || reader.TokenType == JsonToken.Undefined) + ? RequiredValue.Null + : RequiredValue.Value; + } + } + + private void HandleError(JsonReader reader, int initialDepth) + { + ClearErrorContext(); + + reader.Skip(); + + while (reader.Depth > (initialDepth + 1)) + { + reader.Read(); + } + } + + internal enum RequiredValue + { + None, + Null, + Value + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs new file mode 100644 index 0000000..b929840 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonSerializerInternalWriter.cs @@ -0,0 +1,637 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +#if !(NET35 || NET20 || WINDOWS_PHONE) +using System.Dynamic; +#endif +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization.Formatters; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Utilities; +using System.Runtime.Serialization; +using System.Security; + +namespace Newtonsoft.Json.Serialization +{ + internal class JsonSerializerInternalWriter : JsonSerializerInternalBase + { + private JsonSerializerProxy _internalSerializer; + private List _serializeStack; + + private List SerializeStack + { + get + { + if (_serializeStack == null) + _serializeStack = new List(); + + return _serializeStack; + } + } + + public JsonSerializerInternalWriter(JsonSerializer serializer) + : base(serializer) + { + } + + public void Serialize(JsonWriter jsonWriter, object value) + { + if (jsonWriter == null) + throw new ArgumentNullException("jsonWriter"); + + SerializeValue(jsonWriter, value, GetContractSafe(value), null, null); + } + + private JsonSerializerProxy GetInternalSerializer() + { + if (_internalSerializer == null) + _internalSerializer = new JsonSerializerProxy(this); + + return _internalSerializer; + } + + private JsonContract GetContractSafe(object value) + { + if (value == null) + return null; + + return Serializer.ContractResolver.ResolveContract(value.GetType()); + } + + private void SerializeValue(JsonWriter writer, object value, JsonContract valueContract, JsonProperty member, JsonContract collectionValueContract) + { + JsonConverter converter = (member != null) ? member.Converter : null; + + if (value == null) + { + writer.WriteNull(); + return; + } + + if ((converter != null + || ((converter = valueContract.Converter) != null) + || ((converter = Serializer.GetMatchingConverter(valueContract.UnderlyingType)) != null) + || ((converter = valueContract.InternalConverter) != null)) + && converter.CanWrite) + { + SerializeConvertable(writer, converter, value, valueContract); + } + else if (valueContract is JsonPrimitiveContract) + { + writer.WriteValue(value); + } + else if (valueContract is JsonStringContract) + { + SerializeString(writer, value, (JsonStringContract)valueContract); + } + else if (valueContract is JsonObjectContract) + { + SerializeObject(writer, value, (JsonObjectContract)valueContract, member, collectionValueContract); + } + else if (valueContract is JsonDictionaryContract) + { + JsonDictionaryContract dictionaryContract = (JsonDictionaryContract)valueContract; + SerializeDictionary(writer, dictionaryContract.CreateWrapper(value), dictionaryContract, member, collectionValueContract); + } + else if (valueContract is JsonArrayContract) + { + JsonArrayContract arrayContract = (JsonArrayContract)valueContract; + SerializeList(writer, arrayContract.CreateWrapper(value), arrayContract, member, collectionValueContract); + } + else if (valueContract is JsonLinqContract) + { + ((JToken)value).WriteTo(writer, (Serializer.Converters != null) ? Serializer.Converters.ToArray() : null); + } +#if !SILVERLIGHT && !PocketPC + else if (valueContract is JsonISerializableContract) + { + SerializeISerializable(writer, (ISerializable)value, (JsonISerializableContract)valueContract); + } +#endif +#if !(NET35 || NET20 || WINDOWS_PHONE) + else if (valueContract is JsonDynamicContract) + { + SerializeDynamic(writer, (IDynamicMetaObjectProvider)value, (JsonDynamicContract)valueContract); + } +#endif + } + + private bool ShouldWriteReference(object value, JsonProperty property, JsonContract contract) + { + if (value == null) + return false; + if (contract is JsonPrimitiveContract) + return false; + + bool? isReference = null; + + // value could be coming from a dictionary or array and not have a property + if (property != null) + isReference = property.IsReference; + + if (isReference == null) + isReference = contract.IsReference; + + if (isReference == null) + { + if (contract is JsonArrayContract) + isReference = HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays); + else + isReference = HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); + } + + if (!isReference.Value) + return false; + + return Serializer.ReferenceResolver.IsReferenced(this, value); + } + + private void WriteMemberInfoProperty(JsonWriter writer, object memberValue, JsonProperty property, JsonContract contract) + { + string propertyName = property.PropertyName; + object defaultValue = property.DefaultValue; + + if (property.NullValueHandling.GetValueOrDefault(Serializer.NullValueHandling) == NullValueHandling.Ignore && + memberValue == null) + return; + + if (property.DefaultValueHandling.GetValueOrDefault(Serializer.DefaultValueHandling) == + DefaultValueHandling.Ignore && MiscellaneousUtils.ValueEquals(memberValue, defaultValue)) + return; + + if (ShouldWriteReference(memberValue, property, contract)) + { + writer.WritePropertyName(propertyName); + WriteReference(writer, memberValue); + return; + } + + if (!CheckForCircularReference(memberValue, property.ReferenceLoopHandling, contract)) + return; + + if (memberValue == null && property.Required == Required.Always) + throw new JsonSerializationException("Cannot write a null value for property '{0}'. Property requires a value.".FormatWith(CultureInfo.InvariantCulture, property.PropertyName)); + + writer.WritePropertyName(propertyName); + SerializeValue(writer, memberValue, contract, property, null); + } + + private bool CheckForCircularReference(object value, ReferenceLoopHandling? referenceLoopHandling, JsonContract contract) + { + if (value == null || contract is JsonPrimitiveContract) + return true; + + if (SerializeStack.IndexOf(value) != -1) + { + switch (referenceLoopHandling.GetValueOrDefault(Serializer.ReferenceLoopHandling)) + { + case ReferenceLoopHandling.Error: + throw new JsonSerializationException("Self referencing loop detected for type '{0}'.".FormatWith(CultureInfo.InvariantCulture, value.GetType())); + case ReferenceLoopHandling.Ignore: + return false; + case ReferenceLoopHandling.Serialize: + return true; + default: + throw new InvalidOperationException("Unexpected ReferenceLoopHandling value: '{0}'".FormatWith(CultureInfo.InvariantCulture, Serializer.ReferenceLoopHandling)); + } + } + + return true; + } + + private void WriteReference(JsonWriter writer, object value) + { + writer.WriteStartObject(); + writer.WritePropertyName(JsonTypeReflector.RefPropertyName); + writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, value)); + writer.WriteEndObject(); + } + + internal static bool TryConvertToString(object value, Type type, out string s) + { +#if !PocketPC + TypeConverter converter = ConvertUtils.GetConverter(type); + + // use the objectType's TypeConverter if it has one and can convert to a string + if (converter != null +#if !SILVERLIGHT + && !(converter is ComponentConverter) +#endif + && converter.GetType() != typeof(TypeConverter)) + { + if (converter.CanConvertTo(typeof(string))) + { +#if !SILVERLIGHT + s = converter.ConvertToInvariantString(value); +#else + s = converter.ConvertToString(value); +#endif + return true; + } + } +#endif + +#if SILVERLIGHT || PocketPC + if (value is Guid || value is Uri || value is TimeSpan) + { + s = value.ToString(); + return true; + } +#endif + + if (value is Type) + { + s = ((Type)value).AssemblyQualifiedName; + return true; + } + + s = null; + return false; + } + + private void SerializeString(JsonWriter writer, object value, JsonStringContract contract) + { + contract.InvokeOnSerializing(value, Serializer.Context); + + string s; + TryConvertToString(value, contract.UnderlyingType, out s); + writer.WriteValue(s); + + contract.InvokeOnSerialized(value, Serializer.Context); + } + + private void SerializeObject(JsonWriter writer, object value, JsonObjectContract contract, JsonProperty member, JsonContract collectionValueContract) + { + contract.InvokeOnSerializing(value, Serializer.Context); + + SerializeStack.Add(value); + writer.WriteStartObject(); + + bool isReference = contract.IsReference ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); + if (isReference) + { + writer.WritePropertyName(JsonTypeReflector.IdPropertyName); + writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, value)); + } + if (ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionValueContract)) + { + WriteTypeProperty(writer, contract.UnderlyingType); + } + + int initialDepth = writer.Top; + + foreach (JsonProperty property in contract.Properties) + { + try + { + if (!property.Ignored && property.Readable && ShouldSerialize(property, value) && IsSpecified(property, value)) + { + object memberValue = property.ValueProvider.GetValue(value); + JsonContract memberContract = GetContractSafe(memberValue); + + WriteMemberInfoProperty(writer, memberValue, property, memberContract); + } + } + catch (Exception ex) + { + if (IsErrorHandled(value, contract, property.PropertyName, ex)) + HandleError(writer, initialDepth); + else + throw; + } + } + + writer.WriteEndObject(); + SerializeStack.RemoveAt(SerializeStack.Count - 1); + + contract.InvokeOnSerialized(value, Serializer.Context); + } + + private void WriteTypeProperty(JsonWriter writer, Type type) + { + writer.WritePropertyName(JsonTypeReflector.TypePropertyName); + writer.WriteValue(ReflectionUtils.GetTypeName(type, Serializer.TypeNameAssemblyFormat)); + } + + private bool HasFlag(PreserveReferencesHandling value, PreserveReferencesHandling flag) + { + return ((value & flag) == flag); + } + + private bool HasFlag(TypeNameHandling value, TypeNameHandling flag) + { + return ((value & flag) == flag); + } + + private void SerializeConvertable(JsonWriter writer, JsonConverter converter, object value, JsonContract contract) + { + if (ShouldWriteReference(value, null, contract)) + { + WriteReference(writer, value); + } + else + { + if (!CheckForCircularReference(value, null, contract)) + return; + + SerializeStack.Add(value); + + converter.WriteJson(writer, value, GetInternalSerializer()); + + SerializeStack.RemoveAt(SerializeStack.Count - 1); + } + } + + private void SerializeList(JsonWriter writer, IWrappedCollection values, JsonArrayContract contract, JsonProperty member, JsonContract collectionValueContract) + { + contract.InvokeOnSerializing(values.UnderlyingCollection, Serializer.Context); + + SerializeStack.Add(values.UnderlyingCollection); + + bool isReference = contract.IsReference ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Arrays); + bool includeTypeDetails = ShouldWriteType(TypeNameHandling.Arrays, contract, member, collectionValueContract); + + if (isReference || includeTypeDetails) + { + writer.WriteStartObject(); + + if (isReference) + { + writer.WritePropertyName(JsonTypeReflector.IdPropertyName); + writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, values.UnderlyingCollection)); + } + if (includeTypeDetails) + { + WriteTypeProperty(writer, values.UnderlyingCollection.GetType()); + } + writer.WritePropertyName(JsonTypeReflector.ArrayValuesPropertyName); + } + + JsonContract childValuesContract = Serializer.ContractResolver.ResolveContract(contract.CollectionItemType ?? typeof(object)); + + writer.WriteStartArray(); + + int initialDepth = writer.Top; + + int index = 0; + // note that an error in the IEnumerable won't be caught + foreach (object value in values) + { + try + { + JsonContract valueContract = GetContractSafe(value); + + if (ShouldWriteReference(value, null, valueContract)) + { + WriteReference(writer, value); + } + else + { + if (CheckForCircularReference(value, null, contract)) + { + SerializeValue(writer, value, valueContract, null, childValuesContract); + } + } + } + catch (Exception ex) + { + if (IsErrorHandled(values.UnderlyingCollection, contract, index, ex)) + HandleError(writer, initialDepth); + else + throw; + } + finally + { + index++; + } + } + + writer.WriteEndArray(); + + if (isReference || includeTypeDetails) + { + writer.WriteEndObject(); + } + + SerializeStack.RemoveAt(SerializeStack.Count - 1); + + contract.InvokeOnSerialized(values.UnderlyingCollection, Serializer.Context); + } + +#if !SILVERLIGHT && !PocketPC +#if !NET20 + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1903:UseOnlyApiFromTargetedFramework", MessageId = "System.Security.SecuritySafeCriticalAttribute")] + [SecuritySafeCritical] +#endif + private void SerializeISerializable(JsonWriter writer, ISerializable value, JsonISerializableContract contract) + { + contract.InvokeOnSerializing(value, Serializer.Context); + SerializeStack.Add(value); + + writer.WriteStartObject(); + + SerializationInfo serializationInfo = new SerializationInfo(contract.UnderlyingType, new FormatterConverter()); + value.GetObjectData(serializationInfo, Serializer.Context); + + foreach (SerializationEntry serializationEntry in serializationInfo) + { + writer.WritePropertyName(serializationEntry.Name); + SerializeValue(writer, serializationEntry.Value, GetContractSafe(serializationEntry.Value), null, null); + } + + writer.WriteEndObject(); + + SerializeStack.RemoveAt(SerializeStack.Count - 1); + contract.InvokeOnSerialized(value, Serializer.Context); + } +#endif + +#if !(NET35 || NET20 || WINDOWS_PHONE) + /// + /// Serializes the dynamic. + /// + /// The writer. + /// The value. + /// The contract. + private void SerializeDynamic(JsonWriter writer, IDynamicMetaObjectProvider value, JsonDynamicContract contract) + { + contract.InvokeOnSerializing(value, Serializer.Context); + SerializeStack.Add(value); + + writer.WriteStartObject(); + + foreach (string memberName in value.GetDynamicMemberNames()) + { + object memberValue; + if (DynamicUtils.TryGetMember(value, memberName, out memberValue)) + { + string resolvedPropertyName = (contract.PropertyNameResolver != null) + ? contract.PropertyNameResolver(memberName) + : memberName; + + writer.WritePropertyName(resolvedPropertyName); + SerializeValue(writer, memberValue, GetContractSafe(memberValue), null, null); + } + } + + writer.WriteEndObject(); + + SerializeStack.RemoveAt(SerializeStack.Count - 1); + contract.InvokeOnSerialized(value, Serializer.Context); + } +#endif + + private bool ShouldWriteType(TypeNameHandling typeNameHandlingFlag, JsonContract contract, JsonProperty member, JsonContract collectionValueContract) + { + if (HasFlag(((member != null) ? member.TypeNameHandling : null) ?? Serializer.TypeNameHandling, typeNameHandlingFlag)) + return true; + + if (member != null) + { + if ((member.TypeNameHandling ?? Serializer.TypeNameHandling) == TypeNameHandling.Auto && contract.UnderlyingType != member.PropertyType) + return true; + } + else if (collectionValueContract != null) + { + if (Serializer.TypeNameHandling == TypeNameHandling.Auto && contract.UnderlyingType != collectionValueContract.UnderlyingType) + return true; + } + + return false; + } + + private void SerializeDictionary(JsonWriter writer, IWrappedDictionary values, JsonDictionaryContract contract, JsonProperty member, JsonContract collectionValueContract) + { + contract.InvokeOnSerializing(values.UnderlyingDictionary, Serializer.Context); + + SerializeStack.Add(values.UnderlyingDictionary); + writer.WriteStartObject(); + + bool isReference = contract.IsReference ?? HasFlag(Serializer.PreserveReferencesHandling, PreserveReferencesHandling.Objects); + if (isReference) + { + writer.WritePropertyName(JsonTypeReflector.IdPropertyName); + writer.WriteValue(Serializer.ReferenceResolver.GetReference(this, values.UnderlyingDictionary)); + } + if (ShouldWriteType(TypeNameHandling.Objects, contract, member, collectionValueContract)) + { + WriteTypeProperty(writer, values.UnderlyingDictionary.GetType()); + } + + JsonContract childValuesContract = Serializer.ContractResolver.ResolveContract(contract.DictionaryValueType ?? typeof(object)); + + int initialDepth = writer.Top; + + // Mono Unity 3.0 fix + IDictionary d = values; + + foreach (DictionaryEntry entry in d) + { + string propertyName = GetPropertyName(entry); + + propertyName = (contract.PropertyNameResolver != null) + ? contract.PropertyNameResolver(propertyName) + : propertyName; + + try + { + object value = entry.Value; + JsonContract valueContract = GetContractSafe(value); + + if (ShouldWriteReference(value, null, valueContract)) + { + writer.WritePropertyName(propertyName); + WriteReference(writer, value); + } + else + { + if (!CheckForCircularReference(value, null, contract)) + continue; + + writer.WritePropertyName(propertyName); + + SerializeValue(writer, value, valueContract, null, childValuesContract); + } + } + catch (Exception ex) + { + if (IsErrorHandled(values.UnderlyingDictionary, contract, propertyName, ex)) + HandleError(writer, initialDepth); + else + throw; + } + } + + writer.WriteEndObject(); + SerializeStack.RemoveAt(SerializeStack.Count - 1); + + contract.InvokeOnSerialized(values.UnderlyingDictionary, Serializer.Context); + } + + private string GetPropertyName(DictionaryEntry entry) + { + string propertyName; + + if (entry.Key is IConvertible) + return Convert.ToString(entry.Key, CultureInfo.InvariantCulture); + else if (TryConvertToString(entry.Key, entry.Key.GetType(), out propertyName)) + return propertyName; + else + return entry.Key.ToString(); + } + + private void HandleError(JsonWriter writer, int initialDepth) + { + ClearErrorContext(); + + while (writer.Top > initialDepth) + { + writer.WriteEnd(); + } + } + + private bool ShouldSerialize(JsonProperty property, object target) + { + if (property.ShouldSerialize == null) + return true; + + return property.ShouldSerialize(target); + } + + private bool IsSpecified(JsonProperty property, object target) + { + if (property.GetIsSpecified == null) + return true; + + return property.GetIsSpecified(target); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs new file mode 100644 index 0000000..304da80 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonSerializerProxy.cs @@ -0,0 +1,176 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Runtime.Serialization.Formatters; +using Newtonsoft.Json.Utilities; +using System.Runtime.Serialization; + +namespace Newtonsoft.Json.Serialization +{ + internal class JsonSerializerProxy : JsonSerializer + { + private readonly JsonSerializerInternalReader _serializerReader; + private readonly JsonSerializerInternalWriter _serializerWriter; + private readonly JsonSerializer _serializer; + + public override event EventHandler Error + { + add { _serializer.Error += value; } + remove { _serializer.Error -= value; } + } + + public override IReferenceResolver ReferenceResolver + { + get { return _serializer.ReferenceResolver; } + set { _serializer.ReferenceResolver = value; } + } + + public override JsonConverterCollection Converters + { + get { return _serializer.Converters; } + } + + public override DefaultValueHandling DefaultValueHandling + { + get { return _serializer.DefaultValueHandling; } + set { _serializer.DefaultValueHandling = value; } + } + + public override IContractResolver ContractResolver + { + get { return _serializer.ContractResolver; } + set { _serializer.ContractResolver = value; } + } + + public override MissingMemberHandling MissingMemberHandling + { + get { return _serializer.MissingMemberHandling; } + set { _serializer.MissingMemberHandling = value; } + } + + public override NullValueHandling NullValueHandling + { + get { return _serializer.NullValueHandling; } + set { _serializer.NullValueHandling = value; } + } + + public override ObjectCreationHandling ObjectCreationHandling + { + get { return _serializer.ObjectCreationHandling; } + set { _serializer.ObjectCreationHandling = value; } + } + + public override ReferenceLoopHandling ReferenceLoopHandling + { + get { return _serializer.ReferenceLoopHandling; } + set { _serializer.ReferenceLoopHandling = value; } + } + + public override PreserveReferencesHandling PreserveReferencesHandling + { + get { return _serializer.PreserveReferencesHandling; } + set { _serializer.PreserveReferencesHandling = value; } + } + + public override TypeNameHandling TypeNameHandling + { + get { return _serializer.TypeNameHandling; } + set { _serializer.TypeNameHandling = value; } + } + + public override FormatterAssemblyStyle TypeNameAssemblyFormat + { + get { return _serializer.TypeNameAssemblyFormat; } + set { _serializer.TypeNameAssemblyFormat = value; } + } + + public override ConstructorHandling ConstructorHandling + { + get { return _serializer.ConstructorHandling; } + set { _serializer.ConstructorHandling = value; } + } + + public override SerializationBinder Binder + { + get { return _serializer.Binder; } + set { _serializer.Binder = value; } + } + + public override StreamingContext Context + { + get { return _serializer.Context; } + set { _serializer.Context = value; } + } + + internal JsonSerializerInternalBase GetInternalSerializer() + { + if (_serializerReader != null) + return _serializerReader; + else + return _serializerWriter; + } + + public JsonSerializerProxy(JsonSerializerInternalReader serializerReader) + { + ValidationUtils.ArgumentNotNull(serializerReader, "serializerReader"); + + _serializerReader = serializerReader; + _serializer = serializerReader.Serializer; + } + + public JsonSerializerProxy(JsonSerializerInternalWriter serializerWriter) + { + ValidationUtils.ArgumentNotNull(serializerWriter, "serializerWriter"); + + _serializerWriter = serializerWriter; + _serializer = serializerWriter.Serializer; + } + + internal override object DeserializeInternal(JsonReader reader, Type objectType) + { + if (_serializerReader != null) + return _serializerReader.Deserialize(reader, objectType); + else + return _serializer.Deserialize(reader, objectType); + } + + internal override void PopulateInternal(JsonReader reader, object target) + { + if (_serializerReader != null) + _serializerReader.Populate(reader, target); + else + _serializer.Populate(reader, target); + } + + internal override void SerializeInternal(JsonWriter jsonWriter, object value) + { + if (_serializerWriter != null) + _serializerWriter.Serialize(jsonWriter, value); + else + _serializer.Serialize(jsonWriter, value); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonStringContract.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonStringContract.cs new file mode 100644 index 0000000..4a0c5fb --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonStringContract.cs @@ -0,0 +1,47 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Contract details for a used by the . + /// + public class JsonStringContract : JsonContract + { + /// + /// Initializes a new instance of the class. + /// + /// The underlying type for the contract. + public JsonStringContract(Type underlyingType) + : base(underlyingType) + { + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs new file mode 100644 index 0000000..93936cf --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/JsonTypeReflector.cs @@ -0,0 +1,301 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Runtime.Serialization; +using System.Security.Permissions; +using Newtonsoft.Json.Utilities; + +namespace Newtonsoft.Json.Serialization +{ +#if !SILVERLIGHT && !PocketPC && !NET20 + internal interface IMetadataTypeAttribute + { + Type MetadataClassType { get; } + } +#endif + + internal static class JsonTypeReflector + { + public const string IdPropertyName = "$id"; + public const string RefPropertyName = "$ref"; + public const string TypePropertyName = "$type"; + public const string ArrayValuesPropertyName = "$values"; + + public const string ShouldSerializePrefix = "ShouldSerialize"; + public const string SpecifiedPostfix = "Specified"; + + private static readonly ThreadSafeStore JsonConverterTypeCache = new ThreadSafeStore(GetJsonConverterTypeFromAttribute); +#if !SILVERLIGHT && !PocketPC && !NET20 + private static readonly ThreadSafeStore AssociatedMetadataTypesCache = new ThreadSafeStore(GetAssociateMetadataTypeFromAttribute); + + private const string MetadataTypeAttributeTypeName = + "System.ComponentModel.DataAnnotations.MetadataTypeAttribute, System.ComponentModel.DataAnnotations, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; + private static Type _cachedMetadataTypeAttributeType; +#endif +#if SILVERLIGHT + private static readonly ThreadSafeStore TypeConverterTypeCache = new ThreadSafeStore(GetTypeConverterTypeFromAttribute); + + private static Type GetTypeConverterTypeFromAttribute(ICustomAttributeProvider attributeProvider) + { + TypeConverterAttribute converterAttribute = GetAttribute(attributeProvider); + if (converterAttribute == null) + return null; + + return Type.GetType(converterAttribute.ConverterTypeName); + } + + private static Type GetTypeConverterType(ICustomAttributeProvider attributeProvider) + { + return TypeConverterTypeCache.Get(attributeProvider); + } +#endif + + public static JsonContainerAttribute GetJsonContainerAttribute(Type type) + { + return CachedAttributeGetter.GetAttribute(type); + } + + public static JsonObjectAttribute GetJsonObjectAttribute(Type type) + { + return GetJsonContainerAttribute(type) as JsonObjectAttribute; + } + + public static JsonArrayAttribute GetJsonArrayAttribute(Type type) + { + return GetJsonContainerAttribute(type) as JsonArrayAttribute; + } + +#if !PocketPC && !NET20 + public static DataContractAttribute GetDataContractAttribute(Type type) + { + return CachedAttributeGetter.GetAttribute(type); + } +#endif + + public static MemberSerialization GetObjectMemberSerialization(Type objectType) + { + JsonObjectAttribute objectAttribute = GetJsonObjectAttribute(objectType); + + if (objectAttribute == null) + { +#if !PocketPC && !NET20 + DataContractAttribute dataContractAttribute = GetDataContractAttribute(objectType); + + if (dataContractAttribute != null) + return MemberSerialization.OptIn; +#endif + + return MemberSerialization.OptOut; + } + + return objectAttribute.MemberSerialization; + } + + private static Type GetJsonConverterType(ICustomAttributeProvider attributeProvider) + { + return JsonConverterTypeCache.Get(attributeProvider); + } + + private static Type GetJsonConverterTypeFromAttribute(ICustomAttributeProvider attributeProvider) + { + JsonConverterAttribute converterAttribute = GetAttribute(attributeProvider); + return (converterAttribute != null) + ? converterAttribute.ConverterType + : null; + } + + public static JsonConverter GetJsonConverter(ICustomAttributeProvider attributeProvider, Type targetConvertedType) + { + Type converterType = GetJsonConverterType(attributeProvider); + + if (converterType != null) + { + JsonConverter memberConverter = JsonConverterAttribute.CreateJsonConverterInstance(converterType); + + if (!memberConverter.CanConvert(targetConvertedType)) + throw new JsonSerializationException("JsonConverter {0} on {1} is not compatible with member type {2}.".FormatWith(CultureInfo.InvariantCulture, memberConverter.GetType().Name, attributeProvider, targetConvertedType.Name)); + + return memberConverter; + } + + return null; + } + +#if !PocketPC + public static TypeConverter GetTypeConverter(Type type) + { +#if !SILVERLIGHT + return TypeDescriptor.GetConverter(type); +#else + Type converterType = GetTypeConverterType(type); + + if (converterType != null) + return (TypeConverter)ReflectionUtils.CreateInstance(converterType); + + return null; +#endif + } +#endif + +#if !SILVERLIGHT && !PocketPC && !NET20 + private static Type GetAssociatedMetadataType(Type type) + { + return AssociatedMetadataTypesCache.Get(type); + } + + private static Type GetAssociateMetadataTypeFromAttribute(Type type) + { + Type metadataTypeAttributeType = GetMetadataTypeAttributeType(); + if (metadataTypeAttributeType == null) + return null; + + object attribute = type.GetCustomAttributes(metadataTypeAttributeType, true).SingleOrDefault(); + if (attribute == null) + return null; + + IMetadataTypeAttribute metadataTypeAttribute = (DynamicCodeGeneration) + ? DynamicWrapper.CreateWrapper(attribute) + : new LateBoundMetadataTypeAttribute(attribute); + + return metadataTypeAttribute.MetadataClassType; + } + + private static Type GetMetadataTypeAttributeType() + { + // always attempt to get the metadata type attribute type + // the assembly may have been loaded since last time + if (_cachedMetadataTypeAttributeType == null) + { + Type metadataTypeAttributeType = Type.GetType(MetadataTypeAttributeTypeName); + + if (metadataTypeAttributeType != null) + _cachedMetadataTypeAttributeType = metadataTypeAttributeType; + else + return null; + } + + return _cachedMetadataTypeAttributeType; + } + + private static T GetAttribute(Type type) where T : Attribute + { + Type metadataType = GetAssociatedMetadataType(type); + if (metadataType != null) + { + T attribute = ReflectionUtils.GetAttribute(metadataType, true); + if (attribute != null) + return attribute; + } + + return ReflectionUtils.GetAttribute(type, true); + } + + private static T GetAttribute(MemberInfo memberInfo) where T : Attribute + { + Type metadataType = GetAssociatedMetadataType(memberInfo.DeclaringType); + if (metadataType != null) + { + MemberInfo metadataTypeMemberInfo = metadataType.GetMember(memberInfo.Name, + memberInfo.MemberType, + BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance).SingleOrDefault(); + + if (metadataTypeMemberInfo != null) + { + T attribute = ReflectionUtils.GetAttribute(metadataTypeMemberInfo, true); + if (attribute != null) + return attribute; + } + } + + return ReflectionUtils.GetAttribute(memberInfo, true); + } + + public static T GetAttribute(ICustomAttributeProvider attributeProvider) where T : Attribute + { + Type type = attributeProvider as Type; + if (type != null) + return GetAttribute(type); + + MemberInfo memberInfo = attributeProvider as MemberInfo; + if (memberInfo != null) + return GetAttribute(memberInfo); + + return ReflectionUtils.GetAttribute(attributeProvider, true); + } +#else + public static T GetAttribute(ICustomAttributeProvider attributeProvider) where T : Attribute + { + return ReflectionUtils.GetAttribute(attributeProvider, true); + } +#endif + + private static bool? _dynamicCodeGeneration; + + public static bool DynamicCodeGeneration + { + get + { + if (_dynamicCodeGeneration == null) + { +#if !PocketPC && !SILVERLIGHT + try + { + new ReflectionPermission(ReflectionPermissionFlag.MemberAccess).Demand(); + new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess).Demand(); + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); + _dynamicCodeGeneration = true; + } + catch (Exception) + { + _dynamicCodeGeneration = false; + } +#else + _dynamicCodeGeneration = false; +#endif + } + + return _dynamicCodeGeneration.Value; + } + } + + public static ReflectionDelegateFactory ReflectionDelegateFactory + { + get + { +#if !PocketPC && !SILVERLIGHT + if (DynamicCodeGeneration) + return DynamicReflectionDelegateFactory.Instance; +#endif + + return LateBoundReflectionDelegateFactory.Instance; + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/LateBoundMetadataTypeAttribute.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/LateBoundMetadataTypeAttribute.cs new file mode 100644 index 0000000..106f854 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/LateBoundMetadataTypeAttribute.cs @@ -0,0 +1,59 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !SILVERLIGHT && !PocketPC && !NET20 +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json.Utilities; +using System.Reflection; + +namespace Newtonsoft.Json.Serialization +{ + internal class LateBoundMetadataTypeAttribute : IMetadataTypeAttribute + { + private static PropertyInfo _metadataClassTypeProperty; + + private readonly object _attribute; + + public LateBoundMetadataTypeAttribute(object attribute) + { + _attribute = attribute; + } + + public Type MetadataClassType + { + get + { + if (_metadataClassTypeProperty == null) + _metadataClassTypeProperty = _attribute.GetType().GetProperty("MetadataClassType"); + + return (Type)ReflectionUtils.GetMemberValue(_metadataClassTypeProperty, _attribute); + } + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/ObjectConstructor.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/ObjectConstructor.cs new file mode 100644 index 0000000..75f5387 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/ObjectConstructor.cs @@ -0,0 +1,32 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Represents a method that constructs an object. + /// + public delegate object ObjectConstructor(params object[] args); +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/OnErrorAttribute.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/OnErrorAttribute.cs new file mode 100644 index 0000000..73e2df0 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/OnErrorAttribute.cs @@ -0,0 +1,37 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// When applied to a method, specifies that the method is called when an error occurs serializing an object. + /// + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + public sealed class OnErrorAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/ReflectionValueProvider.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/ReflectionValueProvider.cs new file mode 100644 index 0000000..148c98a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Serialization/ReflectionValueProvider.cs @@ -0,0 +1,87 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reflection; +using Newtonsoft.Json.Utilities; +using System.Globalization; + +namespace Newtonsoft.Json.Serialization +{ + /// + /// Get and set values for a using reflection. + /// + public class ReflectionValueProvider : IValueProvider + { + private readonly MemberInfo _memberInfo; + + /// + /// Initializes a new instance of the class. + /// + /// The member info. + public ReflectionValueProvider(MemberInfo memberInfo) + { + ValidationUtils.ArgumentNotNull(memberInfo, "memberInfo"); + _memberInfo = memberInfo; + } + + /// + /// Sets the value. + /// + /// The target to set the value on. + /// The value to set on the target. + public void SetValue(object target, object value) + { + try + { + ReflectionUtils.SetMemberValue(_memberInfo, target, value); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error setting value to '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex); + } + } + + /// + /// Gets the value. + /// + /// The target to get the value from. + /// The value. + public object GetValue(object target) + { + try + { + return ReflectionUtils.GetMemberValue(_memberInfo, target); + } + catch (Exception ex) + { + throw new JsonSerializationException("Error getting value from '{0}' on '{1}'.".FormatWith(CultureInfo.InvariantCulture, _memberInfo.Name, target.GetType()), ex); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/SerializationBinder.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/SerializationBinder.cs new file mode 100644 index 0000000..b112db5 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/SerializationBinder.cs @@ -0,0 +1,21 @@ +#if SILVERLIGHT || PocketPC +using System; +using System.Reflection; + +namespace Newtonsoft.Json +{ + /// + /// Allows users to control class loading and mandate what class to load. + /// + public abstract class SerializationBinder + { + /// + /// When overridden in a derived class, controls the binding of a serialized object to a type. + /// + /// Specifies the name of the serialized object. + /// Specifies the name of the serialized object + /// + public abstract Type BindToType(string assemblyName, string typeName); + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/StreamingContext.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/StreamingContext.cs new file mode 100644 index 0000000..a602d16 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/StreamingContext.cs @@ -0,0 +1,64 @@ +#if PocketPC +#pragma warning disable 1591 + +// This class is... borrowed from .NET and Microsoft for a short time. +// Hopefully Microsoft will add DateTimeOffset to the compact framework +// or I will rewrite a striped down version of this file myself + +namespace System.Runtime.Serialization +{ + public enum StreamingContextStates + { + All = 255, + Clone = 64, + CrossAppDomain = 128, + CrossMachine = 2, + CrossProcess = 1, + File = 4, + Other = 32, + Persistence = 8, + Remoting = 16 + } + + public struct StreamingContext + { + internal object m_additionalContext; + internal StreamingContextStates m_state; + public StreamingContext(StreamingContextStates state) + : this(state, null) + { + } + + public StreamingContext(StreamingContextStates state, object additional) + { + this.m_state = state; + this.m_additionalContext = additional; + } + + public object Context + { + get + { + return this.m_additionalContext; + } + } + public override bool Equals(object obj) + { + return ((obj is StreamingContext) && ((((StreamingContext)obj).m_additionalContext == this.m_additionalContext) && (((StreamingContext)obj).m_state == this.m_state))); + } + + public override int GetHashCode() + { + return (int)this.m_state; + } + + public StreamingContextStates State + { + get + { + return this.m_state; + } + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/TypeNameHandling.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/TypeNameHandling.cs new file mode 100644 index 0000000..9a70ab6 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/TypeNameHandling.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json +{ + /// + /// Specifies type name handling options for the . + /// + [Flags] + public enum TypeNameHandling + { + /// + /// Do not include the .NET type name when serializing types. + /// + None = 0, + /// + /// Include the .NET type name when serializing into a JSON object structure. + /// + Objects = 1, + /// + /// Include the .NET type name when serializing into a JSON array structure. + /// + Arrays = 2, + /// + /// Include the .NET type name when the type of the object being serialized is not the same as its declared type. + /// + Auto = 4, + /// + /// Always include the .NET type name when serializing. + /// + All = Objects | Arrays + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/Base64Encoder.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/Base64Encoder.cs new file mode 100644 index 0000000..af83816 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/Base64Encoder.cs @@ -0,0 +1,95 @@ +using System; +using System.IO; + +namespace Newtonsoft.Json.Utilities +{ + internal class Base64Encoder + { + private const int Base64LineSize = 76; + private const int LineSizeInBytes = 57; + + private readonly char[] _charsLine = new char[Base64LineSize]; + private readonly TextWriter _writer; + + private byte[] _leftOverBytes; + private int _leftOverBytesCount; + + public Base64Encoder(TextWriter writer) + { + ValidationUtils.ArgumentNotNull(writer, "writer"); + _writer = writer; + } + + public void Encode(byte[] buffer, int index, int count) + { + if (buffer == null) + throw new ArgumentNullException("buffer"); + + if (index < 0) + throw new ArgumentOutOfRangeException("index"); + + if (count < 0) + throw new ArgumentOutOfRangeException("count"); + + if (count > (buffer.Length - index)) + throw new ArgumentOutOfRangeException("count"); + + if (_leftOverBytesCount > 0) + { + int leftOverBytesCount = _leftOverBytesCount; + while (leftOverBytesCount < 3 && count > 0) + { + _leftOverBytes[leftOverBytesCount++] = buffer[index++]; + count--; + } + if (count == 0 && leftOverBytesCount < 3) + { + _leftOverBytesCount = leftOverBytesCount; + return; + } + int num2 = Convert.ToBase64CharArray(_leftOverBytes, 0, 3, _charsLine, 0); + WriteChars(_charsLine, 0, num2); + } + _leftOverBytesCount = count % 3; + if (_leftOverBytesCount > 0) + { + count -= _leftOverBytesCount; + if (_leftOverBytes == null) + { + _leftOverBytes = new byte[3]; + } + for (int i = 0; i < _leftOverBytesCount; i++) + { + _leftOverBytes[i] = buffer[(index + count) + i]; + } + } + int num4 = index + count; + int length = LineSizeInBytes; + while (index < num4) + { + if ((index + length) > num4) + { + length = num4 - index; + } + int num6 = Convert.ToBase64CharArray(buffer, index, length, _charsLine, 0); + WriteChars(_charsLine, 0, num6); + index += length; + } + } + + public void Flush() + { + if (_leftOverBytesCount > 0) + { + int count = Convert.ToBase64CharArray(_leftOverBytes, 0, _leftOverBytesCount, _charsLine, 0); + WriteChars(_charsLine, 0, count); + _leftOverBytesCount = 0; + } + } + + private void WriteChars(char[] chars, int index, int count) + { + _writer.Write(chars, index, count); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/BidirectionalDictionary.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/BidirectionalDictionary.cs new file mode 100644 index 0000000..9a17023 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/BidirectionalDictionary.cs @@ -0,0 +1,69 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Utilities +{ + internal class BidirectionalDictionary + { + private readonly IDictionary _firstToSecond; + private readonly IDictionary _secondToFirst; + + public BidirectionalDictionary() + : this(EqualityComparer.Default, EqualityComparer.Default) + { + } + + public BidirectionalDictionary(IEqualityComparer firstEqualityComparer, IEqualityComparer secondEqualityComparer) + { + _firstToSecond = new Dictionary(firstEqualityComparer); + _secondToFirst = new Dictionary(secondEqualityComparer); + } + + public void Add(TFirst first, TSecond second) + { + if (_firstToSecond.ContainsKey(first) || _secondToFirst.ContainsKey(second)) + { + throw new ArgumentException("Duplicate first or second"); + } + _firstToSecond.Add(first, second); + _secondToFirst.Add(second, first); + } + + public bool TryGetByFirst(TFirst first, out TSecond second) + { + return _firstToSecond.TryGetValue(first, out second); + } + + public bool TryGetBySecond(TSecond second, out TFirst first) + { + return _secondToFirst.TryGetValue(second, out first); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs new file mode 100644 index 0000000..8050053 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/CollectionUtils.cs @@ -0,0 +1,635 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Text; +using System.Collections; +using System.Linq; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal static class CollectionUtils + { + public static IEnumerable CastValid(this IEnumerable enumerable) + { + ValidationUtils.ArgumentNotNull(enumerable, "enumerable"); + + return enumerable.Cast().Where(o => o is T).Cast(); + } + + public static List CreateList(params T[] values) + { + return new List(values); + } + + /// + /// Determines whether the collection is null or empty. + /// + /// The collection. + /// + /// true if the collection is null or empty; otherwise, false. + /// + public static bool IsNullOrEmpty(ICollection collection) + { + if (collection != null) + { + return (collection.Count == 0); + } + return true; + } + + /// + /// Determines whether the collection is null or empty. + /// + /// The collection. + /// + /// true if the collection is null or empty; otherwise, false. + /// + public static bool IsNullOrEmpty(ICollection collection) + { + if (collection != null) + { + return (collection.Count == 0); + } + return true; + } + + /// + /// Determines whether the collection is null, empty or its contents are uninitialized values. + /// + /// The list. + /// + /// true if the collection is null or empty or its contents are uninitialized values; otherwise, false. + /// + public static bool IsNullOrEmptyOrDefault(IList list) + { + if (IsNullOrEmpty(list)) + return true; + + return ReflectionUtils.ItemsUnitializedValue(list); + } + + /// + /// Makes a slice of the specified list in between the start and end indexes. + /// + /// The list. + /// The start index. + /// The end index. + /// A slice of the list. + public static IList Slice(IList list, int? start, int? end) + { + return Slice(list, start, end, null); + } + + /// + /// Makes a slice of the specified list in between the start and end indexes, + /// getting every so many items based upon the step. + /// + /// The list. + /// The start index. + /// The end index. + /// The step. + /// A slice of the list. + public static IList Slice(IList list, int? start, int? end, int? step) + { + if (list == null) + throw new ArgumentNullException("list"); + + if (step == 0) + throw new ArgumentException("Step cannot be zero.", "step"); + + List slicedList = new List(); + + // nothing to slice + if (list.Count == 0) + return slicedList; + + // set defaults for null arguments + int s = step ?? 1; + int startIndex = start ?? 0; + int endIndex = end ?? list.Count; + + // start from the end of the list if start is negitive + startIndex = (startIndex < 0) ? list.Count + startIndex : startIndex; + + // end from the start of the list if end is negitive + endIndex = (endIndex < 0) ? list.Count + endIndex : endIndex; + + // ensure indexes keep within collection bounds + startIndex = Math.Max(startIndex, 0); + endIndex = Math.Min(endIndex, list.Count - 1); + + // loop between start and end indexes, incrementing by the step + for (int i = startIndex; i < endIndex; i += s) + { + slicedList.Add(list[i]); + } + + return slicedList; + } + + + /// + /// Group the collection using a function which returns the key. + /// + /// The source collection to group. + /// The key selector. + /// A Dictionary with each key relating to a list of objects in a list grouped under it. + public static Dictionary> GroupBy(ICollection source, Func keySelector) + { + if (keySelector == null) + throw new ArgumentNullException("keySelector"); + + Dictionary> groupedValues = new Dictionary>(); + + foreach (V value in source) + { + // using delegate to get the value's key + K key = keySelector(value); + List groupedValueList; + + // add a list for grouped values if the key is not already in Dictionary + if (!groupedValues.TryGetValue(key, out groupedValueList)) + { + groupedValueList = new List(); + groupedValues.Add(key, groupedValueList); + } + + groupedValueList.Add(value); + } + + return groupedValues; + } + + /// + /// Adds the elements of the specified collection to the specified generic IList. + /// + /// The list to add to. + /// The collection of elements to add. + public static void AddRange(this IList initial, IEnumerable collection) + { + if (initial == null) + throw new ArgumentNullException("initial"); + + if (collection == null) + return; + + foreach (T value in collection) + { + initial.Add(value); + } + } + + public static void AddRange(this IList initial, IEnumerable collection) + { + ValidationUtils.ArgumentNotNull(initial, "initial"); + + ListWrapper wrapper = new ListWrapper(initial); + wrapper.AddRange(collection.Cast()); + } + + public static List Distinct(List collection) + { + List distinctList = new List(); + + foreach (T value in collection) + { + if (!distinctList.Contains(value)) + distinctList.Add(value); + } + + return distinctList; + } + + public static List> Flatten(params IList[] lists) + { + List> flattened = new List>(); + Dictionary currentList = new Dictionary(); + + Recurse(new List>(lists), 0, currentList, flattened); + + return flattened; + } + + private static void Recurse(IList> global, int current, Dictionary currentSet, List> flattenedResult) + { + IList currentArray = global[current]; + + for (int i = 0; i < currentArray.Count; i++) + { + currentSet[current] = currentArray[i]; + + if (current == global.Count - 1) + { + List items = new List(); + + for (int k = 0; k < currentSet.Count; k++) + { + items.Add(currentSet[k]); + } + + flattenedResult.Add(items); + } + else + { + Recurse(global, current + 1, currentSet, flattenedResult); + } + } + } + + public static List CreateList(ICollection collection) + { + if (collection == null) + throw new ArgumentNullException("collection"); + + T[] array = new T[collection.Count]; + collection.CopyTo(array, 0); + + return new List(array); + } + + public static bool ListEquals(IList a, IList b) + { + if (a == null || b == null) + return (a == null && b == null); + + if (a.Count != b.Count) + return false; + + EqualityComparer comparer = EqualityComparer.Default; + + for (int i = 0; i < a.Count; i++) + { + if (!comparer.Equals(a[i], b[i])) + return false; + } + + return true; + } + + #region GetSingleItem + public static bool TryGetSingleItem(IList list, out T value) + { + return TryGetSingleItem(list, false, out value); + } + + public static bool TryGetSingleItem(IList list, bool returnDefaultIfEmpty, out T value) + { + return MiscellaneousUtils.TryAction(delegate { return GetSingleItem(list, returnDefaultIfEmpty); }, out value); + } + + public static T GetSingleItem(IList list) + { + return GetSingleItem(list, false); + } + + public static T GetSingleItem(IList list, bool returnDefaultIfEmpty) + { + if (list.Count == 1) + return list[0]; + else if (returnDefaultIfEmpty && list.Count == 0) + return default(T); + else + throw new Exception("Expected single {0} in list but got {1}.".FormatWith(CultureInfo.InvariantCulture, typeof(T), list.Count)); + } + #endregion + + public static IList Minus(IList list, IList minus) + { + ValidationUtils.ArgumentNotNull(list, "list"); + + List result = new List(list.Count); + foreach (T t in list) + { + if (minus == null || !minus.Contains(t)) + result.Add(t); + } + + return result; + } + + public static IList CreateGenericList(Type listType) + { + ValidationUtils.ArgumentNotNull(listType, "listType"); + + return (IList)ReflectionUtils.CreateGeneric(typeof(List<>), listType); + } + + public static IDictionary CreateGenericDictionary(Type keyType, Type valueType) + { + ValidationUtils.ArgumentNotNull(keyType, "keyType"); + ValidationUtils.ArgumentNotNull(valueType, "valueType"); + + return (IDictionary)ReflectionUtils.CreateGeneric(typeof(Dictionary<,>), keyType, valueType); + } + + public static bool IsListType(Type type) + { + ValidationUtils.ArgumentNotNull(type, "type"); + + if (type.IsArray) + return true; + if (typeof(IList).IsAssignableFrom(type)) + return true; + if (ReflectionUtils.ImplementsGenericDefinition(type, typeof(IList<>))) + return true; + + return false; + } + + public static bool IsCollectionType(Type type) + { + ValidationUtils.ArgumentNotNull(type, "type"); + + if (type.IsArray) + return true; + if (typeof(ICollection).IsAssignableFrom(type)) + return true; + if (ReflectionUtils.ImplementsGenericDefinition(type, typeof(ICollection<>))) + return true; + + return false; + } + + public static bool IsDictionaryType(Type type) + { + ValidationUtils.ArgumentNotNull(type, "type"); + + if (typeof(IDictionary).IsAssignableFrom(type)) + return true; + if (ReflectionUtils.ImplementsGenericDefinition(type, typeof (IDictionary<,>))) + return true; + + return false; + } + + public static IWrappedCollection CreateCollectionWrapper(object list) + { + ValidationUtils.ArgumentNotNull(list, "list"); + + Type collectionDefinition; + if (ReflectionUtils.ImplementsGenericDefinition(list.GetType(), typeof(ICollection<>), out collectionDefinition)) + { + Type collectionItemType = ReflectionUtils.GetCollectionItemType(collectionDefinition); + + // Activator.CreateInstance throws AmbiguousMatchException. Manually invoke constructor + Func, object> instanceCreator = (t, a) => + { + ConstructorInfo c = t.GetConstructor(new[] { collectionDefinition }); + return c.Invoke(new[] { list }); + }; + + return (IWrappedCollection)ReflectionUtils.CreateGeneric(typeof(CollectionWrapper<>), new[] { collectionItemType }, instanceCreator, list); + } + else if (list is IList) + { + return new CollectionWrapper((IList)list); + } + else + { + throw new Exception("Can not create ListWrapper for type {0}.".FormatWith(CultureInfo.InvariantCulture, list.GetType())); + } + } + public static IWrappedList CreateListWrapper(object list) + { + ValidationUtils.ArgumentNotNull(list, "list"); + + Type listDefinition; + if (ReflectionUtils.ImplementsGenericDefinition(list.GetType(), typeof(IList<>), out listDefinition)) + { + Type collectionItemType = ReflectionUtils.GetCollectionItemType(listDefinition); + + // Activator.CreateInstance throws AmbiguousMatchException. Manually invoke constructor + Func, object> instanceCreator = (t, a) => + { + ConstructorInfo c = t.GetConstructor(new[] {listDefinition}); + return c.Invoke(new[] { list }); + }; + + return (IWrappedList)ReflectionUtils.CreateGeneric(typeof(ListWrapper<>), new[] { collectionItemType }, instanceCreator, list); + } + else if (list is IList) + { + return new ListWrapper((IList)list); + } + else + { + throw new Exception("Can not create ListWrapper for type {0}.".FormatWith(CultureInfo.InvariantCulture, list.GetType())); + } + } + + public static IWrappedDictionary CreateDictionaryWrapper(object dictionary) + { + ValidationUtils.ArgumentNotNull(dictionary, "dictionary"); + + Type dictionaryDefinition; + if (ReflectionUtils.ImplementsGenericDefinition(dictionary.GetType(), typeof(IDictionary<,>), out dictionaryDefinition)) + { + Type dictionaryKeyType = ReflectionUtils.GetDictionaryKeyType(dictionaryDefinition); + Type dictionaryValueType = ReflectionUtils.GetDictionaryValueType(dictionaryDefinition); + + // Activator.CreateInstance throws AmbiguousMatchException. Manually invoke constructor + Func, object> instanceCreator = (t, a) => + { + ConstructorInfo c = t.GetConstructor(new[] { dictionaryDefinition }); + return c.Invoke(new[] { dictionary }); + }; + + return (IWrappedDictionary)ReflectionUtils.CreateGeneric(typeof(DictionaryWrapper<,>), new[] { dictionaryKeyType, dictionaryValueType }, instanceCreator, dictionary); + } + else if (dictionary is IDictionary) + { + return new DictionaryWrapper((IDictionary)dictionary); + } + else + { + throw new Exception("Can not create DictionaryWrapper for type {0}.".FormatWith(CultureInfo.InvariantCulture, dictionary.GetType())); + } + } + + public static object CreateAndPopulateList(Type listType, Action populateList) + { + ValidationUtils.ArgumentNotNull(listType, "listType"); + ValidationUtils.ArgumentNotNull(populateList, "populateList"); + + IList list; + Type collectionType; + bool isReadOnlyOrFixedSize = false; + + if (listType.IsArray) + { + // have to use an arraylist when creating array + // there is no way to know the size until it is finised + list = new List(); + isReadOnlyOrFixedSize = true; + } + else if (ReflectionUtils.InheritsGenericDefinition(listType, typeof(ReadOnlyCollection<>), out collectionType)) + { + Type readOnlyCollectionContentsType = collectionType.GetGenericArguments()[0]; + Type genericEnumerable = ReflectionUtils.MakeGenericType(typeof(IEnumerable<>), readOnlyCollectionContentsType); + bool suitableConstructor = false; + + foreach (ConstructorInfo constructor in listType.GetConstructors()) + { + IList parameters = constructor.GetParameters(); + + if (parameters.Count == 1) + { + if (genericEnumerable.IsAssignableFrom(parameters[0].ParameterType)) + { + suitableConstructor = true; + break; + } + } + } + + if (!suitableConstructor) + throw new Exception("Read-only type {0} does not have a public constructor that takes a type that implements {1}.".FormatWith(CultureInfo.InvariantCulture, listType, genericEnumerable)); + + // can't add or modify a readonly list + // use List and convert once populated + list = CreateGenericList(readOnlyCollectionContentsType); + isReadOnlyOrFixedSize = true; + } + else if (typeof(IList).IsAssignableFrom(listType)) + { + if (ReflectionUtils.IsInstantiatableType(listType)) + list = (IList)Activator.CreateInstance(listType); + else if (listType == typeof(IList)) + list = new List(); + else + list = null; + } + else if (ReflectionUtils.ImplementsGenericDefinition(listType, typeof(ICollection<>))) + { + if (ReflectionUtils.IsInstantiatableType(listType)) + list = CreateCollectionWrapper(Activator.CreateInstance(listType)); + else + list = null; + } + else + { + list = null; + } + + if (list == null) + throw new Exception("Cannot create and populate list type {0}.".FormatWith(CultureInfo.InvariantCulture, listType)); + + populateList(list, isReadOnlyOrFixedSize); + + // create readonly and fixed sized collections using the temporary list + if (isReadOnlyOrFixedSize) + { + if (listType.IsArray) + list = ToArray(((List)list).ToArray(), ReflectionUtils.GetCollectionItemType(listType)); + else if (ReflectionUtils.InheritsGenericDefinition(listType, typeof(ReadOnlyCollection<>))) + list = (IList)ReflectionUtils.CreateInstance(listType, list); + } + else if (list is IWrappedCollection) + { + return ((IWrappedCollection) list).UnderlyingCollection; + } + + return list; + } + + public static Array ToArray(Array initial, Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + Array destinationArray = Array.CreateInstance(type, initial.Length); + Array.Copy(initial, 0, destinationArray, 0, initial.Length); + return destinationArray; + } + + public static bool AddDistinct(this IList list, T value) + { + return list.AddDistinct(value, EqualityComparer.Default); + } + + public static bool AddDistinct(this IList list, T value, IEqualityComparer comparer) + { + if (list.ContainsValue(value, comparer)) + return false; + + list.Add(value); + return true; + } + + // this is here because LINQ Bridge doesn't support Contains with IEqualityComparer + public static bool ContainsValue(this IEnumerable source, TSource value, IEqualityComparer comparer) + { + if (comparer == null) + comparer = EqualityComparer.Default; + + if (source == null) + throw new ArgumentNullException("source"); + + foreach (TSource local in source) + { + if (comparer.Equals(local, value)) + return true; + } + + return false; + } + + public static bool AddRangeDistinct(this IList list, IEnumerable values) + { + return list.AddRangeDistinct(values, EqualityComparer.Default); + } + + public static bool AddRangeDistinct(this IList list, IEnumerable values, IEqualityComparer comparer) + { + bool allAdded = true; + foreach (T value in values) + { + if (!list.AddDistinct(value, comparer)) + allAdded = false; + } + + return allAdded; + } + + public static int IndexOf(this IEnumerable collection, Func predicate) + { + int index = 0; + foreach (T value in collection) + { + if (predicate(value)) + return index; + + index++; + } + + return -1; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/CollectionWrapper.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/CollectionWrapper.cs new file mode 100644 index 0000000..21fe881 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/CollectionWrapper.cs @@ -0,0 +1,271 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using Newtonsoft.Json.Utilities; +using System.Linq; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal interface IWrappedCollection : IList + { + object UnderlyingCollection { get; } + } + + internal class CollectionWrapper : ICollection, IWrappedCollection + { + private readonly IList _list; + private readonly ICollection _genericCollection; + private object _syncRoot; + + public CollectionWrapper(IList list) + { + ValidationUtils.ArgumentNotNull(list, "list"); + + if (list is ICollection) + _genericCollection = (ICollection)list; + else + _list = list; + } + + public CollectionWrapper(ICollection list) + { + ValidationUtils.ArgumentNotNull(list, "list"); + + _genericCollection = list; + } + + public virtual void Add(T item) + { + if (_genericCollection != null) + _genericCollection.Add(item); + else + _list.Add(item); + } + + public virtual void Clear() + { + if (_genericCollection != null) + _genericCollection.Clear(); + else + _list.Clear(); + } + + public virtual bool Contains(T item) + { + if (_genericCollection != null) + return _genericCollection.Contains(item); + else + return _list.Contains(item); + } + + public virtual void CopyTo(T[] array, int arrayIndex) + { + if (_genericCollection != null) + _genericCollection.CopyTo(array, arrayIndex); + else + _list.CopyTo(array, arrayIndex); + } + + public virtual int Count + { + get + { + if (_genericCollection != null) + return _genericCollection.Count; + else + return _list.Count; + } + } + + public virtual bool IsReadOnly + { + get + { + if (_genericCollection != null) + return _genericCollection.IsReadOnly; + else + return _list.IsReadOnly; + } + } + + public virtual bool Remove(T item) + { + if (_genericCollection != null) + { + return _genericCollection.Remove(item); + } + else + { + bool contains = _list.Contains(item); + + if (contains) + _list.Remove(item); + + return contains; + } + } + + public virtual IEnumerator GetEnumerator() + { + if (_genericCollection != null) + return _genericCollection.GetEnumerator(); + + return _list.Cast().GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + if (_genericCollection != null) + return _genericCollection.GetEnumerator(); + else + return _list.GetEnumerator(); + } + + int IList.Add(object value) + { + VerifyValueType(value); + Add((T)value); + + return (Count - 1); + } + + bool IList.Contains(object value) + { + if (IsCompatibleObject(value)) + return Contains((T)value); + + return false; + } + + int IList.IndexOf(object value) + { + if (_genericCollection != null) + throw new Exception("Wrapped ICollection does not support IndexOf."); + + if (IsCompatibleObject(value)) + return _list.IndexOf((T)value); + + return -1; + } + + void IList.RemoveAt(int index) + { + if (_genericCollection != null) + throw new Exception("Wrapped ICollection does not support RemoveAt."); + + _list.RemoveAt(index); + } + + void IList.Insert(int index, object value) + { + if (_genericCollection != null) + throw new Exception("Wrapped ICollection does not support Insert."); + + VerifyValueType(value); + _list.Insert(index, (T)value); + } + + bool IList.IsFixedSize + { + get { return false; } + } + + void IList.Remove(object value) + { + if (IsCompatibleObject(value)) + Remove((T)value); + } + + object IList.this[int index] + { + get + { + if (_genericCollection != null) + throw new Exception("Wrapped ICollection does not support indexer."); + + return _list[index]; + } + set + { + if (_genericCollection != null) + throw new Exception("Wrapped ICollection does not support indexer."); + + VerifyValueType(value); + _list[index] = (T)value; + } + } + + void ICollection.CopyTo(Array array, int arrayIndex) + { + CopyTo((T[])array, arrayIndex); + } + + bool ICollection.IsSynchronized + { + get { return false; } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + Interlocked.CompareExchange(ref _syncRoot, new object(), null); + + return _syncRoot; + } + } + + private static void VerifyValueType(object value) + { + if (!IsCompatibleObject(value)) + throw new ArgumentException("The value '{0}' is not of type '{1}' and cannot be used in this generic collection.".FormatWith(CultureInfo.InvariantCulture, value, typeof(T)), "value"); + } + + private static bool IsCompatibleObject(object value) + { + if (!(value is T) && (value != null || (typeof(T).IsValueType && !ReflectionUtils.IsNullableType(typeof(T))))) + return false; + + return true; + } + + public object UnderlyingCollection + { + get + { + if (_genericCollection != null) + return _genericCollection; + else + return _list; + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs new file mode 100644 index 0000000..418dc94 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ConvertUtils.cs @@ -0,0 +1,515 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Globalization; +using System.ComponentModel; +using Newtonsoft.Json.Serialization; +using System.Reflection; + +#if !SILVERLIGHT +using System.Data.SqlTypes; +#endif + +namespace Newtonsoft.Json.Utilities +{ + internal static class ConvertUtils + { + internal struct TypeConvertKey : IEquatable + { + private readonly Type _initialType; + private readonly Type _targetType; + + public Type InitialType + { + get { return _initialType; } + } + + public Type TargetType + { + get { return _targetType; } + } + + public TypeConvertKey(Type initialType, Type targetType) + { + _initialType = initialType; + _targetType = targetType; + } + + public override int GetHashCode() + { + return _initialType.GetHashCode() ^ _targetType.GetHashCode(); + } + + public override bool Equals(object obj) + { + if (!(obj is TypeConvertKey)) + return false; + + return Equals((TypeConvertKey)obj); + } + + public bool Equals(TypeConvertKey other) + { + return (_initialType == other._initialType && _targetType == other._targetType); + } + } + + private static readonly ThreadSafeStore> CastConverters = + new ThreadSafeStore>(CreateCastConverter); + + private static Func CreateCastConverter(TypeConvertKey t) + { + MethodInfo castMethodInfo = t.TargetType.GetMethod("op_Implicit", new[] { t.InitialType }); + if (castMethodInfo == null) + castMethodInfo = t.TargetType.GetMethod("op_Explicit", new[] { t.InitialType }); + + if (castMethodInfo == null) + return null; + + MethodCall call = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(castMethodInfo); + + return o => call(null, o); + } + + public static bool CanConvertType(Type initialType, Type targetType, bool allowTypeNameToString) + { + ValidationUtils.ArgumentNotNull(initialType, "initialType"); + ValidationUtils.ArgumentNotNull(targetType, "targetType"); + + if (ReflectionUtils.IsNullableType(targetType)) + targetType = Nullable.GetUnderlyingType(targetType); + + if (targetType == initialType) + return true; + + if (typeof(IConvertible).IsAssignableFrom(initialType) && typeof(IConvertible).IsAssignableFrom(targetType)) + { + return true; + } + +#if !PocketPC && !NET20 + if (initialType == typeof(DateTime) && targetType == typeof(DateTimeOffset)) + return true; +#endif + + if (initialType == typeof(Guid) && (targetType == typeof(Guid) || targetType == typeof(string))) + return true; + + if (initialType == typeof(Type) && targetType == typeof(string)) + return true; + +#if !PocketPC + // see if source or target types have a TypeConverter that converts between the two + TypeConverter toConverter = GetConverter(initialType); + + if (toConverter != null && !IsComponentConverter(toConverter) && toConverter.CanConvertTo(targetType)) + { + if (allowTypeNameToString || toConverter.GetType() != typeof(TypeConverter)) + return true; + } + + TypeConverter fromConverter = GetConverter(targetType); + + if (fromConverter != null && !IsComponentConverter(fromConverter) && fromConverter.CanConvertFrom(initialType)) + return true; +#endif + + // handle DBNull and INullable + if (initialType == typeof(DBNull)) + { + if (ReflectionUtils.IsNullable(targetType)) + return true; + } + + return false; + } + + private static bool IsComponentConverter(TypeConverter converter) + { +#if !SILVERLIGHT && !PocketPC + return (converter is ComponentConverter); +#else + return false; +#endif + } + + #region Convert + /// + /// Converts the value to the specified type. + /// + /// The type to convert the value to. + /// The value to convert. + /// The converted type. + public static T Convert(object initialValue) + { + return Convert(initialValue, CultureInfo.CurrentCulture); + } + + /// + /// Converts the value to the specified type. + /// + /// The type to convert the value to. + /// The value to convert. + /// The culture to use when converting. + /// The converted type. + public static T Convert(object initialValue, CultureInfo culture) + { + return (T)Convert(initialValue, culture, typeof(T)); + } + + /// + /// Converts the value to the specified type. + /// + /// The value to convert. + /// The culture to use when converting. + /// The type to convert the value to. + /// The converted type. + public static object Convert(object initialValue, CultureInfo culture, Type targetType) + { + if (initialValue == null) + throw new ArgumentNullException("initialValue"); + + if (ReflectionUtils.IsNullableType(targetType)) + targetType = Nullable.GetUnderlyingType(targetType); + + Type initialType = initialValue.GetType(); + + if (targetType == initialType) + return initialValue; + + if (initialValue is string && typeof(Type).IsAssignableFrom(targetType)) + return Type.GetType((string) initialValue, true); + + if (targetType.IsInterface || targetType.IsGenericTypeDefinition || targetType.IsAbstract) + throw new ArgumentException("Target type {0} is not a value type or a non-abstract class.".FormatWith(CultureInfo.InvariantCulture, targetType), "targetType"); + + // use Convert.ChangeType if both types are IConvertible + if (initialValue is IConvertible && typeof(IConvertible).IsAssignableFrom(targetType)) + { + if (targetType.IsEnum) + { + if (initialValue is string) + return Enum.Parse(targetType, initialValue.ToString(), true); + else if (IsInteger(initialValue)) + return Enum.ToObject(targetType, initialValue); + } + + return System.Convert.ChangeType(initialValue, targetType, culture); + } + +#if !PocketPC && !NET20 + if (initialValue is DateTime && targetType == typeof(DateTimeOffset)) + return new DateTimeOffset((DateTime)initialValue); +#endif + + if (initialValue is string) + { + if (targetType == typeof (Guid)) + return new Guid((string) initialValue); + if (targetType == typeof (Uri)) + return new Uri((string) initialValue); + if (targetType == typeof (TimeSpan)) +#if !(NET35 || NET20 || SILVERLIGHT) + return TimeSpan.Parse((string) initialValue, CultureInfo.InvariantCulture); +#else + return TimeSpan.Parse((string)initialValue); +#endif + } + +#if !PocketPC + // see if source or target types have a TypeConverter that converts between the two + TypeConverter toConverter = GetConverter(initialType); + + if (toConverter != null && toConverter.CanConvertTo(targetType)) + { +#if !SILVERLIGHT + return toConverter.ConvertTo(null, culture, initialValue, targetType); +#else + return toConverter.ConvertTo(initialValue, targetType); +#endif + } + + TypeConverter fromConverter = GetConverter(targetType); + + if (fromConverter != null && fromConverter.CanConvertFrom(initialType)) + { +#if !SILVERLIGHT + return fromConverter.ConvertFrom(null, culture, initialValue); +#else + return fromConverter.ConvertFrom(initialValue); +#endif + } +#endif + + // handle DBNull and INullable + if (initialValue == DBNull.Value) + { + if (ReflectionUtils.IsNullable(targetType)) + return EnsureTypeAssignable(null, initialType, targetType); + + throw new Exception("Can not convert null {0} into non-nullable {1}.".FormatWith(CultureInfo.InvariantCulture, initialType, targetType)); + } +#if !SILVERLIGHT + if (initialValue is INullable) + return EnsureTypeAssignable(ToValue((INullable)initialValue), initialType, targetType); +#endif + + throw new Exception("Can not convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, initialType, targetType)); + } + #endregion + + #region TryConvert + /// + /// Converts the value to the specified type. + /// + /// The type to convert the value to. + /// The value to convert. + /// The converted value if the conversion was successful or the default value of T if it failed. + /// + /// true if initialValue was converted successfully; otherwise, false. + /// + public static bool TryConvert(object initialValue, out T convertedValue) + { + return TryConvert(initialValue, CultureInfo.CurrentCulture, out convertedValue); + } + + /// + /// Converts the value to the specified type. + /// + /// The type to convert the value to. + /// The value to convert. + /// The culture to use when converting. + /// The converted value if the conversion was successful or the default value of T if it failed. + /// + /// true if initialValue was converted successfully; otherwise, false. + /// + public static bool TryConvert(object initialValue, CultureInfo culture, out T convertedValue) + { + return MiscellaneousUtils.TryAction(delegate + { + object tempConvertedValue; + TryConvert(initialValue, CultureInfo.CurrentCulture, typeof(T), out tempConvertedValue); + + return (T)tempConvertedValue; + }, out convertedValue); + } + + /// + /// Converts the value to the specified type. + /// + /// The value to convert. + /// The culture to use when converting. + /// The type to convert the value to. + /// The converted value if the conversion was successful or the default value of T if it failed. + /// + /// true if initialValue was converted successfully; otherwise, false. + /// + public static bool TryConvert(object initialValue, CultureInfo culture, Type targetType, out object convertedValue) + { + return MiscellaneousUtils.TryAction(delegate { return Convert(initialValue, culture, targetType); }, out convertedValue); + } + #endregion + + #region ConvertOrCast + /// + /// Converts the value to the specified type. If the value is unable to be converted, the + /// value is checked whether it assignable to the specified type. + /// + /// The type to convert or cast the value to. + /// The value to convert. + /// The converted type. If conversion was unsuccessful, the initial value is returned if assignable to the target type + public static T ConvertOrCast(object initialValue) + { + return ConvertOrCast(initialValue, CultureInfo.CurrentCulture); + } + + /// + /// Converts the value to the specified type. If the value is unable to be converted, the + /// value is checked whether it assignable to the specified type. + /// + /// The type to convert or cast the value to. + /// The value to convert. + /// The culture to use when converting. + /// The converted type. If conversion was unsuccessful, the initial value is returned if assignable to the target type + public static T ConvertOrCast(object initialValue, CultureInfo culture) + { + return (T)ConvertOrCast(initialValue, culture, typeof(T)); + } + + /// + /// Converts the value to the specified type. If the value is unable to be converted, the + /// value is checked whether it assignable to the specified type. + /// + /// The value to convert. + /// The culture to use when converting. + /// The type to convert or cast the value to. + /// + /// The converted type. If conversion was unsuccessful, the initial value + /// is returned if assignable to the target type. + /// + public static object ConvertOrCast(object initialValue, CultureInfo culture, Type targetType) + { + object convertedValue; + + if (targetType == typeof(object)) + return initialValue; + + if (initialValue == null && ReflectionUtils.IsNullable(targetType)) + return null; + + if (TryConvert(initialValue, culture, targetType, out convertedValue)) + return convertedValue; + + return EnsureTypeAssignable(initialValue, ReflectionUtils.GetObjectType(initialValue), targetType); + } + #endregion + + #region TryConvertOrCast + /// + /// Converts the value to the specified type. If the value is unable to be converted, the + /// value is checked whether it assignable to the specified type. + /// + /// The type to convert the value to. + /// The value to convert. + /// The converted value if the conversion was successful or the default value of T if it failed. + /// + /// true if initialValue was converted successfully or is assignable; otherwise, false. + /// + public static bool TryConvertOrCast(object initialValue, out T convertedValue) + { + return TryConvertOrCast(initialValue, CultureInfo.CurrentCulture, out convertedValue); + } + + /// + /// Converts the value to the specified type. If the value is unable to be converted, the + /// value is checked whether it assignable to the specified type. + /// + /// The type to convert the value to. + /// The value to convert. + /// The culture to use when converting. + /// The converted value if the conversion was successful or the default value of T if it failed. + /// + /// true if initialValue was converted successfully or is assignable; otherwise, false. + /// + public static bool TryConvertOrCast(object initialValue, CultureInfo culture, out T convertedValue) + { + return MiscellaneousUtils.TryAction(delegate + { + object tempConvertedValue; + TryConvertOrCast(initialValue, CultureInfo.CurrentCulture, typeof(T), out tempConvertedValue); + + return (T)tempConvertedValue; + }, out convertedValue); + } + + /// + /// Converts the value to the specified type. If the value is unable to be converted, the + /// value is checked whether it assignable to the specified type. + /// + /// The value to convert. + /// The culture to use when converting. + /// The type to convert the value to. + /// The converted value if the conversion was successful or the default value of T if it failed. + /// + /// true if initialValue was converted successfully or is assignable; otherwise, false. + /// + public static bool TryConvertOrCast(object initialValue, CultureInfo culture, Type targetType, out object convertedValue) + { + return MiscellaneousUtils.TryAction(delegate { return ConvertOrCast(initialValue, culture, targetType); }, out convertedValue); + } + #endregion + + private static object EnsureTypeAssignable(object value, Type initialType, Type targetType) + { + Type valueType = (value != null) ? value.GetType() : null; + + if (value != null) + { + if (targetType.IsAssignableFrom(valueType)) + return value; + + Func castConverter = CastConverters.Get(new TypeConvertKey(valueType, targetType)); + if (castConverter != null) + return castConverter(value); + } + else + { + if (ReflectionUtils.IsNullable(targetType)) + return null; + } + + throw new Exception("Could not cast or convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, (initialType != null) ? initialType.ToString() : "{null}", targetType)); + } + +#if !SILVERLIGHT + public static object ToValue(INullable nullableValue) + { + if (nullableValue == null) + return null; + else if (nullableValue is SqlInt32) + return ToValue((SqlInt32)nullableValue); + else if (nullableValue is SqlInt64) + return ToValue((SqlInt64)nullableValue); + else if (nullableValue is SqlBoolean) + return ToValue((SqlBoolean)nullableValue); + else if (nullableValue is SqlString) + return ToValue((SqlString)nullableValue); + else if (nullableValue is SqlDateTime) + return ToValue((SqlDateTime)nullableValue); + + throw new Exception("Unsupported INullable type: {0}".FormatWith(CultureInfo.InvariantCulture, nullableValue.GetType())); + } +#endif + +#if !PocketPC + internal static TypeConverter GetConverter(Type t) + { + return JsonTypeReflector.GetTypeConverter(t); + } +#endif + + public static bool IsInteger(object value) + { + switch (System.Convert.GetTypeCode(value)) + { + case TypeCode.SByte: + case TypeCode.Byte: + case TypeCode.Int16: + case TypeCode.UInt16: + case TypeCode.Int32: + case TypeCode.UInt32: + case TypeCode.Int64: + case TypeCode.UInt64: + return true; + default: + return false; + } + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DateTimeUtils.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DateTimeUtils.cs new file mode 100644 index 0000000..86a399a --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DateTimeUtils.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal static class DateTimeUtils + { + public static string GetLocalOffset(this DateTime d) + { + TimeSpan utcOffset; +#if PocketPC || NET20 + utcOffset = TimeZone.CurrentTimeZone.GetUtcOffset(d); +#else + utcOffset = TimeZoneInfo.Local.GetUtcOffset(d); +#endif + + return utcOffset.Hours.ToString("+00;-00", CultureInfo.InvariantCulture) + ":" + utcOffset.Minutes.ToString("00;00", CultureInfo.InvariantCulture); + } + + public static XmlDateTimeSerializationMode ToSerializationMode(DateTimeKind kind) + { + switch (kind) + { + case DateTimeKind.Local: + return XmlDateTimeSerializationMode.Local; + case DateTimeKind.Unspecified: + return XmlDateTimeSerializationMode.Unspecified; + case DateTimeKind.Utc: + return XmlDateTimeSerializationMode.Utc; + default: + throw MiscellaneousUtils.CreateArgumentOutOfRangeException("kind", kind, "Unexpected DateTimeKind value."); + } + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DictionaryWrapper.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DictionaryWrapper.cs new file mode 100644 index 0000000..9d31cf6 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DictionaryWrapper.cs @@ -0,0 +1,400 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Collections; +using System.Threading; + +namespace Newtonsoft.Json.Utilities +{ + internal interface IWrappedDictionary : IDictionary + { + object UnderlyingDictionary { get; } + } + + internal class DictionaryWrapper : IDictionary, IWrappedDictionary + { + private readonly IDictionary _dictionary; + private readonly IDictionary _genericDictionary; + private object _syncRoot; + + public DictionaryWrapper(IDictionary dictionary) + { + ValidationUtils.ArgumentNotNull(dictionary, "dictionary"); + + _dictionary = dictionary; + } + + public DictionaryWrapper(IDictionary dictionary) + { + ValidationUtils.ArgumentNotNull(dictionary, "dictionary"); + + _genericDictionary = dictionary; + } + + public void Add(TKey key, TValue value) + { + if (_genericDictionary != null) + _genericDictionary.Add(key, value); + else + _dictionary.Add(key, value); + } + + public bool ContainsKey(TKey key) + { + if (_genericDictionary != null) + return _genericDictionary.ContainsKey(key); + else + return _dictionary.Contains(key); + } + + public ICollection Keys + { + get + { + if (_genericDictionary != null) + return _genericDictionary.Keys; + else + return _dictionary.Keys.Cast().ToList(); + } + } + + public bool Remove(TKey key) + { + if (_genericDictionary != null) + { + return _genericDictionary.Remove(key); + } + else + { + if (_dictionary.Contains(key)) + { + _dictionary.Remove(key); + return true; + } + else + { + return false; + } + } + } + + public bool TryGetValue(TKey key, out TValue value) + { + if (_genericDictionary != null) + { + return _genericDictionary.TryGetValue(key, out value); + } + else + { + if (!_dictionary.Contains(key)) + { + value = default(TValue); + return false; + } + else + { + value = (TValue)_dictionary[key]; + return true; + } + } + } + + public ICollection Values + { + get + { + if (_genericDictionary != null) + return _genericDictionary.Values; + else + return _dictionary.Values.Cast().ToList(); + } + } + + public TValue this[TKey key] + { + get + { + if (_genericDictionary != null) + return _genericDictionary[key]; + else + return (TValue)_dictionary[key]; + } + set + { + if (_genericDictionary != null) + _genericDictionary[key] = value; + else + _dictionary[key] = value; + } + } + + public void Add(KeyValuePair item) + { + if (_genericDictionary != null) + _genericDictionary.Add(item); + else + ((IList)_dictionary).Add(item); + } + + public void Clear() + { + if (_genericDictionary != null) + _genericDictionary.Clear(); + else + _dictionary.Clear(); + } + + public bool Contains(KeyValuePair item) + { + if (_genericDictionary != null) + return _genericDictionary.Contains(item); + else + return ((IList)_dictionary).Contains(item); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + if (_genericDictionary != null) + { + _genericDictionary.CopyTo(array, arrayIndex); + } + else + { + foreach (DictionaryEntry item in _dictionary) + { + array[arrayIndex++] = new KeyValuePair((TKey)item.Key, (TValue)item.Value); + } + } + } + + public int Count + { + get + { + if (_genericDictionary != null) + return _genericDictionary.Count; + else + return _dictionary.Count; + } + } + + public bool IsReadOnly + { + get + { + if (_genericDictionary != null) + return _genericDictionary.IsReadOnly; + else + return _dictionary.IsReadOnly; + } + } + + public bool Remove(KeyValuePair item) + { + if (_genericDictionary != null) + { + return _genericDictionary.Remove(item); + } + else + { + if (_dictionary.Contains(item.Key)) + { + object value = _dictionary[item.Key]; + + if (object.Equals(value, item.Value)) + { + _dictionary.Remove(item.Key); + return true; + } + else + { + return false; + } + } + else + { + return true; + } + } + } + + public IEnumerator> GetEnumerator() + { + if (_genericDictionary != null) + return _genericDictionary.GetEnumerator(); + else + return _dictionary.Cast().Select(de => new KeyValuePair((TKey)de.Key, (TValue)de.Value)).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + void IDictionary.Add(object key, object value) + { + if (_genericDictionary != null) + _genericDictionary.Add((TKey)key, (TValue)value); + else + _dictionary.Add(key, value); + } + + bool IDictionary.Contains(object key) + { + if (_genericDictionary != null) + return _genericDictionary.ContainsKey((TKey)key); + else + return _dictionary.Contains(key); + } + + private struct DictionaryEnumerator : IDictionaryEnumerator + { + private readonly IEnumerator> _e; + + public DictionaryEnumerator(IEnumerator> e) + { + ValidationUtils.ArgumentNotNull(e, "e"); + _e = e; + } + + public DictionaryEntry Entry + { + get { return (DictionaryEntry)Current; } + } + + public object Key + { + get { return Entry.Key; } + } + + public object Value + { + get { return Entry.Value; } + } + + public object Current + { + get { return new DictionaryEntry(_e.Current.Key, _e.Current.Value); } + } + + public bool MoveNext() + { + return _e.MoveNext(); + } + + public void Reset() + { + _e.Reset(); + } + } + + IDictionaryEnumerator IDictionary.GetEnumerator() + { + if (_genericDictionary != null) + return new DictionaryEnumerator(_genericDictionary.GetEnumerator()); + else + return _dictionary.GetEnumerator(); + } + + bool IDictionary.IsFixedSize + { + get + { + if (_genericDictionary != null) + return false; + else + return _dictionary.IsFixedSize; + } + } + + ICollection IDictionary.Keys + { + get + { + if (_genericDictionary != null) + return _genericDictionary.Keys.ToList(); + else + return _dictionary.Keys; + } + } + + public void Remove(object key) + { + if (_genericDictionary != null) + _genericDictionary.Remove((TKey)key); + else + _dictionary.Remove(key); + } + + ICollection IDictionary.Values + { + get + { + if (_genericDictionary != null) + return _genericDictionary.Values.ToList(); + else + return _dictionary.Values; + } + } + + object IDictionary.this[object key] + { + get + { + if (_genericDictionary != null) + return _genericDictionary[(TKey)key]; + else + return _dictionary[key]; + } + set + { + if (_genericDictionary != null) + _genericDictionary[(TKey)key] = (TValue)value; + else + _dictionary[key] = value; + } + } + + void ICollection.CopyTo(Array array, int index) + { + if (_genericDictionary != null) + _genericDictionary.CopyTo((KeyValuePair[])array, index); + else + _dictionary.CopyTo(array, index); + } + + bool ICollection.IsSynchronized + { + get + { + if (_genericDictionary != null) + return false; + else + return _dictionary.IsSynchronized; + } + } + + object ICollection.SyncRoot + { + get + { + if (_syncRoot == null) + Interlocked.CompareExchange(ref _syncRoot, new object(), null); + + return _syncRoot; + } + } + + public object UnderlyingDictionary + { + get + { + if (_genericDictionary != null) + return _genericDictionary; + else + return _dictionary; + } + } + } +} diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicProxy.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicProxy.cs new file mode 100644 index 0000000..fc4e496 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicProxy.cs @@ -0,0 +1,87 @@ +#if !(NET35 || NET20 || WINDOWS_PHONE) +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Text; + +namespace Newtonsoft.Json.Utilities +{ + internal class DynamicProxy + { + public virtual IEnumerable GetDynamicMemberNames(T instance) + { + return new string[0]; + } + + public virtual bool TryBinaryOperation(T instance, BinaryOperationBinder binder, object arg, out object result) + { + result = null; + return false; + } + + public virtual bool TryConvert(T instance, ConvertBinder binder, out object result) + { + result = null; + return false; + } + + public virtual bool TryCreateInstance(T instance, CreateInstanceBinder binder, object[] args, out object result) + { + result = null; + return false; + } + + public virtual bool TryDeleteIndex(T instance, DeleteIndexBinder binder, object[] indexes) + { + return false; + } + + public virtual bool TryDeleteMember(T instance, DeleteMemberBinder binder) + { + return false; + } + + public virtual bool TryGetIndex(T instance, GetIndexBinder binder, object[] indexes, out object result) + { + result = null; + return false; + } + + public virtual bool TryGetMember(T instance, GetMemberBinder binder, out object result) + { + result = null; + return false; + } + + public virtual bool TryInvoke(T instance, InvokeBinder binder, object[] args, out object result) + { + result = null; + return false; + } + + public virtual bool TryInvokeMember(T instance, InvokeMemberBinder binder, object[] args, out object result) + { + result = null; + return false; + } + + public virtual bool TrySetIndex(T instance, SetIndexBinder binder, object[] indexes, object value) + { + return false; + } + + public virtual bool TrySetMember(T instance, SetMemberBinder binder, object value) + { + return false; + } + + public virtual bool TryUnaryOperation(T instance, UnaryOperationBinder binder, out object result) + { + result = null; + return false; + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs new file mode 100644 index 0000000..51b9d4c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicProxyMetaObject.cs @@ -0,0 +1,398 @@ +#if !(NET35 || NET20 || WINDOWS_PHONE) +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; + +namespace Newtonsoft.Json.Utilities +{ + internal sealed class DynamicProxyMetaObject : DynamicMetaObject + { + private readonly DynamicProxy _proxy; + private readonly bool _dontFallbackFirst; + + internal DynamicProxyMetaObject(Expression expression, T value, DynamicProxy proxy, bool dontFallbackFirst) + : base(expression, BindingRestrictions.Empty, value) + { + _proxy = proxy; + _dontFallbackFirst = dontFallbackFirst; + } + + private new T Value { get { return (T)base.Value; } } + + private bool IsOverridden(string method) + { + return _proxy.GetType().GetMember(method, MemberTypes.Method, BindingFlags.Public | BindingFlags.Instance).Cast() + .Any(info => + // check that the method overrides the original on DynamicObjectProxy + info.DeclaringType != typeof(DynamicProxy) && + info.GetBaseDefinition().DeclaringType == typeof(DynamicProxy)); + } + + public override DynamicMetaObject BindGetMember(GetMemberBinder binder) + { + return IsOverridden("TryGetMember") + ? CallMethodWithResult("TryGetMember", binder, NoArgs, e => binder.FallbackGetMember(this, e)) + : base.BindGetMember(binder); + } + + public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) + { + return IsOverridden("TrySetMember") + ? CallMethodReturnLast("TrySetMember", binder, GetArgs(value), e => binder.FallbackSetMember(this, value, e)) + : base.BindSetMember(binder, value); + } + + public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) + { + return IsOverridden("TryDeleteMember") + ? CallMethodNoResult("TryDeleteMember", binder, NoArgs, e => binder.FallbackDeleteMember(this, e)) + : base.BindDeleteMember(binder); + } + + + public override DynamicMetaObject BindConvert(ConvertBinder binder) + { + return IsOverridden("TryConvert") + ? CallMethodWithResult("TryConvert", binder, NoArgs, e => binder.FallbackConvert(this, e)) + : base.BindConvert(binder); + } + + public override DynamicMetaObject BindInvokeMember(InvokeMemberBinder binder, DynamicMetaObject[] args) + { + if (!IsOverridden("TryInvokeMember")) + return base.BindInvokeMember(binder, args); + + // + // Generate a tree like: + // + // { + // object result; + // TryInvokeMember(payload, out result) + // ? result + // : TryGetMember(payload, out result) + // ? FallbackInvoke(result) + // : fallbackResult + // } + // + // Then it calls FallbackInvokeMember with this tree as the + // "error", giving the language the option of using this + // tree or doing .NET binding. + // + Fallback fallback = e => binder.FallbackInvokeMember(this, args, e); + + DynamicMetaObject call = BuildCallMethodWithResult( + "TryInvokeMember", + binder, + GetArgArray(args), + BuildCallMethodWithResult( + "TryGetMember", + new GetBinderAdapter(binder), + NoArgs, + fallback(null), + e => binder.FallbackInvoke(e, args, null) + ), + null + ); + + return _dontFallbackFirst ? call : fallback(call); + } + + + public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder, DynamicMetaObject[] args) + { + return IsOverridden("TryCreateInstance") + ? CallMethodWithResult("TryCreateInstance", binder, GetArgArray(args), e => binder.FallbackCreateInstance(this, args, e)) + : base.BindCreateInstance(binder, args); + } + + public override DynamicMetaObject BindInvoke(InvokeBinder binder, DynamicMetaObject[] args) + { + return IsOverridden("TryInvoke") + ? CallMethodWithResult("TryInvoke", binder, GetArgArray(args), e => binder.FallbackInvoke(this, args, e)) + : base.BindInvoke(binder, args); + } + + public override DynamicMetaObject BindBinaryOperation(BinaryOperationBinder binder, DynamicMetaObject arg) + { + return IsOverridden("TryBinaryOperation") + ? CallMethodWithResult("TryBinaryOperation", binder, GetArgs(arg), e => binder.FallbackBinaryOperation(this, arg, e)) + : base.BindBinaryOperation(binder, arg); + } + + public override DynamicMetaObject BindUnaryOperation(UnaryOperationBinder binder) + { + return IsOverridden("TryUnaryOperation") + ? CallMethodWithResult("TryUnaryOperation", binder, NoArgs, e => binder.FallbackUnaryOperation(this, e)) + : base.BindUnaryOperation(binder); + } + + public override DynamicMetaObject BindGetIndex(GetIndexBinder binder, DynamicMetaObject[] indexes) + { + return IsOverridden("TryGetIndex") + ? CallMethodWithResult("TryGetIndex", binder, GetArgArray(indexes), e => binder.FallbackGetIndex(this, indexes, e)) + : base.BindGetIndex(binder, indexes); + } + + public override DynamicMetaObject BindSetIndex(SetIndexBinder binder, DynamicMetaObject[] indexes, DynamicMetaObject value) + { + return IsOverridden("TrySetIndex") + ? CallMethodReturnLast("TrySetIndex", binder, GetArgArray(indexes, value), e => binder.FallbackSetIndex(this, indexes, value, e)) + : base.BindSetIndex(binder, indexes, value); + } + + public override DynamicMetaObject BindDeleteIndex(DeleteIndexBinder binder, DynamicMetaObject[] indexes) + { + return IsOverridden("TryDeleteIndex") + ? CallMethodNoResult("TryDeleteIndex", binder, GetArgArray(indexes), e => binder.FallbackDeleteIndex(this, indexes, e)) + : base.BindDeleteIndex(binder, indexes); + } + + private delegate DynamicMetaObject Fallback(DynamicMetaObject errorSuggestion); + + private readonly static Expression[] NoArgs = new Expression[0]; + + private static Expression[] GetArgs(params DynamicMetaObject[] args) + { + return args.Select(arg => Expression.Convert(arg.Expression, typeof(object))).ToArray(); + } + + private static Expression[] GetArgArray(DynamicMetaObject[] args) + { + return new[] { Expression.NewArrayInit(typeof(object), GetArgs(args)) }; + } + + private static Expression[] GetArgArray(DynamicMetaObject[] args, DynamicMetaObject value) + { + return new Expression[] + { + Expression.NewArrayInit(typeof(object), GetArgs(args)), + Expression.Convert(value.Expression, typeof(object)) + }; + } + + private static ConstantExpression Constant(DynamicMetaObjectBinder binder) + { + Type t = binder.GetType(); + while (!t.IsVisible) + t = t.BaseType; + return Expression.Constant(binder, t); + } + + /// + /// Helper method for generating a MetaObject which calls a + /// specific method on Dynamic that returns a result + /// + private DynamicMetaObject CallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback, Fallback fallbackInvoke = null) + { + // + // First, call fallback to do default binding + // This produces either an error or a call to a .NET member + // + DynamicMetaObject fallbackResult = fallback(null); + + DynamicMetaObject callDynamic = BuildCallMethodWithResult(methodName, binder, args, fallbackResult, fallbackInvoke); + + // + // Now, call fallback again using our new MO as the error + // When we do this, one of two things can happen: + // 1. Binding will succeed, and it will ignore our call to + // the dynamic method, OR + // 2. Binding will fail, and it will use the MO we created + // above. + // + + return _dontFallbackFirst ? callDynamic : fallback(callDynamic); + } + + private DynamicMetaObject BuildCallMethodWithResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, DynamicMetaObject fallbackResult, Fallback fallbackInvoke) + { + // + // Build a new expression like: + // { + // object result; + // TryGetMember(payload, out result) ? fallbackInvoke(result) : fallbackResult + // } + // + ParameterExpression result = Expression.Parameter(typeof(object), null); + + IList callArgs = new List(); + callArgs.Add(Expression.Convert(Expression, typeof(T))); + callArgs.Add(Constant(binder)); + callArgs.AddRange(args); + callArgs.Add(result); + + DynamicMetaObject resultMO = new DynamicMetaObject(result, BindingRestrictions.Empty); + + // Need to add a conversion if calling TryConvert + if (binder.ReturnType != typeof (object)) + { + UnaryExpression convert = Expression.Convert(resultMO.Expression, binder.ReturnType); + // will always be a cast or unbox + + resultMO = new DynamicMetaObject(convert, resultMO.Restrictions); + } + + if (fallbackInvoke != null) + resultMO = fallbackInvoke(resultMO); + + DynamicMetaObject callDynamic = new DynamicMetaObject( + Expression.Block( + new[] {result}, + Expression.Condition( + Expression.Call( + Expression.Constant(_proxy), + typeof(DynamicProxy).GetMethod(methodName), + callArgs + ), + resultMO.Expression, + fallbackResult.Expression, + binder.ReturnType + ) + ), + GetRestrictions().Merge(resultMO.Restrictions).Merge(fallbackResult.Restrictions) + ); + + return callDynamic; + } + + /// + /// Helper method for generating a MetaObject which calls a + /// specific method on Dynamic, but uses one of the arguments for + /// the result. + /// + private DynamicMetaObject CallMethodReturnLast(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback) + { + // + // First, call fallback to do default binding + // This produces either an error or a call to a .NET member + // + DynamicMetaObject fallbackResult = fallback(null); + + // + // Build a new expression like: + // { + // object result; + // TrySetMember(payload, result = value) ? result : fallbackResult + // } + // + ParameterExpression result = Expression.Parameter(typeof(object), null); + + IList callArgs = new List(); + callArgs.Add(Expression.Convert(Expression, typeof (T))); + callArgs.Add(Constant(binder)); + callArgs.AddRange(args); + callArgs[args.Length + 1] = Expression.Assign(result, callArgs[args.Length + 1]); + + DynamicMetaObject callDynamic = new DynamicMetaObject( + Expression.Block( + new[] { result }, + Expression.Condition( + Expression.Call( + Expression.Constant(_proxy), + typeof(DynamicProxy).GetMethod(methodName), + callArgs + ), + result, + fallbackResult.Expression, + typeof(object) + ) + ), + GetRestrictions().Merge(fallbackResult.Restrictions) + ); + + // + // Now, call fallback again using our new MO as the error + // When we do this, one of two things can happen: + // 1. Binding will succeed, and it will ignore our call to + // the dynamic method, OR + // 2. Binding will fail, and it will use the MO we created + // above. + // + return _dontFallbackFirst ? callDynamic : fallback(callDynamic); + } + + /// + /// Helper method for generating a MetaObject which calls a + /// specific method on Dynamic, but uses one of the arguments for + /// the result. + /// + private DynamicMetaObject CallMethodNoResult(string methodName, DynamicMetaObjectBinder binder, Expression[] args, Fallback fallback) + { + // + // First, call fallback to do default binding + // This produces either an error or a call to a .NET member + // + DynamicMetaObject fallbackResult = fallback(null); + + IList callArgs = new List(); + callArgs.Add(Expression.Convert(Expression, typeof(T))); + callArgs.Add(Constant(binder)); + callArgs.AddRange(args); + + // + // Build a new expression like: + // if (TryDeleteMember(payload)) { } else { fallbackResult } + // + DynamicMetaObject callDynamic = new DynamicMetaObject( + Expression.Condition( + Expression.Call( + Expression.Constant(_proxy), + typeof(DynamicProxy).GetMethod(methodName), + callArgs + ), + Expression.Empty(), + fallbackResult.Expression, + typeof (void) + ), + GetRestrictions().Merge(fallbackResult.Restrictions) + ); + + // + // Now, call fallback again using our new MO as the error + // When we do this, one of two things can happen: + // 1. Binding will succeed, and it will ignore our call to + // the dynamic method, OR + // 2. Binding will fail, and it will use the MO we created + // above. + // + return _dontFallbackFirst ? callDynamic : fallback(callDynamic); + } + + /// + /// Returns a Restrictions object which includes our current restrictions merged + /// with a restriction limiting our type + /// + private BindingRestrictions GetRestrictions() + { + return (Value == null && HasValue) + ? BindingRestrictions.GetInstanceRestriction(Expression, null) + : BindingRestrictions.GetTypeRestriction(Expression, LimitType); + } + + public override IEnumerable GetDynamicMemberNames() + { + return _proxy.GetDynamicMemberNames(Value); + } + + // It is okay to throw NotSupported from this binder. This object + // is only used by DynamicObject.GetMember--it is not expected to + // (and cannot) implement binding semantics. It is just so the DO + // can use the Name and IgnoreCase properties. + private sealed class GetBinderAdapter : GetMemberBinder + { + internal GetBinderAdapter(InvokeMemberBinder binder) : + base(binder.Name, binder.IgnoreCase) + { + } + + public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) + { + throw new NotSupportedException(); + } + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs new file mode 100644 index 0000000..7dc47e5 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicReflectionDelegateFactory.cs @@ -0,0 +1,200 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC && !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal class DynamicReflectionDelegateFactory : ReflectionDelegateFactory + { + public static DynamicReflectionDelegateFactory Instance = new DynamicReflectionDelegateFactory(); + + private static DynamicMethod CreateDynamicMethod(string name, Type returnType, Type[] parameterTypes, Type owner) + { + DynamicMethod dynamicMethod = !owner.IsInterface + ? new DynamicMethod(name, returnType, parameterTypes, owner, true) + : new DynamicMethod(name, returnType, parameterTypes, owner.Module, true); + + return dynamicMethod; + } + + public override MethodCall CreateMethodCall(MethodBase method) + { + DynamicMethod dynamicMethod = CreateDynamicMethod(method.ToString(), typeof(object), new[] { typeof(object), typeof(object[]) }, method.DeclaringType); + ILGenerator generator = dynamicMethod.GetILGenerator(); + + ParameterInfo[] args = method.GetParameters(); + + Label argsOk = generator.DefineLabel(); + + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldlen); + generator.Emit(OpCodes.Ldc_I4, args.Length); + generator.Emit(OpCodes.Beq, argsOk); + + generator.Emit(OpCodes.Newobj, typeof(TargetParameterCountException).GetConstructor(Type.EmptyTypes)); + generator.Emit(OpCodes.Throw); + + generator.MarkLabel(argsOk); + + if (!method.IsConstructor && !method.IsStatic) + generator.PushInstance(method.DeclaringType); + + for (int i = 0; i < args.Length; i++) + { + generator.Emit(OpCodes.Ldarg_1); + generator.Emit(OpCodes.Ldc_I4, i); + generator.Emit(OpCodes.Ldelem_Ref); + + generator.UnboxIfNeeded(args[i].ParameterType); + } + + if (method.IsConstructor) + generator.Emit(OpCodes.Newobj, (ConstructorInfo)method); + else if (method.IsFinal || !method.IsVirtual) + generator.CallMethod((MethodInfo)method); + + Type returnType = method.IsConstructor + ? method.DeclaringType + : ((MethodInfo)method).ReturnType; + + if (returnType != typeof(void)) + generator.BoxIfNeeded(returnType); + else + generator.Emit(OpCodes.Ldnull); + + generator.Return(); + + return (MethodCall)dynamicMethod.CreateDelegate(typeof(MethodCall)); + } + + public override Func CreateDefaultConstructor(Type type) + { + DynamicMethod dynamicMethod = CreateDynamicMethod("Create" + type.FullName, typeof(object), Type.EmptyTypes, type); + dynamicMethod.InitLocals = true; + ILGenerator generator = dynamicMethod.GetILGenerator(); + + if (type.IsValueType) + { + generator.DeclareLocal(type); + generator.Emit(OpCodes.Ldloc_0); + generator.Emit(OpCodes.Box, type); + } + else + { + ConstructorInfo constructorInfo = + type.GetConstructor(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, + Type.EmptyTypes, null); + + if (constructorInfo == null) + throw new Exception("Could not get constructor for {0}.".FormatWith(CultureInfo.InvariantCulture, type)); + + generator.Emit(OpCodes.Newobj, constructorInfo); + } + + generator.Return(); + + return (Func)dynamicMethod.CreateDelegate(typeof(Func)); + } + + public override Func CreateGet(PropertyInfo propertyInfo) + { + MethodInfo getMethod = propertyInfo.GetGetMethod(true); + if (getMethod == null) + throw new Exception("Property '{0}' does not have a getter.".FormatWith(CultureInfo.InvariantCulture, + propertyInfo.Name)); + + DynamicMethod dynamicMethod = CreateDynamicMethod("Get" + propertyInfo.Name, typeof(T), new[] { typeof(object) }, propertyInfo.DeclaringType); + + ILGenerator generator = dynamicMethod.GetILGenerator(); + + if (!getMethod.IsStatic) + generator.PushInstance(propertyInfo.DeclaringType); + + generator.CallMethod(getMethod); + generator.BoxIfNeeded(propertyInfo.PropertyType); + generator.Return(); + + return (Func)dynamicMethod.CreateDelegate(typeof(Func)); + } + + public override Func CreateGet(FieldInfo fieldInfo) + { + DynamicMethod dynamicMethod = CreateDynamicMethod("Get" + fieldInfo.Name, typeof(T), new[] { typeof(object) }, fieldInfo.DeclaringType); + + ILGenerator generator = dynamicMethod.GetILGenerator(); + + if (!fieldInfo.IsStatic) + generator.PushInstance(fieldInfo.DeclaringType); + + generator.Emit(OpCodes.Ldfld, fieldInfo); + generator.BoxIfNeeded(fieldInfo.FieldType); + generator.Return(); + + return (Func)dynamicMethod.CreateDelegate(typeof(Func)); + } + + public override Action CreateSet(FieldInfo fieldInfo) + { + DynamicMethod dynamicMethod = CreateDynamicMethod("Set" + fieldInfo.Name, null, new[] { typeof(object), typeof(object) }, fieldInfo.DeclaringType); + ILGenerator generator = dynamicMethod.GetILGenerator(); + + if (!fieldInfo.IsStatic) + generator.PushInstance(fieldInfo.DeclaringType); + + generator.Emit(OpCodes.Ldarg_1); + generator.UnboxIfNeeded(fieldInfo.FieldType); + generator.Emit(OpCodes.Stfld, fieldInfo); + generator.Return(); + + return (Action)dynamicMethod.CreateDelegate(typeof(Action)); + } + + public override Action CreateSet(PropertyInfo propertyInfo) + { + MethodInfo setMethod = propertyInfo.GetSetMethod(true); + DynamicMethod dynamicMethod = CreateDynamicMethod("Set" + propertyInfo.Name, null, new[] { typeof(object), typeof(object) }, propertyInfo.DeclaringType); + ILGenerator generator = dynamicMethod.GetILGenerator(); + + if (!setMethod.IsStatic) + generator.PushInstance(propertyInfo.DeclaringType); + + generator.Emit(OpCodes.Ldarg_1); + generator.UnboxIfNeeded(propertyInfo.PropertyType); + generator.CallMethod(setMethod); + generator.Return(); + + return (Action)dynamicMethod.CreateDelegate(typeof(Action)); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs new file mode 100644 index 0000000..b028d61 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicUtils.cs @@ -0,0 +1,200 @@ +#if !(NET35 || NET20 || WINDOWS_PHONE) +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; +using System.Globalization; +using Newtonsoft.Json.Serialization; + +namespace Newtonsoft.Json.Utilities +{ + internal static class DynamicUtils + { + internal static class BinderWrapper + { +#if !SILVERLIGHT + public const string CSharpAssemblyName = "Microsoft.CSharp, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; +#else + public const string CSharpAssemblyName = "Microsoft.CSharp, Version=2.0.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"; +#endif + + private const string BinderTypeName = "Microsoft.CSharp.RuntimeBinder.Binder, " + CSharpAssemblyName; + private const string CSharpArgumentInfoTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo, " + CSharpAssemblyName; + private const string CSharpArgumentInfoFlagsTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, " + CSharpAssemblyName; + private const string CSharpBinderFlagsTypeName = "Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, " + CSharpAssemblyName; + + private static object _getCSharpArgumentInfoArray; + private static object _setCSharpArgumentInfoArray; + private static MethodCall _getMemberCall; + private static MethodCall _setMemberCall; + private static bool _init; + + private static void Init() + { + if (!_init) + { + Type binderType = Type.GetType(BinderTypeName, false); + if (binderType == null) + throw new Exception("Could not resolve type '{0}'. You may need to add a reference to Microsoft.CSharp.dll to work with dynamic types.".FormatWith(CultureInfo.InvariantCulture, BinderTypeName)); + + // None + _getCSharpArgumentInfoArray = CreateSharpArgumentInfoArray(0); + // None, Constant | UseCompileTimeType + _setCSharpArgumentInfoArray = CreateSharpArgumentInfoArray(0, 3); + CreateMemberCalls(); + + _init = true; + } + } + + private static object CreateSharpArgumentInfoArray(params int[] values) + { + Type csharpArgumentInfoType = Type.GetType(CSharpArgumentInfoTypeName); + Type csharpArgumentInfoFlags = Type.GetType(CSharpArgumentInfoFlagsTypeName); + + Array a = Array.CreateInstance(csharpArgumentInfoType, values.Length); + + for (int i = 0; i < values.Length; i++) + { + MethodInfo createArgumentInfoMethod = csharpArgumentInfoType.GetMethod("Create", BindingFlags.Public | BindingFlags.Static, null, new[] { csharpArgumentInfoFlags, typeof(string) }, null); + object arg = createArgumentInfoMethod.Invoke(null, new object[] { 0, null }); + a.SetValue(arg, i); + } + + return a; + } + + private static void CreateMemberCalls() + { + Type csharpArgumentInfoType = Type.GetType(CSharpArgumentInfoTypeName); + Type csharpBinderFlagsType = Type.GetType(CSharpBinderFlagsTypeName); + Type binderType = Type.GetType(BinderTypeName); + + Type csharpArgumentInfoTypeEnumerableType = typeof(IEnumerable<>).MakeGenericType(csharpArgumentInfoType); + + MethodInfo getMemberMethod = binderType.GetMethod("GetMember", BindingFlags.Public | BindingFlags.Static, null, new[] { csharpBinderFlagsType, typeof(string), typeof(Type), csharpArgumentInfoTypeEnumerableType }, null); + _getMemberCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(getMemberMethod); + + MethodInfo setMemberMethod = binderType.GetMethod("SetMember", BindingFlags.Public | BindingFlags.Static, null, new[] { csharpBinderFlagsType, typeof(string), typeof(Type), csharpArgumentInfoTypeEnumerableType }, null); + _setMemberCall = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall(setMemberMethod); + } + + public static CallSiteBinder GetMember(string name, Type context) + { + Init(); + return (CallSiteBinder)_getMemberCall(null, 0, name, context, _getCSharpArgumentInfoArray); + } + + public static CallSiteBinder SetMember(string name, Type context) + { + Init(); + return (CallSiteBinder)_setMemberCall(null, 0, name, context, _setCSharpArgumentInfoArray); + } + } + + public static bool TryGetMember(this IDynamicMetaObjectProvider dynamicProvider, string name, out object value) + { + ValidationUtils.ArgumentNotNull(dynamicProvider, "dynamicProvider"); + + GetMemberBinder getMemberBinder = (GetMemberBinder) BinderWrapper.GetMember(name, typeof (DynamicUtils)); + + CallSite> callSite = CallSite>.Create(new NoThrowGetBinderMember(getMemberBinder)); + + object result = callSite.Target(callSite, dynamicProvider); + + if (!ReferenceEquals(result, NoThrowExpressionVisitor.ErrorResult)) + { + value = result; + return true; + } + else + { + value = null; + return false; + } + } + + public static bool TrySetMember(this IDynamicMetaObjectProvider dynamicProvider, string name, object value) + { + ValidationUtils.ArgumentNotNull(dynamicProvider, "dynamicProvider"); + + SetMemberBinder binder = (SetMemberBinder)BinderWrapper.SetMember(name, typeof(DynamicUtils)); + + var setterSite = CallSite>.Create(new NoThrowSetBinderMember(binder)); + + object result = setterSite.Target(setterSite, dynamicProvider, value); + + return !ReferenceEquals(result, NoThrowExpressionVisitor.ErrorResult); + } + + public static IEnumerable GetDynamicMemberNames(this IDynamicMetaObjectProvider dynamicProvider) + { + DynamicMetaObject metaObject = dynamicProvider.GetMetaObject(Expression.Constant(dynamicProvider)); + return metaObject.GetDynamicMemberNames(); + } + + internal class NoThrowGetBinderMember : GetMemberBinder + { + private readonly GetMemberBinder _innerBinder; + + public NoThrowGetBinderMember(GetMemberBinder innerBinder) + : base(innerBinder.Name, innerBinder.IgnoreCase) + { + _innerBinder = innerBinder; + } + + public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion) + { + DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { }); + + NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor(); + Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression); + + DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions); + return finalMetaObject; + } + } + + internal class NoThrowSetBinderMember : SetMemberBinder + { + private readonly SetMemberBinder _innerBinder; + + public NoThrowSetBinderMember(SetMemberBinder innerBinder) + : base(innerBinder.Name, innerBinder.IgnoreCase) + { + _innerBinder = innerBinder; + } + + public override DynamicMetaObject FallbackSetMember(DynamicMetaObject target, DynamicMetaObject value, DynamicMetaObject errorSuggestion) + { + DynamicMetaObject retMetaObject = _innerBinder.Bind(target, new DynamicMetaObject[] { value }); + + NoThrowExpressionVisitor noThrowVisitor = new NoThrowExpressionVisitor(); + Expression resultExpression = noThrowVisitor.Visit(retMetaObject.Expression); + + DynamicMetaObject finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions); + return finalMetaObject; + } + } + + + internal class NoThrowExpressionVisitor : ExpressionVisitor + { + internal static readonly object ErrorResult = new object(); + + protected override Expression VisitConditional(ConditionalExpression node) + { + // if the result of a test is to throw an error, rewrite to result an error result value + if (node.IfFalse.NodeType == ExpressionType.Throw) + return Expression.Condition(node.Test, node.IfTrue, Expression.Constant(ErrorResult)); + + return base.VisitConditional(node); + } + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicWrapper.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicWrapper.cs new file mode 100644 index 0000000..d056fd2 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/DynamicWrapper.cs @@ -0,0 +1,294 @@ +#if !SILVERLIGHT && !PocketPC +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Reflection.Emit; +using System.Resources; +using System.Text; +using System.Threading; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal class DynamicWrapperBase + { + internal protected object UnderlyingObject; + } + + internal static class DynamicWrapper + { + private static readonly object _lock = new object(); + private static readonly WrapperDictionary _wrapperDictionary = new WrapperDictionary(); + + private static ModuleBuilder _moduleBuilder; + + private static ModuleBuilder ModuleBuilder + { + get + { + Init(); + return _moduleBuilder; + } + } + + private static void Init() + { + if (_moduleBuilder == null) + { + lock (_lock) + { + if (_moduleBuilder == null) + { + AssemblyName assemblyName = new AssemblyName("Newtonsoft.Json.Dynamic"); + assemblyName.KeyPair = new StrongNameKeyPair(GetStrongKey()); + + AssemblyBuilder assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run); + _moduleBuilder = assembly.DefineDynamicModule("Newtonsoft.Json.DynamicModule", false); + } + } + } + } + + private static byte[] GetStrongKey() + { + string name; +#if NET35 + name = "Newtonsoft.Json.Net35.Dynamic.snk"; +#else + name = "Newtonsoft.Json.Dynamic.snk"; +#endif + + using (Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(name)) + { + if (stream == null) + throw new MissingManifestResourceException("Should have a Newtonsoft.Json.Dynamic.snk as an embedded resource."); + + int length = (int)stream.Length; + byte[] buffer = new byte[length]; + stream.Read(buffer, 0, length); + + return buffer; + } + } + + public static Type GetWrapper(Type interfaceType, Type realObjectType) + { + Type wrapperType = _wrapperDictionary.GetType(interfaceType, realObjectType); + + if (wrapperType == null) + { + lock (_lock) + { + wrapperType = _wrapperDictionary.GetType(interfaceType, realObjectType); + + if (wrapperType == null) + { + wrapperType = GenerateWrapperType(interfaceType, realObjectType); + _wrapperDictionary.SetType(interfaceType, realObjectType, wrapperType); + } + } + } + + return wrapperType; + } + + public static object GetUnderlyingObject(object wrapper) + { + DynamicWrapperBase wrapperBase = wrapper as DynamicWrapperBase; + if (wrapperBase == null) + throw new ArgumentException("Object is not a wrapper.", "wrapper"); + + return wrapperBase.UnderlyingObject; + } + + private static Type GenerateWrapperType(Type interfaceType, Type underlyingType) + { + TypeBuilder wrapperBuilder = ModuleBuilder.DefineType( + "{0}_{1}_Wrapper".FormatWith(CultureInfo.InvariantCulture, interfaceType.Name, underlyingType.Name), + TypeAttributes.NotPublic | TypeAttributes.Sealed, + typeof(DynamicWrapperBase), + new[] { interfaceType }); + + WrapperMethodBuilder wrapperMethod = new WrapperMethodBuilder(underlyingType, wrapperBuilder); + + foreach (MethodInfo method in interfaceType.AllMethods()) + { + wrapperMethod.Generate(method); + } + + return wrapperBuilder.CreateType(); + } + + public static T CreateWrapper(object realObject) where T : class + { + var dynamicType = GetWrapper(typeof(T), realObject.GetType()); + var dynamicWrapper = (DynamicWrapperBase)Activator.CreateInstance(dynamicType); + + dynamicWrapper.UnderlyingObject = realObject; + + return dynamicWrapper as T; + } + } + + internal class WrapperMethodBuilder + { + private readonly Type _realObjectType; + private readonly TypeBuilder _wrapperBuilder; + + public WrapperMethodBuilder(Type realObjectType, TypeBuilder proxyBuilder) + { + _realObjectType = realObjectType; + _wrapperBuilder = proxyBuilder; + } + + public void Generate(MethodInfo newMethod) + { + if (newMethod.IsGenericMethod) + newMethod = newMethod.GetGenericMethodDefinition(); + + FieldInfo srcField = typeof(DynamicWrapperBase).GetField("UnderlyingObject", BindingFlags.Instance | BindingFlags.NonPublic); + + var parameters = newMethod.GetParameters(); + var parameterTypes = parameters.Select(parameter => parameter.ParameterType).ToArray(); + + MethodBuilder methodBuilder = _wrapperBuilder.DefineMethod( + newMethod.Name, + MethodAttributes.Public | MethodAttributes.Virtual, + newMethod.ReturnType, + parameterTypes); + + if (newMethod.IsGenericMethod) + { + methodBuilder.DefineGenericParameters( + newMethod.GetGenericArguments().Select(arg => arg.Name).ToArray()); + } + + ILGenerator ilGenerator = methodBuilder.GetILGenerator(); + + LoadUnderlyingObject(ilGenerator, srcField); + PushParameters(parameters, ilGenerator); + ExecuteMethod(newMethod, parameterTypes, ilGenerator); + Return(ilGenerator); + } + + private static void Return(ILGenerator ilGenerator) + { + ilGenerator.Emit(OpCodes.Ret); + } + + private void ExecuteMethod(MethodBase newMethod, Type[] parameterTypes, ILGenerator ilGenerator) + { + MethodInfo srcMethod = GetMethod(newMethod, parameterTypes); + + if (srcMethod == null) + throw new MissingMethodException("Unable to find method " + newMethod.Name + " on " + _realObjectType.FullName); + + ilGenerator.Emit(OpCodes.Call, srcMethod); + } + + private MethodInfo GetMethod(MethodBase realMethod, Type[] parameterTypes) + { + if (realMethod.IsGenericMethod) + return _realObjectType.GetGenericMethod(realMethod.Name, parameterTypes); + + return _realObjectType.GetMethod(realMethod.Name, parameterTypes); + } + + private static void PushParameters(ICollection parameters, ILGenerator ilGenerator) + { + for (int i = 1; i < parameters.Count + 1; i++) + ilGenerator.Emit(OpCodes.Ldarg, i); + } + + private static void LoadUnderlyingObject(ILGenerator ilGenerator, FieldInfo srcField) + { + ilGenerator.Emit(OpCodes.Ldarg_0); + ilGenerator.Emit(OpCodes.Ldfld, srcField); + } + } + + internal class WrapperDictionary + { + private readonly Dictionary _wrapperTypes = new Dictionary(); + + private static string GenerateKey(Type interfaceType, Type realObjectType) + { + return interfaceType.Name + "_" + realObjectType.Name; + } + + public Type GetType(Type interfaceType, Type realObjectType) + { + string key = GenerateKey(interfaceType, realObjectType); + + if (_wrapperTypes.ContainsKey(key)) + return _wrapperTypes[key]; + + return null; + } + + public void SetType(Type interfaceType, Type realObjectType, Type wrapperType) + { + string key = GenerateKey(interfaceType, realObjectType); + + if (_wrapperTypes.ContainsKey(key)) + _wrapperTypes[key] = wrapperType; + else + _wrapperTypes.Add(key, wrapperType); + } + } + + internal static class TypeExtensions + { + public static MethodInfo GetGenericMethod(this Type type, string name, params Type[] parameterTypes) + { + var methods = type.GetMethods().Where(method => method.Name == name); + + foreach (var method in methods) + { + if (method.HasParameters(parameterTypes)) + return method; + } + + return null; + } + + public static bool HasParameters(this MethodInfo method, params Type[] parameterTypes) + { + var methodParameters = method.GetParameters().Select(parameter => parameter.ParameterType).ToArray(); + + if (methodParameters.Length != parameterTypes.Length) + return false; + + for (int i = 0; i < methodParameters.Length; i++) + if (methodParameters[i].ToString() != parameterTypes[i].ToString()) + return false; + + return true; + } + + public static IEnumerable AllInterfaces(this Type target) + { + foreach (var IF in target.GetInterfaces()) + { + yield return IF; + foreach (var childIF in IF.AllInterfaces()) + { + yield return childIF; + } + } + } + + public static IEnumerable AllMethods(this Type target) + { + var allTypes = target.AllInterfaces().ToList(); + allTypes.Add(target); + + return from type in allTypes + from method in type.GetMethods() + select method; + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/EnumUtils.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/EnumUtils.cs new file mode 100644 index 0000000..8c2beb4 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/EnumUtils.cs @@ -0,0 +1,236 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Reflection; + +namespace Newtonsoft.Json.Utilities +{ + internal static class EnumUtils + { + /// + /// Parses the specified enum member name, returning it's value. + /// + /// Name of the enum member. + /// + public static T Parse(string enumMemberName) where T : struct + { + return Parse(enumMemberName, false); + } + + /// + /// Parses the specified enum member name, returning it's value. + /// + /// Name of the enum member. + /// If set to true ignore case. + /// + public static T Parse(string enumMemberName, bool ignoreCase) where T : struct + { + ValidationUtils.ArgumentTypeIsEnum(typeof(T), "T"); + + return (T)Enum.Parse(typeof(T), enumMemberName, ignoreCase); + } + + public static bool TryParse(string enumMemberName, bool ignoreCase, out T value) where T : struct + { + ValidationUtils.ArgumentTypeIsEnum(typeof(T), "T"); + + return MiscellaneousUtils.TryAction(() => Parse(enumMemberName, ignoreCase), out value); + } + + public static IList GetFlagsValues(T value) where T : struct + { + Type enumType = typeof(T); + + if (!enumType.IsDefined(typeof(FlagsAttribute), false)) + throw new Exception("Enum type {0} is not a set of flags.".FormatWith(CultureInfo.InvariantCulture, enumType)); + + Type underlyingType = Enum.GetUnderlyingType(value.GetType()); + + ulong num = Convert.ToUInt64(value, CultureInfo.InvariantCulture); + EnumValues enumNameValues = GetNamesAndValues(); + IList selectedFlagsValues = new List(); + + foreach (EnumValue enumNameValue in enumNameValues) + { + if ((num & enumNameValue.Value) == enumNameValue.Value && enumNameValue.Value != 0) + selectedFlagsValues.Add((T)Convert.ChangeType(enumNameValue.Value, underlyingType, CultureInfo.CurrentCulture)); + } + + if (selectedFlagsValues.Count == 0 && enumNameValues.SingleOrDefault(v => v.Value == 0) != null) + selectedFlagsValues.Add(default(T)); + + return selectedFlagsValues; + } + + /// + /// Gets a dictionary of the names and values of an Enum type. + /// + /// + public static EnumValues GetNamesAndValues() where T : struct + { + return GetNamesAndValues(typeof(T)); + } + + /// + /// Gets a dictionary of the names and values of an Enum type. + /// + /// + public static EnumValues GetNamesAndValues() + where TEnum : struct + where TUnderlyingType : struct + { + return GetNamesAndValues(typeof(TEnum)); + } + + /// + /// Gets a dictionary of the names and values of an Enum type. + /// + /// The enum type to get names and values for. + /// + public static EnumValues GetNamesAndValues(Type enumType) where TUnderlyingType : struct + { + if (enumType == null) + throw new ArgumentNullException("enumType"); + + ValidationUtils.ArgumentTypeIsEnum(enumType, "enumType"); + + IList enumValues = GetValues(enumType); + IList enumNames = GetNames(enumType); + + EnumValues nameValues = new EnumValues(); + + for (int i = 0; i < enumValues.Count; i++) + { + try + { + nameValues.Add(new EnumValue(enumNames[i], (TUnderlyingType)Convert.ChangeType(enumValues[i], typeof(TUnderlyingType), CultureInfo.CurrentCulture))); + } + catch (OverflowException e) + { + throw new Exception( + string.Format(CultureInfo.InvariantCulture, "Value from enum with the underlying type of {0} cannot be added to dictionary with a value type of {1}. Value was too large: {2}", + Enum.GetUnderlyingType(enumType), typeof(TUnderlyingType), Convert.ToUInt64(enumValues[i], CultureInfo.InvariantCulture)), e); + } + } + + return nameValues; + } + + public static IList GetValues() + { + return GetValues(typeof(T)).Cast().ToList(); + } + + public static IList GetValues(Type enumType) + { + if (!enumType.IsEnum) + throw new ArgumentException("Type '" + enumType.Name + "' is not an enum."); + + List values = new List(); + + var fields = from field in enumType.GetFields() + where field.IsLiteral + select field; + + foreach (FieldInfo field in fields) + { + object value = field.GetValue(enumType); + values.Add(value); + } + + return values; + } + + public static IList GetNames() + { + return GetNames(typeof(T)); + } + + public static IList GetNames(Type enumType) + { + if (!enumType.IsEnum) + throw new ArgumentException("Type '" + enumType.Name + "' is not an enum."); + + List values = new List(); + + var fields = from field in enumType.GetFields() + where field.IsLiteral + select field; + + foreach (FieldInfo field in fields) + { + values.Add(field.Name); + } + + return values; + } + + + /// + /// Gets the maximum valid value of an Enum type. Flags enums are ORed. + /// + /// The type of the returned value. Must be assignable from the enum's underlying value type. + /// The enum type to get the maximum value for. + /// + public static TEnumType GetMaximumValue(Type enumType) where TEnumType : IConvertible, IComparable + { + if (enumType == null) + throw new ArgumentNullException("enumType"); + + Type enumUnderlyingType = Enum.GetUnderlyingType(enumType); + + if (!typeof(TEnumType).IsAssignableFrom(enumUnderlyingType)) + throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "TEnumType is not assignable from the enum's underlying type of {0}.", enumUnderlyingType.Name)); + + ulong maximumValue = 0; + IList enumValues = GetValues(enumType); + + if (enumType.IsDefined(typeof(FlagsAttribute), false)) + { + foreach (TEnumType value in enumValues) + { + maximumValue = maximumValue | value.ToUInt64(CultureInfo.InvariantCulture); + } + } + else + { + foreach (TEnumType value in enumValues) + { + ulong tempValue = value.ToUInt64(CultureInfo.InvariantCulture); + + // maximumValue is smaller than the enum value + if (maximumValue.CompareTo(tempValue) == -1) + maximumValue = tempValue; + } + } + + return (TEnumType)Convert.ChangeType(maximumValue, typeof(TEnumType), CultureInfo.InvariantCulture); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/EnumValue.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/EnumValue.cs new file mode 100644 index 0000000..7f93f92 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/EnumValue.cs @@ -0,0 +1,53 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Utilities +{ + internal class EnumValue where T : struct + { + private string _name; + private T _value; + + public string Name + { + get { return _name; } + } + public T Value + { + get { return _value; } + } + + public EnumValue(string name, T value) + { + _name = name; + _value = value; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/EnumValues.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/EnumValues.cs new file mode 100644 index 0000000..6d70965 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/EnumValues.cs @@ -0,0 +1,41 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Text; + +namespace Newtonsoft.Json.Utilities +{ + internal class EnumValues : KeyedCollection> where T : struct + { + protected override string GetKeyForItem(EnumValue item) + { + return item.Name; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ILGeneratorExtensions.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ILGeneratorExtensions.cs new file mode 100644 index 0000000..8ffc8c8 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ILGeneratorExtensions.cs @@ -0,0 +1,77 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +#if !PocketPC && !SILVERLIGHT +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection.Emit; +using System.Text; +using System.Reflection; + +namespace Newtonsoft.Json.Utilities +{ + internal static class ILGeneratorExtensions + { + public static void PushInstance(this ILGenerator generator, Type type) + { + generator.Emit(OpCodes.Ldarg_0); + if (type.IsValueType) + generator.Emit(OpCodes.Unbox, type); + else + generator.Emit(OpCodes.Castclass, type); + } + + public static void BoxIfNeeded(this ILGenerator generator, Type type) + { + if (type.IsValueType) + generator.Emit(OpCodes.Box, type); + else + generator.Emit(OpCodes.Castclass, type); + } + + public static void UnboxIfNeeded(this ILGenerator generator, Type type) + { + if (type.IsValueType) + generator.Emit(OpCodes.Unbox_Any, type); + else + generator.Emit(OpCodes.Castclass, type); + } + + public static void CallMethod(this ILGenerator generator, MethodInfo methodInfo) + { + if (methodInfo.IsFinal || !methodInfo.IsVirtual) + generator.Emit(OpCodes.Call, methodInfo); + else + generator.Emit(OpCodes.Callvirt, methodInfo); + } + + public static void Return(this ILGenerator generator) + { + generator.Emit(OpCodes.Ret); + } + } +} +#endif \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs new file mode 100644 index 0000000..df1007c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/JavaScriptUtils.cs @@ -0,0 +1,148 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Globalization; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Utilities +{ + internal static class JavaScriptUtils + { + public static void WriteEscapedJavaScriptString(TextWriter writer, string value, char delimiter, bool appendDelimiters) + { + // leading delimiter + if (appendDelimiters) + writer.Write(delimiter); + + if (value != null) + { + int lastWritePosition = 0; + int skipped = 0; + char[] chars = null; + + for (int i = 0; i < value.Length; i++) + { + char c = value[i]; + string escapedValue; + + switch (c) + { + case '\t': + escapedValue = @"\t"; + break; + case '\n': + escapedValue = @"\n"; + break; + case '\r': + escapedValue = @"\r"; + break; + case '\f': + escapedValue = @"\f"; + break; + case '\b': + escapedValue = @"\b"; + break; + case '\\': + escapedValue = @"\\"; + break; + case '\u0085': // Next Line + escapedValue = @"\u0085"; + break; + case '\u2028': // Line Separator + escapedValue = @"\u2028"; + break; + case '\u2029': // Paragraph Separator + escapedValue = @"\u2029"; + break; + case '\'': + // only escape if this charater is being used as the delimiter + escapedValue = (delimiter == '\'') ? @"\'" : null; + break; + case '"': + // only escape if this charater is being used as the delimiter + escapedValue = (delimiter == '"') ? "\\\"" : null; + break; + default: + escapedValue = (c <= '\u001f') ? StringUtils.ToCharAsUnicode(c) : null; + break; + } + + if (escapedValue != null) + { + if (chars == null) + chars = value.ToCharArray(); + + // write skipped text + if (skipped > 0) + { + writer.Write(chars, lastWritePosition, skipped); + skipped = 0; + } + + // write escaped value and note position + writer.Write(escapedValue); + lastWritePosition = i + 1; + } + else + { + skipped++; + } + } + + // write any remaining skipped text + if (skipped > 0) + { + if (lastWritePosition == 0) + writer.Write(value); + else + writer.Write(chars, lastWritePosition, skipped); + } + } + + // trailing delimiter + if (appendDelimiters) + writer.Write(delimiter); + } + + public static string ToEscapedJavaScriptString(string value) + { + return ToEscapedJavaScriptString(value, '"', true); + } + + public static string ToEscapedJavaScriptString(string value, char delimiter, bool appendDelimiters) + { + using (StringWriter w = StringUtils.CreateStringWriter(StringUtils.GetLength(value) ?? 16)) + { + WriteEscapedJavaScriptString(w, value, delimiter, appendDelimiters); + return w.ToString(); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/LateBoundReflectionDelegateFactory.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/LateBoundReflectionDelegateFactory.cs new file mode 100644 index 0000000..cebafab --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/LateBoundReflectionDelegateFactory.cs @@ -0,0 +1,87 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Globalization; +using System.Reflection; + +namespace Newtonsoft.Json.Utilities +{ + internal class LateBoundReflectionDelegateFactory : ReflectionDelegateFactory + { + public static readonly LateBoundReflectionDelegateFactory Instance = new LateBoundReflectionDelegateFactory(); + + public override MethodCall CreateMethodCall(MethodBase method) + { + ValidationUtils.ArgumentNotNull(method, "method"); + + ConstructorInfo c = method as ConstructorInfo; + if (c != null) + return (o, a) => c.Invoke(a); + + return (o, a) => method.Invoke(o, a); + } + + public override Func CreateDefaultConstructor(Type type) + { + ValidationUtils.ArgumentNotNull(type, "type"); + + if (type.IsValueType) + return () => (T)ReflectionUtils.CreateInstance(type); + + ConstructorInfo constructorInfo = ReflectionUtils.GetDefaultConstructor(type, true); + + return () => (T)constructorInfo.Invoke(null); + } + + public override Func CreateGet(PropertyInfo propertyInfo) + { + ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo"); + + return o => propertyInfo.GetValue(o, null); + } + + public override Func CreateGet(FieldInfo fieldInfo) + { + ValidationUtils.ArgumentNotNull(fieldInfo, "fieldInfo"); + + return o => fieldInfo.GetValue(o); + } + + public override Action CreateSet(FieldInfo fieldInfo) + { + ValidationUtils.ArgumentNotNull(fieldInfo, "fieldInfo"); + + return (o, v) => fieldInfo.SetValue(o, v); + } + + public override Action CreateSet(PropertyInfo propertyInfo) + { + ValidationUtils.ArgumentNotNull(propertyInfo, "propertyInfo"); + + return (o, v) => propertyInfo.SetValue(o, v, null); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ListWrapper.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ListWrapper.cs new file mode 100644 index 0000000..150a00c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ListWrapper.cs @@ -0,0 +1,194 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using Newtonsoft.Json.Utilities; +using System.Linq; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal interface IWrappedList : IList + { + object UnderlyingList { get; } + } + + internal class ListWrapper : CollectionWrapper, IList, IWrappedList + { + private readonly IList _genericList; + + public ListWrapper(IList list) + : base(list) + { + ValidationUtils.ArgumentNotNull(list, "list"); + + if (list is IList) + _genericList = (IList) list; + } + + public ListWrapper(IList list) + : base(list) + { + ValidationUtils.ArgumentNotNull(list, "list"); + + _genericList = list; + } + + public int IndexOf(T item) + { + if (_genericList != null) + return _genericList.IndexOf(item); + else + return ((IList)this).IndexOf(item); + } + + public void Insert(int index, T item) + { + if (_genericList != null) + _genericList.Insert(index, item); + else + ((IList)this).Insert(index, item); + } + + public void RemoveAt(int index) + { + if (_genericList != null) + _genericList.RemoveAt(index); + else + ((IList)this).RemoveAt(index); + } + + public T this[int index] + { + get + { + if (_genericList != null) + return _genericList[index]; + else + return (T)((IList)this)[index]; + } + set + { + if (_genericList != null) + _genericList[index] = value; + else + ((IList)this)[index] = value; + } + } + + public override void Add(T item) + { + if (_genericList != null) + _genericList.Add(item); + else + base.Add(item); + } + + public override void Clear() + { + if (_genericList != null) + _genericList.Clear(); + else + base.Clear(); + } + + public override bool Contains(T item) + { + if (_genericList != null) + return _genericList.Contains(item); + else + return base.Contains(item); + } + + public override void CopyTo(T[] array, int arrayIndex) + { + if (_genericList != null) + _genericList.CopyTo(array, arrayIndex); + else + base.CopyTo(array, arrayIndex); + } + + public override int Count + { + get + { + if (_genericList != null) + return _genericList.Count; + else + return base.Count; + } + } + + public override bool IsReadOnly + { + get + { + if (_genericList != null) + return _genericList.IsReadOnly; + else + return base.IsReadOnly; + } + } + + public override bool Remove(T item) + { + if (_genericList != null) + { + return _genericList.Remove(item); + } + else + { + bool contains = base.Contains(item); + + if (contains) + base.Remove(item); + + return contains; + } + } + + public override IEnumerator GetEnumerator() + { + if (_genericList != null) + return _genericList.GetEnumerator(); + + return base.GetEnumerator(); + } + + public object UnderlyingList + { + get + { + if (_genericList != null) + return _genericList; + else + return UnderlyingCollection; + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/MathUtils.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/MathUtils.cs new file mode 100644 index 0000000..d367874 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/MathUtils.cs @@ -0,0 +1,134 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Text; + +namespace Newtonsoft.Json.Utilities +{ + internal class MathUtils + { + public static int IntLength(int i) + { + if (i < 0) + throw new ArgumentOutOfRangeException(); + + if (i == 0) + return 1; + + return (int)Math.Floor(Math.Log10(i)) + 1; + } + + public static int HexToInt(char h) + { + if ((h >= '0') && (h <= '9')) + { + return (h - '0'); + } + if ((h >= 'a') && (h <= 'f')) + { + return ((h - 'a') + 10); + } + if ((h >= 'A') && (h <= 'F')) + { + return ((h - 'A') + 10); + } + return -1; + } + + public static char IntToHex(int n) + { + if (n <= 9) + { + return (char)(n + 48); + } + return (char)((n - 10) + 97); + } + + public static int GetDecimalPlaces(double value) + { + // increasing max decimal places above 10 produces weirdness + int maxDecimalPlaces = 10; + double threshold = Math.Pow(0.1d, maxDecimalPlaces); + + if (value == 0.0) + return 0; + int decimalPlaces = 0; + while (value - Math.Floor(value) > threshold && decimalPlaces < maxDecimalPlaces) + { + value *= 10.0; + decimalPlaces++; + } + return decimalPlaces; + } + + public static int? Min(int? val1, int? val2) + { + if (val1 == null) + return val2; + if (val2 == null) + return val1; + + return Math.Min(val1.Value, val2.Value); + } + + public static int? Max(int? val1, int? val2) + { + if (val1 == null) + return val2; + if (val2 == null) + return val1; + + return Math.Max(val1.Value, val2.Value); + } + + public static double? Min(double? val1, double? val2) + { + if (val1 == null) + return val2; + if (val2 == null) + return val1; + + return Math.Min(val1.Value, val2.Value); + } + + public static double? Max(double? val1, double? val2) + { + if (val1 == null) + return val2; + if (val2 == null) + return val1; + + return Math.Max(val1.Value, val2.Value); + } + + public static bool ApproxEquals(double d1, double d2) + { + // are values equal to within 6 (or so) digits of precision? + return Math.Abs(d1 - d2) < (Math.Abs(d1) * 1e-6); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/MethodCall.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/MethodCall.cs new file mode 100644 index 0000000..6305dca --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/MethodCall.cs @@ -0,0 +1,29 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +namespace Newtonsoft.Json.Utilities +{ + internal delegate TResult MethodCall(T target, params object[] args); +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs new file mode 100644 index 0000000..733aed1 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/MiscellaneousUtils.cs @@ -0,0 +1,162 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; +using System.Text; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal delegate T Creator(); + + internal static class MiscellaneousUtils + { + public static bool ValueEquals(object objA, object objB) + { + if (objA == null && objB == null) + return true; + if (objA != null && objB == null) + return false; + if (objA == null && objB != null) + return false; + + // comparing an Int32 and Int64 both of the same value returns false + // make types the same then compare + if (objA.GetType() != objB.GetType()) + { + if (ConvertUtils.IsInteger(objA) && ConvertUtils.IsInteger(objB)) + return Convert.ToDecimal(objA, CultureInfo.CurrentCulture).Equals(Convert.ToDecimal(objB, CultureInfo.CurrentCulture)); + else if ((objA is double || objA is float || objA is decimal) && (objB is double || objB is float || objB is decimal)) + return MathUtils.ApproxEquals(Convert.ToDouble(objA, CultureInfo.CurrentCulture), Convert.ToDouble(objB, CultureInfo.CurrentCulture)); + else + return false; + } + + return objA.Equals(objB); + } + + public static ArgumentOutOfRangeException CreateArgumentOutOfRangeException(string paramName, object actualValue, string message) + { + string newMessage = message + Environment.NewLine + @"Actual value was {0}.".FormatWith(CultureInfo.InvariantCulture, actualValue); + + return new ArgumentOutOfRangeException(paramName, newMessage); + } + + public static bool TryAction(Creator creator, out T output) + { + ValidationUtils.ArgumentNotNull(creator, "creator"); + + try + { + output = creator(); + return true; + } + catch + { + output = default(T); + return false; + } + } + + public static string ToString(object value) + { + if (value == null) + return "{null}"; + + return (value is string) ? @"""" + value.ToString() + @"""" : value.ToString(); + } + + public static byte[] HexToBytes(string hex) + { + string fixedHex = hex.Replace("-", string.Empty); + + // array to put the result in + byte[] bytes = new byte[fixedHex.Length / 2]; + // variable to determine shift of high/low nibble + int shift = 4; + // offset of the current byte in the array + int offset = 0; + // loop the characters in the string + foreach (char c in fixedHex) + { + // get character code in range 0-9, 17-22 + // the % 32 handles lower case characters + int b = (c - '0') % 32; + // correction for a-f + if (b > 9) b -= 7; + // store nibble (4 bits) in byte array + bytes[offset] |= (byte)(b << shift); + // toggle the shift variable between 0 and 4 + shift ^= 4; + // move to next byte + if (shift != 0) offset++; + } + return bytes; + } + + public static string BytesToHex(byte[] bytes) + { + return BytesToHex(bytes, false); + } + + public static string BytesToHex(byte[] bytes, bool removeDashes) + { + string hex = BitConverter.ToString(bytes); + if (removeDashes) + hex = hex.Replace("-", ""); + + return hex; + } + + public static int ByteArrayCompare(byte[] a1, byte[] a2) + { + int lengthCompare = a1.Length.CompareTo(a2.Length); + if (lengthCompare != 0) + return lengthCompare; + + for (int i = 0; i < a1.Length; i++) + { + int valueCompare = a1[i].CompareTo(a2[i]); + if (valueCompare != 0) + return valueCompare; + } + + return 0; + } + + public static string GetPrefix(string qualifiedName) + { + string prefix; + string localName; + GetQualifiedNameParts(qualifiedName, out prefix, out localName); + + return prefix; + } + + public static string GetLocalName(string qualifiedName) + { + string prefix; + string localName; + GetQualifiedNameParts(qualifiedName, out prefix, out localName); + + return localName; + } + + public static void GetQualifiedNameParts(string qualifiedName, out string prefix, out string localName) + { + int colonPosition = qualifiedName.IndexOf(':'); + + if ((colonPosition == -1 || colonPosition == 0) || (qualifiedName.Length - 1) == colonPosition) + { + prefix = null; + localName = qualifiedName; + } + else + { + prefix = qualifiedName.Substring(0, colonPosition); + localName = qualifiedName.Substring(colonPosition + 1); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ReflectionDelegateFactory.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ReflectionDelegateFactory.cs new file mode 100644 index 0000000..ec7e9a9 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ReflectionDelegateFactory.cs @@ -0,0 +1,67 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Globalization; +using System.Reflection; + +namespace Newtonsoft.Json.Utilities +{ + internal abstract class ReflectionDelegateFactory + { + public Func CreateGet(MemberInfo memberInfo) + { + PropertyInfo propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + return CreateGet(propertyInfo); + + FieldInfo fieldInfo = memberInfo as FieldInfo; + if (fieldInfo != null) + return CreateGet(fieldInfo); + + throw new Exception("Could not create getter for {0}.".FormatWith(CultureInfo.InvariantCulture, memberInfo)); + } + + public Action CreateSet(MemberInfo memberInfo) + { + PropertyInfo propertyInfo = memberInfo as PropertyInfo; + if (propertyInfo != null) + return CreateSet(propertyInfo); + + FieldInfo fieldInfo = memberInfo as FieldInfo; + if (fieldInfo != null) + return CreateSet(fieldInfo); + + throw new Exception("Could not create setter for {0}.".FormatWith(CultureInfo.InvariantCulture, memberInfo)); + } + + public abstract MethodCall CreateMethodCall(MethodBase method); + public abstract Func CreateDefaultConstructor(Type type); + public abstract Func CreateGet(PropertyInfo propertyInfo); + public abstract Func CreateGet(FieldInfo fieldInfo); + public abstract Action CreateSet(FieldInfo fieldInfo); + public abstract Action CreateSet(PropertyInfo propertyInfo); + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs new file mode 100644 index 0000000..b0350bc --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ReflectionUtils.cs @@ -0,0 +1,950 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Collections; +using System.Linq; +using System.Globalization; +using System.Runtime.Serialization.Formatters; +using System.Text; +using System.Text.RegularExpressions; + +namespace Newtonsoft.Json.Utilities +{ + internal static class ReflectionUtils + { + public static Type GetObjectType(object v) + { + return (v != null) ? v.GetType() : null; + } + + public static string GetTypeName(Type t, FormatterAssemblyStyle assemblyFormat) + { + switch (assemblyFormat) + { + case FormatterAssemblyStyle.Simple: + return GetSimpleTypeName(t); + case FormatterAssemblyStyle.Full: + return t.AssemblyQualifiedName; + default: + throw new ArgumentOutOfRangeException(); + } + } + + private static string GetSimpleTypeName(Type type) + { +#if !SILVERLIGHT + string fullyQualifiedTypeName = type.FullName + ", " + type.Assembly.GetName().Name; + + // for type names with no nested type names then return + if (!type.IsGenericType || type.IsGenericTypeDefinition) + return fullyQualifiedTypeName; +#else + // Assembly.GetName() is marked SecurityCritical + string fullyQualifiedTypeName = type.AssemblyQualifiedName; +#endif + + StringBuilder builder = new StringBuilder(); + + // loop through the type name and filter out qualified assembly details from nested type names + bool writingAssemblyName = false; + bool skippingAssemblyDetails = false; + for (int i = 0; i < fullyQualifiedTypeName.Length; i++) + { + char current = fullyQualifiedTypeName[i]; + switch (current) + { + case '[': + writingAssemblyName = false; + skippingAssemblyDetails = false; + builder.Append(current); + break; + case ']': + writingAssemblyName = false; + skippingAssemblyDetails = false; + builder.Append(current); + break; + case ',': + if (!writingAssemblyName) + { + writingAssemblyName = true; + builder.Append(current); + } + else + { + skippingAssemblyDetails = true; + } + break; + default: + if (!skippingAssemblyDetails) + builder.Append(current); + break; + } + } + + return builder.ToString(); + } + + public static bool IsInstantiatableType(Type t) + { + ValidationUtils.ArgumentNotNull(t, "t"); + + if (t.IsAbstract || t.IsInterface || t.IsArray || t.IsGenericTypeDefinition || t == typeof(void)) + return false; + + if (!HasDefaultConstructor(t)) + return false; + + return true; + } + + public static bool HasDefaultConstructor(Type t) + { + return HasDefaultConstructor(t, false); + } + + public static bool HasDefaultConstructor(Type t, bool nonPublic) + { + ValidationUtils.ArgumentNotNull(t, "t"); + + if (t.IsValueType) + return true; + + return (GetDefaultConstructor(t, nonPublic) != null); + } + + public static ConstructorInfo GetDefaultConstructor(Type t) + { + return GetDefaultConstructor(t, false); + } + + public static ConstructorInfo GetDefaultConstructor(Type t, bool nonPublic) + { + BindingFlags accessModifier = BindingFlags.Public; + + if (nonPublic) + accessModifier = accessModifier | BindingFlags.NonPublic; + + return t.GetConstructor(accessModifier | BindingFlags.Instance, null, new Type[0], null); + } + + public static bool IsNullable(Type t) + { + ValidationUtils.ArgumentNotNull(t, "t"); + + if (t.IsValueType) + return IsNullableType(t); + + return true; + } + + public static bool IsNullableType(Type t) + { + ValidationUtils.ArgumentNotNull(t, "t"); + + return (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)); + } + + public static Type EnsureNotNullableType(Type t) + { + return (IsNullableType(t)) + ? Nullable.GetUnderlyingType(t) + : t; + } + + //public static bool IsValueTypeUnitializedValue(ValueType value) + //{ + // if (value == null) + // return true; + + // return value.Equals(CreateUnitializedValue(value.GetType())); + //} + + public static bool IsUnitializedValue(object value) + { + if (value == null) + { + return true; + } + else + { + object unitializedValue = CreateUnitializedValue(value.GetType()); + return value.Equals(unitializedValue); + } + } + + public static object CreateUnitializedValue(Type type) + { + ValidationUtils.ArgumentNotNull(type, "type"); + + if (type.IsGenericTypeDefinition) + throw new ArgumentException("Type {0} is a generic type definition and cannot be instantiated.".FormatWith(CultureInfo.InvariantCulture, type), "type"); + + if (type.IsClass || type.IsInterface || type == typeof(void)) + return null; + else if (type.IsValueType) + return Activator.CreateInstance(type); + else + throw new ArgumentException("Type {0} cannot be instantiated.".FormatWith(CultureInfo.InvariantCulture, type), "type"); + } + + public static bool IsPropertyIndexed(PropertyInfo property) + { + ValidationUtils.ArgumentNotNull(property, "property"); + + return !CollectionUtils.IsNullOrEmpty(property.GetIndexParameters()); + } + + public static bool ImplementsGenericDefinition(Type type, Type genericInterfaceDefinition) + { + Type implementingType; + return ImplementsGenericDefinition(type, genericInterfaceDefinition, out implementingType); + } + + public static bool ImplementsGenericDefinition(Type type, Type genericInterfaceDefinition, out Type implementingType) + { + ValidationUtils.ArgumentNotNull(type, "type"); + ValidationUtils.ArgumentNotNull(genericInterfaceDefinition, "genericInterfaceDefinition"); + + if (!genericInterfaceDefinition.IsInterface || !genericInterfaceDefinition.IsGenericTypeDefinition) + throw new ArgumentNullException("'{0}' is not a generic interface definition.".FormatWith(CultureInfo.InvariantCulture, genericInterfaceDefinition)); + + if (type.IsInterface) + { + if (type.IsGenericType) + { + Type interfaceDefinition = type.GetGenericTypeDefinition(); + + if (genericInterfaceDefinition == interfaceDefinition) + { + implementingType = type; + return true; + } + } + } + + foreach (Type i in type.GetInterfaces()) + { + if (i.IsGenericType) + { + Type interfaceDefinition = i.GetGenericTypeDefinition(); + + if (genericInterfaceDefinition == interfaceDefinition) + { + implementingType = i; + return true; + } + } + } + + implementingType = null; + return false; + } + + public static bool AssignableToTypeName(this Type type, string fullTypeName, out Type match) + { + Type current = type; + + while (current != null) + { + if (string.Equals(current.FullName, fullTypeName, StringComparison.Ordinal)) + { + match = current; + return true; + } + + current = current.BaseType; + } + + foreach (Type i in type.GetInterfaces()) + { + if (string.Equals(i.Name, fullTypeName, StringComparison.Ordinal)) + { + match = type; + return true; + } + } + + match = null; + return false; + } + + public static bool AssignableToTypeName(this Type type, string fullTypeName) + { + Type match; + return type.AssignableToTypeName(fullTypeName, out match); + } + + public static bool InheritsGenericDefinition(Type type, Type genericClassDefinition) + { + Type implementingType; + return InheritsGenericDefinition(type, genericClassDefinition, out implementingType); + } + + public static bool InheritsGenericDefinition(Type type, Type genericClassDefinition, out Type implementingType) + { + ValidationUtils.ArgumentNotNull(type, "type"); + ValidationUtils.ArgumentNotNull(genericClassDefinition, "genericClassDefinition"); + + if (!genericClassDefinition.IsClass || !genericClassDefinition.IsGenericTypeDefinition) + throw new ArgumentNullException("'{0}' is not a generic class definition.".FormatWith(CultureInfo.InvariantCulture, genericClassDefinition)); + + return InheritsGenericDefinitionInternal(type, genericClassDefinition, out implementingType); + } + + private static bool InheritsGenericDefinitionInternal(Type currentType, Type genericClassDefinition, out Type implementingType) + { + if (currentType.IsGenericType) + { + Type currentGenericClassDefinition = currentType.GetGenericTypeDefinition(); + + if (genericClassDefinition == currentGenericClassDefinition) + { + implementingType = currentType; + return true; + } + } + + if (currentType.BaseType == null) + { + implementingType = null; + return false; + } + + return InheritsGenericDefinitionInternal(currentType.BaseType, genericClassDefinition, out implementingType); + } + + /// + /// Gets the type of the typed collection's items. + /// + /// The type. + /// The type of the typed collection's items. + public static Type GetCollectionItemType(Type type) + { + ValidationUtils.ArgumentNotNull(type, "type"); + Type genericListType; + + if (type.IsArray) + { + return type.GetElementType(); + } + else if (ImplementsGenericDefinition(type, typeof(IEnumerable<>), out genericListType)) + { + if (genericListType.IsGenericTypeDefinition) + throw new Exception("Type {0} is not a collection.".FormatWith(CultureInfo.InvariantCulture, type)); + + return genericListType.GetGenericArguments()[0]; + } + else if (typeof(IEnumerable).IsAssignableFrom(type)) + { + return null; + } + else + { + throw new Exception("Type {0} is not a collection.".FormatWith(CultureInfo.InvariantCulture, type)); + } + } + + public static void GetDictionaryKeyValueTypes(Type dictionaryType, out Type keyType, out Type valueType) + { + ValidationUtils.ArgumentNotNull(dictionaryType, "type"); + + Type genericDictionaryType; + if (ImplementsGenericDefinition(dictionaryType, typeof(IDictionary<,>), out genericDictionaryType)) + { + if (genericDictionaryType.IsGenericTypeDefinition) + throw new Exception("Type {0} is not a dictionary.".FormatWith(CultureInfo.InvariantCulture, dictionaryType)); + + Type[] dictionaryGenericArguments = genericDictionaryType.GetGenericArguments(); + + keyType = dictionaryGenericArguments[0]; + valueType = dictionaryGenericArguments[1]; + return; + } + else if (typeof(IDictionary).IsAssignableFrom(dictionaryType)) + { + keyType = null; + valueType = null; + return; + } + else + { + throw new Exception("Type {0} is not a dictionary.".FormatWith(CultureInfo.InvariantCulture, dictionaryType)); + } + } + + public static Type GetDictionaryValueType(Type dictionaryType) + { + Type keyType; + Type valueType; + GetDictionaryKeyValueTypes(dictionaryType, out keyType, out valueType); + + return valueType; + } + + public static Type GetDictionaryKeyType(Type dictionaryType) + { + Type keyType; + Type valueType; + GetDictionaryKeyValueTypes(dictionaryType, out keyType, out valueType); + + return keyType; + } + + /// + /// Tests whether the list's items are their unitialized value. + /// + /// The list. + /// Whether the list's items are their unitialized value + public static bool ItemsUnitializedValue(IList list) + { + ValidationUtils.ArgumentNotNull(list, "list"); + + Type elementType = GetCollectionItemType(list.GetType()); + + if (elementType.IsValueType) + { + object unitializedValue = CreateUnitializedValue(elementType); + + for (int i = 0; i < list.Count; i++) + { + if (!list[i].Equals(unitializedValue)) + return false; + } + } + else if (elementType.IsClass) + { + for (int i = 0; i < list.Count; i++) + { + object value = list[i]; + + if (value != null) + return false; + } + } + else + { + throw new Exception("Type {0} is neither a ValueType or a Class.".FormatWith(CultureInfo.InvariantCulture, elementType)); + } + + return true; + } + + /// + /// Gets the member's underlying type. + /// + /// The member. + /// The underlying type of the member. + public static Type GetMemberUnderlyingType(MemberInfo member) + { + ValidationUtils.ArgumentNotNull(member, "member"); + + switch (member.MemberType) + { + case MemberTypes.Field: + return ((FieldInfo)member).FieldType; + case MemberTypes.Property: + return ((PropertyInfo)member).PropertyType; + case MemberTypes.Event: + return ((EventInfo)member).EventHandlerType; + default: + throw new ArgumentException("MemberInfo must be of type FieldInfo, PropertyInfo or EventInfo", "member"); + } + } + + /// + /// Determines whether the member is an indexed property. + /// + /// The member. + /// + /// true if the member is an indexed property; otherwise, false. + /// + public static bool IsIndexedProperty(MemberInfo member) + { + ValidationUtils.ArgumentNotNull(member, "member"); + + PropertyInfo propertyInfo = member as PropertyInfo; + + if (propertyInfo != null) + return IsIndexedProperty(propertyInfo); + else + return false; + } + + /// + /// Determines whether the property is an indexed property. + /// + /// The property. + /// + /// true if the property is an indexed property; otherwise, false. + /// + public static bool IsIndexedProperty(PropertyInfo property) + { + ValidationUtils.ArgumentNotNull(property, "property"); + + return (property.GetIndexParameters().Length > 0); + } + + /// + /// Gets the member's value on the object. + /// + /// The member. + /// The target object. + /// The member's value on the object. + public static object GetMemberValue(MemberInfo member, object target) + { + ValidationUtils.ArgumentNotNull(member, "member"); + ValidationUtils.ArgumentNotNull(target, "target"); + + switch (member.MemberType) + { + case MemberTypes.Field: + return ((FieldInfo)member).GetValue(target); + case MemberTypes.Property: + try + { + return ((PropertyInfo)member).GetValue(target, null); + } + catch (TargetParameterCountException e) + { + throw new ArgumentException("MemberInfo '{0}' has index parameters".FormatWith(CultureInfo.InvariantCulture, member.Name), e); + } + default: + throw new ArgumentException("MemberInfo '{0}' is not of type FieldInfo or PropertyInfo".FormatWith(CultureInfo.InvariantCulture, CultureInfo.InvariantCulture, member.Name), "member"); + } + } + + /// + /// Sets the member's value on the target object. + /// + /// The member. + /// The target. + /// The value. + public static void SetMemberValue(MemberInfo member, object target, object value) + { + ValidationUtils.ArgumentNotNull(member, "member"); + ValidationUtils.ArgumentNotNull(target, "target"); + + switch (member.MemberType) + { + case MemberTypes.Field: + ((FieldInfo)member).SetValue(target, value); + break; + case MemberTypes.Property: + ((PropertyInfo)member).SetValue(target, value, null); + break; + default: + throw new ArgumentException("MemberInfo '{0}' must be of type FieldInfo or PropertyInfo".FormatWith(CultureInfo.InvariantCulture, member.Name), "member"); + } + } + + /// + /// Determines whether the specified MemberInfo can be read. + /// + /// The MemberInfo to determine whether can be read. + /// /// if set to true then allow the member to be gotten non-publicly. + /// + /// true if the specified MemberInfo can be read; otherwise, false. + /// + public static bool CanReadMemberValue(MemberInfo member, bool nonPublic) + { + switch (member.MemberType) + { + case MemberTypes.Field: + FieldInfo fieldInfo = (FieldInfo)member; + + if (nonPublic) + return true; + else if (fieldInfo.IsPublic) + return true; + return false; + case MemberTypes.Property: + PropertyInfo propertyInfo = (PropertyInfo) member; + + if (!propertyInfo.CanRead) + return false; + if (nonPublic) + return true; + return (propertyInfo.GetGetMethod(nonPublic) != null); + default: + return false; + } + } + + /// + /// Determines whether the specified MemberInfo can be set. + /// + /// The MemberInfo to determine whether can be set. + /// if set to true then allow the member to be set non-publicly. + /// + /// true if the specified MemberInfo can be set; otherwise, false. + /// + public static bool CanSetMemberValue(MemberInfo member, bool nonPublic) + { + switch (member.MemberType) + { + case MemberTypes.Field: + FieldInfo fieldInfo = (FieldInfo)member; + + if (fieldInfo.IsInitOnly) + return false; + if (nonPublic) + return true; + else if (fieldInfo.IsPublic) + return true; + return false; + case MemberTypes.Property: + PropertyInfo propertyInfo = (PropertyInfo)member; + + if (!propertyInfo.CanWrite) + return false; + if (nonPublic) + return true; + return (propertyInfo.GetSetMethod(nonPublic) != null); + default: + return false; + } + } + + public static List GetFieldsAndProperties(BindingFlags bindingAttr) + { + return GetFieldsAndProperties(typeof(T), bindingAttr); + } + + public static List GetFieldsAndProperties(Type type, BindingFlags bindingAttr) + { + List targetMembers = new List(); + + targetMembers.AddRange(GetFields(type, bindingAttr)); + targetMembers.AddRange(GetProperties(type, bindingAttr)); + + // for some reason .NET returns multiple members when overriding a generic member on a base class + // http://forums.msdn.microsoft.com/en-US/netfxbcl/thread/b5abbfee-e292-4a64-8907-4e3f0fb90cd9/ + // filter members to only return the override on the topmost class + // update: I think this is fixed in .NET 3.5 SP1 - leave this in for now... + List distinctMembers = new List(targetMembers.Count); + + var groupedMembers = targetMembers.GroupBy(m => m.Name).Select(g => new { Count = g.Count(), Members = g.Cast() }); + foreach (var groupedMember in groupedMembers) + { + if (groupedMember.Count == 1) + { + distinctMembers.Add(groupedMember.Members.First()); + } + else + { + var members = groupedMember.Members.Where(m => !IsOverridenGenericMember(m, bindingAttr) || m.Name == "Item"); + + distinctMembers.AddRange(members); + } + } + + return distinctMembers; + } + + private static bool IsOverridenGenericMember(MemberInfo memberInfo, BindingFlags bindingAttr) + { + if (memberInfo.MemberType != MemberTypes.Field && memberInfo.MemberType != MemberTypes.Property) + throw new ArgumentException("Member must be a field or property."); + + Type declaringType = memberInfo.DeclaringType; + if (!declaringType.IsGenericType) + return false; + Type genericTypeDefinition = declaringType.GetGenericTypeDefinition(); + if (genericTypeDefinition == null) + return false; + MemberInfo[] members = genericTypeDefinition.GetMember(memberInfo.Name, bindingAttr); + if (members.Length == 0) + return false; + Type memberUnderlyingType = GetMemberUnderlyingType(members[0]); + if (!memberUnderlyingType.IsGenericParameter) + return false; + + return true; + } + + public static T GetAttribute(ICustomAttributeProvider attributeProvider) where T : Attribute + { + return GetAttribute(attributeProvider, true); + } + + public static T GetAttribute(ICustomAttributeProvider attributeProvider, bool inherit) where T : Attribute + { + T[] attributes = GetAttributes(attributeProvider, inherit); + + return CollectionUtils.GetSingleItem(attributes, true); + } + + public static T[] GetAttributes(ICustomAttributeProvider attributeProvider, bool inherit) where T : Attribute + { + ValidationUtils.ArgumentNotNull(attributeProvider, "attributeProvider"); + + // http://hyperthink.net/blog/getcustomattributes-gotcha/ + // ICustomAttributeProvider doesn't do inheritance + + if (attributeProvider is Assembly) + return (T[])Attribute.GetCustomAttributes((Assembly)attributeProvider, typeof(T), inherit); + + if (attributeProvider is MemberInfo) + return (T[])Attribute.GetCustomAttributes((MemberInfo)attributeProvider, typeof(T), inherit); + + if (attributeProvider is Module) + return (T[])Attribute.GetCustomAttributes((Module)attributeProvider, typeof(T), inherit); + + if (attributeProvider is ParameterInfo) + return (T[])Attribute.GetCustomAttributes((ParameterInfo)attributeProvider, typeof(T), inherit); + + return (T[])attributeProvider.GetCustomAttributes(typeof(T), inherit); + } + + public static string GetNameAndAssessmblyName(Type t) + { + ValidationUtils.ArgumentNotNull(t, "t"); + + return t.FullName + ", " + t.Assembly.GetName().Name; + } + + public static Type MakeGenericType(Type genericTypeDefinition, params Type[] innerTypes) + { + ValidationUtils.ArgumentNotNull(genericTypeDefinition, "genericTypeDefinition"); + ValidationUtils.ArgumentNotNullOrEmpty(innerTypes, "innerTypes"); + ValidationUtils.ArgumentConditionTrue(genericTypeDefinition.IsGenericTypeDefinition, "genericTypeDefinition", "Type {0} is not a generic type definition.".FormatWith(CultureInfo.InvariantCulture, genericTypeDefinition)); + + return genericTypeDefinition.MakeGenericType(innerTypes); + } + + public static object CreateGeneric(Type genericTypeDefinition, Type innerType, params object[] args) + { + return CreateGeneric(genericTypeDefinition, new [] { innerType }, args); + } + + public static object CreateGeneric(Type genericTypeDefinition, IList innerTypes, params object[] args) + { + return CreateGeneric(genericTypeDefinition, innerTypes, (t, a) => CreateInstance(t, a.ToArray()), args); + } + + public static object CreateGeneric(Type genericTypeDefinition, IList innerTypes, Func, object> instanceCreator, params object[] args) + { + ValidationUtils.ArgumentNotNull(genericTypeDefinition, "genericTypeDefinition"); + ValidationUtils.ArgumentNotNullOrEmpty(innerTypes, "innerTypes"); + ValidationUtils.ArgumentNotNull(instanceCreator, "createInstance"); + + Type specificType = MakeGenericType(genericTypeDefinition, innerTypes.ToArray()); + + return instanceCreator(specificType, args); + } + + public static bool IsCompatibleValue(object value, Type type) + { + if (value == null) + return IsNullable(type); + + if (type.IsAssignableFrom(value.GetType())) + return true; + + return false; + } + + public static object CreateInstance(Type type, params object[] args) + { + ValidationUtils.ArgumentNotNull(type, "type"); + +#if !PocketPC + return Activator.CreateInstance(type, args); +#else + // CF doesn't have a Activator.CreateInstance overload that takes args + // lame + + if (type.IsValueType && CollectionUtils.IsNullOrEmpty(args)) + return Activator.CreateInstance(type); + + ConstructorInfo[] constructors = type.GetConstructors(); + ConstructorInfo match = constructors.Where(c => + { + ParameterInfo[] parameters = c.GetParameters(); + if (parameters.Length != args.Length) + return false; + + for (int i = 0; i < parameters.Length; i++) + { + ParameterInfo parameter = parameters[i]; + object value = args[i]; + + if (!IsCompatibleValue(value, parameter.ParameterType)) + return false; + } + + return true; + }).FirstOrDefault(); + + if (match == null) + throw new Exception("Could not create '{0}' with given parameters.".FormatWith(CultureInfo.InvariantCulture, type)); + + return match.Invoke(args); +#endif + } + + public static void SplitFullyQualifiedTypeName(string fullyQualifiedTypeName, out string typeName, out string assemblyName) + { + int? assemblyDelimiterIndex = GetAssemblyDelimiterIndex(fullyQualifiedTypeName); + + if (assemblyDelimiterIndex != null) + { + typeName = fullyQualifiedTypeName.Substring(0, assemblyDelimiterIndex.Value).Trim(); + assemblyName = fullyQualifiedTypeName.Substring(assemblyDelimiterIndex.Value + 1, fullyQualifiedTypeName.Length - assemblyDelimiterIndex.Value - 1).Trim(); + } + else + { + typeName = fullyQualifiedTypeName; + assemblyName = null; + } + + } + + private static int? GetAssemblyDelimiterIndex(string fullyQualifiedTypeName) + { + // we need to get the first comma following all surrounded in brackets because of generic types + // e.g. System.Collections.Generic.Dictionary`2[[System.String, mscorlib,Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + int scope = 0; + for (int i = 0; i < fullyQualifiedTypeName.Length; i++) + { + char current = fullyQualifiedTypeName[i]; + switch (current) + { + case '[': + scope++; + break; + case ']': + scope--; + break; + case ',': + if (scope == 0) + return i; + break; + } + } + + return null; + } + + public static IEnumerable GetFields(Type targetType, BindingFlags bindingAttr) + { + ValidationUtils.ArgumentNotNull(targetType, "targetType"); + + List fieldInfos = new List(targetType.GetFields(bindingAttr)); + // Type.GetFields doesn't return inherited private fields + // manually find private fields from base class + GetChildPrivateFields(fieldInfos, targetType, bindingAttr); + + return fieldInfos.Cast(); + } + + private static void GetChildPrivateFields(IList initialFields, Type targetType, BindingFlags bindingAttr) + { + // fix weirdness with private FieldInfos only being returned for the current Type + // find base type fields and add them to result + if ((bindingAttr & BindingFlags.NonPublic) != 0) + { + // modify flags to not search for public fields + BindingFlags nonPublicBindingAttr = bindingAttr.RemoveFlag(BindingFlags.Public); + + while ((targetType = targetType.BaseType) != null) + { + // filter out protected fields + IEnumerable childPrivateFields = + targetType.GetFields(nonPublicBindingAttr).Where(f => f.IsPrivate).Cast(); + + initialFields.AddRange(childPrivateFields); + } + } + } + + public static IEnumerable GetProperties(Type targetType, BindingFlags bindingAttr) + { + ValidationUtils.ArgumentNotNull(targetType, "targetType"); + + List propertyInfos = new List(targetType.GetProperties(bindingAttr)); + GetChildPrivateProperties(propertyInfos, targetType, bindingAttr); + + // a base class private getter/setter will be inaccessable unless the property was gotten from the base class + for (int i = 0; i < propertyInfos.Count; i++) + { + PropertyInfo member = propertyInfos[i]; + if (member.DeclaringType != targetType) + { + Type[] types = member.GetIndexParameters().Select(p => p.ParameterType).ToArray(); + + PropertyInfo declaredMember = member.DeclaringType.GetProperty(member.Name, bindingAttr, null, member.PropertyType, types, null); + propertyInfos[i] = declaredMember; + } + } + + return propertyInfos; + } + + public static BindingFlags RemoveFlag(this BindingFlags bindingAttr, BindingFlags flag) + { + return ((bindingAttr & flag) == flag) + ? bindingAttr ^ flag + : bindingAttr; + } + + private static void GetChildPrivateProperties(IList initialProperties, Type targetType, BindingFlags bindingAttr) + { + // fix weirdness with private PropertyInfos only being returned for the current Type + // find base type properties and add them to result + if ((bindingAttr & BindingFlags.NonPublic) != 0) + { + // modify flags to not search for public fields + BindingFlags nonPublicBindingAttr = bindingAttr.RemoveFlag(BindingFlags.Public); + + while ((targetType = targetType.BaseType) != null) + { + foreach (PropertyInfo propertyInfo in targetType.GetProperties(nonPublicBindingAttr)) + { + PropertyInfo nonPublicProperty = propertyInfo; + + // have to test on name rather than reference because instances are different + // depending on the type that GetProperties was called on + int index = initialProperties.IndexOf(p => p.Name == nonPublicProperty.Name); + if (index == -1) + { + initialProperties.Add(nonPublicProperty); + } + else + { + // replace nonpublic properties for a child, but gotten from + // the parent with the one from the child + // the property gotten from the child will have access to private getter/setter + initialProperties[index] = nonPublicProperty; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/StringBuffer.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/StringBuffer.cs new file mode 100644 index 0000000..11bb816 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/StringBuffer.cs @@ -0,0 +1,99 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; + +namespace Newtonsoft.Json.Utilities +{ + /// + /// Builds a string. Unlike StringBuilder this class lets you reuse it's internal buffer. + /// + internal class StringBuffer + { + private char[] _buffer; + private int _position; + + private static readonly char[] _emptyBuffer = new char[0]; + + public int Position + { + get { return _position; } + set { _position = value; } + } + + public StringBuffer() + { + _buffer = _emptyBuffer; + } + + public StringBuffer(int initalSize) + { + _buffer = new char[initalSize]; + } + + public void Append(char value) + { + // test if the buffer array is large enough to take the value + if (_position == _buffer.Length) + { + EnsureSize(1); + } + + // set value and increment poisition + _buffer[_position++] = value; + } + + public void Clear() + { + _buffer = _emptyBuffer; + _position = 0; + } + + private void EnsureSize(int appendLength) + { + char[] newBuffer = new char[(_position + appendLength) * 2]; + + Array.Copy(_buffer, newBuffer, _position); + + _buffer = newBuffer; + } + + public override string ToString() + { + return ToString(0, _position); + } + + public string ToString(int start, int length) + { + // TODO: validation + return new string(_buffer, start, length); + } + + public char[] GetInternalBuffer() + { + return _buffer; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/StringUtils.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/StringUtils.cs new file mode 100644 index 0000000..96929f0 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/StringUtils.cs @@ -0,0 +1,388 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Text.RegularExpressions; +using System.Linq; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal static class StringUtils + { + public const string CarriageReturnLineFeed = "\r\n"; + public const string Empty = ""; + public const char CarriageReturn = '\r'; + public const char LineFeed = '\n'; + public const char Tab = '\t'; + + //public static string FormatWith(this string format, params object[] args) + //{ + // return FormatWith(format, null, args); + //} + + public static string FormatWith(this string format, IFormatProvider provider, params object[] args) + { + ValidationUtils.ArgumentNotNull(format, "format"); + + return string.Format(provider, format, args); + } + + /// + /// Determines whether the string contains white space. + /// + /// The string to test for white space. + /// + /// true if the string contains white space; otherwise, false. + /// + public static bool ContainsWhiteSpace(string s) + { + if (s == null) + throw new ArgumentNullException("s"); + + for (int i = 0; i < s.Length; i++) + { + if (char.IsWhiteSpace(s[i])) + return true; + } + return false; + } + + /// + /// Determines whether the string is all white space. Empty string will return false. + /// + /// The string to test whether it is all white space. + /// + /// true if the string is all white space; otherwise, false. + /// + public static bool IsWhiteSpace(string s) + { + if (s == null) + throw new ArgumentNullException("s"); + + if (s.Length == 0) + return false; + + for (int i = 0; i < s.Length; i++) + { + if (!char.IsWhiteSpace(s[i])) + return false; + } + + return true; + } + + /// + /// Ensures the target string ends with the specified string. + /// + /// The target. + /// The value. + /// The target string with the value string at the end. + public static string EnsureEndsWith(string target, string value) + { + if (target == null) + throw new ArgumentNullException("target"); + + if (value == null) + throw new ArgumentNullException("value"); + + if (target.Length >= value.Length) + { + if (string.Compare(target, target.Length - value.Length, value, 0, value.Length, StringComparison.OrdinalIgnoreCase) == + 0) + return target; + + string trimmedString = target.TrimEnd(null); + + if (string.Compare(trimmedString, trimmedString.Length - value.Length, value, 0, value.Length, + StringComparison.OrdinalIgnoreCase) == 0) + return target; + } + + return target + value; + } + + public static bool IsNullOrEmptyOrWhiteSpace(string s) + { + if (string.IsNullOrEmpty(s)) + return true; + else if (IsWhiteSpace(s)) + return true; + else + return false; + } + + /// + /// Perform an action if the string is not null or empty. + /// + /// The value. + /// The action to perform. + public static void IfNotNullOrEmpty(string value, Action action) + { + IfNotNullOrEmpty(value, action, null); + } + + private static void IfNotNullOrEmpty(string value, Action trueAction, Action falseAction) + { + if (!string.IsNullOrEmpty(value)) + { + if (trueAction != null) + trueAction(value); + } + else + { + if (falseAction != null) + falseAction(value); + } + } + + /// + /// Indents the specified string. + /// + /// The string to indent. + /// The number of characters to indent by. + /// + public static string Indent(string s, int indentation) + { + return Indent(s, indentation, ' '); + } + + /// + /// Indents the specified string. + /// + /// The string to indent. + /// The number of characters to indent by. + /// The indent character. + /// + public static string Indent(string s, int indentation, char indentChar) + { + if (s == null) + throw new ArgumentNullException("s"); + + if (indentation <= 0) + throw new ArgumentException("Must be greater than zero.", "indentation"); + + StringReader sr = new StringReader(s); + StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); + + ActionTextReaderLine(sr, sw, delegate(TextWriter tw, string line) + { + tw.Write(new string(indentChar, indentation)); + tw.Write(line); + }); + + return sw.ToString(); + } + + private delegate void ActionLine(TextWriter textWriter, string line); + + private static void ActionTextReaderLine(TextReader textReader, TextWriter textWriter, ActionLine lineAction) + { + string line; + bool firstLine = true; + while ((line = textReader.ReadLine()) != null) + { + if (!firstLine) + textWriter.WriteLine(); + else + firstLine = false; + + lineAction(textWriter, line); + } + } + + /// + /// Numbers the lines. + /// + /// The string to number. + /// + public static string NumberLines(string s) + { + if (s == null) + throw new ArgumentNullException("s"); + + StringReader sr = new StringReader(s); + StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); + + int lineNumber = 1; + + ActionTextReaderLine(sr, sw, delegate(TextWriter tw, string line) + { + tw.Write(lineNumber.ToString(CultureInfo.InvariantCulture).PadLeft(4)); + tw.Write(". "); + tw.Write(line); + + lineNumber++; + }); + + return sw.ToString(); + } + + /// + /// Nulls an empty string. + /// + /// The string. + /// Null if the string was null, otherwise the string unchanged. + public static string NullEmptyString(string s) + { + return (string.IsNullOrEmpty(s)) ? null : s; + } + + public static string ReplaceNewLines(string s, string replacement) + { + StringReader sr = new StringReader(s); + StringBuilder sb = new StringBuilder(); + + bool first = true; + + string line; + while ((line = sr.ReadLine()) != null) + { + if (first) + first = false; + else + sb.Append(replacement); + + sb.Append(line); + } + + return sb.ToString(); + } + + public static string Truncate(string s, int maximumLength) + { + return Truncate(s, maximumLength, "..."); + } + + public static string Truncate(string s, int maximumLength, string suffix) + { + if (suffix == null) + throw new ArgumentNullException("suffix"); + + if (maximumLength <= 0) + throw new ArgumentException("Maximum length must be greater than zero.", "maximumLength"); + + int subStringLength = maximumLength - suffix.Length; + + if (subStringLength <= 0) + throw new ArgumentException("Length of suffix string is greater or equal to maximumLength"); + + if (s != null && s.Length > maximumLength) + { + string truncatedString = s.Substring(0, subStringLength); + // incase the last character is a space + truncatedString = truncatedString.Trim(); + truncatedString += suffix; + + return truncatedString; + } + else + { + return s; + } + } + + public static StringWriter CreateStringWriter(int capacity) + { + StringBuilder sb = new StringBuilder(capacity); + StringWriter sw = new StringWriter(sb, CultureInfo.InvariantCulture); + + return sw; + } + + public static int? GetLength(string value) + { + if (value == null) + return null; + else + return value.Length; + } + + public static string ToCharAsUnicode(char c) + { + char h1 = MathUtils.IntToHex((c >> 12) & '\x000f'); + char h2 = MathUtils.IntToHex((c >> 8) & '\x000f'); + char h3 = MathUtils.IntToHex((c >> 4) & '\x000f'); + char h4 = MathUtils.IntToHex(c & '\x000f'); + + return new string(new[] { '\\', 'u', h1, h2, h3, h4 }); + } + + public static void WriteCharAsUnicode(TextWriter writer, char c) + { + ValidationUtils.ArgumentNotNull(writer, "writer"); + + char h1 = MathUtils.IntToHex((c >> 12) & '\x000f'); + char h2 = MathUtils.IntToHex((c >> 8) & '\x000f'); + char h3 = MathUtils.IntToHex((c >> 4) & '\x000f'); + char h4 = MathUtils.IntToHex(c & '\x000f'); + + writer.Write('\\'); + writer.Write('u'); + writer.Write(h1); + writer.Write(h2); + writer.Write(h3); + writer.Write(h4); + } + + public static TSource ForgivingCaseSensitiveFind(this IEnumerable source, Func valueSelector, string testValue) + { + if (source == null) + throw new ArgumentNullException("source"); + if (valueSelector == null) + throw new ArgumentNullException("valueSelector"); + + var caseInsensitiveResults = source.Where(s => string.Compare(valueSelector(s), testValue, StringComparison.OrdinalIgnoreCase) == 0); + if (caseInsensitiveResults.Count() <= 1) + { + return caseInsensitiveResults.SingleOrDefault(); + } + else + { + // multiple results returned. now filter using case sensitivity + var caseSensitiveResults = source.Where(s => string.Compare(valueSelector(s), testValue, StringComparison.Ordinal) == 0); + return caseSensitiveResults.SingleOrDefault(); + } + } + + public static string ToCamelCase(string s) + { + if (string.IsNullOrEmpty(s)) + return s; + + if (!char.IsUpper(s[0])) + return s; + + string camelCase = char.ToLower(s[0], CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture); + if (s.Length > 1) + camelCase += s.Substring(1); + + return camelCase; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs new file mode 100644 index 0000000..e339e36 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ThreadSafeStore.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; + +namespace Newtonsoft.Json.Utilities +{ + internal class ThreadSafeStore + { + private readonly object _lock = new object(); + private Dictionary _store; + private readonly Func _creator; + + public ThreadSafeStore(Func creator) + { + if (creator == null) + throw new ArgumentNullException("creator"); + + _creator = creator; + } + + public TValue Get(TKey key) + { + if (_store == null) + return AddValue(key); + + TValue value; + if (!_store.TryGetValue(key, out value)) + return AddValue(key); + + return value; + } + + private TValue AddValue(TKey key) + { + TValue value = _creator(key); + + lock (_lock) + { + if (_store == null) + { + _store = new Dictionary(); + _store[key] = value; + } + else + { + // double check locking + TValue checkValue; + if (_store.TryGetValue(key, out checkValue)) + return checkValue; + + Dictionary newStore = new Dictionary(_store); + newStore[key] = value; + + _store = newStore; + } + + return value; + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ValidationUtils.cs b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ValidationUtils.cs new file mode 100644 index 0000000..d77cb52 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Src/Newtonsoft.Json/Utilities/ValidationUtils.cs @@ -0,0 +1,149 @@ +#region License +// Copyright (c) 2007 James Newton-King +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, +// copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following +// conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +// OTHER DEALINGS IN THE SOFTWARE. +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text; +using System.Globalization; + +namespace Newtonsoft.Json.Utilities +{ + internal static class ValidationUtils + { + public const string EmailAddressRegex = @"^([a-zA-Z0-9_'+*$%\^&!\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9:]{2,4})+$"; + public const string CurrencyRegex = @"(^\$?(?!0,?\d)\d{1,3}(,?\d{3})*(\.\d\d)?)$"; + public const string DateRegex = + @"^(((0?[1-9]|[12]\d|3[01])[\.\-\/](0?[13578]|1[02])[\.\-\/]((1[6-9]|[2-9]\d)?\d{2}|\d))|((0?[1-9]|[12]\d|30)[\.\-\/](0?[13456789]|1[012])[\.\-\/]((1[6-9]|[2-9]\d)?\d{2}|\d))|((0?[1-9]|1\d|2[0-8])[\.\-\/]0?2[\.\-\/]((1[6-9]|[2-9]\d)?\d{2}|\d))|(29[\.\-\/]0?2[\.\-\/]((1[6-9]|[2-9]\d)?(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00)|00|[048])))$"; + public const string NumericRegex = @"\d*"; + + public static void ArgumentNotNullOrEmpty(string value, string parameterName) + { + if (value == null) + throw new ArgumentNullException(parameterName); + + if (value.Length == 0) + throw new ArgumentException("'{0}' cannot be empty.".FormatWith(CultureInfo.InvariantCulture, parameterName), parameterName); + } + + public static void ArgumentNotNullOrEmptyOrWhitespace(string value, string parameterName) + { + ArgumentNotNullOrEmpty(value, parameterName); + + if (StringUtils.IsWhiteSpace(value)) + throw new ArgumentException("'{0}' cannot only be whitespace.".FormatWith(CultureInfo.InvariantCulture, parameterName), parameterName); + } + + public static void ArgumentTypeIsEnum(Type enumType, string parameterName) + { + ArgumentNotNull(enumType, "enumType"); + + if (!enumType.IsEnum) + throw new ArgumentException("Type {0} is not an Enum.".FormatWith(CultureInfo.InvariantCulture, enumType), parameterName); + } + + public static void ArgumentNotNullOrEmpty(ICollection collection, string parameterName) + { + ArgumentNotNullOrEmpty(collection, parameterName, "Collection '{0}' cannot be empty.".FormatWith(CultureInfo.InvariantCulture, parameterName)); + } + + public static void ArgumentNotNullOrEmpty(ICollection collection, string parameterName, string message) + { + if (collection == null) + throw new ArgumentNullException(parameterName); + + if (collection.Count == 0) + throw new ArgumentException(message, parameterName); + } + + public static void ArgumentNotNullOrEmpty(ICollection collection, string parameterName) + { + ArgumentNotNullOrEmpty(collection, parameterName, "Collection '{0}' cannot be empty.".FormatWith(CultureInfo.InvariantCulture, parameterName)); + } + + public static void ArgumentNotNullOrEmpty(ICollection collection, string parameterName, string message) + { + if (collection == null) + throw new ArgumentNullException(parameterName); + + if (collection.Count == 0) + throw new ArgumentException(message, parameterName); + } + + public static void ArgumentNotNull(object value, string parameterName) + { + if (value == null) + throw new ArgumentNullException(parameterName); + } + + public static void ArgumentNotNegative(int value, string parameterName) + { + if (value <= 0) + throw MiscellaneousUtils.CreateArgumentOutOfRangeException(parameterName, value, "Argument cannot be negative."); + } + + public static void ArgumentNotNegative(int value, string parameterName, string message) + { + if (value <= 0) + throw MiscellaneousUtils.CreateArgumentOutOfRangeException(parameterName, value, message); + } + + public static void ArgumentNotZero(int value, string parameterName) + { + if (value == 0) + throw MiscellaneousUtils.CreateArgumentOutOfRangeException(parameterName, value, "Argument cannot be zero."); + } + + public static void ArgumentNotZero(int value, string parameterName, string message) + { + if (value == 0) + throw MiscellaneousUtils.CreateArgumentOutOfRangeException(parameterName, value, message); + } + + public static void ArgumentIsPositive(T value, string parameterName) where T : struct, IComparable + { + if (value.CompareTo(default(T)) != 1) + throw MiscellaneousUtils.CreateArgumentOutOfRangeException(parameterName, value, "Positive number required."); + } + + public static void ArgumentIsPositive(int value, string parameterName, string message) + { + if (value > 0) + throw MiscellaneousUtils.CreateArgumentOutOfRangeException(parameterName, value, message); + } + + public static void ObjectNotDisposed(bool disposed, Type objectType) + { + if (disposed) + throw new ObjectDisposedException(objectType.Name); + } + + public static void ArgumentConditionTrue(bool condition, string parameterName, string message) + { + if (!condition) + throw new ArgumentException(message, parameterName); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Tools/7-zip/7-zip.chm b/trunk/Libraries/Json40r2/Source/Tools/7-zip/7-zip.chm new file mode 100644 index 0000000..eee875e Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/7-zip/7-zip.chm differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/7-zip/copying.txt b/trunk/Libraries/Json40r2/Source/Tools/7-zip/copying.txt new file mode 100644 index 0000000..4c38901 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/7-zip/copying.txt @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/trunk/Libraries/Json40r2/Source/Tools/7-zip/license.txt b/trunk/Libraries/Json40r2/Source/Tools/7-zip/license.txt new file mode 100644 index 0000000..2b4c60c --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/7-zip/license.txt @@ -0,0 +1,30 @@ + 7-Zip Command line version + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + License for use and distribution + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + 7-Zip Copyright (C) 1999-2009 Igor Pavlov. + + 7za.exe is distributed under the GNU LGPL license + + Notes: + You can use 7-Zip on any computer, including a computer in a commercial + organization. You don't need to register or pay for 7-Zip. + + + GNU LGPL information + -------------------- + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA diff --git a/trunk/Libraries/Json40r2/Source/Tools/7-zip/readme.txt b/trunk/Libraries/Json40r2/Source/Tools/7-zip/readme.txt new file mode 100644 index 0000000..91bb3cd --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/7-zip/readme.txt @@ -0,0 +1,42 @@ +7-Zip Command line version 4.65 +------------------------------- + +7-Zip is a file archiver with high compression ratio. +7za.exe is a standalone command line version of 7-Zip. + +7-Zip Copyright (C) 1999-2009 Igor Pavlov. + +Features of 7za.exe: + - High compression ratio in new 7z format + - Supported formats: + - Packing / unpacking: 7z, ZIP, GZIP, BZIP2 and TAR + - Unpacking only: Z + - Highest compression ratio for ZIP and GZIP formats. + - Fast compression and decompression + - Strong AES-256 encryption in 7z and ZIP formats. + +7za.exe is a free software distributed under the GNU LGPL. +Read license.txt for more information. + +Source code of 7za.exe and 7-Zip can be found at +http://www.7-zip.org/ + +7za.exe can work in Windows 95/98/ME/NT/2000/XP/2003/Vista. + +There is also port of 7za.exe for POSIX systems like Unix (Linux, Solaris, OpenBSD, +FreeBSD, Cygwin, AIX, ...), MacOS X and BeOS: + +http://p7zip.sourceforge.net/ + + + This distributive packet contains the following files: + + 7za.exe - 7-Zip standalone command line version. + readme.txt - This file. + copying.txt - GNU LGPL license. + license.txt - License information. + 7-zip.chm - User's Manual in HTML Help format. + + +--- +End of document diff --git a/trunk/Libraries/Json40r2/Source/Tools/ILMerge/ILMerge License.rtf b/trunk/Libraries/Json40r2/Source/Tools/ILMerge/ILMerge License.rtf new file mode 100644 index 0000000..e800151 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/ILMerge/ILMerge License.rtf @@ -0,0 +1,104 @@ +{\rtf1\ansi\ansicpg1252\uc1\deff0\stshfdbch0\stshfloch0\stshfhich0\stshfbi0\deflang1033\deflangfe1033{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f37\fswiss\fcharset0\fprq2{\*\panose 020b0604030504040204}Tahoma;} +{\f39\froman\fcharset0\fprq2{\*\panose 02040602050305030304}Book Antiqua;}{\f40\fswiss\fcharset0\fprq2{\*\panose 020b0706030402020204}Franklin Gothic Demi Cond;}{\f41\fswiss\fcharset0\fprq2{\*\panose 020b0503020102020204}Franklin Gothic Book;} +{\f42\froman\fcharset238\fprq2 Times New Roman CE;}{\f43\froman\fcharset204\fprq2 Times New Roman Cyr;}{\f45\froman\fcharset161\fprq2 Times New Roman Greek;}{\f46\froman\fcharset162\fprq2 Times New Roman Tur;} +{\f47\froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f48\froman\fcharset178\fprq2 Times New Roman (Arabic);}{\f49\froman\fcharset186\fprq2 Times New Roman Baltic;}{\f50\froman\fcharset163\fprq2 Times New Roman (Vietnamese);} +{\f412\fswiss\fcharset238\fprq2 Tahoma CE;}{\f413\fswiss\fcharset204\fprq2 Tahoma Cyr;}{\f415\fswiss\fcharset161\fprq2 Tahoma Greek;}{\f416\fswiss\fcharset162\fprq2 Tahoma Tur;}{\f417\fswiss\fcharset177\fprq2 Tahoma (Hebrew);} +{\f418\fswiss\fcharset178\fprq2 Tahoma (Arabic);}{\f419\fswiss\fcharset186\fprq2 Tahoma Baltic;}{\f420\fswiss\fcharset163\fprq2 Tahoma (Vietnamese);}{\f421\fswiss\fcharset222\fprq2 Tahoma (Thai);}{\f432\froman\fcharset238\fprq2 Book Antiqua CE;} +{\f433\froman\fcharset204\fprq2 Book Antiqua Cyr;}{\f435\froman\fcharset161\fprq2 Book Antiqua Greek;}{\f436\froman\fcharset162\fprq2 Book Antiqua Tur;}{\f439\froman\fcharset186\fprq2 Book Antiqua Baltic;} +{\f442\fswiss\fcharset238\fprq2 Franklin Gothic Demi Cond CE;}{\f443\fswiss\fcharset204\fprq2 Franklin Gothic Demi Cond Cyr;}{\f445\fswiss\fcharset161\fprq2 Franklin Gothic Demi Cond Greek;}{\f446\fswiss\fcharset162\fprq2 Franklin Gothic Demi Cond Tur;} +{\f449\fswiss\fcharset186\fprq2 Franklin Gothic Demi Cond Baltic;}{\f452\fswiss\fcharset238\fprq2 Franklin Gothic Book CE;}{\f453\fswiss\fcharset204\fprq2 Franklin Gothic Book Cyr;}{\f455\fswiss\fcharset161\fprq2 Franklin Gothic Book Greek;} +{\f456\fswiss\fcharset162\fprq2 Franklin Gothic Book Tur;}{\f459\fswiss\fcharset186\fprq2 Franklin Gothic Book Baltic;}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255; +\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;} +{\stylesheet{\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \snext0 Normal;}{\*\cs10 \additive \ssemihidden Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tscellwidthfts0\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv +\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \fs20\lang1024\langfe1024\cgrid\langnp1024\langfenp1024 \snext11 \ssemihidden Normal Table;}{\s15\ql \fi-274\li274\ri0\sb120\sl460\slmult0 +\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin274\itap0 \f40\fs44\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext15 \styrsid9786739 1sectionhead;}{\s16\ql \li0\ri0\sb120\sl200\slmult0 +\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \caps\f40\fs16\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext16 \styrsid9786739 4laparahead;}{\s17\ql \li0\ri-18\sb120\sl240\slmult0 +\widctlpar\aspalpha\aspnum\faauto\adjustright\rin-18\lin0\itap0 \f40\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext17 \styrsid9786739 2lasubhead;}{\s18\ql \fi-187\li187\ri0\sb60\sl180\slmult0 +\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin187\itap0 \f41\fs16\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext18 \styrsid9786739 3cnumbered;}{\s19\ql \fi-340\li624\ri0\sb60\sl160\slmult0 +\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin624\itap0 \f41\fs14\cf1\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext19 \styrsid9786739 3inumbered2ndlevel;}{\s20\ql \li0\ri0\sb240\sl240\slmult0 +\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \f40\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext20 \styrsid9786739 2afrenchsubhead;}{\s21\ql \li0\ri0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 +\cbpat9 \f37\fs20\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext21 \ssemihidden \styrsid7154712 Document Map;}}{\*\latentstyles\lsdstimax156\lsdlockeddef0}{\*\pgptbl {\pgp\ipgp2\itap0\li0\ri0\sb0\sa0\brdrt\brdrs\brdrw20 }{\pgp\ipgp0 +\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid2099452\rsid4207571\rsid5465292\rsid5510097\rsid5510644\rsid7154712\rsid7241305\rsid7672529\rsid7735936\rsid9179139\rsid9786739\rsid10440675\rsid11303133\rsid13130884\rsid14028235\rsid14100361\rsid14113652 +\rsid15276140\rsid16213514}{\*\generator Microsoft Word 11.0.6359;}{\info{\title ILMerge EULA}{\author Ken Leppert}{\operator mbarnett}{\creatim\yr2005\mo3\dy16\hr15\min43}{\revtim\yr2005\mo3\dy16\hr15\min43}{\printim\yr2004\mo4\dy30\hr13\min9}{\version2} +{\edmins0}{\nofpages3}{\nofwords1188}{\nofchars6775}{\*\company Microsoft Corporation}{\nofcharsws7948}{\vern24703}}\widowctrl\ftnbj\aenddoc\noxlattoyen\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180 +\dghorigin1800\dgvorigin1440\dghshow1\dgvshow1\jexpand\viewkind1\viewscale68\viewzk2\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel +\wrppunct\asianbrkrule\rsidroot9786739\newtblstyruls\nogrowautofit \fet0\sectd \linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang +{\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang +{\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain +\s15\ql \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin0\itap0\pararsid7154712 \f40\fs44\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\b\f39\fs28\insrsid9786739 MICROSOFT }{ +\b\f39\fs28\insrsid5465292 ILMerge}{\insrsid9786739 +\par }{\b\f39\fs22\insrsid9786739 END-USER LICENSE AGREEMENT FOR MICROSOFT SOFTWARE}{\insrsid9786739 +\par }\pard\plain \s17\qj \li0\ri-17\sb100\sa100\sbauto1\saauto1\widctlpar\aspalpha\aspnum\faauto\adjustright\rin-17\lin0\itap0\pararsid14100361 \f40\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\b\f39\insrsid9786739 IMPORTANT\emdash +READ CAREFULLY: }{\f39\insrsid9786739 This End-User License Agreement (\'93EULA\'94) is a legal agreement between you (either an individual or a single entity) and Microsoft Corporation (\'93Microsoft\'94) for th +e Microsoft software that accompanies this EULA, which includes computer software and may include associated media, printed materials, \'93online\'94 or electronic documentation, and Internet-based services (\'93Software\'94).\~ + An amendment or addendum to this EULA may accompany the Software.\~ }{\b\f39\insrsid9786739 +YOU AGREE TO BE BOUND BY THE TERMS OF THIS EULA BY INSTALLING, COPYING, OR OTHERWISE USING THE SOFTWARE. IF YOU DO NOT AGREE, DO NOT INSTALL, COPY, OR USE THE SOFTWARE.}{\insrsid9786739 +\par }\pard\plain \qj \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14100361 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\f39\fs22\insrsid9786739 1.\~\~\~\~\~\~\~\~ }{ +\b\f39\fs22\insrsid9786739 GRANTS OF LICENSE}{\f39\fs22\insrsid9786739 . Microsoft grants you the rights described in this EULA provided that you comply with all terms and conditions of this EULA.\~ }{\insrsid9786739 +\par }\pard\plain \s19\qj \fi720\li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14100361 \f41\fs14\cf1\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\f39\fs22\cf0\insrsid9786739 1.1\~\~\~\~\~\~ }{ +\b\i\f39\fs22\cf0\insrsid9786739 License Grant}{\f39\fs22\cf0\insrsid9786739 . Microsoft grants to you a personal, nonexclusive, nontransferable, limited license to }{\f39\fs22\insrsid9786739 install and use a reasonable number of copies of +the Software on computers residing on your premises }{\f39\fs22\cf0\insrsid9786739 for the purposes of designing, developing, and testing, your software product(s), provided that you are the only individual using the Software.\~ }{\insrsid9786739 +\par }\pard\plain \s18\qj \fi720\li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14100361 \f41\fs16\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\f39\fs22\insrsid9786739 1.2\~\~\~\~\~\~ }{ +\b\i\f39\fs22\insrsid9786739 Documentation}{\f39\fs22\insrsid9786739 .}{\b\f39\fs22\insrsid9786739 \~ }{\f39\fs22\insrsid9786739 You may make and use a reasonabl +e number of copies of any documentation, provided that such copies shall be used only for your personal purposes and are not to be republished or distributed (either in hard copy or electronic form) beyond your premises.}{\insrsid9786739 +\par }\pard \s18\qj \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14100361 {\f39\fs22\insrsid9786739 2.\~\~\~\~\~\~\~\~ }{\b\f39\fs22\insrsid9786739 RESERVATION OF RIGHTS AND OWNERSHIP.\~ }{ +\f39\fs22\insrsid9786739 The Software is licensed as a single product.\~ Its component parts may not be separated. Microsoft reserves all rights not expressly granted to you in this EULA.\~ + The Software is protected by copyright and other intellectual property laws and treaties}{\f39\fs22\insrsid14028235 , and}{\f39\fs22\insrsid9786739 Microsoft }{\f39\fs22\insrsid14028235 (}{\f39\fs22\insrsid9786739 or its suppliers}{ +\f39\fs22\insrsid14028235 , where applicable)}{\f39\fs22\insrsid9786739 own }{\f39\fs22\insrsid14028235 all right, }{\f39\fs22\insrsid9786739 title, }{\f39\fs22\insrsid14028235 and interest in all }{\f39\fs22\insrsid9786739 +intellectual property rights in the Software.\~ }{\b\f39\fs22\insrsid9786739 The Software is licensed, not sold.}{\insrsid9786739 +\par }\pard\plain \s19\qj \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14100361 \f41\fs14\cf1\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\f39\fs22\cf0\insrsid9786739 3.\~\~\~\~\~\~\~\~ }{ +\b\f39\fs22\cf0\insrsid9786739 LIMITATIONS ON REVERSE ENGINEERING, DECOMPILATION, AND DISASSEMBLY}{\b\i\f39\fs22\cf0\insrsid9786739 .}{\f39\fs22\cf0\insrsid9786739 \~ + You may not reverse engineer, decompile, or disassemble the Software, except and only to the extent that such activity is expressly permitted by applicable law notwithstanding this limitation.}{\insrsid9786739 +\par }\pard\plain \s18\qj \li0\ri0\sb100\sa100\sbauto1\saauto1\sl220\slmult0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14100361 \f41\fs16\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\f39\fs22\insrsid9786739 4.\~\~\~\~\~\~\~\~ +}{\b\f39\fs22\insrsid9786739 NO RENTAL/COMMERCIAL HOSTING.}{\b\i\f39\fs22\insrsid9786739 }{\f39\fs22\insrsid9786739 You may not rent, lease, lend or provide commercial hosting services with the Software.}{\insrsid9786739 +\par }\pard\plain \s19\qj \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14100361 \f41\fs14\cf1\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\f39\fs22\cf0\insrsid9786739 5.\~\~\~\~\~\~\~\~ }{ +\b\f39\fs22\cf0\insrsid9786739 NO SOFTWARE TRANSFER.\~ }{\f39\fs22\cf0\insrsid9786739 You may not assign or otherwise transfer the SOFTWARE or any of your rights hereunder to any third party.}{\insrsid9786739 +\par }{\f39\fs22\cf0\insrsid9786739 6.\~\~\~\~\~\~\~\~ }{\b\f39\fs22\cf0\insrsid9786739 CONSENT TO USE OF DATA.\~ }{\f39\fs22\cf0\insrsid9786739 You + agree that Microsoft and its affiliates may collect and use technical information gathered as part of the product support services provided to you, if any, related to the Software.\~ + Microsoft may use this information solely to improve our products or to provide customized services or technologies to you and will not disclose this information in a form that personally identifies you.\~\~ }{\insrsid9786739 +\par }{\f39\fs22\cf0\insrsid5510644 7}{\f39\fs22\cf0\insrsid9786739 .\~\~\~\~\~\~\~\~ }{\b\f39\fs22\cf0\insrsid9786739 ADDITIONAL SOFTWARE/SERVICES.\~ }{\f39\fs22\insrsid9786739 Microsoft is not obligated to provide maintenance, technical supplements}{ +\f39\fs22\insrsid14028235 , updates,}{\f39\fs22\insrsid9786739 or other support to you for the Software licensed under this EULA. }{\f39\fs22\insrsid7241305 }{\f39\fs22\insrsid9786739 In the event that Microsoft does provide such supplements or updates} +{\b\f39\fs22\insrsid9786739 , }{\f39\fs22\insrsid9786739 this EULA applies to such updates, supplements, or add-on components of the Software that Microsoft may provide to +you or make available to you after the date you obtain your initial copy of the Software, unless we provide other terms along with the update, supplement, or add-on component}{\f39\fs22\cf0\insrsid9786739 .\~ + Microsoft reserves the right to discontinue any Internet-based services provided to you or made available to you through the use of the Software.\~ }{\insrsid9786739 +\par }\pard\plain \s18\qj \li0\ri0\sb100\sa100\sbauto1\saauto1\sl220\slmult0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14100361 \f41\fs16\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\f39\fs22\insrsid5510644 8}{ +\f39\fs22\insrsid9786739 .\~\~\~\~\~\~\~\~ }{\b\f39\fs22\insrsid9786739 EXPORT RESTRICTIONS}{\f39\fs22\insrsid9786739 .\~ }{\f39\fs22\cgrid0\insrsid9786739 You acknowledge that the Software is subject to U.S. export jurisdiction.\~ + You agree to comply with all applicable international and national laws that apply to the Software, including the U.S. Export Administration Regulations, as well as end-user, end-use, and destination restrictions issued by U.S. and other governments.\~\~ + For additional information see }{\f39\fs22\ul\cgrid0\insrsid9786739 http://www.microsoft.com/exporting/}{\f39\fs22\cgrid0\insrsid9786739 .}{\insrsid9786739 +\par }\pard\plain \s19\qj \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14100361 \f41\fs14\cf1\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\f39\fs22\cf0\insrsid14113652 9}{ +\f39\fs22\cf0\insrsid9786739 .\~\~\~\~\~\~ }{\b\f39\fs22\cf0\insrsid9786739 TERMINATION.}{\f39\fs22\cf0\insrsid9786739 \~ Without prejudice to any other rights, Microsoft may terminate this EULA if you fail to comply with }{\f39\fs22\cf0\insrsid7241305 +any }{\f39\fs22\cf0\insrsid9786739 term}{\f39\fs22\cf0\insrsid7241305 or}{\f39\fs22\cf0\insrsid9786739 condition of this EULA. }{\f39\fs22\cf0\insrsid7241305 }{\f39\fs22\cf0\insrsid9786739 +In such event, you must destroy all copies of the Software and all of its component parts.}{\insrsid9786739 +\par }\pard\plain \s18\qj \li0\ri0\sb100\sa100\sbauto1\saauto1\sl220\slmult0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14100361 \f41\fs16\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\f39\fs22\insrsid9786739 1}{ +\f39\fs22\insrsid14113652 0}{\f39\fs22\insrsid9786739 .\~\~\~\~\~\~ }{\b\f39\fs22\ul\insrsid9786739 DISCLAIMER OF WARRANTIES}{\b\f39\fs22\insrsid9786739 .\~ + TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, MICROSOFT AND ITS SUPPLIERS PROVIDE THE SOFTWARE}{\f39\fs22\insrsid9786739 }{\b\f39\fs22\insrsid9786739 AND SUPPORT SERVICES (IF ANY) }{\b\i\f39\fs22\insrsid9786739 AS IS AND WITH ALL FAULTS}{ +\b\f39\fs22\insrsid9786739 , AND HEREBY DISCLAIM ALL OTHER WARRANTIES AND CONDITIONS, WHETHER EXPRESS, IMPLIED + OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY (IF ANY) IMPLIED WARRANTIES, DUTIES OR CONDITIONS OF MERCHANTABILITY, OF FITNESS FOR A PARTICULAR PURPOSE, OF RELIABILITY OR AVAILABILITY, OF ACCURACY OR COMPLETENESS OF RESPONSES, OF RESULTS, OF WORKMANLI +K +E EFFORT, OF LACK OF VIRUSES, AND OF LACK OF NEGLIGENCE, ALL WITH REGARD TO THE SOFTWARE, AND THE PROVISION OF OR FAILURE TO PROVIDE SUPPORT OR OTHER SERVICES, INFORMATION, SOFTWARE, AND RELATED CONTENT THROUGH THE SOFTWARE OR OTHERWISE ARISING OUT OF THE + USE OF THE SOFTWARE.\~ ALSO, THERE IS NO WARRANTY OR CONDITION OF TITLE, QUIET ENJOYMENT, QUIET POSSESSION, CORRESPONDENCE TO DESCRIPTION OR NON-INFRINGEMENT WITH REGARD TO THE SOFTWARE.}{\insrsid9786739 +\par }{\f39\fs22\insrsid9786739 1}{\f39\fs22\insrsid14113652 1}{\f39\fs22\insrsid9786739 .}{\b\f39\fs22\insrsid9786739 \~\~\~\~\~\~ }{\b\f39\fs22\ul\insrsid9786739 EXCLUSION OF INCIDENTAL, CONSEQUENTIAL AND CERTAIN OTHER DAMAGES}{\b\f39\fs22\insrsid9786739 .\~ + }{\b\caps\f39\fs22\insrsid9786739 +To the maximum extent permitted by applicable law, in no event shall Microsoft or its suppliers be liable for any special, incidental, punitive, indirect, or consequential damages whatsoever (including, but not limited to, damages for loss of profit +s, LOSS OF DATA, or confidential or other information}{\b\f39\fs22\insrsid9786739 , }{\b\caps\f39\fs22\insrsid9786739 +for business interruption, for personal injury, for loss of privacy, for failure to meet any duty including of good faith or of reasonable care, for negligence, and}{\b\f39\fs22\insrsid9786739 }{\b\caps\f39\fs22\insrsid9786739 +for any other pecuniary or other los +s whatsoever) arising out of or in any way related to the use of or inability to use the SOFTWARE, the provision of or failure to provide Support OR OTHER Services, informatIon, software, and related CONTENT through the software or otherwise arising out o +f + the use of the software, or otherwise under or in connection with any provision of this EULA, even in the event of the fault, tort (including negligence), misrepresentation, strict liability, breach of contract or breach of warranty of Microsoft or any s +upplier, and even if Microsoft or any supplier has been advised of the possibility of such damages. }{\insrsid9786739 +\par }{\f39\fs22\insrsid9786739 1}{\f39\fs22\insrsid14113652 2}{\f39\fs22\insrsid9786739 .}{\b\f39\fs22\insrsid9786739 \~\~\~\~\~\~ }{\b\f39\fs22\ul\insrsid9786739 LIMITATION OF LIABILITY AND REMEDIES}{\b\f39\fs22\insrsid9786739 +. NOTWITHSTANDING ANY DAMAGES THAT YOU MIGHT INCUR FOR ANY REASON WHATSOEVER (INCLUDING, WITHOUT LIMITATION, A +LL DAMAGES REFERENCED HEREIN AND ALL DIRECT OR GENERAL DAMAGES IN CONTRACT OR ANYTHING ELSE), THE ENTIRE LIABILITY OF MICROSOFT AND ANY OF ITS SUPPLIERS UNDER ANY PROVISION OF THIS EULA AND YOUR EXCLUSIVE REMEDY HEREUNDER SHALL BE LIMITED TO THE GREATER O +F THE ACTUAL DAMAGES YOU INCUR IN REASONABLE RELIANCE ON THE SOFTWARE UP TO THE AMOUNT ACTUALLY PAID BY YOU FOR THE SOFTWARE}{\f39\fs22\insrsid9786739 }{\b\f39\fs22\insrsid9786739 OR US$5.00.\~ + THE FOREGOING LIMITATIONS, EXCLUSIONS AND DISCLAIMERS SHALL APPLY TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, EVEN IF ANY REMEDY FAILS ITS ESSENTIAL PURPOSE.}{\insrsid9786739 +\par }{\f39\fs22\insrsid9786739 1}{\f39\fs22\insrsid14113652 3}{\f39\fs22\insrsid9786739 .\~\~\~\~\~\~ }{\b\f39\fs22\insrsid9786739 APPLICABLE LAW.\~ }{\f39\fs22\insrsid7735936 T}{\f39\fs22\insrsid9786739 his EULA }{\f39\fs22\insrsid7735936 +shall be construed under and }{\f39\fs22\insrsid9786739 governed by the laws of the State of Washington}{\f39\fs22\insrsid7735936 , without regard to conflicts of law principles}{\f39\fs22\insrsid9786739 .\~ }{\insrsid9786739 +\par }\pard\plain \s20\qj \li0\ri0\sb100\sa100\sbauto1\saauto1\sl240\slmult0\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14100361 \f40\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\f39\insrsid9786739 1}{\f39\insrsid14113652 +4}{\f39\insrsid9786739 .\~\~\~\~ }{\b\f39\insrsid9786739 ENTIRE AGREEMENT; SEVERABILITY.\~ }{\f39\insrsid9786739 This +EULA (including any addendum or amendment to this EULA which is included with the Software) are the entire agreement between you and Microsoft relating to the Software and the support services (if any) and they supersede all prior or contemporaneous oral +or written communications,\~proposals and representations with respect to the Software or any other subject matter covered by this EULA.\~ + If any provision of this EULA is held to be void, invalid, unenforceable or illegal, the other provisions shall continue in full force and effect}{\insrsid9786739 +\par }\pard\plain \qj \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14100361 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\insrsid10440675 +\par }} \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Tools/ILMerge/ILMerge.doc b/trunk/Libraries/Json40r2/Source/Tools/ILMerge/ILMerge.doc new file mode 100644 index 0000000..1b85114 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/ILMerge/ILMerge.doc differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/NUnitFitTests.html b/trunk/Libraries/Json40r2/Source/Tools/NUnit/NUnitFitTests.html new file mode 100644 index 0000000..ca5cd4f --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/NUnitFitTests.html @@ -0,0 +1,277 @@ + + + +

NUnit Acceptance Tests

+

+ Developers love self-referential programs! Hence, NUnit has always run all it's + own tests, even those that are not really unit tests. +

Now, beginning with NUnit 2.4, NUnit has top-level tests using Ward Cunningham's + FIT framework. At this time, the tests are pretty rudimentary, but it's a start + and it's a framework for doing more. +

Running the Tests

+

Open a console or shell window and navigate to the NUnit bin directory, which + contains this file. To run the test under Microsoft .Net, enter the command +

    runFile NUnitFitTests.html TestResults.html .
+ To run it under Mono, enter +
    mono runFile.exe NUnitFitTests.html TestResults.html .
+ Note the space and dot at the end of each command. The results of your test + will be in TestResults.html in the same directory. +

Platform and CLR Version

+ + + + +
NUnit.Fixtures.PlatformInfo
+

Verify Unit Tests

+

+ Load and run the NUnit unit tests, verifying that the results are as expected. + When these tests are run on different platforms, different numbers of tests may + be skipped, so the values for Skipped and Run tests are informational only. +

+ The number of tests in each assembly should be constant across all platforms - + any discrepancy usually means that one of the test source files was not + compiled on the platform. There should be no failures and no tests ignored. +

Note: + At the moment, the nunit.extensions.tests assembly is failing because the + fixture doesn't initialize addins in the test domain. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NUnit.Fixtures.AssemblyRunner
AssemblyTests()Run()Skipped()Ignored()Failures()
nunit.framework.tests.dll397  00
nunit.core.tests.dll355  00
nunit.util.tests.dll238  00
nunit.mocks.tests.dll43  00
nunit.extensions.tests.dll5  00
nunit-console.tests.dll40  00
nunit.uikit.tests.dll34  00
nunit-gui.tests.dll15  00
nunit.fixtures.tests.dll6  00
+

Code Snippet Tests

+

+ These tests create a test assembly from a snippet of code and then load and run + the tests that it contains, verifying that the structure of the loaded tests is + as expected and that the number of tests run, skipped, ignored or failed is + correct. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NUnit.Fixtures.SnippetRunner
CodeTree()Run()Skipped()Ignored()Failures()
public class TestClass
+{
+}
+
EMPTY0000
using NUnit.Framework;
+
+[TestFixture]
+public class TestClass
+{
+}
+
TestClass0000
using NUnit.Framework;
+
+[TestFixture]
+public class TestClass
+{
+    [Test]
+    public void T1() { }
+    [Test]
+    public void T2() { }
+    [Test]
+    public void T3() { }
+}
+
TestClass
+>T1
+>T2
+>T3
+
3000
using NUnit.Framework;
+
+[TestFixture]
+public class TestClass1
+{
+    [Test]
+    public void T1() { }
+}
+
+[TestFixture]
+public class TestClass2
+{
+    [Test]
+    public void T2() { }
+    [Test]
+    public void T3() { }
+}
+
TestClass1
+>T1
+TestClass2
+>T2
+>T3
+
3000
using NUnit.Framework;
+
+[TestFixture]
+public class TestClass
+{
+    [Test]
+    public void T1() { }
+    [Test, Ignore]
+    public void T2() { }
+    [Test]
+    public void T3() { }
+}
+
TestClass
+>T1
+>T2
+>T3
+
2010
using NUnit.Framework;
+
+[TestFixture]
+public class TestClass
+{
+    [Test]
+    public void T1() { }
+    [Test, Explicit]
+    public void T2() { }
+    [Test]
+    public void T3() { }
+}
+
TestClass
+>T1
+>T2
+>T3
+
2100
+

Summary Information

+ + + + +
fit.Summary
+ + diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/NUnitTests.config b/trunk/Libraries/Json40r2/Source/Tools/NUnit/NUnitTests.config new file mode 100644 index 0000000..de8a656 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/NUnitTests.config @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/NUnitTests.nunit b/trunk/Libraries/Json40r2/Source/Tools/NUnit/NUnitTests.nunit new file mode 100644 index 0000000..e7bb7f4 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/NUnitTests.nunit @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/agent.conf b/trunk/Libraries/Json40r2/Source/Tools/NUnit/agent.conf new file mode 100644 index 0000000..ddbcd8e --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/agent.conf @@ -0,0 +1,4 @@ + + 8080 + . + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/agent.log.conf b/trunk/Libraries/Json40r2/Source/Tools/NUnit/agent.log.conf new file mode 100644 index 0000000..4bd90ca --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/agent.log.conf @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/framework/nunit.framework.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/framework/nunit.framework.dll new file mode 100644 index 0000000..639dbb0 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/framework/nunit.framework.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/framework/nunit.framework.xml b/trunk/Libraries/Json40r2/Source/Tools/NUnit/framework/nunit.framework.xml new file mode 100644 index 0000000..e67b2cd --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/framework/nunit.framework.xml @@ -0,0 +1,10228 @@ + + + + nunit.framework + + + + + BinaryConstraint is the abstract base of all constraints + that combine two other constraints in some fashion. + + + + + The Constraint class is the base of all built-in constraints + within NUnit. It provides the operator overloads used to combine + constraints. + + + + + The IConstraintExpression interface is implemented by all + complete and resolvable constraints and expressions. + + + + + Return the top-level constraint for this expression + + + + + + Static UnsetObject used to detect derived constraints + failing to set the actual value. + + + + + The actual value being tested against a constraint + + + + + The display name of this Constraint for use by ToString() + + + + + Argument fields used by ToString(); + + + + + The builder holding this constraint + + + + + Construct a constraint with no arguments + + + + + Construct a constraint with one argument + + + + + Construct a constraint with two arguments + + + + + Sets the ConstraintBuilder holding this constraint + + + + + Write the failure message to the MessageWriter provided + as an argument. The default implementation simply passes + the constraint and the actual value to the writer, which + then displays the constraint description and the value. + + Constraints that need to provide additional details, + such as where the error occured can override this. + + The MessageWriter on which to display the message + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Test whether the constraint is satisfied by an + ActualValueDelegate that returns the value to be tested. + The default implementation simply evaluates the delegate + but derived classes may override it to provide for delayed + processing. + + An ActualValueDelegate + True for success, false for failure + + + + Test whether the constraint is satisfied by a given reference. + The default implementation simply dereferences the value but + derived classes may override it to provide for delayed processing. + + A reference to the value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + Default override of ToString returns the constraint DisplayName + followed by any arguments within angle brackets. + + + + + + Returns the string representation of this constraint + + + + + This operator creates a constraint that is satisfied only if both + argument constraints are satisfied. + + + + + This operator creates a constraint that is satisfied if either + of the argument constraints is satisfied. + + + + + This operator creates a constraint that is satisfied if the + argument constraint is not satisfied. + + + + + Returns a DelayedConstraint with the specified delay time. + + The delay in milliseconds. + + + + + Returns a DelayedConstraint with the specified delay time + and polling interval. + + The delay in milliseconds. + The interval at which to test the constraint. + + + + + The display name of this Constraint for use by ToString(). + The default value is the name of the constraint with + trailing "Constraint" removed. Derived classes may set + this to another name in their constructors. + + + + + Returns a ConstraintExpression by appending And + to the current constraint. + + + + + Returns a ConstraintExpression by appending And + to the current constraint. + + + + + Returns a ConstraintExpression by appending Or + to the current constraint. + + + + + Class used to detect any derived constraints + that fail to set the actual value in their + Matches override. + + + + + The first constraint being combined + + + + + The second constraint being combined + + + + + Construct a BinaryConstraint from two other constraints + + The first constraint + The second constraint + + + + AndConstraint succeeds only if both members succeed. + + + + + Create an AndConstraint from two other constraints + + The first constraint + The second constraint + + + + Apply both member constraints to an actual value, succeeding + succeeding only if both of them succeed. + + The actual value + True if the constraints both succeeded + + + + Write a description for this contraint to a MessageWriter + + The MessageWriter to receive the description + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + OrConstraint succeeds if either member succeeds + + + + + Create an OrConstraint from two other constraints + + The first constraint + The second constraint + + + + Apply the member constraints to an actual value, succeeding + succeeding as soon as one of them succeeds. + + The actual value + True if either constraint succeeded + + + + Write a description for this contraint to a MessageWriter + + The MessageWriter to receive the description + + + + CollectionConstraint is the abstract base class for + constraints that operate on collections. + + + + + Construct an empty CollectionConstraint + + + + + Construct a CollectionConstraint + + + + + + Determines whether the specified enumerable is empty. + + The enumerable. + + true if the specified enumerable is empty; otherwise, false. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Protected method to be implemented by derived classes + + + + + + + CollectionItemsEqualConstraint is the abstract base class for all + collection constraints that apply some notion of item equality + as a part of their operation. + + + + + Construct an empty CollectionConstraint + + + + + Construct a CollectionConstraint + + + + + + Flag the constraint to use the supplied IComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied Comparison object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IEqualityComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IEqualityComparer object. + + The IComparer object to use. + Self. + + + + Compares two collection members for equality + + + + + Return a new CollectionTally for use in making tests + + The collection to be included in the tally + + + + Flag the constraint to ignore case and return self. + + + + + EmptyCollectionConstraint tests whether a collection is empty. + + + + + Check that the collection is empty + + + + + + + Write the constraint description to a MessageWriter + + + + + + UniqueItemsConstraint tests whether all the items in a + collection are unique. + + + + + Check that all items are unique. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + CollectionContainsConstraint is used to test whether a collection + contains an expected object as a member. + + + + + Construct a CollectionContainsConstraint + + + + + + Test whether the expected item is contained in the collection + + + + + + + Write a descripton of the constraint to a MessageWriter + + + + + + CollectionEquivalentCOnstraint is used to determine whether two + collections are equivalent. + + + + + Construct a CollectionEquivalentConstraint + + + + + + Test whether two collections are equivalent + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + CollectionSubsetConstraint is used to determine whether + one collection is a subset of another + + + + + Construct a CollectionSubsetConstraint + + The collection that the actual value is expected to be a subset of + + + + Test whether the actual collection is a subset of + the expected collection provided. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + CollectionOrderedConstraint is used to test whether a collection is ordered. + + + + + Construct a CollectionOrderedConstraint + + + + + Modifies the constraint to use an IComparer and returns self. + + + + + Modifies the constraint to use an IComparer<T> and returns self. + + + + + Modifies the constraint to use a Comparison<T> and returns self. + + + + + Modifies the constraint to test ordering by the value of + a specified property and returns self. + + + + + Test whether the collection is ordered + + + + + + + Write a description of the constraint to a MessageWriter + + + + + + Returns the string representation of the constraint. + + + + + + If used performs a reverse comparison + + + + + Abstract base class for constraints that compare values to + determine if one is greater than, equal to or less than + the other. + + + + + The value against which a comparison is to be made + + + + + If true, less than returns success + + + + + if true, equal returns success + + + + + if true, greater than returns success + + + + + The predicate used as a part of the description + + + + + ComparisonAdapter to be used in making the comparison + + + + + Initializes a new instance of the class. + + The value against which to make a comparison. + if set to true less succeeds. + if set to true equal succeeds. + if set to true greater succeeds. + String used in describing the constraint. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Modifies the constraint to use an IComparer and returns self + + + + + Modifies the constraint to use an IComparer<T> and returns self + + + + + Modifies the constraint to use a Comparison<T> and returns self + + + + + Tests whether a value is greater than the value supplied to its constructor + + + + + Initializes a new instance of the class. + + The expected value. + + + + Tests whether a value is greater than or equal to the value supplied to its constructor + + + + + Initializes a new instance of the class. + + The expected value. + + + + Tests whether a value is less than the value supplied to its constructor + + + + + Initializes a new instance of the class. + + The expected value. + + + + Tests whether a value is less than or equal to the value supplied to its constructor + + + + + Initializes a new instance of the class. + + The expected value. + + + + Delegate used to delay evaluation of the actual value + to be used in evaluating a constraint + + + + + ConstraintBuilder maintains the stacks that are used in + processing a ConstraintExpression. An OperatorStack + is used to hold operators that are waiting for their + operands to be reognized. a ConstraintStack holds + input constraints as well as the results of each + operator applied. + + + + + Initializes a new instance of the class. + + + + + Appends the specified operator to the expression by first + reducing the operator stack and then pushing the new + operator on the stack. + + The operator to push. + + + + Appends the specified constraint to the expresson by pushing + it on the constraint stack. + + The constraint to push. + + + + Sets the top operator right context. + + The right context. + + + + Reduces the operator stack until the topmost item + precedence is greater than or equal to the target precedence. + + The target precedence. + + + + Resolves this instance, returning a Constraint. If the builder + is not currently in a resolvable state, an exception is thrown. + + The resolved constraint + + + + Gets a value indicating whether this instance is resolvable. + + + true if this instance is resolvable; otherwise, false. + + + + + OperatorStack is a type-safe stack for holding ConstraintOperators + + + + + Initializes a new instance of the class. + + The builder. + + + + Pushes the specified operator onto the stack. + + The op. + + + + Pops the topmost operator from the stack. + + + + + + Gets a value indicating whether this is empty. + + true if empty; otherwise, false. + + + + Gets the topmost operator without modifying the stack. + + The top. + + + + ConstraintStack is a type-safe stack for holding Constraints + + + + + Initializes a new instance of the class. + + The builder. + + + + Pushes the specified constraint. As a side effect, + the constraint's builder field is set to the + ConstraintBuilder owning this stack. + + The constraint. + + + + Pops this topmost constrait from the stack. + As a side effect, the constraint's builder + field is set to null. + + + + + + Gets a value indicating whether this is empty. + + true if empty; otherwise, false. + + + + Gets the topmost constraint without modifying the stack. + + The topmost constraint + + + + EmptyConstraint tests a whether a string or collection is empty, + postponing the decision about which test is applied until the + type of the actual argument is known. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + EqualConstraint is able to compare an actual value with the + expected value provided in its constructor. Two objects are + considered equal if both are null, or if both have the same + value. NUnit has special semantics for some object types. + + + + + If true, strings in error messages will be clipped + + + + + NUnitEqualityComparer used to test equality. + + + + + Initializes a new instance of the class. + + The expected value. + + + + Flag the constraint to use a tolerance when determining equality. + + Tolerance value to be used + Self. + + + + Flag the constraint to use the supplied IComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied Comparison object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IEqualityComparer object. + + The IComparer object to use. + Self. + + + + Flag the constraint to use the supplied IEqualityComparer object. + + The IComparer object to use. + Self. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write a failure message. Overridden to provide custom + failure messages for EqualConstraint. + + The MessageWriter to write to + + + + Write description of this constraint + + The MessageWriter to write to + + + + Display the failure information for two collections that did not match. + + The MessageWriter on which to display + The expected collection. + The actual collection + The depth of this failure in a set of nested collections + + + + Displays a single line showing the types and sizes of the expected + and actual collections or arrays. If both are identical, the value is + only shown once. + + The MessageWriter on which to display + The expected collection or array + The actual collection or array + The indentation level for the message line + + + + Displays a single line showing the point in the expected and actual + arrays at which the comparison failed. If the arrays have different + structures or dimensions, both values are shown. + + The MessageWriter on which to display + The expected array + The actual array + Index of the failure point in the underlying collections + The indentation level for the message line + + + + Flag the constraint to ignore case and return self. + + + + + Flag the constraint to suppress string clipping + and return self. + + + + + Flag the constraint to compare arrays as collections + and return self. + + + + + Switches the .Within() modifier to interpret its tolerance as + a distance in representable values (see remarks). + + Self. + + Ulp stands for "unit in the last place" and describes the minimum + amount a given value can change. For any integers, an ulp is 1 whole + digit. For floating point values, the accuracy of which is better + for smaller numbers and worse for larger numbers, an ulp depends + on the size of the number. Using ulps for comparison of floating + point results instead of fixed tolerances is safer because it will + automatically compensate for the added inaccuracy of larger numbers. + + + + + Switches the .Within() modifier to interpret its tolerance as + a percentage that the actual values is allowed to deviate from + the expected value. + + Self + + + + Causes the tolerance to be interpreted as a TimeSpan in days. + + Self + + + + Causes the tolerance to be interpreted as a TimeSpan in hours. + + Self + + + + Causes the tolerance to be interpreted as a TimeSpan in minutes. + + Self + + + + Causes the tolerance to be interpreted as a TimeSpan in seconds. + + Self + + + + Causes the tolerance to be interpreted as a TimeSpan in milliseconds. + + Self + + + + Causes the tolerance to be interpreted as a TimeSpan in clock ticks. + + Self + + + + SameAsConstraint tests whether an object is identical to + the object passed to its constructor + + + + + Initializes a new instance of the class. + + The expected object. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + StringConstraint is the abstract base for constraints + that operate on strings. It supports the IgnoreCase + modifier for string operations. + + + + + The expected value + + + + + Indicates whether tests should be case-insensitive + + + + + Constructs a StringConstraint given an expected value + + The expected value + + + + Modify the constraint to ignore case in matching. + + + + + EmptyStringConstraint tests whether a string is empty. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + NullEmptyStringConstraint tests whether a string is either null or empty. + + + + + Constructs a new NullOrEmptyStringConstraint + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + SubstringConstraint can test whether a string contains + the expected substring. + + + + + Initializes a new instance of the class. + + The expected. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + StartsWithConstraint can test whether a string starts + with an expected substring. + + + + + Initializes a new instance of the class. + + The expected string + + + + Test whether the constraint is matched by the actual value. + This is a template method, which calls the IsMatch method + of the derived class. + + + + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + EndsWithConstraint can test whether a string ends + with an expected substring. + + + + + Initializes a new instance of the class. + + The expected string + + + + Test whether the constraint is matched by the actual value. + This is a template method, which calls the IsMatch method + of the derived class. + + + + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + RegexConstraint can test whether a string matches + the pattern provided. + + + + + Initializes a new instance of the class. + + The pattern. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + TypeConstraint is the abstract base for constraints + that take a Type as their expected value. + + + + + The expected Type used by the constraint + + + + + Construct a TypeConstraint for a given Type + + + + + + Write the actual value for a failing constraint test to a + MessageWriter. TypeConstraints override this method to write + the name of the type. + + The writer on which the actual value is displayed + + + + ExactTypeConstraint is used to test that an object + is of the exact type provided in the constructor + + + + + Construct an ExactTypeConstraint for a given Type + + The expected Type. + + + + Test that an object is of the exact type specified + + The actual value. + True if the tested object is of the exact type provided, otherwise false. + + + + Write the description of this constraint to a MessageWriter + + The MessageWriter to use + + + + InstanceOfTypeConstraint is used to test that an object + is of the same type provided or derived from it. + + + + + Construct an InstanceOfTypeConstraint for the type provided + + The expected Type + + + + Test whether an object is of the specified type or a derived type + + The object to be tested + True if the object is of the provided type or derives from it, otherwise false. + + + + Write a description of this constraint to a MessageWriter + + The MessageWriter to use + + + + AssignableFromConstraint is used to test that an object + can be assigned from a given Type. + + + + + Construct an AssignableFromConstraint for the type provided + + + + + + Test whether an object can be assigned from the specified type + + The object to be tested + True if the object can be assigned a value of the expected Type, otherwise false. + + + + Write a description of this constraint to a MessageWriter + + The MessageWriter to use + + + + AssignableToConstraint is used to test that an object + can be assigned to a given Type. + + + + + Construct an AssignableToConstraint for the type provided + + + + + + Test whether an object can be assigned to the specified type + + The object to be tested + True if the object can be assigned a value of the expected Type, otherwise false. + + + + Write a description of this constraint to a MessageWriter + + The MessageWriter to use + + + + ContainsConstraint tests a whether a string contains a substring + or a collection contains an object. It postpones the decision of + which test to use until the type of the actual argument is known. + This allows testing whether a string is contained in a collection + or as a substring of another string using the same syntax. + + + + + Initializes a new instance of the class. + + The expected. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Flag the constraint to ignore case and return self. + + + + + PropertyExistsConstraint tests that a named property + exists on the object provided through Match. + + Originally, PropertyConstraint provided this feature + in addition to making optional tests on the vaue + of the property. The two constraints are now separate. + + + + + Initializes a new instance of the class. + + The name of the property. + + + + Test whether the property exists for a given object + + The object to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. + + The writer on which the actual value is displayed + + + + Returns the string representation of the constraint. + + + + + + PropertyConstraint extracts a named property and uses + its value as the actual value for a chained constraint. + + + + + Abstract base class used for prefixes + + + + + The base constraint + + + + + Construct given a base constraint + + + + + + Initializes a new instance of the class. + + The name. + The constraint to apply to the property. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + Returns the string representation of the constraint. + + + + + + NotConstraint negates the effect of some other constraint + + + + + Initializes a new instance of the class. + + The base constraint to be negated. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for if the base constraint fails, false if it succeeds + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a MessageWriter. + + The writer on which the actual value is displayed + + + + AllItemsConstraint applies another constraint to each + item in a collection, succeeding if they all succeed. + + + + + Construct an AllItemsConstraint on top of an existing constraint + + + + + + Apply the item constraint to each item in the collection, + failing if any item fails. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + SomeItemsConstraint applies another constraint to each + item in a collection, succeeding if any of them succeeds. + + + + + Construct a SomeItemsConstraint on top of an existing constraint + + + + + + Apply the item constraint to each item in the collection, + succeeding if any item succeeds. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + NoItemConstraint applies another constraint to each + item in a collection, failing if any of them succeeds. + + + + + Construct a SomeItemsConstraint on top of an existing constraint + + + + + + Apply the item constraint to each item in the collection, + failing if any item fails. + + + + + + + Write a description of this constraint to a MessageWriter + + + + + + The Numerics class contains common operations on numeric values. + + + + + Checks the type of the object, returning true if + the object is a numeric type. + + The object to check + true if the object is a numeric type + + + + Checks the type of the object, returning true if + the object is a floating point numeric type. + + The object to check + true if the object is a floating point numeric type + + + + Checks the type of the object, returning true if + the object is a fixed point numeric type. + + The object to check + true if the object is a fixed point numeric type + + + + Test two numeric values for equality, performing the usual numeric + conversions and using a provided or default tolerance. If the tolerance + provided is Empty, this method may set it to a default tolerance. + + The expected value + The actual value + A reference to the tolerance in effect + True if the values are equal + + + + Compare two numeric values, performing the usual numeric conversions. + + The expected value + The actual value + The relationship of the values to each other + + + + MessageWriter is the abstract base for classes that write + constraint descriptions and messages in some form. The + class has separate methods for writing various components + of a message, allowing implementations to tailor the + presentation as needed. + + + + + Construct a MessageWriter given a culture + + + + + Method to write single line message with optional args, usually + written to precede the general failure message. + + The message to be written + Any arguments used in formatting the message + + + + Method to write single line message with optional args, usually + written to precede the general failure message, at a givel + indentation level. + + The indentation level of the message + The message to be written + Any arguments used in formatting the message + + + + Display Expected and Actual lines for a constraint. This + is called by MessageWriter's default implementation of + WriteMessageTo and provides the generic two-line display. + + The constraint that failed + + + + Display Expected and Actual lines for given values. This + method may be called by constraints that need more control over + the display of actual and expected values than is provided + by the default implementation. + + The expected value + The actual value causing the failure + + + + Display Expected and Actual lines for given values, including + a tolerance value on the Expected line. + + The expected value + The actual value causing the failure + The tolerance within which the test was made + + + + Display the expected and actual string values on separate lines. + If the mismatch parameter is >=0, an additional line is displayed + line containing a caret that points to the mismatch point. + + The expected string value + The actual string value + The point at which the strings don't match or -1 + If true, case is ignored in locating the point where the strings differ + If true, the strings should be clipped to fit the line + + + + Writes the text for a connector. + + The connector. + + + + Writes the text for a predicate. + + The predicate. + + + + Writes the text for an expected value. + + The expected value. + + + + Writes the text for a modifier + + The modifier. + + + + Writes the text for an actual value. + + The actual value. + + + + Writes the text for a generalized value. + + The value. + + + + Writes the text for a collection value, + starting at a particular point, to a max length + + The collection containing elements to write. + The starting point of the elements to write + The maximum number of elements to write + + + + Abstract method to get the max line length + + + + + Static methods used in creating messages + + + + + Static string used when strings are clipped + + + + + Returns the representation of a type as used in NUnitLite. + This is the same as Type.ToString() except for arrays, + which are displayed with their declared sizes. + + + + + + + Converts any control characters in a string + to their escaped representation. + + The string to be converted + The converted string + + + + Return the a string representation for a set of indices into an array + + Array of indices for which a string is needed + + + + Get an array of indices representing the point in a collection or + array corresponding to a single int index into the collection. + + The collection to which the indices apply + Index in the collection + Array of indices + + + + Clip a string to a given length, starting at a particular offset, returning the clipped + string with ellipses representing the removed parts + + The string to be clipped + The maximum permitted length of the result string + The point at which to start clipping + The clipped string + + + + Clip the expected and actual strings in a coordinated fashion, + so that they may be displayed together. + + + + + + + + + Shows the position two strings start to differ. Comparison + starts at the start index. + + The expected string + The actual string + The index in the strings at which comparison should start + Boolean indicating whether case should be ignored + -1 if no mismatch found, or the index where mismatch found + + + + PathConstraint serves as the abstract base of constraints + that operate on paths and provides several helper methods. + + + + + The expected path used in the constraint + + + + + Flag indicating whether a caseInsensitive comparison should be made + + + + + Construct a PathConstraint for a give expected path + + The expected path + + + + Returns the string representation of this constraint + + + + + Canonicalize the provided path + + + The path in standardized form + + + + Test whether two paths are the same + + The first path + The second path + + + + + Test whether one path is the same as or under another path + + The first path - supposed to be the parent path + The second path - supposed to be the child path + + + + + Modifies the current instance to be case-insensitve + and returns it. + + + + + Modifies the current instance to be case-sensitve + and returns it. + + + + + Summary description for SamePathConstraint. + + + + + Initializes a new instance of the class. + + The expected path + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + SamePathOrUnderConstraint tests that one path is under another + + + + + Initializes a new instance of the class. + + The expected path + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + EmptyDirectoryConstraint is used to test that a directory is empty + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + SubDirectoryConstraint is used to test that one directory is a subdirectory of another. + + + + + Initializes a new instance of the class. + + The dir info. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Builds a list of DirectoryInfo objects, recursing where necessary + + directory to recurse + list of DirectoryInfo objects from the top level + + + + private method to determine whether a directory is within the path + + top-level directory to search + directory to search for + true if found, false if not + + + + Method to compare two DirectoryInfo objects + + first directory to compare + second directory to compare + true if equivalent, false if not + + + + ThrowsConstraint is used to test the exception thrown by + a delegate by applying a constraint to it. + + + + + Initializes a new instance of the class, + using a constraint to be applied to the exception. + + A constraint to apply to the caught exception. + + + + Executes the code of the delegate and captures any exception. + If a non-null base constraint was provided, it applies that + constraint to the exception. + + A delegate representing the code to be tested + True if an exception is thrown and the constraint succeeds, otherwise false + + + + Converts an ActualValueDelegate to a TestDelegate + before calling the primary overload. + + + + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + Returns the string representation of this constraint + + + + + Get the actual exception thrown - used by Assert.Throws. + + + + + ThrowsNothingConstraint tests that a delegate does not + throw an exception. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True if no exception is thrown, otherwise false + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + RangeConstraint tests whethe two values are within a + specified range. + + + + + Initializes a new instance of the class. + + From. + To. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Modifies the constraint to use an IComparer and returns self. + + + + + Modifies the constraint to use an IComparer<T> and returns self. + + + + + Modifies the constraint to use a Comparison<T> and returns self. + + + + + Helper class with properties and methods that supply + a number of constraints used in Asserts. + + + + + Returns a new PropertyConstraintExpression, which will either + test for the existence of the named property on the object + being tested or apply any following constraint to that property. + + + + + Returns a new AttributeConstraint checking for the + presence of a particular attribute on an object. + + + + + Returns a new AttributeConstraint checking for the + presence of a particular attribute on an object. + + + + + Returns a constraint that tests two items for equality + + + + + Returns a constraint that tests that two references are the same object + + + + + Returns a constraint that tests whether the + actual value is greater than the suppled argument + + + + + Returns a constraint that tests whether the + actual value is greater than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is greater than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than or equal to the suppled argument + + + + + Returns a constraint that tests whether the actual + value is of the exact type supplied as an argument. + + + + + Returns a constraint that tests whether the actual + value is of the exact type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is a collection containing the same elements as the + collection supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is a subset of the collection supplied as an argument. + + + + + Returns a new CollectionContainsConstraint checking for the + presence of a particular object in the collection. + + + + + Returns a new CollectionContainsConstraint checking for the + presence of a particular object in the collection. + + + + + Returns a new ContainsConstraint. This constraint + will, in turn, make use of the appropriate second-level + constraint, depending on the type of the actual argument. + This overload is only used if the item sought is a string, + since any other type implies that we are looking for a + collection member. + + + + + Returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that fails if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that fails if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that fails if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value matches the Regex pattern supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value matches the Regex pattern supplied as an argument. + + + + + Returns a constraint that fails if the actual + value matches the pattern supplied as an argument. + + + + + Returns a constraint that tests whether the path provided + is the same as an expected path after canonicalization. + + + + + Returns a constraint that tests whether the path provided + is the same path or under an expected path after canonicalization. + + + + + Returns a constraint that tests whether the actual value falls + within a specified range. + + + + + Returns a ConstraintExpression that negates any + following constraint. + + + + + Returns a ConstraintExpression that negates any + following constraint. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them succeed. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if at least one of them succeeds. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them fail. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Length property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Count property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Message property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the InnerException property of the object being tested. + + + + + Returns a constraint that tests for null + + + + + Returns a constraint that tests for True + + + + + Returns a constraint that tests for False + + + + + Returns a constraint that tests for NaN + + + + + Returns a constraint that tests for empty + + + + + Returns a constraint that tests whether a collection + contains all unique items. + + + + + Returns a constraint that tests whether an object graph is serializable in binary format. + + + + + Returns a constraint that tests whether an object graph is serializable in xml format. + + + + + Returns a constraint that tests whether a collection is ordered + + + + + The ConstraintOperator class is used internally by a + ConstraintBuilder to represent an operator that + modifies or combines constraints. + + Constraint operators use left and right precedence + values to determine whether the top operator on the + stack should be reduced before pushing a new operator. + + + + + The precedence value used when the operator + is about to be pushed to the stack. + + + + + The precedence value used when the operator + is on the top of the stack. + + + + + Reduce produces a constraint from the operator and + any arguments. It takes the arguments from the constraint + stack and pushes the resulting constraint on it. + + + + + + The syntax element preceding this operator + + + + + The syntax element folowing this operator + + + + + The precedence value used when the operator + is about to be pushed to the stack. + + + + + The precedence value used when the operator + is on the top of the stack. + + + + + PrefixOperator takes a single constraint and modifies + it's action in some way. + + + + + Reduce produces a constraint from the operator and + any arguments. It takes the arguments from the constraint + stack and pushes the resulting constraint on it. + + + + + + Returns the constraint created by applying this + prefix to another constraint. + + + + + + + Negates the test of the constraint it wraps. + + + + + Constructs a new NotOperator + + + + + Returns a NotConstraint applied to its argument. + + + + + Abstract base for operators that indicate how to + apply a constraint to items in a collection. + + + + + Constructs a CollectionOperator + + + + + Represents a constraint that succeeds if all the + members of a collection match a base constraint. + + + + + Returns a constraint that will apply the argument + to the members of a collection, succeeding if + they all succeed. + + + + + Represents a constraint that succeeds if any of the + members of a collection match a base constraint. + + + + + Returns a constraint that will apply the argument + to the members of a collection, succeeding if + any of them succeed. + + + + + Represents a constraint that succeeds if none of the + members of a collection match a base constraint. + + + + + Returns a constraint that will apply the argument + to the members of a collection, succeeding if + none of them succeed. + + + + + Represents a constraint that simply wraps the + constraint provided as an argument, without any + further functionality, but which modifes the + order of evaluation because of its precedence. + + + + + Constructor for the WithOperator + + + + + Returns a constraint that wraps its argument + + + + + Abstract base class for operators that are able to reduce to a + constraint whether or not another syntactic element follows. + + + + + Operator used to test for the presence of a named Property + on an object and optionally apply further tests to the + value of that property. + + + + + Constructs a PropOperator for a particular named property + + + + + Reduce produces a constraint from the operator and + any arguments. It takes the arguments from the constraint + stack and pushes the resulting constraint on it. + + + + + + Gets the name of the property to which the operator applies + + + + + Operator that tests for the presence of a particular attribute + on a type and optionally applies further tests to the attribute. + + + + + Construct an AttributeOperator for a particular Type + + The Type of attribute tested + + + + Reduce produces a constraint from the operator and + any arguments. It takes the arguments from the constraint + stack and pushes the resulting constraint on it. + + + + + Operator that tests that an exception is thrown and + optionally applies further tests to the exception. + + + + + Construct a ThrowsOperator + + + + + Reduce produces a constraint from the operator and + any arguments. It takes the arguments from the constraint + stack and pushes the resulting constraint on it. + + + + + Abstract base class for all binary operators + + + + + Reduce produces a constraint from the operator and + any arguments. It takes the arguments from the constraint + stack and pushes the resulting constraint on it. + + + + + + Abstract method that produces a constraint by applying + the operator to its left and right constraint arguments. + + + + + Gets the left precedence of the operator + + + + + Gets the right precedence of the operator + + + + + Operator that requires both it's arguments to succeed + + + + + Construct an AndOperator + + + + + Apply the operator to produce an AndConstraint + + + + + Operator that requires at least one of it's arguments to succeed + + + + + Construct an OrOperator + + + + + Apply the operator to produce an OrConstraint + + + + + ConstraintExpression represents a compound constraint in the + process of being constructed from a series of syntactic elements. + + Individual elements are appended to the expression as they are + reognized. Once an actual Constraint is appended, the expression + returns a resolvable Constraint. + + + + + ConstraintExpressionBase is the abstract base class for the + generated ConstraintExpression class, which represents a + compound constraint in the process of being constructed + from a series of syntactic elements. + + NOTE: ConstraintExpressionBase is aware of some of its + derived classes, which is an apparent violation of + encapsulation. Ideally, these classes would be a + single class, but they must be separated in order to + allow parts to be generated under .NET 1.x and to + provide proper user feedback in syntactically + aware IDEs. + + + + + The ConstraintBuilder holding the elements recognized so far + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the + class passing in a ConstraintBuilder, which may be pre-populated. + + The builder. + + + + Returns a string representation of the expression as it + currently stands. This should only be used for testing, + since it has the side-effect of resolving the expression. + + + + + + Appends an operator to the expression and returns the + resulting expression itself. + + + + + Appends a self-resolving operator to the expression and + returns a new ResolvableConstraintExpression. + + + + + Appends a constraint to the expression and returns that + constraint, which is associated with the current state + of the expression being built. + + + + + Initializes a new instance of the class. + + + + + Initializes a new instance of the + class passing in a ConstraintBuilder, which may be pre-populated. + + The builder. + + + + Returns a new PropertyConstraintExpression, which will either + test for the existence of the named property on the object + being tested or apply any following constraint to that property. + + + + + Returns a new AttributeConstraint checking for the + presence of a particular attribute on an object. + + + + + Returns a new AttributeConstraint checking for the + presence of a particular attribute on an object. + + + + + Returns the constraint provided as an argument - used to allow custom + custom constraints to easily participate in the syntax. + + + + + Returns the constraint provided as an argument - used to allow custom + custom constraints to easily participate in the syntax. + + + + + Returns a constraint that tests two items for equality + + + + + Returns a constraint that tests that two references are the same object + + + + + Returns a constraint that tests whether the + actual value is greater than the suppled argument + + + + + Returns a constraint that tests whether the + actual value is greater than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is greater than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than or equal to the suppled argument + + + + + Returns a constraint that tests whether the actual + value is of the exact type supplied as an argument. + + + + + Returns a constraint that tests whether the actual + value is of the exact type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is a collection containing the same elements as the + collection supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is a subset of the collection supplied as an argument. + + + + + Returns a new CollectionContainsConstraint checking for the + presence of a particular object in the collection. + + + + + Returns a new CollectionContainsConstraint checking for the + presence of a particular object in the collection. + + + + + Returns a new ContainsConstraint. This constraint + will, in turn, make use of the appropriate second-level + constraint, depending on the type of the actual argument. + This overload is only used if the item sought is a string, + since any other type implies that we are looking for a + collection member. + + + + + Returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value matches the Regex pattern supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value matches the Regex pattern supplied as an argument. + + + + + Returns a constraint that tests whether the path provided + is the same as an expected path after canonicalization. + + + + + Returns a constraint that tests whether the path provided + is the same path or under an expected path after canonicalization. + + + + + Returns a constraint that tests whether the actual value falls + within a specified range. + + + + + Returns a ConstraintExpression that negates any + following constraint. + + + + + Returns a ConstraintExpression that negates any + following constraint. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them succeed. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if at least one of them succeeds. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them fail. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Length property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Count property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Message property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the InnerException property of the object being tested. + + + + + With is currently a NOP - reserved for future use. + + + + + Returns a constraint that tests for null + + + + + Returns a constraint that tests for True + + + + + Returns a constraint that tests for False + + + + + Returns a constraint that tests for NaN + + + + + Returns a constraint that tests for empty + + + + + Returns a constraint that tests whether a collection + contains all unique items. + + + + + Returns a constraint that tests whether an object graph is serializable in binary format. + + + + + Returns a constraint that tests whether an object graph is serializable in xml format. + + + + + Returns a constraint that tests whether a collection is ordered + + + + + BinarySerializableConstraint tests whether + an object is serializable in binary format. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + Returns the string representation + + + + + BinarySerializableConstraint tests whether + an object is serializable in binary format. + + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a + MessageWriter. The default implementation simply writes + the raw value of actual, leaving it to the writer to + perform any formatting. + + The writer on which the actual value is displayed + + + + Returns the string representation of this constraint + + + + + BasicConstraint is the abstract base for constraints that + perform a simple comparison to a constant value. + + + + + Initializes a new instance of the class. + + The expected. + The description. + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + NullConstraint tests that the actual value is null + + + + + Initializes a new instance of the class. + + + + + TrueConstraint tests that the actual value is true + + + + + Initializes a new instance of the class. + + + + + FalseConstraint tests that the actual value is false + + + + + Initializes a new instance of the class. + + + + + NaNConstraint tests that the actual value is a double or float NaN + + + + + Test that the actual value is an NaN + + + + + + + Write the constraint description to a specified writer + + + + + + AttributeExistsConstraint tests for the presence of a + specified attribute on a Type. + + + + + Constructs an AttributeExistsConstraint for a specific attribute Type + + + + + + Tests whether the object provides the expected attribute. + + A Type, MethodInfo, or other ICustomAttributeProvider + True if the expected attribute is present, otherwise false + + + + Writes the description of the constraint to the specified writer + + + + + AttributeConstraint tests that a specified attribute is present + on a Type or other provider and that the value of the attribute + satisfies some other constraint. + + + + + Constructs an AttributeConstraint for a specified attriute + Type and base constraint. + + + + + + + Determines whether the Type or other provider has the + expected attribute and if its value matches the + additional constraint specified. + + + + + Writes a description of the attribute to the specified writer. + + + + + Writes the actual value supplied to the specified writer. + + + + + Returns a string representation of the constraint. + + + + + ResolvableConstraintExpression is used to represent a compound + constraint being constructed at a point where the last operator + may either terminate the expression or may have additional + qualifying constraints added to it. + + It is used, for example, for a Property element or for + an Exception element, either of which may be optionally + followed by constraints that apply to the property or + exception. + + + + + Create a new instance of ResolvableConstraintExpression + + + + + Create a new instance of ResolvableConstraintExpression, + passing in a pre-populated ConstraintBuilder. + + + + + Resolve the current expression to a Constraint + + + + + Appends an And Operator to the expression + + + + + Appends an Or operator to the expression. + + + + + Applies a delay to the match so that a match can be evaluated in the future. + + + + + Creates a new DelayedConstraint + + The inner constraint two decorate + The time interval after which the match is performed + If the value of is less than 0 + + + + Creates a new DelayedConstraint + + The inner constraint two decorate + The time interval after which the match is performed + The time interval used for polling + If the value of is less than 0 + + + + Test whether the constraint is satisfied by a given value + + The value to be tested + True for if the base constraint fails, false if it succeeds + + + + Test whether the constraint is satisfied by a delegate + + The delegate whose value is to be tested + True for if the base constraint fails, false if it succeeds + + + + Test whether the constraint is satisfied by a given reference. + Overridden to wait for the specified delay period before + calling the base constraint with the dereferenced value. + + A reference to the value to be tested + True for success, false for failure + + + + Write the constraint description to a MessageWriter + + The writer on which the description is displayed + + + + Write the actual value for a failing constraint test to a MessageWriter. + + The writer on which the actual value is displayed + + + + Returns the string representation of the constraint. + + + + Helper routines for working with floating point numbers + + + The floating point comparison code is based on this excellent article: + http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm + + + "ULP" means Unit in the Last Place and in the context of this library refers to + the distance between two adjacent floating point numbers. IEEE floating point + numbers can only represent a finite subset of natural numbers, with greater + accuracy for smaller numbers and lower accuracy for very large numbers. + + + If a comparison is allowed "2 ulps" of deviation, that means the values are + allowed to deviate by up to 2 adjacent floating point values, which might be + as low as 0.0000001 for small numbers or as high as 10.0 for large numbers. + + + + + Compares two floating point values for equality + First floating point value to be compared + Second floating point value t be compared + + Maximum number of representable floating point values that are allowed to + be between the left and the right floating point values + + True if both numbers are equal or close to being equal + + + Floating point values can only represent a finite subset of natural numbers. + For example, the values 2.00000000 and 2.00000024 can be stored in a float, + but nothing inbetween them. + + + This comparison will count how many possible floating point values are between + the left and the right number. If the number of possible values between both + numbers is less than or equal to maxUlps, then the numbers are considered as + being equal. + + + Implementation partially follows the code outlined here: + http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/ + + + + + Compares two double precision floating point values for equality + First double precision floating point value to be compared + Second double precision floating point value t be compared + + Maximum number of representable double precision floating point values that are + allowed to be between the left and the right double precision floating point values + + True if both numbers are equal or close to being equal + + + Double precision floating point values can only represent a limited series of + natural numbers. For example, the values 2.0000000000000000 and 2.0000000000000004 + can be stored in a double, but nothing inbetween them. + + + This comparison will count how many possible double precision floating point + values are between the left and the right number. If the number of possible + values between both numbers is less than or equal to maxUlps, then the numbers + are considered as being equal. + + + Implementation partially follows the code outlined here: + http://www.anttirt.net/2007/08/19/proper-floating-point-comparisons/ + + + + + + Reinterprets the memory contents of a floating point value as an integer value + + + Floating point value whose memory contents to reinterpret + + + The memory contents of the floating point value interpreted as an integer + + + + + Reinterprets the memory contents of a double precision floating point + value as an integer value + + + Double precision floating point value whose memory contents to reinterpret + + + The memory contents of the double precision floating point value + interpreted as an integer + + + + + Reinterprets the memory contents of an integer as a floating point value + + Integer value whose memory contents to reinterpret + + The memory contents of the integer value interpreted as a floating point value + + + + + Reinterprets the memory contents of an integer value as a double precision + floating point value + + Integer whose memory contents to reinterpret + + The memory contents of the integer interpreted as a double precision + floating point value + + + + Union of a floating point variable and an integer + + + The union's value as a floating point variable + + + The union's value as an integer + + + The union's value as an unsigned integer + + + Union of a double precision floating point variable and a long + + + The union's value as a double precision floating point variable + + + The union's value as a long + + + The union's value as an unsigned long + + + + Modes in which the tolerance value for a comparison can + be interpreted. + + + + + The tolerance was created with a value, without specifying + how the value would be used. This is used to prevent setting + the mode more than once and is generally changed to Linear + upon execution of the test. + + + + + The tolerance is used as a numeric range within which + two compared values are considered to be equal. + + + + + Interprets the tolerance as the percentage by which + the two compared values my deviate from each other. + + + + + Compares two values based in their distance in + representable numbers. + + + + + The Tolerance class generalizes the notion of a tolerance + within which an equality test succeeds. Normally, it is + used with numeric types, but it can be used with any + type that supports taking a difference between two + objects and comparing that difference to a value. + + + + + Constructs a linear tolerance of a specdified amount + + + + + Constructs a tolerance given an amount and ToleranceMode + + + + + Tests that the current Tolerance is linear with a + numeric value, throwing an exception if it is not. + + + + + Returns an empty Tolerance object, equivalent to + specifying an exact match. + + + + + Gets the ToleranceMode for the current Tolerance + + + + + Gets the value of the current Tolerance instance. + + + + + Returns a new tolerance, using the current amount as a percentage. + + + + + Returns a new tolerance, using the current amount in Ulps. + + + + + Returns a new tolerance with a TimeSpan as the amount, using + the current amount as a number of days. + + + + + Returns a new tolerance with a TimeSpan as the amount, using + the current amount as a number of hours. + + + + + Returns a new tolerance with a TimeSpan as the amount, using + the current amount as a number of minutes. + + + + + Returns a new tolerance with a TimeSpan as the amount, using + the current amount as a number of seconds. + + + + + Returns a new tolerance with a TimeSpan as the amount, using + the current amount as a number of milliseconds. + + + + + Returns a new tolerance with a TimeSpan as the amount, using + the current amount as a number of clock ticks. + + + + + Returns true if the current tolerance is empty. + + + + + ComparisonAdapter class centralizes all comparisons of + values in NUnit, adapting to the use of any provided + IComparer, IComparer<T> or Comparison<T> + + + + + Returns a ComparisonAdapter that wraps an IComparer + + + + + Returns a ComparisonAdapter that wraps an IComparer<T> + + + + + Returns a ComparisonAdapter that wraps a Comparison<T> + + + + + Compares two objects + + + + + Gets the default ComparisonAdapter, which wraps an + NUnitComparer object. + + + + + Construct a ComparisonAdapter for an IComparer + + + + + Compares two objects + + + + + + + + Construct a default ComparisonAdapter + + + + + ComparisonAdapter<T> extends ComparisonAdapter and + allows use of an IComparer<T> or Comparison<T> + to actually perform the comparison. + + + + + Construct a ComparisonAdapter for an IComparer<T> + + + + + Compare a Type T to an object + + + + + Construct a ComparisonAdapter for a Comparison<T> + + + + + Compare a Type T to an object + + + + + EqualityAdapter class handles all equality comparisons + that use an IEqualityComparer, IEqualityComparer<T> + or a ComparisonAdapter. + + + + + Compares two objects, returning true if they are equal + + + + + Returns an EqualityAdapter that wraps an IComparer. + + + + + Returns an EqualityAdapter that wraps an IEqualityComparer. + + + + + Returns an EqualityAdapter that wraps an IEqualityComparer<T>. + + + + + Returns an EqualityAdapter that wraps an IComparer<T>. + + + + + Returns an EqualityAdapter that wraps a Comparison<T>. + + + + + NUnitComparer encapsulates NUnit's default behavior + in comparing two objects. + + + + + Compares two objects + + + + + + + + Returns the default NUnitComparer. + + + + + NUnitEqualityComparer encapsulates NUnit's handling of + equality tests between objects. + + + + + If true, all string comparisons will ignore case + + + + + If true, arrays will be treated as collections, allowing + those of different dimensions to be compared + + + + + If non-zero, equality comparisons within the specified + tolerance will succeed. + + + + + Comparison object used in comparisons for some constraints. + + + + + Compares two objects for equality. + + + + + Helper method to compare two arrays + + + + + Method to compare two DirectoryInfo objects + + first directory to compare + second directory to compare + true if equivalent, false if not + + + + Returns the default NUnitEqualityComparer + + + + + Gets and sets a flag indicating whether case should + be ignored in determining equality. + + + + + Gets and sets a flag indicating that arrays should be + compared as collections, without regard to their shape. + + + + + Gets and sets an external comparer to be used to + test for equality. It is applied to members of + collections, in place of NUnit's own logic. + + + + + Gets and sets a tolerance used to compare objects of + certin types. + + + + + Gets the list of failure points for the last Match performed. + + + + + Predicate constraint wraps a Predicate in a constraint, + returning success if the predicate is true. + + + + + Construct a PredicateConstraint from a predicate + + + + + Determines whether the predicate succeeds when applied + to the actual value. + + + + + Writes the description to a MessageWriter + + + + + CollectionTally counts (tallies) the number of + occurences of each object in one or more enumerations. + + + + + Construct a CollectionTally object from a comparer and a collection + + + + + Try to remove an object from the tally + + The object to remove + True if successful, false if the object was not found + + + + Try to remove a set of objects from the tally + + The objects to remove + True if successful, false if any object was not found + + + + The number of objects remaining in the tally + + + + + SetUpFixtureAttribute is used to identify a SetUpFixture + + + + + Basic Asserts on strings. + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + Asserts that a string is found within another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string is found within another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string is found within another string. + + The expected string + The string to be examined + + + + Asserts that a string is not found within another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string is found within another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string is found within another string. + + The expected string + The string to be examined + + + + Asserts that a string starts with another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string starts with another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string starts with another string. + + The expected string + The string to be examined + + + + Asserts that a string does not start with another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string does not start with another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string does not start with another string. + + The expected string + The string to be examined + + + + Asserts that a string ends with another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string ends with another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string ends with another string. + + The expected string + The string to be examined + + + + Asserts that a string does not end with another string. + + The expected string + The string to be examined + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string does not end with another string. + + The expected string + The string to be examined + The message to display in case of failure + + + + Asserts that a string does not end with another string. + + The expected string + The string to be examined + + + + Asserts that two strings are equal, without regard to case. + + The expected string + The actual string + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that two strings are equal, without regard to case. + + The expected string + The actual string + The message to display in case of failure + + + + Asserts that two strings are equal, without regard to case. + + The expected string + The actual string + + + + Asserts that two strings are not equal, without regard to case. + + The expected string + The actual string + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that two strings are Notequal, without regard to case. + + The expected string + The actual string + The message to display in case of failure + + + + Asserts that two strings are not equal, without regard to case. + + The expected string + The actual string + + + + Asserts that a string matches an expected regular expression pattern. + + The regex pattern to be matched + The actual string + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string matches an expected regular expression pattern. + + The regex pattern to be matched + The actual string + The message to display in case of failure + + + + Asserts that a string matches an expected regular expression pattern. + + The regex pattern to be matched + The actual string + + + + Asserts that a string does not match an expected regular expression pattern. + + The regex pattern to be used + The actual string + The message to display in case of failure + Arguments used in formatting the message + + + + Asserts that a string does not match an expected regular expression pattern. + + The regex pattern to be used + The actual string + The message to display in case of failure + + + + Asserts that a string does not match an expected regular expression pattern. + + The regex pattern to be used + The actual string + + + + PropertyAttribute is used to attach information to a test as a name/value pair.. + + + + + Construct a PropertyAttribute with a name and string value + + The name of the property + The property value + + + + Construct a PropertyAttribute with a name and int value + + The name of the property + The property value + + + + Construct a PropertyAttribute with a name and double value + + The name of the property + The property value + + + + Constructor for derived classes that set the + property dictionary directly. + + + + + Constructor for use by derived classes that use the + name of the type as the property name. Derived classes + must ensure that the Type of the property value is + a standard type supported by the BCL. Any custom + types will cause a serialization Exception when + in the client. + + + + + Gets the property dictionary for this attribute + + + + + A set of Assert methods operationg on one or more collections + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + Asserts that all items contained in collection are of the type specified by expectedType. + + IEnumerable containing objects to be considered + System.Type that all objects in collection must be instances of + + + + Asserts that all items contained in collection are of the type specified by expectedType. + + IEnumerable containing objects to be considered + System.Type that all objects in collection must be instances of + The message that will be displayed on failure + + + + Asserts that all items contained in collection are of the type specified by expectedType. + + IEnumerable containing objects to be considered + System.Type that all objects in collection must be instances of + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that all items contained in collection are not equal to null. + + IEnumerable containing objects to be considered + + + + Asserts that all items contained in collection are not equal to null. + + IEnumerable containing objects to be considered + The message that will be displayed on failure + + + + Asserts that all items contained in collection are not equal to null. + + IEnumerable of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Ensures that every object contained in collection exists within the collection + once and only once. + + IEnumerable of objects to be considered + + + + Ensures that every object contained in collection exists within the collection + once and only once. + + IEnumerable of objects to be considered + The message that will be displayed on failure + + + + Ensures that every object contained in collection exists within the collection + once and only once. + + IEnumerable of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + If comparer is not null then it will be used to compare the objects. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The IComparer to use in comparing objects from each IEnumerable + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + If comparer is not null then it will be used to compare the objects. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The IComparer to use in comparing objects from each IEnumerable + The message that will be displayed on failure + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are exactly equal. The collections must have the same count, + and contain the exact same objects in the same order. + If comparer is not null then it will be used to compare the objects. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The IComparer to use in comparing objects from each IEnumerable + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are equivalent, containing the same objects but the match may be in any order. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + + + + Asserts that expected and actual are equivalent, containing the same objects but the match may be in any order. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + + + + Asserts that expected and actual are equivalent, containing the same objects but the match may be in any order. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are not exactly equal. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + + + + Asserts that expected and actual are not exactly equal. + If comparer is not null then it will be used to compare the objects. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The IComparer to use in comparing objects from each IEnumerable + + + + Asserts that expected and actual are not exactly equal. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + + + + Asserts that expected and actual are not exactly equal. + If comparer is not null then it will be used to compare the objects. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The IComparer to use in comparing objects from each IEnumerable + The message that will be displayed on failure + + + + Asserts that expected and actual are not exactly equal. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are not exactly equal. + If comparer is not null then it will be used to compare the objects. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The IComparer to use in comparing objects from each IEnumerable + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that expected and actual are not equivalent. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + + + + Asserts that expected and actual are not equivalent. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + + + + Asserts that expected and actual are not equivalent. + + The first IEnumerable of objects to be considered + The second IEnumerable of objects to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that collection contains actual as an item. + + IEnumerable of objects to be considered + Object to be found within collection + + + + Asserts that collection contains actual as an item. + + IEnumerable of objects to be considered + Object to be found within collection + The message that will be displayed on failure + + + + Asserts that collection contains actual as an item. + + IEnumerable of objects to be considered + Object to be found within collection + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that collection does not contain actual as an item. + + IEnumerable of objects to be considered + Object that cannot exist within collection + + + + Asserts that collection does not contain actual as an item. + + IEnumerable of objects to be considered + Object that cannot exist within collection + The message that will be displayed on failure + + + + Asserts that collection does not contain actual as an item. + + IEnumerable of objects to be considered + Object that cannot exist within collection + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that superset is not a subject of subset. + + The IEnumerable superset to be considered + The IEnumerable subset to be considered + + + + Asserts that superset is not a subject of subset. + + The IEnumerable superset to be considered + The IEnumerable subset to be considered + The message that will be displayed on failure + + + + Asserts that superset is not a subject of subset. + + The IEnumerable superset to be considered + The IEnumerable subset to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that superset is a subset of subset. + + The IEnumerable superset to be considered + The IEnumerable subset to be considered + + + + Asserts that superset is a subset of subset. + + The IEnumerable superset to be considered + The IEnumerable subset to be considered + The message that will be displayed on failure + + + + Asserts that superset is a subset of subset. + + The IEnumerable superset to be considered + The IEnumerable subset to be considered + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing IEnumerable + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing IEnumerable + The message to be displayed on failure + + + + Assert that an array,list or other collection is empty + + An array, list or other collection implementing IEnumerable + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing IEnumerable + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing IEnumerable + The message to be displayed on failure + + + + Assert that an array,list or other collection is empty + + An array, list or other collection implementing IEnumerable + + + + Assert that an array, list or other collection is ordered + + An array, list or other collection implementing IEnumerable + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is ordered + + An array, list or other collection implementing IEnumerable + The message to be displayed on failure + + + + Assert that an array, list or other collection is ordered + + An array, list or other collection implementing IEnumerable + + + + Assert that an array, list or other collection is ordered + + An array, list or other collection implementing IEnumerable + A custom comparer to perform the comparisons + The message to be displayed on failure + Arguments to be used in formatting the message + + + + Assert that an array, list or other collection is ordered + + An array, list or other collection implementing IEnumerable + A custom comparer to perform the comparisons + The message to be displayed on failure + + + + Assert that an array, list or other collection is ordered + + An array, list or other collection implementing IEnumerable + A custom comparer to perform the comparisons + + + + Summary description for FileAssert. + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + We don't actually want any instances of this object, but some people + like to inherit from it to add other static methods. Hence, the + protected constructor disallows any instances of this object. + + + + + Verifies that two Streams are equal. Two Streams are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The expected Stream + The actual Stream + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Verifies that two Streams are equal. Two Streams are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The expected Stream + The actual Stream + The message to display if objects are not equal + + + + Verifies that two Streams are equal. Two Streams are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The expected Stream + The actual Stream + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A file containing the value that is expected + A file containing the actual value + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A file containing the value that is expected + A file containing the actual value + The message to display if objects are not equal + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A file containing the value that is expected + A file containing the actual value + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + The message to display if objects are not equal + + + + Verifies that two files are equal. Two files are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + + + + Asserts that two Streams are not equal. If they are equal + an is thrown. + + The expected Stream + The actual Stream + The message to be displayed when the two Stream are the same. + Arguments to be used in formatting the message + + + + Asserts that two Streams are not equal. If they are equal + an is thrown. + + The expected Stream + The actual Stream + The message to be displayed when the Streams are the same. + + + + Asserts that two Streams are not equal. If they are equal + an is thrown. + + The expected Stream + The actual Stream + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + A file containing the value that is expected + A file containing the actual value + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + A file containing the value that is expected + A file containing the actual value + The message to display if objects are not equal + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + A file containing the value that is expected + A file containing the actual value + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + The message to display if Streams are not equal + Arguments to be used in formatting the message + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + The message to display if objects are not equal + + + + Asserts that two files are not equal. If they are equal + an is thrown. + + The path to a file containing the value that is expected + The path to a file containing the actual value + + + + Attribute used to provide descriptive text about a + test case or fixture. + + + + + Construct the attribute + + Text describing the test + + + + Gets the test description + + + + + Interface implemented by a user fixture in order to + validate any expected exceptions. It is only called + for test methods marked with the ExpectedException + attribute. + + + + + Method to handle an expected exception + + The exception to be handled + + + + TextMessageWriter writes constraint descriptions and messages + in displayable form as a text stream. It tailors the display + of individual message components to form the standard message + format of NUnit assertion failure messages. + + + + + Prefix used for the expected value line of a message + + + + + Prefix used for the actual value line of a message + + + + + Length of a message prefix + + + + + Construct a TextMessageWriter + + + + + Construct a TextMessageWriter, specifying a user message + and optional formatting arguments. + + + + + + + Method to write single line message with optional args, usually + written to precede the general failure message, at a givel + indentation level. + + The indentation level of the message + The message to be written + Any arguments used in formatting the message + + + + Display Expected and Actual lines for a constraint. This + is called by MessageWriter's default implementation of + WriteMessageTo and provides the generic two-line display. + + The constraint that failed + + + + Display Expected and Actual lines for given values. This + method may be called by constraints that need more control over + the display of actual and expected values than is provided + by the default implementation. + + The expected value + The actual value causing the failure + + + + Display Expected and Actual lines for given values, including + a tolerance value on the expected line. + + The expected value + The actual value causing the failure + The tolerance within which the test was made + + + + Display the expected and actual string values on separate lines. + If the mismatch parameter is >=0, an additional line is displayed + line containing a caret that points to the mismatch point. + + The expected string value + The actual string value + The point at which the strings don't match or -1 + If true, case is ignored in string comparisons + If true, clip the strings to fit the max line length + + + + Writes the text for a connector. + + The connector. + + + + Writes the text for a predicate. + + The predicate. + + + + Write the text for a modifier. + + The modifier. + + + + Writes the text for an expected value. + + The expected value. + + + + Writes the text for an actual value. + + The actual value. + + + + Writes the text for a generalized value. + + The value. + + + + Writes the text for a collection value, + starting at a particular point, to a max length + + The collection containing elements to write. + The starting point of the elements to write + The maximum number of elements to write + + + + Write the generic 'Expected' line for a constraint + + The constraint that failed + + + + Write the generic 'Expected' line for a given value + + The expected value + + + + Write the generic 'Expected' line for a given value + and tolerance. + + The expected value + The tolerance within which the test was made + + + + Write the generic 'Actual' line for a constraint + + The constraint for which the actual value is to be written + + + + Write the generic 'Actual' line for a given value + + The actual value causing a failure + + + + Gets or sets the maximum line length for this writer + + + + + AssertionHelper is an optional base class for user tests, + allowing the use of shorter names for constraints and + asserts and avoiding conflict with the definition of + , from which it inherits much of its + behavior, in certain mock object frameworks. + + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. Works + identically to + + A Constraint to be applied + The actual value to test + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. Works + identically to + + A Constraint to be applied + The actual value to test + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. Works + identically to + + A Constraint to be applied + The actual value to test + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint expression to be applied + An ActualValueDelegate returning the value to be tested + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint expression to be applied + An ActualValueDelegate returning the value to be tested + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + An ActualValueDelegate returning the value to be tested + A Constraint expression to be applied + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint to be applied + The actual value to test + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint to be applied + The actual value to test + The message that will be displayed on failure + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint to be applied + The actual value to test + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . Works Identically to + . + + The evaluated condition + The message to display if the condition is false + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . Works Identically to + . + + The evaluated condition + The message to display if the condition is false + + + + Asserts that a condition is true. If the condition is false the method throws + an . Works Identically to . + + The evaluated condition + + + + Asserts that the code represented by a delegate throws an exception + that satisfies the constraint provided. + + A TestDelegate to be executed + A ThrowsConstraint used in the test + + + + Returns a ListMapper based on a collection. + + The original collection + + + + + Abstract base for Attributes that are used to include tests + in the test run based on environmental settings. + + + + + Constructor with no included items specified, for use + with named property syntax. + + + + + Constructor taking one or more included items + + Comma-delimited list of included items + + + + Name of the item that is needed in order for + a test to run. Multiple itemss may be given, + separated by a comma. + + + + + Name of the item to be excluded. Multiple items + may be given, separated by a comma. + + + + + The reason for including or excluding the test + + + + + PlatformAttribute is used to mark a test fixture or an + individual method as applying to a particular platform only. + + + + + Constructor with no platforms specified, for use + with named property syntax. + + + + + Constructor taking one or more platforms + + Comma-deliminted list of platforms + + + + CultureAttribute is used to mark a test fixture or an + individual method as applying to a particular Culture only. + + + + + Constructor with no cultures specified, for use + with named property syntax. + + + + + Constructor taking one or more cultures + + Comma-deliminted list of cultures + + + + Summary description for SetCultureAttribute. + + + + + Construct given the name of a culture + + + + + + GlobalSettings is a place for setting default values used + by the framework in performing asserts. + + + + + Default tolerance for floating point equality + + + + + Summary description for DirectoryAssert + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + We don't actually want any instances of this object, but some people + like to inherit from it to add other static methods. Hence, the + protected constructor disallows any instances of this object. + + + + + Verifies that two directories are equal. Two directories are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A directory containing the value that is expected + A directory containing the actual value + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Verifies that two directories are equal. Two directories are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A directory containing the value that is expected + A directory containing the actual value + The message to display if directories are not equal + + + + Verifies that two directories are equal. Two directories are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A directory containing the value that is expected + A directory containing the actual value + + + + Verifies that two directories are equal. Two directories are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A directory path string containing the value that is expected + A directory path string containing the actual value + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Verifies that two directories are equal. Two directories are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A directory path string containing the value that is expected + A directory path string containing the actual value + The message to display if directories are not equal + + + + Verifies that two directories are equal. Two directories are considered + equal if both are null, or if both have the same value byte for byte. + If they are not equal an is thrown. + + A directory path string containing the value that is expected + A directory path string containing the actual value + + + + Asserts that two directories are not equal. If they are equal + an is thrown. + + A directory containing the value that is expected + A directory containing the actual value + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Asserts that two directories are not equal. If they are equal + an is thrown. + + A directory containing the value that is expected + A directory containing the actual value + The message to display if directories are not equal + + + + Asserts that two directories are not equal. If they are equal + an is thrown. + + A directory containing the value that is expected + A directory containing the actual value + + + + Asserts that two directories are not equal. If they are equal + an is thrown. + + A directory path string containing the value that is expected + A directory path string containing the actual value + The message to display if directories are equal + Arguments to be used in formatting the message + + + + Asserts that two directories are not equal. If they are equal + an is thrown. + + A directory path string containing the value that is expected + A directory path string containing the actual value + The message to display if directories are equal + + + + Asserts that two directories are not equal. If they are equal + an is thrown. + + A directory path string containing the value that is expected + A directory path string containing the actual value + + + + Asserts that the directory is empty. If it is not empty + an is thrown. + + A directory to search + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Asserts that the directory is empty. If it is not empty + an is thrown. + + A directory to search + The message to display if directories are not equal + + + + Asserts that the directory is empty. If it is not empty + an is thrown. + + A directory to search + + + + Asserts that the directory is empty. If it is not empty + an is thrown. + + A directory to search + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Asserts that the directory is empty. If it is not empty + an is thrown. + + A directory to search + The message to display if directories are not equal + + + + Asserts that the directory is empty. If it is not empty + an is thrown. + + A directory to search + + + + Asserts that the directory is not empty. If it is empty + an is thrown. + + A directory to search + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Asserts that the directory is not empty. If it is empty + an is thrown. + + A directory to search + The message to display if directories are not equal + + + + Asserts that the directory is not empty. If it is empty + an is thrown. + + A directory to search + + + + Asserts that the directory is not empty. If it is empty + an is thrown. + + A directory to search + The message to display if directories are not equal + Arguments to be used in formatting the message + + + + Asserts that the directory is not empty. If it is empty + an is thrown. + + A directory to search + The message to display if directories are not equal + + + + Asserts that the directory is not empty. If it is empty + an is thrown. + + A directory to search + + + + Asserts that path contains actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + Arguments to be used in formatting the message + + + + Asserts that path contains actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + + + + Asserts that path contains actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + + + + Asserts that path contains actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + Arguments to be used in formatting the message + + + + Asserts that path contains actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + + + + Asserts that path contains actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + + + + Asserts that path does not contain actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + Arguments to be used in formatting the message + + + + Asserts that path does not contain actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + + + + Asserts that path does not contain actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + + + + Asserts that path does not contain actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + Arguments to be used in formatting the message + + + + Asserts that path does not contain actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + The message to display if directory is not within the path + + + + Asserts that path does not contain actual as a subdirectory or + an is thrown. + + A directory to search + sub-directory asserted to exist under directory + + + + TestCaseAttribute is used to mark parameterized test cases + and provide them with their arguments. + + + + + The ITestCaseData interface is implemented by a class + that is able to return complete testcases for use by + a parameterized test method. + + NOTE: This interface is used in both the framework + and the core, even though that results in two different + types. However, sharing the source code guarantees that + the various implementations will be compatible and that + the core is able to reflect successfully over the + framework implementations of ITestCaseData. + + + + + Gets the argument list to be provided to the test + + + + + Gets the expected result + + + + + Gets the expected exception Type + + + + + Gets the FullName of the expected exception + + + + + Gets the name to be used for the test + + + + + Gets the description of the test + + + + + Gets a value indicating whether this is ignored. + + true if ignored; otherwise, false. + + + + Gets the ignore reason. + + The ignore reason. + + + + Construct a TestCaseAttribute with a list of arguments. + This constructor is not CLS-Compliant + + + + + + Construct a TestCaseAttribute with a single argument + + + + + + Construct a TestCaseAttribute with a two arguments + + + + + + + Construct a TestCaseAttribute with a three arguments + + + + + + + + Gets the list of arguments to a test case + + + + + Gets or sets the expected result. + + The result. + + + + Gets or sets the expected exception. + + The expected exception. + + + + Gets or sets the name the expected exception. + + The expected name of the exception. + + + + Gets or sets the expected message of the expected exception + + The expected message of the exception. + + + + Gets or sets the type of match to be performed on the expected message + + + + + Gets or sets the description. + + The description. + + + + Gets or sets the name of the test. + + The name of the test. + + + + Gets or sets the ignored status of the test + + + + + Gets or sets the ignored status of the test + + + + + Gets the ignore reason. + + The ignore reason. + + + + The TestCaseData class represents a set of arguments + and other parameter info to be used for a parameterized + test case. It provides a number of instance modifiers + for use in initializing the test case. + + Note: Instance modifiers are getters that return + the same instance after modifying it's state. + + + + + The argument list to be provided to the test + + + + + The expected result to be returned + + + + + The expected exception Type + + + + + The FullName of the expected exception + + + + + The name to be used for the test + + + + + The description of the test + + + + + A dictionary of properties, used to add information + to tests without requiring the class to change. + + + + + If true, indicates that the test case is to be ignored + + + + + The reason for ignoring a test case + + + + + Initializes a new instance of the class. + + The arguments. + + + + Initializes a new instance of the class. + + The argument. + + + + Initializes a new instance of the class. + + The first argument. + The second argument. + + + + Initializes a new instance of the class. + + The first argument. + The second argument. + The third argument. + + + + Sets the expected result for the test + + The expected result + A modified TestCaseData + + + + Sets the expected exception type for the test + + Type of the expected exception. + The modified TestCaseData instance + + + + Sets the expected exception type for the test + + FullName of the expected exception. + The modified TestCaseData instance + + + + Sets the name of the test case + + The modified TestCaseData instance + + + + Sets the description for the test case + being constructed. + + The description. + The modified TestCaseData instance. + + + + Applies a category to the test + + + + + + + Applies a named property to the test + + + + + + + + Applies a named property to the test + + + + + + + + Applies a named property to the test + + + + + + + + Ignores this TestCase. + + + + + + Ignores this TestCase, specifying the reason. + + The reason. + + + + + Gets the argument list to be provided to the test + + + + + Gets the expected result + + + + + Gets the expected exception Type + + + + + Gets the FullName of the expected exception + + + + + Gets the name to be used for the test + + + + + Gets the description of the test + + + + + Gets a value indicating whether this is ignored. + + true if ignored; otherwise, false. + + + + Gets the ignore reason. + + The ignore reason. + + + + Gets a list of categories associated with this test. + + + + + Gets the property dictionary for this test + + + + + Thrown when an assertion failed. + + + + + + + The error message that explains + the reason for the exception + The exception that caused the + current exception + + + + Serialization Constructor + + + + + Thrown when a test executes inconclusively. + + + + + The error message that explains + the reason for the exception + + + The error message that explains + the reason for the exception + The exception that caused the + current exception + + + + Serialization Constructor + + + + + Attribute used to identify a method that is + called before any tests in a fixture are run. + + + + + Attribute used to identify a method that is called after + all the tests in a fixture have run. The method is + guaranteed to be called, even if an exception is thrown. + + + + + ExplicitAttribute marks a test or test fixture so that it will + only be run if explicitly executed from the gui or command line + or if it is included by use of a filter. The test will not be + run simply because an enclosing suite is run. + + + + + Default constructor + + + + + Constructor with a reason + + The reason test is marked explicit + + + + The reason test is marked explicit + + + + + Thrown when an assertion failed. + + + + + The error message that explains + the reason for the exception + + + The error message that explains + the reason for the exception + The exception that caused the + current exception + + + + Serialization Constructor + + + + + Thrown when an assertion failed. + + + + + + + The error message that explains + the reason for the exception + The exception that caused the + current exception + + + + Serialization Constructor + + + + + Enumeration indicating how the expected message parameter is to be used + + + + Expect an exact match + + + Expect a message containing the parameter string + + + Match the regular expression provided as a parameter + + + Expect a message that starts with the parameter string + + + + ExpectedExceptionAttribute + + + + + + Constructor for a non-specific exception + + + + + Constructor for a given type of exception + + The type of the expected exception + + + + Constructor for a given exception name + + The full name of the expected exception + + + + Gets or sets the expected exception type + + + + + Gets or sets the full Type name of the expected exception + + + + + Gets or sets the expected message text + + + + + Gets or sets the user message displayed in case of failure + + + + + Gets or sets the type of match to be performed on the expected message + + + + + Gets the name of a method to be used as an exception handler + + + + + Attribute used to mark a test that is to be ignored. + Ignored tests result in a warning message when the + tests are run. + + + + + Constructs the attribute without giving a reason + for ignoring the test. + + + + + Constructs the attribute giving a reason for ignoring the test + + The reason for ignoring the test + + + + The reason for ignoring a test + + + + + Attribute used to mark a class that contains one-time SetUp + and/or TearDown methods that apply to all the tests in a + namespace or an assembly. + + + + + Attribute used to mark a static (shared in VB) property + that returns a list of tests. + + + + + Attribute used to identify a method that is called + immediately after each test is run. The method is + guaranteed to be called, even if an exception is thrown. + + + + + Adding this attribute to a method within a + class makes the method callable from the NUnit test runner. There is a property + called Description which is optional which you can provide a more detailed test + description. This class cannot be inherited. + + + + [TestFixture] + public class Fixture + { + [Test] + public void MethodToTest() + {} + + [Test(Description = "more detailed description")] + publc void TestDescriptionMethod() + {} + } + + + + + + Descriptive text for this test + + + + + [TestFixture] + public class ExampleClass + {} + + + + + Default constructor + + + + + Construct with a object[] representing a set of arguments. + In .NET 2.0, the arguments may later be separated into + type arguments and constructor arguments. + + + + + + Descriptive text for this fixture + + + + + The arguments originally provided to the attribute + + + + + Gets or sets a value indicating whether this should be ignored. + + true if ignore; otherwise, false. + + + + Gets or sets the ignore reason. May set Ignored as a side effect. + + The ignore reason. + + + + Get or set the type arguments. If not set + explicitly, any leading arguments that are + Types are taken as type arguments. + + + + + RequiredAddinAttribute may be used to indicate the names of any addins + that must be present in order to run some or all of the tests in an + assembly. If the addin is not loaded, the entire assembly is marked + as NotRunnable. + + + + + Initializes a new instance of the class. + + The required addin. + + + + Gets the name of required addin. + + The required addin name. + + + + Marks a test to use a combinatorial join of any argument data + provided. NUnit will create a test case for every combination of + the arguments provided. This can result in a large number of test + cases and so should be used judiciously. This is the default join + type, so the attribute need not be used except as documentation. + + + + + Default constructor + + + + + Marks a test to use pairwise join of any argument data provided. + NUnit will attempt too excercise every pair of argument values at + least once, using as small a number of test cases as it can. With + only two arguments, this is the same as a combinatorial join. + + + + + Default constructor + + + + + Marks a test to use a sequential join of any argument data + provided. NUnit will use arguements for each parameter in + sequence, generating test cases up to the largest number + of argument values provided and using null for any arguments + for which it runs out of values. Normally, this should be + used with the same number of arguments for each parameter. + + + + + Default constructor + + + + + Abstract base class for attributes that apply to parameters + and supply data for the parameter. + + + + + Gets the data to be provided to the specified parameter + + + + + ValuesAttribute is used to provide literal arguments for + an individual parameter of a test. + + + + + The collection of data to be returned. Must + be set by any derived attribute classes. + We use an object[] so that the individual + elements may have their type changed in GetData + if necessary. + + + + + Construct with one argument + + + + + + Construct with two arguments + + + + + + + Construct with three arguments + + + + + + + + Construct with an array of arguments + + + + + + Get the collection of values to be used as arguments + + + + + RandomAttribute is used to supply a set of random values + to a single parameter of a parameterized test. + + + + + Construct a set of doubles from 0.0 to 1.0, + specifying only the count. + + + + + + Construct a set of doubles from min to max + + + + + + + + Construct a set of ints from min to max + + + + + + + + Get the collection of values to be used as arguments + + + + + RangeAttribute is used to supply a range of values to an + individual parameter of a parameterized test. + + + + + Construct a range of ints using default step of 1 + + + + + + + Construct a range of ints specifying the step size + + + + + + + + Construct a range of longs + + + + + + + + Construct a range of doubles + + + + + + + + Construct a range of floats + + + + + + + + Helper class with properties and methods that supply + a number of constraints used in Asserts. + + + + + Returns a new PropertyConstraintExpression, which will either + test for the existence of the named property on the object + being tested or apply any following constraint to that property. + + + + + Returns a new AttributeConstraint checking for the + presence of a particular attribute on an object. + + + + + Returns a new AttributeConstraint checking for the + presence of a particular attribute on an object. + + + + + Returns a new CollectionContainsConstraint checking for the + presence of a particular object in the collection. + + + + + Returns a ConstraintExpression that negates any + following constraint. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them succeed. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if at least one of them succeeds. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them fail. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Length property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Count property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the Message property of the object being tested. + + + + + Returns a new ConstraintExpression, which will apply the following + constraint to the InnerException property of the object being tested. + + + + + Helper class with properties and methods that supply + a number of constraints used in Asserts. + + + + + Returns a constraint that tests two items for equality + + + + + Returns a constraint that tests that two references are the same object + + + + + Returns a constraint that tests whether the + actual value is greater than the suppled argument + + + + + Returns a constraint that tests whether the + actual value is greater than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is greater than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than or equal to the suppled argument + + + + + Returns a constraint that tests whether the + actual value is less than or equal to the suppled argument + + + + + Returns a constraint that tests whether the actual + value is of the exact type supplied as an argument. + + + + + Returns a constraint that tests whether the actual + value is of the exact type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is of the type supplied as an argument or a derived type. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is assignable from the type supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is a collection containing the same elements as the + collection supplied as an argument. + + + + + Returns a constraint that tests whether the actual value + is a subset of the collection supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value matches the Regex pattern supplied as an argument. + + + + + Returns a constraint that tests whether the path provided + is the same as an expected path after canonicalization. + + + + + Returns a constraint that tests whether the path provided + is the same path or under an expected path after canonicalization. + + + + + Returns a constraint that tests whether the actual value falls + within a specified range. + + + + + Returns a ConstraintExpression that negates any + following constraint. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them succeed. + + + + + Returns a constraint that tests for null + + + + + Returns a constraint that tests for True + + + + + Returns a constraint that tests for False + + + + + Returns a constraint that tests for NaN + + + + + Returns a constraint that tests for empty + + + + + Returns a constraint that tests whether a collection + contains all unique items. + + + + + Returns a constraint that tests whether an object graph is serializable in binary format. + + + + + Returns a constraint that tests whether an object graph is serializable in xml format. + + + + + Returns a constraint that tests whether a collection is ordered + + + + + The List class is a helper class with properties and methods + that supply a number of constraints used with lists and collections. + + + + + List.Map returns a ListMapper, which can be used to map + the original collection to another collection. + + + + + + + ListMapper is used to transform a collection used as an actual argument + producing another collection to be used in the assertion. + + + + + Construct a ListMapper based on a collection + + The collection to be transformed + + + + Produces a collection containing all the values of a property + + The collection of property values + + + + + Helper class with static methods used to supply constraints + that operate on strings. + + + + + Returns a constraint that succeeds if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that fails if the actual + value contains the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that fails if the actual + value starts with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that fails if the actual + value ends with the substring supplied as an argument. + + + + + Returns a constraint that succeeds if the actual + value matches the Regex pattern supplied as an argument. + + + + + Returns a constraint that fails if the actual + value matches the pattern supplied as an argument. + + + + + Returns a ConstraintExpression, which will apply + the following constraint to all members of a collection, + succeeding if all of them succeed. + + + + + Helper class with properties and methods that supply + constraints that operate on exceptions. + + + + + Creates a constraint specifying the exact type of exception expected + + + + + Creates a constraint specifying the exact type of exception expected + + + + + Creates a constraint specifying the type of exception expected + + + + + Creates a constraint specifying the type of exception expected + + + + + Creates a constraint specifying an expected exception + + + + + Creates a constraint specifying an exception with a given InnerException + + + + + Creates a constraint specifying an expected TargetInvocationException + + + + + Creates a constraint specifying an expected TargetInvocationException + + + + + Creates a constraint specifying an expected TargetInvocationException + + + + + Creates a constraint specifying that no exception is thrown + + + + + FactoryAttribute indicates the source to be used to + provide test cases for a test method. + + + + + Construct with the name of the factory - for use with languages + that don't support params arrays. + + An array of the names of the factories that will provide data + + + + Construct with a Type and name - for use with languages + that don't support params arrays. + + The Type that will provide data + The name of the method, property or field that will provide data + + + + The name of a the method, property or fiend to be used as a source + + + + + A Type to be used as a source + + + + + ValueSourceAttribute indicates the source to be used to + provide data for one parameter of a test method. + + + + + Construct with the name of the factory - for use with languages + that don't support params arrays. + + The name of the data source to be used + + + + Construct with a Type and name - for use with languages + that don't support params arrays. + + The Type that will provide data + The name of the method, property or field that will provide data + + + + The name of a the method, property or fiend to be used as a source + + + + + A Type to be used as a source + + + + + The Iz class is a synonym for Is intended for use in VB, + which regards Is as a keyword. + + + + + WUsed on a method, marks the test with a timeout value in milliseconds. + The test will be run in a separate thread and is cancelled if the timeout + is exceeded. Used on a method or assembly, sets the default timeout + for all contained test methods. + + + + + Construct a TimeoutAttribute given a time in milliseconds + + The timeout value in milliseconds + + + + Marks a test that must run in the STA, causing it + to run in a separate thread if necessary. + + On methods, you may also use STAThreadAttribute + to serve the same purpose. + + + + + Construct a RequiresSTAAttribute + + + + + Marks a test that must run in the MTA, causing it + to run in a separate thread if necessary. + + On methods, you may also use MTAThreadAttribute + to serve the same purpose. + + + + + Construct a RequiresMTAAttribute + + + + + Marks a test that must run on a separate thread. + + + + + Construct a RequiresThreadAttribute + + + + + Construct a RequiresThreadAttribute, specifying the apartment + + + + + Summary description for MaxTimeAttribute. + + + + + Construct a MaxTimeAttribute, given a time in milliseconds. + + The maximum elapsed time in milliseconds + + + + RepeatAttribute may be applied to test case in order + to run it multiple times. + + + + + Construct a RepeatAttribute + + The number of times to run the test + + + + Provides static methods to express the assumptions + that must be met for a test to give a meaningful + result. If an assumption is not met, the test + should produce an inconclusive result. + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + The actual value to test + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + The actual value to test + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + The actual value to test + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + An ActualValueDelegate returning the value to be tested + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + An ActualValueDelegate returning the value to be tested + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + An ActualValueDelegate returning the value to be tested + A Constraint expression to be applied + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + The actual value to test + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + The actual value to test + The message that will be displayed on failure + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an InconclusiveException on failure. + + A Constraint expression to be applied + The actual value to test + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display if the condition is false + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display if the condition is false + + + + Asserts that a condition is true. If the condition is false the + method throws an . + + The evaluated condition + + + + Asserts that the code represented by a delegate throws an exception + that satisfies the constraint provided. + + A TestDelegate to be executed + A ThrowsConstraint used in the test + + + + Randomizer returns a set of random values in a repeatable + way, to allow re-running of tests if necessary. + + + + + Get a randomizer for a particular member, returning + one that has already been created if it exists. + This ensures that the same values are generated + each time the tests are reloaded. + + + + + Get a randomizer for a particular parameter, returning + one that has already been created if it exists. + This ensures that the same values are generated + each time the tests are reloaded. + + + + + Construct a randomizer using a random seed + + + + + Construct a randomizer using a specified seed + + + + + Return an array of random doubles between 0.0 and 1.0. + + + + + + + Return an array of random doubles with values in a specified range. + + + + + Return an array of random ints with values in a specified range. + + + + + Get a random seed for use in creating a randomizer. + + + + + Adding this attribute to a method within a + class makes the method callable from the NUnit test runner. There is a property + called Description which is optional which you can provide a more detailed test + description. This class cannot be inherited. + + + + [TestFixture] + public class Fixture + { + [Test] + public void MethodToTest() + {} + + [Test(Description = "more detailed description")] + publc void TestDescriptionMethod() + {} + } + + + + + + Used to mark a field for use as a datapoint when executing a theory + within the same fixture that requires an argument of the field's Type. + + + + + Used to mark an array as containing a set of datapoints to be used + executing a theory within the same fixture that requires an argument + of the Type of the array elements. + + + + + The SpecialValue enum is used to represent TestCase arguments + that cannot be used as arguments to an Attribute. + + + + + Null represents a null value, which cannot be used as an + argument to an attriute under .NET 1.x + + + + + Summary description for SetUICultureAttribute. + + + + + Construct given the name of a culture + + + + + + Delegate used by tests that execute code and + capture any thrown exception. + + + + + The Assert class contains a collection of static methods that + implement the most common assertions used in NUnit. + + + + + We don't actually want any instances of this object, but some people + like to inherit from it to add other static methods. Hence, the + protected constructor disallows any instances of this object. + + + + + The Equals method throws an AssertionException. This is done + to make sure there is no mistake by calling this function. + + + + + + + override the default ReferenceEquals to throw an AssertionException. This + implementation makes sure there is no mistake in calling this function + as part of Assert. + + + + + + + Helper for Assert.AreEqual(double expected, double actual, ...) + allowing code generation to work consistently. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Throws a with the message and arguments + that are passed in. This allows a test to be cut short, with a result + of success returned to NUnit. + + The message to initialize the with. + Arguments to be used in formatting the message + + + + Throws a with the message and arguments + that are passed in. This allows a test to be cut short, with a result + of success returned to NUnit. + + The message to initialize the with. + + + + Throws a with the message and arguments + that are passed in. This allows a test to be cut short, with a result + of success returned to NUnit. + + + + + Throws an with the message and arguments + that are passed in. This is used by the other Assert functions. + + The message to initialize the with. + Arguments to be used in formatting the message + + + + Throws an with the message that is + passed in. This is used by the other Assert functions. + + The message to initialize the with. + + + + Throws an . + This is used by the other Assert functions. + + + + + Throws an with the message and arguments + that are passed in. This causes the test to be reported as ignored. + + The message to initialize the with. + Arguments to be used in formatting the message + + + + Throws an with the message that is + passed in. This causes the test to be reported as ignored. + + The message to initialize the with. + + + + Throws an . + This causes the test to be reported as ignored. + + + + + Throws an with the message and arguments + that are passed in. This causes the test to be reported as inconclusive. + + The message to initialize the with. + Arguments to be used in formatting the message + + + + Throws an with the message that is + passed in. This causes the test to be reported as inconclusive. + + The message to initialize the with. + + + + Throws an . + This causes the test to be reported as Inconclusive. + + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint to be applied + The actual value to test + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint to be applied + The actual value to test + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint expression to be applied + The actual value to test + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint expression to be applied + An ActualValueDelegate returning the value to be tested + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint expression to be applied + An ActualValueDelegate returning the value to be tested + The message that will be displayed on failure + + + + Apply a constraint to an actual value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + An ActualValueDelegate returning the value to be tested + A Constraint expression to be applied + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint to be applied + The actual value to test + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint to be applied + The actual value to test + The message that will be displayed on failure + + + + Apply a constraint to a referenced value, succeeding if the constraint + is satisfied and throwing an assertion exception on failure. + + A Constraint to be applied + The actual value to test + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display if the condition is false + Arguments to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display if the condition is false + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + + + + Asserts that the code represented by a delegate throws an exception + that satisfies the constraint provided. + + A TestDelegate to be executed + A ThrowsConstraint used in the test + + + + Verifies that a delegate throws a particular exception when called. + + A constraint to be satisfied by the exception + A TestSnippet delegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate throws a particular exception when called. + + A constraint to be satisfied by the exception + A TestSnippet delegate + The message that will be displayed on failure + + + + Verifies that a delegate throws a particular exception when called. + + A constraint to be satisfied by the exception + A TestSnippet delegate + + + + Verifies that a delegate throws a particular exception when called. + + The exception Type expected + A TestSnippet delegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate throws a particular exception when called. + + The exception Type expected + A TestSnippet delegate + The message that will be displayed on failure + + + + Verifies that a delegate throws a particular exception when called. + + The exception Type expected + A TestSnippet delegate + + + + Verifies that a delegate throws a particular exception when called. + + Type of the expected exception + A TestSnippet delegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate throws a particular exception when called. + + Type of the expected exception + A TestSnippet delegate + The message that will be displayed on failure + + + + Verifies that a delegate throws a particular exception when called. + + Type of the expected exception + A TestSnippet delegate + + + + Verifies that a delegate throws an exception when called + and returns it. + + A TestDelegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate throws an exception when called + and returns it. + + A TestDelegate + The message that will be displayed on failure + + + + Verifies that a delegate throws an exception when called + and returns it. + + A TestDelegate + + + + Verifies that a delegate throws an exception of a certain Type + or one derived from it when called and returns it. + + The expected Exception Type + A TestDelegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate throws an exception of a certain Type + or one derived from it when called and returns it. + + The expected Exception Type + A TestDelegate + The message that will be displayed on failure + + + + Verifies that a delegate throws an exception of a certain Type + or one derived from it when called and returns it. + + The expected Exception Type + A TestDelegate + + + + Verifies that a delegate throws an exception of a certain Type + or one derived from it when called and returns it. + + The expected Exception Type + A TestDelegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate throws an exception of a certain Type + or one derived from it when called and returns it. + + The expected Exception Type + A TestDelegate + The message that will be displayed on failure + + + + Verifies that a delegate throws an exception of a certain Type + or one derived from it when called and returns it. + + The expected Exception Type + A TestDelegate + + + + Verifies that a delegate does not throw an exception + + A TestSnippet delegate + The message that will be displayed on failure + Arguments to be used in formatting the message + + + + Verifies that a delegate does not throw an exception. + + A TestSnippet delegate + The message that will be displayed on failure + + + + Verifies that a delegate does not throw an exception. + + A TestSnippet delegate + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display in case of failure + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + The message to display in case of failure + + + + Asserts that a condition is true. If the condition is false the method throws + an . + + The evaluated condition + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + The message to display in case of failure + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + The message to display in case of failure + + + + Asserts that a condition is false. If the condition is true the method throws + an . + + The evaluated condition + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + + + + Verifies that the object that is passed in is not equal to null + If the object is null then an + is thrown. + + The object that is to be tested + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + The message to display in case of failure + + + + Verifies that the object that is passed in is equal to null + If the object is not null then an + is thrown. + + The object that is to be tested + + + + Verifies that the double that is passed in is an NaN value. + If the object is not NaN then an + is thrown. + + The value that is to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the double that is passed in is an NaN value. + If the object is not NaN then an + is thrown. + + The value that is to be tested + The message to display in case of failure + + + + Verifies that the double that is passed in is an NaN value. + If the object is not NaN then an + is thrown. + + The value that is to be tested + + + + Verifies that the double that is passed in is an NaN value. + If the object is not NaN then an + is thrown. + + The value that is to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the double that is passed in is an NaN value. + If the object is not NaN then an + is thrown. + + The value that is to be tested + The message to display in case of failure + + + + Verifies that the double that is passed in is an NaN value. + If the object is not NaN then an + is thrown. + + The value that is to be tested + + + + Assert that a string is empty - that is equal to string.Empty + + The string to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Assert that a string is empty - that is equal to string.Empty + + The string to be tested + The message to display in case of failure + + + + Assert that a string is empty - that is equal to string.Empty + + The string to be tested + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + The message to display in case of failure + + + + Assert that an array, list or other collection is empty + + An array, list or other collection implementing ICollection + + + + Assert that a string is not empty - that is not equal to string.Empty + + The string to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Assert that a string is not empty - that is not equal to string.Empty + + The string to be tested + The message to display in case of failure + + + + Assert that a string is not empty - that is not equal to string.Empty + + The string to be tested + + + + Assert that an array, list or other collection is not empty + + An array, list or other collection implementing ICollection + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Assert that an array, list or other collection is not empty + + An array, list or other collection implementing ICollection + The message to display in case of failure + + + + Assert that an array, list or other collection is not empty + + An array, list or other collection implementing ICollection + + + + Assert that a string is either null or equal to string.Empty + + The string to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Assert that a string is either null or equal to string.Empty + + The string to be tested + The message to display in case of failure + + + + Assert that a string is either null or equal to string.Empty + + The string to be tested + + + + Assert that a string is not null or empty + + The string to be tested + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Assert that a string is not null or empty + + The string to be tested + The message to display in case of failure + + + + Assert that a string is not null or empty + + The string to be tested + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + + + + Asserts that an object may be assigned a value of a given Type. + + The expected Type. + The object under examination + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + The message to display in case of failure + + + + Asserts that an object may not be assigned a value of a given Type. + + The expected Type. + The object under examination + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + + + + Asserts that an object is an instance of a given type. + + The expected Type + The object being examined + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + The message to display in case of failure + + + + Asserts that an object is not an instance of a given type. + + The expected Type + The object being examined + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two values are equal. If they are not, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equal then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equal then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message to display in case of failure + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equal then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equal then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equal then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + The message to display in case of failure + + + + Verifies that two doubles are equal considering a delta. If the + expected value is infinity then the delta value is ignored. If + they are not equal then an is + thrown. + + The expected value + The actual value + The maximum acceptable difference between the + the expected and the actual + + + + Verifies that two objects are equal. Two objects are considered + equal if both are null, or if both have the same value. NUnit + has special semantics for some object types. + If they are not equal an is thrown. + + The value that is expected + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two objects are equal. Two objects are considered + equal if both are null, or if both have the same value. NUnit + has special semantics for some object types. + If they are not equal an is thrown. + + The value that is expected + The actual value + The message to display in case of failure + + + + Verifies that two objects are equal. Two objects are considered + equal if both are null, or if both have the same value. NUnit + has special semantics for some object types. + If they are not equal an is thrown. + + The value that is expected + The actual value + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + The message to display in case of failure + + + + Verifies that two values are not equal. If they are equal, then an + is thrown. + + The expected value + The actual value + + + + Verifies that two objects are not equal. Two objects are considered + equal if both are null, or if both have the same value. NUnit + has special semantics for some object types. + If they are equal an is thrown. + + The value that is expected + The actual value + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that two objects are not equal. Two objects are considered + equal if both are null, or if both have the same value. NUnit + has special semantics for some object types. + If they are equal an is thrown. + + The value that is expected + The actual value + The message to display in case of failure + + + + Verifies that two objects are not equal. Two objects are considered + equal if both are null, or if both have the same value. NUnit + has special semantics for some object types. + If they are equal an is thrown. + + The value that is expected + The actual value + + + + Asserts that two objects refer to the same object. If they + are not the same an is thrown. + + The expected object + The actual object + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that two objects refer to the same object. If they + are not the same an is thrown. + + The expected object + The actual object + The message to display in case of failure + + + + Asserts that two objects refer to the same object. If they + are not the same an is thrown. + + The expected object + The actual object + + + + Asserts that two objects do not refer to the same object. If they + are the same an is thrown. + + The expected object + The actual object + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that two objects do not refer to the same object. If they + are the same an is thrown. + + The expected object + The actual object + The message to display in case of failure + + + + Asserts that two objects do not refer to the same object. If they + are the same an is thrown. + + The expected object + The actual object + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than the second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + The message to display in case of failure + + + + Verifies that the first value is greater than or equal tothe second + value. If it is not, then an + is thrown. + + The first value, expected to be greater + The second value, expected to be less + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + The message to display in case of failure + + + + Verifies that the first value is less than or equal to the second + value. If it is not, then an + is thrown. + + The first value, expected to be less + The second value, expected to be greater + + + + Asserts that an object is contained in a list. + + The expected object + The list to be examined + The message to display in case of failure + Array of objects to be used in formatting the message + + + + Asserts that an object is contained in a list. + + The expected object + The list to be examined + The message to display in case of failure + + + + Asserts that an object is contained in a list. + + The expected object + The list to be examined + + + + Gets the number of assertions executed so far and + resets the counter to zero. + + + + + Static helper class used in the constraint-based syntax + + + + + Creates a new SubstringConstraint + + The value of the substring + A SubstringConstraint + + + + Creates a new CollectionContainsConstraint. + + The item that should be found. + A new CollectionContainsConstraint + + + + Attribute used to apply a category to a test + + + + + The name of the category + + + + + Construct attribute for a given category based on + a name. The name may not contain the characters ',', + '+', '-' or '!'. However, this is not checked in the + constructor since it would cause an error to arise at + as the test was loaded without giving a clear indication + of where the problem is located. The error is handled + in NUnitFramework.cs by marking the test as not + runnable. + + The name of the category + + + + Protected constructor uses the Type name as the name + of the category. + + + + + The name of the category + + + + + The TestStatus enum indicates the result of running a test + + + + + The test was inconclusive + + + + + The test has skipped + + + + + The test succeeded + + + + + The test failed + + + + + Provide the context information of the current test + + + + + The TestState of current test. This maps to the ResultState + used in nunit.core and is subject to change in the future. + + + + + The TestStatus of current test. This enum will be used + in future versions of NUnit and so is to be preferred + to the TestState value. + + + + + The name of the currently executing test. If no + test is running, the name of the last test run. + + + + + The properties of the currently executing test + or, if no test is running, of the last test run. + + + + + The ResultState enum indicates the result of running a test + + + + + The result is inconclusive + + + + + The test was not runnable. + + + + + The test has been skipped. + + + + + The test has been ignored. + + + + + The test succeeded + + + + + The test failed + + + + + The test encountered an unexpected exception + + + + + The test was cancelled by the user + + + + diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/framework/nunit.mocks.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/framework/nunit.mocks.dll new file mode 100644 index 0000000..43aafff Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/framework/nunit.mocks.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/framework/pnunit.framework.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/framework/pnunit.framework.dll new file mode 100644 index 0000000..3783a4a Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/framework/pnunit.framework.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/launcher.log.conf b/trunk/Libraries/Json40r2/Source/Tools/NUnit/launcher.log.conf new file mode 100644 index 0000000..4bd90ca --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/launcher.log.conf @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Failure.png b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Failure.png new file mode 100644 index 0000000..2e400b2 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Failure.png differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Ignored.png b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Ignored.png new file mode 100644 index 0000000..478efbf Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Ignored.png differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Inconclusive.png b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Inconclusive.png new file mode 100644 index 0000000..4807b7c Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Inconclusive.png differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Skipped.png b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Skipped.png new file mode 100644 index 0000000..7c9fc64 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Skipped.png differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Success.png b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Success.png new file mode 100644 index 0000000..2a30150 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/Success.png differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/fit.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/fit.dll new file mode 100644 index 0000000..40bbef0 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/fit.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/log4net.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/log4net.dll new file mode 100644 index 0000000..20a2e1c Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/log4net.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit-console-runner.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit-console-runner.dll new file mode 100644 index 0000000..3d1152c Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit-console-runner.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit-gui-runner.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit-gui-runner.dll new file mode 100644 index 0000000..4398f8f Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit-gui-runner.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.core.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.core.dll new file mode 100644 index 0000000..dd3b2e0 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.core.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.core.interfaces.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.core.interfaces.dll new file mode 100644 index 0000000..39a1127 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.core.interfaces.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.fixtures.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.fixtures.dll new file mode 100644 index 0000000..b48a1d4 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.fixtures.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.uiexception.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.uiexception.dll new file mode 100644 index 0000000..9e970f5 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.uiexception.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.uikit.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.uikit.dll new file mode 100644 index 0000000..bd0ff5b Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.uikit.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.util.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.util.dll new file mode 100644 index 0000000..2d002a8 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/lib/nunit.util.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-agent-x86.exe.config b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-agent-x86.exe.config new file mode 100644 index 0000000..e2d3b78 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-agent-x86.exe.config @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-agent.exe.config b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-agent.exe.config new file mode 100644 index 0000000..e2d3b78 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-agent.exe.config @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-console-x86.exe.config b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-console-x86.exe.config new file mode 100644 index 0000000..f32f92b --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-console-x86.exe.config @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-console.exe.config b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-console.exe.config new file mode 100644 index 0000000..f32f92b --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-console.exe.config @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-x86.exe.config b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-x86.exe.config new file mode 100644 index 0000000..878d91b --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit-x86.exe.config @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit.exe.config b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit.exe.config new file mode 100644 index 0000000..878d91b --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit.exe.config @@ -0,0 +1,91 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit.framework.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit.framework.dll new file mode 100644 index 0000000..639dbb0 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/nunit.framework.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/pnunit-agent.exe.config b/trunk/Libraries/Json40r2/Source/Tools/NUnit/pnunit-agent.exe.config new file mode 100644 index 0000000..0bf29b3 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/pnunit-agent.exe.config @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/pnunit-launcher.exe.config b/trunk/Libraries/Json40r2/Source/Tools/NUnit/pnunit-launcher.exe.config new file mode 100644 index 0000000..0bf29b3 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/pnunit-launcher.exe.config @@ -0,0 +1,77 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/pnunit.framework.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/pnunit.framework.dll new file mode 100644 index 0000000..3783a4a Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/pnunit.framework.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/pnunit.tests.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/pnunit.tests.dll new file mode 100644 index 0000000..77c4a22 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/pnunit.tests.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/runFile.exe.config b/trunk/Libraries/Json40r2/Source/Tools/NUnit/runFile.exe.config new file mode 100644 index 0000000..f58f099 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/runFile.exe.config @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/runpnunit.bat b/trunk/Libraries/Json40r2/Source/Tools/NUnit/runpnunit.bat new file mode 100644 index 0000000..a05cbb7 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/runpnunit.bat @@ -0,0 +1,2 @@ +start pnunit-agent agent.conf +pnunit-launcher test.conf \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/test.conf b/trunk/Libraries/Json40r2/Source/Tools/NUnit/test.conf new file mode 100644 index 0000000..14cd113 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/NUnit/test.conf @@ -0,0 +1,24 @@ + + + + + Testing + + + Testing + pnunit.tests.dll + TestLibraries.Testing.EqualTo19 + localhost:8080 + + ..\server + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/loadtest-assembly.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/loadtest-assembly.dll new file mode 100644 index 0000000..ffcb0e6 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/loadtest-assembly.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/mock-assembly.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/mock-assembly.dll new file mode 100644 index 0000000..1083ced Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/mock-assembly.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nonamespace-assembly.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nonamespace-assembly.dll new file mode 100644 index 0000000..22506fb Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nonamespace-assembly.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit-console.tests.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit-console.tests.dll new file mode 100644 index 0000000..b626c19 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit-console.tests.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit-gui.tests.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit-gui.tests.dll new file mode 100644 index 0000000..0845863 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit-gui.tests.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.core.tests.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.core.tests.dll new file mode 100644 index 0000000..ee09df9 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.core.tests.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.fixtures.tests.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.fixtures.tests.dll new file mode 100644 index 0000000..9ab1d96 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.fixtures.tests.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.framework.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.framework.dll new file mode 100644 index 0000000..639dbb0 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.framework.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.framework.tests.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.framework.tests.dll new file mode 100644 index 0000000..2da0676 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.framework.tests.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.mocks.tests.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.mocks.tests.dll new file mode 100644 index 0000000..289ac49 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.mocks.tests.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.uiexception.tests.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.uiexception.tests.dll new file mode 100644 index 0000000..8c47743 Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.uiexception.tests.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.uikit.tests.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.uikit.tests.dll new file mode 100644 index 0000000..f6e4b4c Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.uikit.tests.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.util.tests.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.util.tests.dll new file mode 100644 index 0000000..21c093d Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/nunit.util.tests.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/test-assembly.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/test-assembly.dll new file mode 100644 index 0000000..2f1d71d Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/test-assembly.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/test-utilities.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/test-utilities.dll new file mode 100644 index 0000000..87a0b2a Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/test-utilities.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/timing-tests.dll b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/timing-tests.dll new file mode 100644 index 0000000..6be4cfc Binary files /dev/null and b/trunk/Libraries/Json40r2/Source/Tools/NUnit/tests/timing-tests.dll differ diff --git a/trunk/Libraries/Json40r2/Source/Tools/PSake/psake.psm1 b/trunk/Libraries/Json40r2/Source/Tools/PSake/psake.psm1 new file mode 100644 index 0000000..4641028 --- /dev/null +++ b/trunk/Libraries/Json40r2/Source/Tools/PSake/psake.psm1 @@ -0,0 +1,1208 @@ +# psake +# Copyright (c) 2010 James Kovacs +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +#Requires -Version 2.0 + +#-- Private Module Variables (Listed here for quick reference) +[system.collections.stack]$script:context + +#-- Public Module Variables -- The psake hashtable variable is initialized in the invoke-psake function +$script:psake = @{} +$script:psake.use_exit_on_error = $false # determines if psake uses the "exit()" function when an exception occurs +$script:psake.log_error = $false # determines if the exception details are written to a file +$script:psake.build_success = $false # indicates that the current build was successful +$script:psake.version = "4.00" # contains the current version of psake +$script:psake.build_script_file = $null # contains a System.IO.FileInfo for the current build file +$script:psake.framework_version = "" # contains the framework version # for the current build +$script:psake.default_build_file_name = 'default.ps1' + +Export-ModuleMember -Variable "psake" + +#-- Private Module Functions +function ExecuteTask +{ + param([string]$taskName) + + Assert (![string]::IsNullOrEmpty($taskName)) "Task name should not be null or empty string" + + $taskKey = $taskName.ToLower() + + Assert ($script:context.Peek().tasks.Contains($taskKey)) "task [$taskName] does not exist" + + if ($script:context.Peek().executedTasks.Contains($taskKey)) + { + return + } + + Assert (!$script:context.Peek().callStack.Contains($taskKey)) "Error: Circular reference found for task, $taskName" + + $script:context.Peek().callStack.Push($taskKey) + + $task = $script:context.Peek().tasks.$taskKey + + $taskName = $task.Name + + $precondition_is_valid = if ($task.Precondition -ne $null) {& $task.Precondition} else {$true} + + if (!$precondition_is_valid) + { + "Precondition was false not executing $name" + } + else + { + if ($taskKey -ne 'default') + { + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + + if ( ($task.PreAction -ne $null) -or ($task.PostAction -ne $null) ) + { + Assert ($task.Action -ne $null) "Error: Action parameter must be specified when using PreAction or PostAction parameters" + } + + if ($task.Action -ne $null) + { + try + { + foreach($childTask in $task.DependsOn) + { + ExecuteTask $childTask + } + + $script:context.Peek().currentTaskName = $taskName + + if ($script:context.Peek().taskSetupScriptBlock -ne $null) + { + & $script:context.Peek().taskSetupScriptBlock + } + + if ($task.PreAction -ne $null) + { + & $task.PreAction + } + + $script:context.Peek().formatTaskNameString -f $taskName + & $task.Action + + if ($task.PostAction -ne $null) + { + & $task.PostAction + } + + if ($script:context.Peek().taskTearDownScriptBlock -ne $null) + { + & $script:context.Peek().taskTearDownScriptBlock + } + } + catch + { + if ($task.ContinueOnError) + { + "-"*70 + "Error in Task [$taskName] $_" + "-"*70 + } + else + { + throw $_ + } + } + } # if ($task.Action -ne $null) + else + { + #no Action was specified but we still execute all the dependencies + foreach($childTask in $task.DependsOn) + { + ExecuteTask $childTask + } + } + $stopwatch.stop() + $task.Duration = $stopwatch.Elapsed + } # if ($name.ToLower() -ne 'default') + else + { + foreach($childTask in $task.DependsOn) + { + ExecuteTask $childTask + } + } + + if ($task.Postcondition -ne $null) + { + Assert (& $task.Postcondition) "Error: Postcondition failed for $taskName" + } + } + + $poppedTaskKey = $script:context.Peek().callStack.Pop() + + Assert ($poppedTaskKey -eq $taskKey) "Error: CallStack was corrupt. Expected $taskKey, but got $poppedTaskKey." + + $script:context.Peek().executedTasks.Push($taskKey) +} + +function Configure-BuildEnvironment +{ + if ($framework.Length -ne 3 -and $framework.Length -ne 6) { + throw "Error: Invalid .NET Framework version, $framework, specified" + } + $versionPart = $framework.Substring(0,3) + $bitnessPart = $framework.Substring(3) + $versions = $null + switch ($versionPart) + { + '1.0' { $versions = @('v1.0.3705') } + '1.1' { $versions = @('v1.1.4322') } + '2.0' { $versions = @('v2.0.50727') } + '3.0' { $versions = @('v2.0.50727') } + '3.5' { $versions = @('v3.5','v2.0.50727') } + '4.0' { $versions = @('v4.0.30319') } + default { throw "Error: Unknown .NET Framework version, $versionPart, specified in $framework" } + } + + $bitness = 'Framework' + if($versionPart -ne '1.0' -and $versionPart -ne '1.1') { + switch ($bitnessPart) + { + 'x86' { $bitness = 'Framework' } + 'x64' { $bitness = 'Framework64' } + $null { + $ptrSize = [System.IntPtr]::Size + switch ($ptrSize) + { + 4 { $bitness = 'Framework' } + 8 { $bitness = 'Framework64' } + default { throw "Error: Unknown pointer size ($ptrSize) returned from System.IntPtr." } + } + } + default { throw "Error: Unknown .NET Framework bitness, $bitnessPart, specified in $framework" } + } + } + $frameworkDirs = $versions | foreach { "$env:windir\Microsoft.NET\$bitness\$_\" } + + $frameworkDirs | foreach { Assert (test-path $_) "Error: No .NET Framework installation directory found at $_" } + + $env:path = [string]::Join(';', $frameworkDirs) + ";$env:path" + #if any error occurs in a PS function then "stop" processing immediately + # this does not effect any external programs that return a non-zero exit code + $global:ErrorActionPreference = "Stop" +} + +function Cleanup-Environment +{ + $env:path = $script:context.Peek().originalEnvPath + Set-Location $script:context.Peek().originalDirectory + $global:ErrorActionPreference = $script:context.Peek().originalErrorActionPreference +} + +#borrowed from Jeffrey Snover http://blogs.msdn.com/powershell/archive/2006/12/07/resolve-error.aspx +function Resolve-Error($ErrorRecord=$Error[0]) +{ + "ErrorRecord" + $ErrorRecord | Format-List * -Force | Out-String -Stream | ? {$_} + "" + "ErrorRecord.InvocationInfo" + $ErrorRecord.InvocationInfo | Format-List * | Out-String -Stream | ? {$_} + "" + "Exception" + $Exception = $ErrorRecord.Exception + for ($i = 0; $Exception; $i++, ($Exception = $Exception.InnerException)) + { + "$i" * 70 + $Exception | Format-List * -Force | Out-String -Stream | ? {$_} + "" + } +} + +function Write-Documentation +{ + $list = New-Object System.Collections.ArrayList + foreach($key in $script:context.Peek().tasks.Keys) + { + if($key -eq "default") + { + continue + } + $task = $script:context.Peek().tasks.$key + $content = "" | Select-Object Name, Description + $content.Name = $task.Name + $content.Description = $task.Description + $index = $list.Add($content) + } + + $list | Sort 'Name' | Format-Table -Auto +} + +function Write-TaskTimeSummary +{ + "-"*70 + "Build Time Report" + "-"*70 + $list = @() + while ($script:context.Peek().executedTasks.Count -gt 0) + { + $taskKey = $script:context.Peek().executedTasks.Pop() + $task = $script:context.Peek().tasks.$taskKey + if($taskKey -eq "default") + { + continue + } + $list += "" | Select-Object @{Name="Name";Expression={$task.Name}}, @{Name="Duration";Expression={$task.Duration}} + } + [Array]::Reverse($list) + $list += "" | Select-Object @{Name="Name";Expression={"Total:"}}, @{Name="Duration";Expression={$stopwatch.Elapsed}} + $list | Format-Table -Auto | Out-String -Stream | ? {$_} # using "Out-String -Stream" to filter out the blank line that Format-Table prepends +} + +#-- Public Module Functions +function Exec +{ +<# +.SYNOPSIS +Helper function for executing command-line programs. + +.DESCRIPTION +This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode to see if an error occcured. +If an error is detected then an exception is thrown. This function allows you to run command-line programs without +having to explicitly check fthe $lastexitcode variable. + +.PARAMETER cmd +The scriptblock to execute. This scriptblock will typically contain the command-line invocation. +Required + +.PARAMETER errorMessage +The error message used for the exception that is thrown. +Optional + +.EXAMPLE +exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed" + +This example calls the svn command-line client. + +.LINK +Assert +Invoke-psake +Task +Properties +Include +FormatTaskName +TaskSetup +TaskTearDown +#> +[CmdletBinding( + SupportsShouldProcess=$False, + SupportsTransactions=$False, + ConfirmImpact="None", + DefaultParameterSetName="")] + + param( + [Parameter(Position=0,Mandatory=1)][scriptblock]$cmd, + [Parameter(Position=1,Mandatory=0)][string]$errorMessage = "Error executing command: " + $cmd + ) + & $cmd + if ($lastexitcode -ne 0) + { + throw $errorMessage + } +} + +function Assert +{ +<# +.SYNOPSIS +Helper function for "Design by Contract" assertion checking. + +.DESCRIPTION +This is a helper function that makes the code less noisy by eliminating many of the "if" statements +that are normally required to verify assumptions in the code. + +.PARAMETER conditionToCheck +The boolean condition to evaluate +Required + +.PARAMETER failureMessage +The error message used for the exception if the conditionToCheck parameter is false +Required + +.EXAMPLE +Assert $false "This always throws an exception" + +This example always throws an exception + +.EXAMPLE +Assert ( ($i % 2) -eq 0 ) "%i is not an even number" + +This exmaple may throw an exception if $i is not an even number + +.LINK +Invoke-psake +Task +Properties +Include +FormatTaskName +TaskSetup +TaskTearDown + +.NOTES +It might be necessary to wrap the condition with paranthesis to force PS to evaluate the condition +so that a boolean value is calculated and passed into the 'conditionToCheck' parameter. + +Example: + Assert 1 -eq 2 "1 doesn't equal 2" + +PS will pass 1 into the condtionToCheck variable and PS will look for a parameter called "eq" and +throw an exception with the following message "A parameter cannot be found that matches parameter name 'eq'" + +The solution is to wrap the condition in () so that PS will evaluate it first. + + Assert (1 -eq 2) "1 doesn't equal 2" +#> +[CmdletBinding( + SupportsShouldProcess=$False, + SupportsTransactions=$False, + ConfirmImpact="None", + DefaultParameterSetName="")] + + param( + [Parameter(Position=0,Mandatory=1)]$conditionToCheck, + [Parameter(Position=1,Mandatory=1)]$failureMessage + ) + if (!$conditionToCheck) { throw $failureMessage } +} + +function Task +{ +<# +.SYNOPSIS +Defines a build task to be executed by psake + +.DESCRIPTION +This function creates a 'task' object that will be used by the psake engine to execute a build task. +Note: There must be at least one task called 'default' in the build script + +.PARAMETER Name +The name of the task +Required + +.PARAMETER Action +A scriptblock containing the statements to execute +Optional + +.PARAMETER PreAction +A scriptblock to be executed before the 'Action' scriptblock. +Note: This parameter is ignored if the 'Action' scriptblock is not defined. +Optional + +.PARAMETER PostAction +A scriptblock to be executed after the 'Action' scriptblock. +Note: This parameter is ignored if the 'Action' scriptblock is not defined. +Optional + +.PARAMETER Precondition +A scriptblock that is executed to determine if the task is executed or skipped. +This scriptblock should return $true or $false +Optional + +.PARAMETER Postcondition +A scriptblock that is executed to determine if the task completed its job correctly. +An exception is thrown if the scriptblock returns $false. +Optional + +.PARAMETER ContinueOnError +If this switch parameter is set then the task will not cause the build to fail when an exception is thrown + +.PARAMETER Depends +An array of tasks that this task depends on. They will be executed before the current task is executed. + +.PARAMETER Description +A description of the task. + +.EXAMPLE +A sample build script is shown below: + +task default -depends Test + +task Test -depends Compile, Clean { + "This is a test" +} + +task Compile -depends Clean { + "Compile" +} + +task Clean { + "Clean" +} + +The 'default' task is required and should not contain an 'Action' parameter. +It uses the 'depends' parameter to specify that 'Test' is a dependency + +The 'Test' task uses the 'depends' parameter to specify that 'Compile' and 'Clean' are dependencies +The 'Compile' task depends on the 'Clean' task. + +Note: +The 'Action' parameter is defaulted to the script block following the 'Clean' task. + +The equivalent 'Test' task is shown below: + +task Test -depends Compile, Clean -Action { + $testMessage +} + +The output for the above sample build script is shown below: +Executing task, Clean... +Clean +Executing task, Compile... +Compile +Executing task, Test... +This is a test + +Build Succeeded! + +---------------------------------------------------------------------- +Build Time Report +---------------------------------------------------------------------- +Name Duration +---- -------- +Clean 00:00:00.0065614 +Compile 00:00:00.0133268 +Test 00:00:00.0225964 +Total: 00:00:00.0782496 + +.LINK +Invoke-psake +Properties +Include +FormatTaskName +TaskSetup +TaskTearDown +Assert +#> +[CmdletBinding( + SupportsShouldProcess=$False, + SupportsTransactions=$False, + ConfirmImpact="None", + DefaultParameterSetName="")] + param( + [Parameter(Position=0,Mandatory=1)] + [string]$name = $null, + [Parameter(Position=1,Mandatory=0)] + [scriptblock]$action = $null, + [Parameter(Position=2,Mandatory=0)] + [scriptblock]$preaction = $null, + [Parameter(Position=3,Mandatory=0)] + [scriptblock]$postaction = $null, + [Parameter(Position=4,Mandatory=0)] + [scriptblock]$precondition = $null, + [Parameter(Position=5,Mandatory=0)] + [scriptblock]$postcondition = $null, + [Parameter(Position=6,Mandatory=0)] + [switch]$continueOnError = $false, + [Parameter(Position=7,Mandatory=0)] + [string[]]$depends = @(), + [Parameter(Position=8,Mandatory=0)] + [string]$description = $null + ) + + if ($name.ToLower() -eq 'default') + { + Assert ($action -eq $null) "Error: 'default' task cannot specify an action" + } + + $newTask = @{ + Name = $name + DependsOn = $depends + PreAction = $preaction + Action = $action + PostAction = $postaction + Precondition = $precondition + Postcondition = $postcondition + ContinueOnError = $continueOnError + Description = $description + Duration = 0 + } + + $taskKey = $name.ToLower() + + Assert (!$script:context.Peek().tasks.ContainsKey($taskKey)) "Error: Task, $name, has already been defined." + + $script:context.Peek().tasks.$taskKey = $newTask +} + +function Properties +{ +<# +.SYNOPSIS +Define a scriptblock that contains assignments to variables that will be available to all tasks in the build script + +.DESCRIPTION +A build script may declare a "Properies" function which allows you to define +variables that will be available to all the "Task" functions in the build script. + +.PARAMETER properties +The script block containing all the variable assignment statements +Required + +.EXAMPLE +A sample build script is shown below: + +Properties { + $build_dir = "c:\build" + $connection_string = "datasource=localhost;initial catalog=northwind;integrated security=sspi" +} + +Task default -depends Test + +Task Test -depends Compile, Clean { +} + +Task Compile -depends Clean { +} + +Task Clean { +} + +.LINK +Invoke-psake +Task +Include +FormatTaskName +TaskSetup +TaskTearDown +Assert + +.NOTES +You can have more than 1 "Properties" function defined in the script +#> +[CmdletBinding( + SupportsShouldProcess=$False, + SupportsTransactions=$False, + ConfirmImpact="None", + DefaultParameterSetName="")] + param( + [Parameter(Position=0,Mandatory=1)] + [scriptblock]$properties + ) + $script:context.Peek().properties += $properties +} + +function Include +{ +<# +.SYNOPSIS +Include the functions or code of another powershell script file into the current build script's scope + +.DESCRIPTION +A build script may declare an "includes" function which allows you to define +a file containing powershell code to be included and added to the scope of +the currently running build script. + +.PARAMETER fileNamePathToInclude +A string containing the path and name of the powershell file to include +Required + +.EXAMPLE +A sample build script is shown below: + +Include ".\build_utils.ps1" + +Task default -depends Test + +Task Test -depends Compile, Clean { +} + +Task Compile -depends Clean { +} + +Task Clean { +} + + +.LINK +Invoke-psake +Task +Properties +FormatTaskName +TaskSetup +TaskTearDown +Assert + +.NOTES +You can have more than 1 "Include" function defined in the script +#> +[CmdletBinding( + SupportsShouldProcess=$False, + SupportsTransactions=$False, + ConfirmImpact="None", + DefaultParameterSetName="")] + param( + [Parameter(Position=0,Mandatory=1)] + [string]$fileNamePathToInclude + ) + Assert (test-path $fileNamePathToInclude) "Error: Unable to include $fileNamePathToInclude. File not found." + $script:context.Peek().includes.Enqueue((Resolve-Path $fileNamePathToInclude)); +} + +function FormatTaskName +{ +<# +.SYNOPSIS +Allows you to define a format mask that will be used when psake displays +the task name + +.DESCRIPTION +Allows you to define a format mask that will be used when psake displays +the task name. The default is "Executing task, {0}..." + +.PARAMETER format +A string containing the format mask to use, it should contain a placeholder ({0}) +that will be used to substitute the task name. +Required + +.EXAMPLE +A sample build script is shown below: + +FormatTaskName "[Task: {0}]" + +Task default -depends Test + +Task Test -depends Compile, Clean { +} + +Task Compile -depends Clean { +} + +Task Clean { +} + +You should get the following output: +------------------------------------ + +[Task: Clean] +[Task: Compile] +[Task: Test] + +Build Succeeded + +---------------------------------------------------------------------- +Build Time Report +---------------------------------------------------------------------- +Name Duration +---- -------- +Clean 00:00:00.0043477 +Compile 00:00:00.0102130 +Test 00:00:00.0182858 +Total: 00:00:00.0698071 + +.LINK +Invoke-psake +Include +Task +Properties +TaskSetup +TaskTearDown +Assert +#> +[CmdletBinding( + SupportsShouldProcess=$False, + SupportsTransactions=$False, + ConfirmImpact="None", + DefaultParameterSetName="")] + param( + [Parameter(Position=0,Mandatory=1)] + [string]$format + ) + $script:context.Peek().formatTaskNameString = $format +} + +function TaskSetup +{ +<# +.SYNOPSIS +Adds a scriptblock that will be executed before each task + +.DESCRIPTION +This function will accept a scriptblock that will be executed before each +task in the build script. + +.PARAMETER include +A scriptblock to execute +Required + +.EXAMPLE +A sample build script is shown below: + +Task default -depends Test + +Task Test -depends Compile, Clean { +} + +Task Compile -depends Clean { +} + +Task Clean { +} + +TaskSetup { + "Running 'TaskSetup' for task $script:context.Peek().currentTaskName" +} + +You should get the following output: +------------------------------------ + +Running 'TaskSetup' for task Clean +Executing task, Clean... +Running 'TaskSetup' for task Compile +Executing task, Compile... +Running 'TaskSetup' for task Test +Executing task, Test... + +Build Succeeded + +---------------------------------------------------------------------- +Build Time Report +---------------------------------------------------------------------- +Name Duration +---- -------- +Clean 00:00:00.0054018 +Compile 00:00:00.0123085 +Test 00:00:00.0236915 +Total: 00:00:00.0739437 + +.LINK +Invoke-psake +Include +Task +Properties +FormatTaskName +TaskTearDown +Assert +#> +[CmdletBinding( + SupportsShouldProcess=$False, + SupportsTransactions=$False, + ConfirmImpact="None", + DefaultParameterSetName="")] + param( + [Parameter(Position=0,Mandatory=1)] + [scriptblock]$setup + ) + $script:context.Peek().taskSetupScriptBlock = $setup +} + +function TaskTearDown +{ +<# +.SYNOPSIS +Adds a scriptblock that will be executed after each task + +.DESCRIPTION +This function will accept a scriptblock that will be executed after each +task in the build script. + +.PARAMETER include +A scriptblock to execute +Required + +.EXAMPLE +A sample build script is shown below: + +Task default -depends Test + +Task Test -depends Compile, Clean { +} + +Task Compile -depends Clean { +} + +Task Clean { +} + +TaskTearDown { + "Running 'TaskTearDown' for task $script:context.Peek().currentTaskName" +} + +You should get the following output: +------------------------------------ + +Executing task, Clean... +Running 'TaskTearDown' for task Clean +Executing task, Compile... +Running 'TaskTearDown' for task Compile +Executing task, Test... +Running 'TaskTearDown' for task Test + +Build Succeeded + +---------------------------------------------------------------------- +Build Time Report +---------------------------------------------------------------------- +Name Duration +---- -------- +Clean 00:00:00.0064555 +Compile 00:00:00.0218902 +Test 00:00:00.0309151 +Total: 00:00:00.0858301 + +.LINK +Invoke-psake +Include +Task +Properties +FormatTaskName +TaskSetup +Assert +#> +[CmdletBinding( + SupportsShouldProcess=$False, + SupportsTransactions=$False, + ConfirmImpact="None", + DefaultParameterSetName="")] + param( + [Parameter(Position=0,Mandatory=1)] + [scriptblock]$teardown) + $script:context.Peek().taskTearDownScriptBlock = $teardown +} + +function Invoke-psake +{ +<# +.SYNOPSIS +Runs a psake build script. + +.DESCRIPTION +This function runs a psake build script + +.PARAMETER BuildFile +The psake build script to execute (default: default.ps1). + +.PARAMETER TaskList +A comma-separated list of task names to execute + +.PARAMETER Framework +The version of the .NET framework you want to build. You can append x86 or x64 to force a specific framework. If not specified, x86 or x64 will be detected based on the bitness of the PowerShell process. +Possible values: '1.0', '1.1', '2.0', '2.0x86', '2.0x64', '3.0', '3.0x86', '3.0x64', '3.5', '3.5x86', '3.5x64', '4.0', '4.0x86', '4.0x64' +Default = '3.5' + +.PARAMETER Docs +Prints a list of tasks and their descriptions + +.PARAMETER Parameters +A hashtable containing parameters to be passed into the current build script. These parameters will be processed before the 'Properties' function of the script is processed. This means you can access parameters from within the 'Properties' function! + +.PARAMETER Properties +A hashtable containing properties to be passed into the current build script. These properties will override matching properties that are found in the 'Properties' function of the script. + +.EXAMPLE +Invoke-psake + +Runs the 'default' task in the 'default.ps1' build script in the current directory + +.EXAMPLE +Invoke-psake '.\build.ps1' + +Runs the 'default' task in the '.build.ps1' build script + +.EXAMPLE +Invoke-psake '.\build.ps1' Tests,Package + +Runs the 'Tests' and 'Package' tasks in the '.build.ps1' build script + +.EXAMPLE +Invoke-psake Tests + +If you have your Tasks in the .\default.ps1. This example will run the 'Tests' tasks in the 'default.ps1' build script. + +.EXAMPLE +Invoke-psake 'Tests, Package' + +If you have your Tasks in the .\default.ps1. This example will run the 'Tests' and 'Package' tasks in the 'default.ps1' build script. +NOTE: the quotes around the list of tasks to execute. + +.EXAMPLE +Invoke-psake '.\build.ps1' -docs + +Prints a report of all the tasks and their descriptions and exits + +.EXAMPLE +Invoke-psake .\parameters.ps1 -parameters @{"p1"="v1";"p2"="v2"} + +Runs the build script called 'parameters.ps1' and passes in parameters 'p1' and 'p2' with values 'v1' and 'v2' + +.EXAMPLE +Invoke-psake .\properties.ps1 -properties @{"x"="1";"y"="2"} + +Runs the build script called 'properties.ps1' and passes in parameters 'x' and 'y' with values '1' and '2' + +.OUTPUTS + If there is an exception and '$psake.use_exit_on_error' -eq $true + then runs exit(1) to set the DOS lastexitcode variable + otherwise set the '$psake.build_success variable' to $true or $false depending + on whether an exception was thrown + +.NOTES +When the psake module is loaded a variabled called $psake is created it is a hashtable +containing some variables that can be used to configure psake: + +$psake.use_exit_on_error = $false # determines if psake uses the "exit()" function when an exception occurs +$psake.log_error = $false # determines if the exception details are written to a file +$psake.build_success = $false # indicates that the current build was successful +$psake.version = "4.00" # contains the current version of psake +$psake.build_script_file = $null # contains a System.IO.FileInfo for the current build file +$psake.framework_version = "" # contains the framework version # for the current build + +$psake.use_exit_on_error and $psake.log_error are boolean variables that can be set before you call Invoke-Psake. + +You should see the following when you display the contents of the $psake variable right after importing psake + +PS projects:\psake> Import-Module .\psake.psm1 +PS projects:\psake> $psake + +Name Value +---- ----- +version 4.00 +build_script_file +use_exit_on_error False +build_success False +log_error False +framework_version + +After a build is executed the following $psake values are updated (build_script_file, build_success, and framework_version) + +PS projects:\psake> Invoke-psake .\examples\default.ps1 +Executing task: Clean +Executed Clean! +Executing task: Compile +Executed Compile! +Executing task: Test +Executed Test! + +Build Succeeded! + +---------------------------------------------------------------------- +Build Time Report +---------------------------------------------------------------------- +Name Duration +---- -------- +Clean 00:00:00.0798486 +Compile 00:00:00.0869948 +Test 00:00:00.0958225 +Total: 00:00:00.2712414 + +PS projects:\psake> $psake + +Name Value +---- ----- +version 4.00 +build_script_file C:\Users\Jorge\Documents\Projects\psake\examples\default.ps1 +use_exit_on_error False +build_success True +log_error False +framework_version 3.5 + +.LINK +Task +Include +Properties +FormatTaskName +TaskSetup +TaskTearDown +Assert +#> +[CmdletBinding( + SupportsShouldProcess=$False, + SupportsTransactions=$False, + ConfirmImpact="None", + DefaultParameterSetName="")] + + param( + [Parameter(Position=0,Mandatory=0)] + [string]$buildFile = $script:psake.default_build_file_name, + [Parameter(Position=1,Mandatory=0)] + [string[]]$taskList = @(), + [Parameter(Position=2,Mandatory=0)] + [string]$framework = '3.5', + [Parameter(Position=3,Mandatory=0)] + [switch]$docs = $false, + [Parameter(Position=4,Mandatory=0)] + [System.Collections.Hashtable]$parameters = @{}, + [Parameter(Position=5, Mandatory=0)] + [System.Collections.Hashtable]$properties = @{} + ) + + Begin + { + $script:psake.build_success = $false + $script:psake.framework_version = $framework + + if ($script:context -eq $null) + { + $script:context = New-Object System.Collections.Stack + } + + $script:context.push(@{ + "formatTaskNameString" = "Executing task: {0}"; + "taskSetupScriptBlock" = $null; + "taskTearDownScriptBlock" = $null; + "executedTasks" = New-Object System.Collections.Stack; + "callStack" = New-Object System.Collections.Stack; + "originalEnvPath" = $env:path; + "originalDirectory" = Get-Location; + "originalErrorActionPreference" = $global:ErrorActionPreference; + "tasks" = @{}; + "properties" = @(); + "includes" = New-Object System.Collections.Queue; + }) + } + + Process + { + try + { + $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() + + <# + If the default.ps1 file exists and the given "buildfile" isn't found assume that the given + $buildFile is actually the target Tasks to execute in the default.ps1 script. + #> + if((Test-Path $script:psake.default_build_file_name ) -and !(test-path $buildFile)) { + $list = New-Object System.Collections.ArrayList + foreach($t in $buildFile.Split(',')) { + $t1 = $t.Trim() + if($t1 -ne $null -or $t1 -ne "") { + $list.Add($t1) + } + } + $taskList = $list.ToArray() + $buildFile = $script:psake.default_build_file_name + } + + # Execute the build file to set up the tasks and defaults + Assert (test-path $buildFile) "Error: Could not find the build file, $buildFile." + + $script:psake.build_script_file = dir $buildFile + set-location $script:psake.build_script_file.Directory + . $script:psake.build_script_file.FullName + + if ($docs) + { + Write-Documentation + Cleanup-Environment + return + } + + Configure-BuildEnvironment + + # N.B. The initial dot (.) indicates that variables initialized/modified + # in the propertyBlock are available in the parent scope. + while ($script:context.Peek().includes.Count -gt 0) + { + $includeBlock = $script:context.Peek().includes.Dequeue() + . $includeBlock + } + + foreach($key in $parameters.keys) + { + if (test-path "variable:\$key") + { + set-item -path "variable:\$key" -value $parameters.$key | out-null + } + else + { + new-item -path "variable:\$key" -value $parameters.$key | out-null + } + } + + foreach($propertyBlock in $script:context.Peek().properties) + { + . $propertyBlock + } + + foreach($key in $properties.keys) + { + if (test-path "variable:\$key") + { + set-item -path "variable:\$key" -value $properties.$key | out-null + } + } + + # Execute the list of tasks or the default task + if($taskList.Length -ne 0) + { + foreach($task in $taskList) + { + ExecuteTask $task + } + } + elseif ($script:context.Peek().tasks.default -ne $null) + { + ExecuteTask default + } + else + { + throw 'Error: default task required' + } + + $stopwatch.Stop() + + "`nBuild Succeeded!`n" + + Write-TaskTimeSummary + + $script:psake.build_success = $true + } + catch + { + #Append detailed exception and script variables to error log file + if ($script:psake.log_error) + { + $errorLogFile = "psake-error-log-{0}.log" -f ([DateTime]::Now.ToString("yyyyMMdd")) + "-" * 70 >> $errorLogFile + "{0}: An Error Occurred. See Error Details Below: " -f [DateTime]::Now >>$errorLogFile + "-" * 70 >> $errorLogFile + Resolve-Error $_ >> $errorLogFile + "-" * 70 >> $errorLogFile + "Script Variables" >> $errorLogFile + "-" * 70 >> $errorLogFile + Get-Variable -scope script >> $errorLogFile + } + + $buildFileName = Split-Path $buildFile -leaf + if (test-path $buildFile) { $buildFileName = $script:psake.build_script_file.Name } + Write-Host -foregroundcolor Red ($buildFileName + ":" + $_) + + if ($script:psake.use_exit_on_error) + { + exit(1) + } + else + { + $script:psake.build_success = $false + } + } + } #Process + + End + { + # Clear out any global variables + Cleanup-Environment + [void]$script:context.Pop() + } +} + +Export-ModuleMember -Function "Invoke-psake","Task","Properties","Include","FormatTaskName","TaskSetup","TaskTearDown","Assert","Exec" \ No newline at end of file diff --git a/trunk/Libraries/Json40r2/readme.txt b/trunk/Libraries/Json40r2/readme.txt new file mode 100644 index 0000000..ca85148 --- /dev/null +++ b/trunk/Libraries/Json40r2/readme.txt @@ -0,0 +1,59 @@ +Json.NET + +http://james.newtonking.com/projects/json-net.aspx +http://www.codeplex.com/json/ + + +Description: + +Json.NET makes working with JSON formatted data in .NET simple. Quickly read and write JSON using LINQ to JSON or serialize your .NET objects with a single method call using the JsonSerializer. + +-Flexible JSON serializer to convert .NET objects to JSON and back again +-LINQ to JSON for reading and writing JSON +-Writes indented, easy to read JSON +-Convert JSON to and from XML +-Supports Silverlight and Windows Phone + + + +Versions: + +Json.NET comes in different versions for the various .NET frameworks. + +-DotNet: + .NET latest (4.0) + +-DotNet35: + .NET 3.5 SP1, Mono + +-DotNet20: + .NET 2.0 + +-Silverlight: + Silverlight 4.0 + +-WindowsPhone: + Windows Phone 7 + +Microsoft stopped support for the Compact Framework in Visual Studio 2010. +For a Compact Framework 3.5 build download Json.NET 3.5. + +For a Silverlight 3.0 build down download Json.NET 3.5. + + +Instructions: + + 1. Extract Newtonsoft.Json.dll and Newtonsoft.Json.xml from the archive's /bin directory into your own applications. + 2. Add a reference to Newtonsoft.Json.dll within Visual Studio.NET to your project. + + + +License: + +Copyright (c) 2007 James Newton-King + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AbstractStreamBase.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AbstractStreamBase.cs new file mode 100644 index 0000000..88d8cd6 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AbstractStreamBase.cs @@ -0,0 +1,85 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: AbstractStreamBase.cs +// +//-------------------------------------------------------------------------- + +namespace System.IO +{ + /// Base stream class that implements all of Stream's abstract members. + public abstract class AbstractStreamBase : Stream + { + /// Determines whether data can be read from the stream. + public override bool CanRead { get { return false; } } + /// Determines whether data can be written to the stream. + public override bool CanWrite { get { return false; } } + /// Determines whether the stream can be seeked. + public override bool CanSeek { get { return false; } } + /// Flushes the contents of the stream to the underlying storage. + public override void Flush() { } + + /// Gets the length of the stream. + public override long Length { get { throw new NotSupportedException(); } } + + /// Gets or sets the current position of the stream. + public override long Position + { + get { throw new NotSupportedException(); } + set { throw new NotSupportedException(); } + } + + /// + /// Reads a sequence of bytes from the current + /// stream and advances the position within the stream by the number of bytes read. + /// + /// + /// An array of bytes. When Read returns, the buffer contains the specified + /// byte array with the values between offset and (offset + count - 1) replaced + /// by the bytes read from the current source. + /// + /// + /// The zero-based byte offset in buffer at which to begin storing the data read + /// from the current stream. + /// + /// The maximum number of bytes to be read from the current stream. + /// + /// The total number of bytes read into the buffer. This can be less than the + /// number of bytes requested if that many bytes are not currently available, + /// or zero (0) if the end of the stream has been reached. + /// + public override int Read(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + + /// Sets the position within the current stream. + /// A byte offset relative to the origin parameter. + /// + /// A value of type System.IO.SeekOrigin indicating the reference point used + /// to obtain the new position. + /// + /// The new position within the current stream. + public override long Seek(long offset, SeekOrigin origin) + { + throw new NotSupportedException(); + } + + /// Sets the length of the current stream. + /// The desired length of the current stream in bytes. + public override void SetLength(long value) + { + throw new NotSupportedException(); + } + + /// Writes a sequence of bytes to the stream. + /// An array of bytes. Write copies count bytes from buffer to the stream. + /// The zero-based byte offset in buffer at which to begin copying bytes to the stream. + /// The number of bytes to be written to the current stream. + public override void Write(byte[] buffer, int offset, int count) + { + throw new NotSupportedException(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ActionCountdownEvent.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ActionCountdownEvent.cs new file mode 100644 index 0000000..ef05e83 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ActionCountdownEvent.cs @@ -0,0 +1,75 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ActionCountdownEvent.cs +// +//-------------------------------------------------------------------------- + +namespace System.Threading +{ + /// Runs an action when the CountdownEvent reaches zero. + public class ActionCountdownEvent : IDisposable + { + private readonly CountdownEvent _event; + private readonly Action _action; + private readonly ExecutionContext _context; + + /// Initializes the ActionCountdownEvent. + /// The number of signals required to set the CountdownEvent. + /// The delegate to be invoked when the count reaches zero. + public ActionCountdownEvent(int initialCount, Action action) + { + // Validate arguments + if (initialCount < 0) throw new ArgumentOutOfRangeException("initialCount"); + if (action == null) throw new ArgumentNullException("action"); + + // Store the action and create the event from the initial count. If the initial count forces the + // event to be set, run the action immediately. Otherwise, capture the current execution context + // so we can run the action in the right context later on. + _action = action; + _event = new CountdownEvent(initialCount); + if (initialCount == 0) action(); + else _context = ExecutionContext.Capture(); + } + + /// Increments the current count by one. + public void AddCount() + { + // Just delegate to the underlying event + _event.AddCount(); + } + + /// Registers a signal with the event, decrementing its count. + public void Signal() + { + // If signaling the event causes it to become set + if (_event.Signal()) + { + // Execute the action. If we were able to capture a context + // at instantiation time, use that context to execute the action. + // Otherwise, just run the action. + if (_context != null) + { + ExecutionContext.Run(_context, _ => _action(), null); + } + else _action(); + } + } + + /// Releases all resources used by the current instance. + public void Dispose() + { + Dispose(true); + } + + /// Releases all resources used by the current instance. + /// + /// true if called because the object is being disposed; otherwise, false. + /// + protected void Dispose(bool disposing) + { + if (disposing) _event.Dispose(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncBarrier.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncBarrier.cs new file mode 100644 index 0000000..8b05a1a --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncBarrier.cs @@ -0,0 +1,57 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: AsyncBarrier.cs +// +//-------------------------------------------------------------------------- + +using System.Diagnostics; +using System.Threading.Tasks; + +namespace System.Threading.Async +{ + /// Provides an asynchronous barrier. + [DebuggerDisplay("ParticipantCount={ParticipantCount}, RemainingCount={RemainingCount}")] + public sealed class AsyncBarrier + { + /// The number of participants in the barrier. + private readonly int _participantCount; + /// The task used to signal completion of the current round. + private TaskCompletionSource _currentSignalTask; + /// The number of participants remaining to arrive for this round. + private int _remainingParticipants; + + /// Initializes the BarrierAsync with the specified number of participants. + /// The number of participants in the barrier. + public AsyncBarrier(int participantCount) + { + if (participantCount <= 0) throw new ArgumentOutOfRangeException("participantCount"); + _participantCount = participantCount; + + _remainingParticipants = participantCount; + _currentSignalTask = new TaskCompletionSource(); + } + + /// Gets the participant count. + public int ParticipantCount { get { return _participantCount; } } + /// Gets the number of participants still not yet arrived in this round. + public int RemainingCount { get { return _remainingParticipants; } } + + /// Signals that a participant has arrived. + /// A Task that will be signaled when the current round completes. + public Task SignalAndWait() + { + var curCts = _currentSignalTask; + #pragma warning disable 420 + if (Interlocked.Decrement(ref _remainingParticipants) == 0) + #pragma warning restore 420 + { + _remainingParticipants = _participantCount; + _currentSignalTask = new TaskCompletionSource(); + curCts.SetResult(null); + } + return curCts.Task; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncCache.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncCache.cs new file mode 100644 index 0000000..b506154 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncCache.cs @@ -0,0 +1,162 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: AsyncCache.cs +// +//-------------------------------------------------------------------------- + +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Threading.Tasks; + +namespace System.Threading +{ + /// Debugger type proxy for AsyncCache. + /// Specifies the type of the cache's keys. + /// Specifies the type of the cache's values. + internal class AsyncCache_DebugView + { + private readonly AsyncCache _asyncCache; + + internal AsyncCache_DebugView(AsyncCache asyncCache) + { + _asyncCache = asyncCache; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + internal KeyValuePair>[] Values + { + get { return _asyncCache.ToArray(); } + } + } + + /// Caches asynchronously retrieved data. + /// Specifies the type of the cache's keys. + /// Specifies the type of the cache's values. + [DebuggerTypeProxy(typeof(AsyncCache_DebugView<,>))] + [DebuggerDisplay("Count={Count}")] + public class AsyncCache : ICollection>> + { + /// The factory to use to create tasks. + private readonly Func> _valueFactory; + /// The dictionary to store all of the tasks. + private readonly ConcurrentDictionary>> _map; + + /// Initializes the cache. + /// A factory for producing the cache's values. + public AsyncCache(Func> valueFactory) + { + if (valueFactory == null) throw new ArgumentNullException("loader"); + _valueFactory = valueFactory; + _map = new ConcurrentDictionary>>(); + } + + /// Gets a Task to retrieve the value for the specified key. + /// The key whose value should be retrieved. + /// A Task for the value of the specified key. + public Task GetValue(TKey key) + { + if (key == null) throw new ArgumentNullException("key"); + var value = new Lazy>(() => _valueFactory(key)); + return _map.GetOrAdd(key, value).Value; + } + + /// Sets the value for the specified key. + /// The key whose value should be set. + /// The value to which the key should be set. + public void SetValue(TKey key, TValue value) + { + SetValue(key, Task.Factory.FromResult(value)); + } + + /// Sets the value for the specified key. + /// The key whose value should be set. + /// The value to which the key should be set. + public void SetValue(TKey key, Task value) + { + if (key == null) throw new ArgumentNullException("key"); + _map[key] = LazyExtensions.Create(value); + } + + /// Gets a Task to retrieve the value for the specified key. + /// The key whose value should be retrieved. + /// A Task for the value of the specified key. + public Task this[TKey key] + { + get { return GetValue(key); } + set { SetValue(key, value); } + } + + /// Empties the cache. + public void Clear() { _map.Clear(); } + + /// Gets the number of items in the cache. + public int Count { get { return _map.Count; } } + + /// Gets an enumerator for the contents of the cache. + /// An enumerator for the contents of the cache. + public IEnumerator>> GetEnumerator() + { + return _map.Select(p => new KeyValuePair>(p.Key, p.Value.Value)).GetEnumerator(); + } + + /// Gets an enumerator for the contents of the cache. + /// An enumerator for the contents of the cache. + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + + /// Adds or overwrites the specified entry in the cache. + /// The item to be added. + void ICollection>>.Add(KeyValuePair> item) + { + this[item.Key] = item.Value; + } + + /// Determines whether the cache contains the specified key. + /// The item contained the key to be searched for. + /// True if the cache contains the key; otherwise, false. + bool ICollection>>.Contains(KeyValuePair> item) + { + return _map.ContainsKey(item.Key); + } + + /// + /// Copies the elements of the System.Collections.Generic.ICollection to an + /// System.Array, starting at a particular System.Array index. + /// + /// + /// The one-dimensional System.Array that is the destination of the elements + /// copied from System.Collections.Generic.ICollection. The System.Array must + /// have zero-based indexing. + /// + /// The zero-based index in array at which copying begins. + void ICollection>>.CopyTo(KeyValuePair>[] array, int arrayIndex) + { + ((ICollection>>)_map).CopyTo(array, arrayIndex); + } + + /// Gets whether the cache is read-only. + bool ICollection>>.IsReadOnly { get { return false; } } + + /// Removes the specified key from the cache. + /// The item containing the key to be removed. + /// True if the item could be removed; otherwise, false. + bool ICollection>>.Remove(KeyValuePair> item) + { + Lazy> value; + return _map.TryRemove(item.Key, out value); + } + } + + /// An asynchronous cache for downloaded HTML. + public sealed class HtmlAsyncCache : AsyncCache + { + /// Initializes the HtmlCache. + public HtmlAsyncCache() : + base(uri => new WebClient().DownloadStringTask(uri)) { } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncCall.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncCall.cs new file mode 100644 index 0000000..58f3196 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncCall.cs @@ -0,0 +1,256 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: AsyncCall.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Threading.Tasks +{ + /// Asynchronously invokes a handler for every posted item. + /// Specifies the type of data processed by the instance. + public sealed class AsyncCall : MarshalByRefObject + { + /// + /// A queue that stores the posted data. Also serves as the syncObj for protected instance state. + /// A ConcurrentQueue is used to enable lock-free dequeues while running with a single consumer task. + /// + private readonly ConcurrentQueue _queue; + /// The delegate to invoke for every element. + private readonly Delegate _handler; + /// The maximum number of items that should be processed by an individual task. + private readonly int _maxItemsPerTask; + /// The TaskFactory to use to launch new tasks. + private readonly TaskFactory _tf; + /// The options to use for parallel processing of data. + private readonly ParallelOptions _parallelOptions; + /// Whether a processing task has been scheduled. + private int _processingCount; + + /// Initializes the AsyncCall with an action to execute for each element. + /// The action to run for every posted item. + /// The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. + /// The scheduler to use. If null, the default scheduler is used. + /// The maximum number of items to be processed per task. If not specified, Int32.MaxValue is used. + public AsyncCall(Action actionHandler, int maxDegreeOfParallelism = 1, int maxItemsPerTask = Int32.MaxValue, TaskScheduler scheduler = null) : + this(maxDegreeOfParallelism, maxItemsPerTask, scheduler) + { + if (actionHandler == null) throw new ArgumentNullException("handler"); + _handler = actionHandler; + } + + /// + /// Initializes the AsyncCall with a function to execute for each element. The function returns an Task + /// that represents the asynchronous completion of that element's processing. + /// + /// The function to run for every posted item. + /// The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. + /// The scheduler to use. If null, the default scheduler is used. + public AsyncCall(Func functionHandler, int maxDegreeOfParallelism = 1, TaskScheduler scheduler = null) : + this(maxDegreeOfParallelism, 1, scheduler) + { + if (functionHandler == null) throw new ArgumentNullException("handler"); + _handler = functionHandler; + } + + /// General initialization of the AsyncCall. Another constructor must initialize the delegate. + /// The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. + /// The maximum number of items to be processed per task. If not specified, Int32.MaxValue is used. + /// The scheduler to use. If null, the default scheduler is used. + private AsyncCall(int maxDegreeOfParallelism = 1, int maxItemsPerTask = Int32.MaxValue, TaskScheduler scheduler = null) + { + // Validate arguments + if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism"); + if (maxItemsPerTask < 1) throw new ArgumentOutOfRangeException("maxItemsPerTask"); + if (scheduler == null) scheduler = TaskScheduler.Default; + + // Configure the instance + _queue = new ConcurrentQueue(); + _maxItemsPerTask = maxItemsPerTask; + _tf = new TaskFactory(scheduler); + if (maxDegreeOfParallelism != 1) + { + _parallelOptions = new ParallelOptions { MaxDegreeOfParallelism = maxDegreeOfParallelism, TaskScheduler = scheduler }; + } + } + + /// Post an item for processing. + /// The item to be processed. + public void Post(T item) + { + lock (_queue) + { + // Add the item to the internal queue + _queue.Enqueue(item); + + // Check to see whether the right number of tasks have been scheduled. + // If they haven't, schedule one for this new piece of data. + if (_handler is Action) + { + if (_processingCount == 0) + { + _processingCount = 1; + _tf.StartNew(ProcessItemsActionTaskBody); + } + } + else if (_handler is Func) + { + if (_processingCount == 0 || // is anyone at all currently processing? + (_parallelOptions != null && _processingCount < _parallelOptions.MaxDegreeOfParallelism && // are enough workers currently processing? + !_queue.IsEmpty)) // and, as an optimization, double check to make sure the item hasn't already been picked up by another worker + { + _processingCount++; + _tf.StartNew(ProcessItemFunctionTaskBody, null); + } + } + else Debug.Fail("_handler is an invalid delegate type"); + } + } + + /// Gets an enumerable that yields the items to be processed at this time. + /// An enumerable of items. + private IEnumerable GetItemsToProcess() + { + // Yield the next elements to be processed until either there are no more elements + // or we've reached the maximum number of elements that an individual task should process. + int processedCount = 0; + T nextItem; + while (processedCount < _maxItemsPerTask && _queue.TryDequeue(out nextItem)) + { + yield return nextItem; + processedCount++; + } + } + + /// Used as the body of an action task to process items in the queue. + private void ProcessItemsActionTaskBody() + { + try + { + // Get the handler + Action handler = (Action)_handler; + + // Process up to _maxItemsPerTask items, either serially or in parallel + // based on the provided maxDegreeOfParallelism (which determines + // whether a ParallelOptions is instantiated). + if (_parallelOptions == null) + foreach (var item in GetItemsToProcess()) handler(item); + else + Parallel.ForEach(GetItemsToProcess(), _parallelOptions, handler); + } + finally + { + lock (_queue) + { + // If there are still items in the queue, schedule another task to continue processing. + // Otherwise, note that we're no longer processing. + if (!_queue.IsEmpty) _tf.StartNew(ProcessItemsActionTaskBody, TaskCreationOptions.PreferFairness); + else _processingCount = 0; + } + } + } + + /// Used as the body of a function task to process items in the queue. + private void ProcessItemFunctionTaskBody(object ignored) + { + bool anotherTaskQueued = false; + try + { + // Get the handler + Func handler = (Func)_handler; + + // Get the next item from the queue to process + T nextItem; + if (_queue.TryDequeue(out nextItem)) + { + // Run the handler and get the follow-on task. + // If we got a follow-on task, run this process again when the task completes. + // If we didn't, just start another task to keep going now. + var task = handler(nextItem); + if (task != null) task.ContinueWith(ProcessItemFunctionTaskBody, _tf.Scheduler); + else _tf.StartNew(ProcessItemFunctionTaskBody, null); + + // We've queued a task to continue processing, which means that logically + // we're still maintaining the same level of parallelism. + anotherTaskQueued = true; + } + } + finally + { + // If we didn't queue up another task to continue processing (either + // because an exception occurred, or we failed to grab an item from the queue) + if (!anotherTaskQueued) + { + lock (_queue) + { + // Verify that there's still nothing in the queue, now under the same + // lock that the queuer needs to take in order to increment the processing count + // and launch a new processor. + if (!_queue.IsEmpty) _tf.StartNew(ProcessItemFunctionTaskBody, null); + else _processingCount--; + } + } + } + } + } + + /// Provides static factory methods for creating AsyncCall(Of T) instances. + public static class AsyncCall + { + /// Initializes the AsyncCall with an action to execute for each element. + /// The action to run for every posted item. + /// The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. + /// The scheduler to use. If null, the default scheduler is used. + /// The maximum number of items to be processed per task. If not specified, Int32.MaxValue is used. + public static AsyncCall Create(Action actionHandler, int maxDegreeOfParallelism = 1, int maxItemsPerTask = Int32.MaxValue, TaskScheduler scheduler = null) + { + return new AsyncCall(actionHandler, maxDegreeOfParallelism, maxItemsPerTask, scheduler); + } + + /// + /// Initializes the AsyncCall with a function to execute for each element. The function returns an Task + /// that represents the asynchronous completion of that element's processing. + /// + /// The function to run for every posted item. + /// The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. + /// The maximum number of items to be processed per task. If not specified, Int32.MaxValue is used. + /// The scheduler to use. If null, the default scheduler is used. + public static AsyncCall Create(Func functionHandler, int maxDegreeOfParallelism = 1, TaskScheduler scheduler = null) + { + return new AsyncCall(functionHandler, maxDegreeOfParallelism, scheduler); + } + + /// Initializes the AsyncCall in the specified AppDomain with an action to execute for each element. + /// The action to run for every posted item. + /// The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. + /// The maximum number of items to be processed per task. If not specified, Int32.MaxValue is used. + public static AsyncCall CreateInTargetAppDomain(AppDomain targetDomain, Action actionHandler, int maxDegreeOfParallelism = 1, int maxItemsPerTask = Int32.MaxValue) + { + return (AsyncCall)targetDomain.CreateInstanceAndUnwrap( + typeof(AsyncCall).Assembly.FullName, typeof(AsyncCall).FullName, + false, Reflection.BindingFlags.CreateInstance, null, + new object[] { actionHandler, maxDegreeOfParallelism, maxItemsPerTask, null }, + null, null); + } + + /// + /// Initializes the AsyncCall in the specified AppDomain with a function to execute for each element. + /// The function returns an Task that represents the asynchronous completion of that element's processing. + /// + /// The action to run for every posted item. + /// The maximum degree of parallelism to use. If not specified, 1 is used for serial execution. + public static AsyncCall CreateInTargetAppDomain(AppDomain targetDomain, Func functionHandler, int maxDegreeOfParallelism = 1) + { + return (AsyncCall)targetDomain.CreateInstanceAndUnwrap( + typeof(AsyncCall).Assembly.FullName, typeof(AsyncCall).FullName, + false, Reflection.BindingFlags.CreateInstance, null, + new object[] { functionHandler, maxDegreeOfParallelism, null }, + null, null); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncProducerConsumerCollection.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncProducerConsumerCollection.cs new file mode 100644 index 0000000..cb0148b --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncProducerConsumerCollection.cs @@ -0,0 +1,68 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: AsyncProducerConsumerCollection.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Threading.Tasks; + +namespace System.Threading.Async +{ + /// Provides an asynchronous producer/consumer collection. + [DebuggerDisplay("Count={CurrentCount}")] + public sealed class AsyncProducerConsumerCollection : IDisposable + { + /// Asynchronous semaphore used to keep track of asynchronous work. + private AsyncSemaphore _semaphore = new AsyncSemaphore(); + /// The data stored in the collection. + private IProducerConsumerCollection _collection; + + /// Initializes the asynchronous producer/consumer collection to store data in a first-in-first-out (FIFO) order. + public AsyncProducerConsumerCollection() : this(new ConcurrentQueue()) { } + + /// Initializes the asynchronous producer/consumer collection. + /// The underlying collection to use to store data. + public AsyncProducerConsumerCollection(IProducerConsumerCollection collection) + { + if (collection == null) throw new ArgumentNullException("collection"); + _collection = collection; + } + + /// Adds an element to the collection. + /// The item to be added. + public void Add(T item) + { + if (_collection.TryAdd(item)) _semaphore.Release(); + else throw new InvalidOperationException("Invalid collection"); + } + + /// Takes an element from the collection asynchronously. + /// A Task that represents the element removed from the collection. + public Task Take() + { + return _semaphore.Wait().ContinueWith(_ => + { + T result; + if (!_collection.TryTake(out result)) throw new InvalidOperationException("Invalid collection"); + return result; + }, TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion); + } + + /// Gets the number of elements in the collection. + public int Count { get { return _collection.Count; } } + + /// Disposes of the collection. + public void Dispose() + { + if (_semaphore != null) + { + _semaphore.Dispose(); + _semaphore = null; + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncReaderWriter.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncReaderWriter.cs new file mode 100644 index 0000000..f893591 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncReaderWriter.cs @@ -0,0 +1,226 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: AsyncReaderWriter.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Diagnostics; + +namespace System.Threading.Async +{ + /// Provides for asynchronous exclusive and concurrent execution support. + [DebuggerDisplay("WaitingConcurrent={WaitingConcurrent}, WaitingExclusive={WaitingExclusive}, CurrentReaders={CurrentConcurrent}, Exclusive={CurrentlyExclusive}")] + public sealed class AsyncReaderWriter + { + /// The lock that protects all shared state in this instance. + private readonly object _lock = new object(); + /// The queue of concurrent readers waiting to execute. + private readonly Queue _waitingConcurrent = new Queue(); + /// The queue of exclusive writers waiting to execute. + private readonly Queue _waitingExclusive = new Queue(); + /// The number of concurrent readers currently executing. + private int _currentConcurrent = 0; + /// The number of exclusive writers currently executing. + private bool _currentlyExclusive = false; + /// The non-generic factory to use for task creation. + private TaskFactory _factory; + + /// Initializes the ReaderWriterAsync. + public AsyncReaderWriter() { _factory = Task.Factory; } + + /// Initializes the ReaderWriterAsync with the specified TaskFactory for us in creating all tasks. + /// The TaskFactory to use to create all tasks. + public AsyncReaderWriter(TaskFactory factory) + { + if (factory == null) throw new ArgumentNullException("factory"); + _factory = factory; + } + + /// Gets the number of exclusive operations currently queued. + public int WaitingExclusive { get { lock (_lock) return _waitingExclusive.Count; } } + /// Gets the number of concurrent operations currently queued. + public int WaitingConcurrent { get { lock (_lock) return _waitingConcurrent.Count; } } + /// Gets the number of concurrent operations currently executing. + public int CurrentConcurrent { get { lock (_lock) return _currentConcurrent; } } + /// Gets whether an exclusive operation is currently executing. + public bool CurrentlyExclusive { get { lock (_lock) return _currentlyExclusive; } } + + /// Queues an exclusive writer action to the ReaderWriterAsync. + /// The action to be executed exclusively. + /// A Task that represents the execution of the provided action. + public Task QueueExclusiveWriter(Action action) + { + // Create the task. This Task will be started by the coordination primitive + // when it's safe to do so, e.g. when there are no other tasks associated + // with this async primitive executing. + var task = _factory.Create(state => + { + // Run the user-provided action + try { ((Action)state)(); } + // Ensure that we clean up when we're done + finally { FinishExclusiveWriter(); } + }, action); + + // Now that we've created the task, we need to do something with it, either queueing it or scheduling it immediately + lock (_lock) + { + // If there's already a task running, or if there are any other exclusive tasks that need to run, + // queue it. Otherwise, no one else is running or wants to run, so schedule it now. + if (_currentlyExclusive || _currentConcurrent > 0 || _waitingExclusive.Count > 0) _waitingExclusive.Enqueue(task); + else RunExclusive_RequiresLock(task); + } + + // Return the created task for the caller to track. + return task; + } + + /// Queues an exclusive writer function to the ReaderWriterAsync. + /// The function to be executed exclusively. + /// A Task that represents the execution of the provided function. + public Task QueueExclusiveWriter(Func function) + { + // Create the task. This Task will be started by the coordination primitive + // when it's safe to do so, e.g. when there are no other tasks associated + // with this async primitive executing. + var task = _factory.Create(state => + { + // Run the user-provided function + try { return ((Func)state)(); } + // Ensure that we clean up when we're done + finally { FinishExclusiveWriter(); } + }, function); + + // Now that we've created the task, we need to do something with it, either queueing it or scheduling it immediately + lock (_lock) + { + // If there's already a task running, or if there are any other exclusive tasks that need to run, + // queue it. Otherwise, no one else is running or wants to run, so schedule it now. + if (_currentlyExclusive || _currentConcurrent > 0 || _waitingExclusive.Count > 0) _waitingExclusive.Enqueue(task); + else RunExclusive_RequiresLock(task); + } + + // Return the created task for the caller to track. + return task; + } + + /// Queues a concurrent reader action to the ReaderWriterAsync. + /// The action to be executed concurrently. + /// A Task that represents the execution of the provided action. + public Task QueueConcurrentReader(Action action) + { + // Create the task. This Task will be started by the coordination primitive + // when it's safe to do so, e.g. when there are no exclusive tasks running + // or waiting to run. + Task task = _factory.Create(state => + { + // Run the user-provided action + try { ((Action)state)(); } + // Ensure that we clean up when we're done + finally { FinishConcurrentReader(); } + }, action); + + // Now that we've created the task, we need to do something with it, either queueing it or scheduling it immediately + lock (_lock) + { + // If there are any exclusive tasks running or waiting, queue the concurrent task + if (_currentlyExclusive || _waitingExclusive.Count > 0) _waitingConcurrent.Enqueue(task); + // Otherwise schedule it immediately + else RunConcurrent_RequiresLock(task); + } + + // Return the task to the caller. + return task; + } + + /// Queues a concurrent reader function to the ReaderWriterAsync. + /// The function to be executed concurrently. + /// A Task that represents the execution of the provided function. + public Task QueueConcurrentReader(Func function) + { + // Create the task. This Task will be started by the coordination primitive + // when it's safe to do so, e.g. when there are no exclusive tasks running + // or waiting to run. + var task = _factory.Create(state => + { + // Run the user-provided function + try { return ((Func)state)(); } + // Ensure that we clean up when we're done + finally { FinishConcurrentReader(); } + }, function); + + // Now that we've created the task, we need to do something with it, either queueing it or scheduling it immediately + lock (_lock) + { + // If there are any exclusive tasks running or waiting, queue the concurrent task + if (_currentlyExclusive || _waitingExclusive.Count > 0) _waitingConcurrent.Enqueue(task); + // Otherwise schedule it immediately + else RunConcurrent_RequiresLock(task); + } + + // Return the task to the caller. + return task; + } + + /// Starts the specified exclusive task. + /// The exclusive task to be started. + /// This must only be executed while holding the instance's lock. + private void RunExclusive_RequiresLock(Task exclusive) + { + _currentlyExclusive = true; + exclusive.Start(_factory.GetTargetScheduler()); + } + + /// Starts the specified concurrent task. + /// The exclusive task to be started. + /// This must only be executed while holding the instance's lock. + private void RunConcurrent_RequiresLock(Task concurrent) + { + _currentConcurrent++; + concurrent.Start(_factory.GetTargetScheduler()); + } + + /// Starts all queued concurrent tasks. + /// This must only be executed while holding the instance's lock. + private void RunConcurrent_RequiresLock() + { + while (_waitingConcurrent.Count > 0) RunConcurrent_RequiresLock(_waitingConcurrent.Dequeue()); + } + + /// Completes the processing of a concurrent reader. + private void FinishConcurrentReader() + { + lock (_lock) + { + // Update the tracking count of the number of concurrently executing tasks + _currentConcurrent--; + + // If we've now hit zero tasks running concurrently and there are any waiting writers, run one of them + if (_currentConcurrent == 0 && _waitingExclusive.Count > 0) RunExclusive_RequiresLock(_waitingExclusive.Dequeue()); + + // Otherwise, if there are no waiting writers but there are waiting readers for some reason (they should + // have started when they were added by the user), run all concurrent tasks waiting. + else if (_waitingExclusive.Count == 0 && _waitingConcurrent.Count > 0) RunConcurrent_RequiresLock(); + } + } + + /// Completes the processing of an exclusive writer. + private void FinishExclusiveWriter() + { + lock (_lock) + { + // We're no longer executing exclusively, though this might get reversed shortly + _currentlyExclusive = false; + + // If there are any more waiting exclusive tasks, run the next one in line + if (_waitingExclusive.Count > 0) RunExclusive_RequiresLock(_waitingExclusive.Dequeue()); + + // Otherwise, if there are any waiting concurrent tasks, run them all + else if (_waitingConcurrent.Count > 0) RunConcurrent_RequiresLock(); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncSemaphore.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncSemaphore.cs new file mode 100644 index 0000000..cdbabb7 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/AsyncCoordination/AsyncSemaphore.cs @@ -0,0 +1,158 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: AsyncSemaphore.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Diagnostics; + +namespace System.Threading.Async +{ + /// Provides an asynchronous semaphore. + [DebuggerDisplay("CurrentCount={CurrentCount}, MaximumCount={MaximumCount}, WaitingCount={WaitingCount}")] + public sealed class AsyncSemaphore : IDisposable + { + /// The current count. + private int _currentCount; + /// The maximum count. If _maxCount isn't positive, the instance has been disposed. + private int _maxCount; + /// Tasks waiting to be completed when the semaphore has count available. + private Queue> _waitingTasks; + + /// Initializes the SemaphoreAsync with a count of zero and a maximum count of Int32.MaxValue. + public AsyncSemaphore() : this(0) { } + + /// Initializes the SemaphoreAsync with the specified count and a maximum count of Int32.MaxValue. + /// The initial count to use as the current count. + public AsyncSemaphore(int initialCount) : this(initialCount, Int32.MaxValue) { } + + /// Initializes the SemaphoreAsync with the specified counts. + /// The initial count to use as the current count. + /// The maximum count allowed. + public AsyncSemaphore(int initialCount, int maxCount) + { + if (maxCount <= 0) throw new ArgumentOutOfRangeException("maxCount"); + if (initialCount > maxCount || initialCount < 0) throw new ArgumentOutOfRangeException("initialCount"); + _currentCount = initialCount; + _maxCount = maxCount; + _waitingTasks = new Queue>(); + } + + /// Gets the current count. + public int CurrentCount { get { return _currentCount; } } + /// Gets the maximum count. + public int MaximumCount { get { return _maxCount; } } + /// Gets the number of operations currently waiting on the semaphore. + public int WaitingCount { get { lock(_waitingTasks) return _waitingTasks.Count; } } + + /// Waits for a unit to be available in the semaphore. + /// A Task that will be completed when a unit is available and this Wait operation succeeds. + public Task Wait() + { + ThrowIfDisposed(); + lock (_waitingTasks) + { + // If there's room, decrement the count and return a completed task + if (_currentCount > 0) + { + _currentCount--; + return CompletedTask.Default; + } + else + { + // Otherwise, cache a new task and return it + var tcs = new TaskCompletionSource(); + _waitingTasks.Enqueue(tcs); + return tcs.Task; + } + } + } + + /// + /// Queues an action that will be executed when space is available + /// in the semaphore. + /// + /// The action to be executed. + /// + /// A Task that represents the execution of the action. + /// + /// + /// Release does not need to be called for this action, as it will be handled implicitly + /// by the Queue method. + /// + public Task Queue(Action action) + { + return Wait().ContinueWith(_ => + { + try { action(); } + finally { Release(); } + }); + } + + /// + /// Queues a function that will be executed when space is available + /// in the semaphore. + /// + /// The function to be executed. + /// + /// A Task that represents the execution of the function. + /// + /// + /// Release does not need to be called for this function, as it will be handled implicitly + /// by the Queue method. + /// + public Task Queue(Func function) + { + return Wait().ContinueWith(_ => + { + try { return function(); } + finally { Release(); } + }); + } + + /// Releases a unit of work to the semaphore. + public void Release() + { + ThrowIfDisposed(); + lock (_waitingTasks) + { + // Validate that there's room + if (_currentCount == _maxCount) throw new SemaphoreFullException(); + + // If there are any tasks waiting, allow one of them access + if (_waitingTasks.Count > 0) + { + var tcs = _waitingTasks.Dequeue(); + tcs.SetResult(null); + } + // Otherwise, increment the available count + else _currentCount++; + } + } + + private void ThrowIfDisposed() + { + if (_maxCount <= 0) throw new ObjectDisposedException(GetType().Name); + } + + /// Releases the resources used by the semaphore. + public void Dispose() + { + if (_maxCount > 0) + { + _maxCount = 0; + lock (_waitingTasks) + { + while (_waitingTasks.Count > 0) + { + _waitingTasks.Dequeue().SetCanceled(); + } + } + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ConcurrentPriorityQueue.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ConcurrentPriorityQueue.cs new file mode 100644 index 0000000..c333f23 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ConcurrentPriorityQueue.cs @@ -0,0 +1,344 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ConcurrentPriorityQueue.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Collections.Concurrent +{ + /// Provides a thread-safe priority queue data structure. + /// Specifies the type of keys used to prioritize values. + /// Specifies the type of elements in the queue. + [DebuggerDisplay("Count={Count}")] + public class ConcurrentPriorityQueue : + IProducerConsumerCollection> + where TKey : IComparable + { + private readonly object _syncLock = new object(); + private readonly MinBinaryHeap _minHeap = new MinBinaryHeap(); + + /// Initializes a new instance of the ConcurrentPriorityQueue class. + public ConcurrentPriorityQueue() {} + + /// Initializes a new instance of the ConcurrentPriorityQueue class that contains elements copied from the specified collection. + /// The collection whose elements are copied to the new ConcurrentPriorityQueue. + public ConcurrentPriorityQueue(IEnumerable> collection) + { + if (collection == null) throw new ArgumentNullException("collection"); + foreach (var item in collection) _minHeap.Insert(item); + } + + /// Adds the key/value pair to the priority queue. + /// The priority of the item to be added. + /// The item to be added. + public void Enqueue(TKey priority, TValue value) + { + Enqueue(new KeyValuePair(priority, value)); + } + + /// Adds the key/value pair to the priority queue. + /// The key/value pair to be added to the queue. + public void Enqueue(KeyValuePair item) + { + lock (_syncLock) _minHeap.Insert(item); + } + + /// Attempts to remove and return the next prioritized item in the queue. + /// + /// When this method returns, if the operation was successful, result contains the object removed. If + /// no object was available to be removed, the value is unspecified. + /// + /// + /// true if an element was removed and returned from the queue succesfully; otherwise, false. + /// + public bool TryDequeue(out KeyValuePair result) + { + result = default(KeyValuePair); + lock (_syncLock) + { + if (_minHeap.Count > 0) + { + result = _minHeap.Remove(); + return true; + } + } + return false; + } + + /// Attempts to return the next prioritized item in the queue. + /// + /// When this method returns, if the operation was successful, result contains the object. + /// The queue was not modified by the operation. + /// + /// + /// true if an element was returned from the queue succesfully; otherwise, false. + /// + public bool TryPeek(out KeyValuePair result) + { + result = default(KeyValuePair); + lock (_syncLock) + { + if (_minHeap.Count > 0) + { + result = _minHeap.Peek(); + return true; + } + } + return false; + } + + /// Empties the queue. + public void Clear() { lock(_syncLock) _minHeap.Clear(); } + + /// Gets whether the queue is empty. + public bool IsEmpty { get { return Count == 0; } } + + /// Gets the number of elements contained in the queue. + public int Count + { + get { lock (_syncLock) return _minHeap.Count; } + } + + /// Copies the elements of the collection to an array, starting at a particular array index. + /// + /// The one-dimensional array that is the destination of the elements copied from the queue. + /// + /// + /// The zero-based index in array at which copying begins. + /// + /// The elements will not be copied to the array in any guaranteed order. + public void CopyTo(KeyValuePair[] array, int index) + { + lock (_syncLock) _minHeap.Items.CopyTo(array, index); + } + + /// Copies the elements stored in the queue to a new array. + /// A new array containing a snapshot of elements copied from the queue. + public KeyValuePair[] ToArray() + { + lock (_syncLock) + { + var clonedHeap = new MinBinaryHeap(_minHeap); + var result = new KeyValuePair[_minHeap.Count]; + for (int i = 0; i < result.Length; i++) + { + result[i] = clonedHeap.Remove(); + } + return result; + } + } + + /// Attempts to add an item in the queue. + /// The key/value pair to be added. + /// + /// true if the pair was added; otherwise, false. + /// + bool IProducerConsumerCollection>.TryAdd(KeyValuePair item) + { + Enqueue(item); + return true; + } + + /// Attempts to remove and return the next prioritized item in the queue. + /// + /// When this method returns, if the operation was successful, result contains the object removed. If + /// no object was available to be removed, the value is unspecified. + /// + /// + /// true if an element was removed and returned from the queue succesfully; otherwise, false. + /// + bool IProducerConsumerCollection>.TryTake(out KeyValuePair item) + { + return TryDequeue(out item); + } + + /// Returns an enumerator that iterates through the collection. + /// An enumerator for the contents of the queue. + /// + /// The enumeration represents a moment-in-time snapshot of the contents of the queue. It does not + /// reflect any updates to the collection after GetEnumerator was called. The enumerator is safe to + /// use concurrently with reads from and writes to the queue. + /// + public IEnumerator> GetEnumerator() + { + var arr = ToArray(); + return ((IEnumerable>)arr).GetEnumerator(); + } + + /// Returns an enumerator that iterates through a collection. + /// An IEnumerator that can be used to iterate through the collection. + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + + /// Copies the elements of the collection to an array, starting at a particular array index. + /// + /// The one-dimensional array that is the destination of the elements copied from the queue. + /// + /// + /// The zero-based index in array at which copying begins. + /// + void ICollection.CopyTo(Array array, int index) + { + lock (_syncLock) ((ICollection)_minHeap.Items).CopyTo(array, index); + } + + /// + /// Gets a value indicating whether access to the ICollection is synchronized with the SyncRoot. + /// + bool ICollection.IsSynchronized { get { return true; } } + + /// + /// Gets an object that can be used to synchronize access to the collection. + /// + object ICollection.SyncRoot { get { return _syncLock; } } + + /// Implements a binary heap that prioritizes smaller values. + private sealed class MinBinaryHeap + { + private readonly List> _items; + + /// Initializes an empty heap. + public MinBinaryHeap() + { + _items = new List>(); + } + + /// Initializes a heap as a copy of another heap instance. + /// The heap to copy. + /// Key/Value values are not deep cloned. + public MinBinaryHeap(MinBinaryHeap heapToCopy) + { + _items = new List>(heapToCopy.Items); + } + + /// Empties the heap. + public void Clear() { _items.Clear(); } + + /// Adds an item to the heap. + public void Insert(TKey key, TValue value) + { + // Create the entry based on the provided key and value + Insert(new KeyValuePair(key, value)); + } + + /// Adds an item to the heap. + public void Insert(KeyValuePair entry) + { + // Add the item to the list, making sure to keep track of where it was added. + _items.Add(entry); + int pos = _items.Count - 1; + + // If the new item is the only item, we're done. + if (pos == 0) return; + + // Otherwise, perform log(n) operations, walking up the tree, swapping + // where necessary based on key values + while (pos > 0) + { + // Get the next position to check + int nextPos = pos / 2; + + // Extract the entry at the next position + var toCheck = _items[nextPos]; + + // Compare that entry to our new one. If our entry has a smaller key, move it up. + // Otherwise, we're done. + if (entry.Key.CompareTo(toCheck.Key) < 0) + { + _items[pos] = toCheck; + pos = nextPos; + } + else break; + } + + // Make sure we put this entry back in, just in case + _items[pos] = entry; + } + + /// Returns the entry at the top of the heap. + public KeyValuePair Peek() + { + // Returns the first item + if (_items.Count == 0) throw new InvalidOperationException("The heap is empty."); + return _items[0]; + } + + /// Removes the entry at the top of the heap. + public KeyValuePair Remove() + { + // Get the first item and save it for later (this is what will be returned). + if (_items.Count == 0) throw new InvalidOperationException("The heap is empty."); + KeyValuePair toReturn = _items[0]; + + // Remove the first item if there will only be 0 or 1 items left after doing so. + if (_items.Count <= 2) _items.RemoveAt(0); + // A reheapify will be required for the removal + else + { + // Remove the first item and move the last item to the front. + _items[0] = _items[_items.Count - 1]; + _items.RemoveAt(_items.Count - 1); + + // Start reheapify + int current = 0, possibleSwap = 0; + + // Keep going until the tree is a heap + while (true) + { + // Get the positions of the node's children + int leftChildPos = 2 * current + 1; + int rightChildPos = leftChildPos + 1; + + // Should we swap with the left child? + if (leftChildPos < _items.Count) + { + // Get the two entries to compare (node and its left child) + var entry1 = _items[current]; + var entry2 = _items[leftChildPos]; + + // If the child has a lower key than the parent, set that as a possible swap + if (entry2.Key.CompareTo(entry1.Key) < 0) possibleSwap = leftChildPos; + } + else break; // if can't swap this, we're done + + // Should we swap with the right child? Note that now we check with the possible swap + // position (which might be current and might be left child). + if (rightChildPos < _items.Count) + { + // Get the two entries to compare (node and its left child) + var entry1 = _items[possibleSwap]; + var entry2 = _items[rightChildPos]; + + // If the child has a lower key than the parent, set that as a possible swap + if (entry2.Key.CompareTo(entry1.Key) < 0) possibleSwap = rightChildPos; + } + + // Now swap current and possible swap if necessary + if (current != possibleSwap) + { + var temp = _items[current]; + _items[current] = _items[possibleSwap]; + _items[possibleSwap] = temp; + } + else break; // if nothing to swap, we're done + + // Update current to the location of the swap + current = possibleSwap; + } + } + + // Return the item from the heap + return toReturn; + } + + /// Gets the number of objects stored in the heap. + public int Count { get { return _items.Count; } } + + internal List> Items { get { return _items; } } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ObjectPool.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ObjectPool.cs new file mode 100644 index 0000000..a83d291 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ObjectPool.cs @@ -0,0 +1,71 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ObjectPool.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Collections.Concurrent +{ + /// Provides a thread-safe object pool. + /// Specifies the type of the elements stored in the pool. + [DebuggerDisplay("Count={Count}")] + [DebuggerTypeProxy(typeof(IProducerConsumerCollection_DebugView<>))] + public sealed class ObjectPool : ProducerConsumerCollectionBase + { + private readonly Func _generator; + + /// Initializes an instance of the ObjectPool class. + /// The function used to create items when no items exist in the pool. + public ObjectPool(Func generator) : this(generator, new ConcurrentQueue()) { } + + /// Initializes an instance of the ObjectPool class. + /// The function used to create items when no items exist in the pool. + /// The collection used to store the elements of the pool. + public ObjectPool(Func generator, IProducerConsumerCollection collection) + : base(collection) + { + if (generator == null) throw new ArgumentNullException("generator"); + _generator = generator; + } + + /// Adds the provided item into the pool. + /// The item to be added. + public void PutObject(T item) { base.TryAdd(item); } + + /// Gets an item from the pool. + /// The removed or created item. + /// If the pool is empty, a new item will be created and returned. + public T GetObject() + { + T value; + return base.TryTake(out value) ? value : _generator(); + } + + /// Clears the object pool, returning all of the data that was in the pool. + /// An array containing all of the elements in the pool. + public T[] ToArrayAndClear() + { + var items = new List(); + T value; + while (base.TryTake(out value)) items.Add(value); + return items.ToArray(); + } + + protected override bool TryAdd(T item) + { + PutObject(item); + return true; + } + + protected override bool TryTake(out T item) + { + item = GetObject(); + return true; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ObservableConcurrentCollection.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ObservableConcurrentCollection.cs new file mode 100644 index 0000000..77424a9 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ObservableConcurrentCollection.cs @@ -0,0 +1,89 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ObservableConcurrentCollection.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Specialized; +using System.ComponentModel; +using System.Threading; +using System.Diagnostics; + +namespace System.Collections.Concurrent +{ + /// + /// Provides a thread-safe, concurrent collection for use with data binding. + /// + /// Specifies the type of the elements in this collection. + [DebuggerDisplay("Count={Count}")] + [DebuggerTypeProxy(typeof(IProducerConsumerCollection_DebugView<>))] + public class ObservableConcurrentCollection : + ProducerConsumerCollectionBase, INotifyCollectionChanged, INotifyPropertyChanged + { + private readonly SynchronizationContext _context; + + /// + /// Initializes an instance of the ObservableConcurrentCollection class with an underlying + /// queue data structure. + /// + public ObservableConcurrentCollection() : this(new ConcurrentQueue()) { } + + /// + /// Initializes an instance of the ObservableConcurrentCollection class with the specified + /// collection as the underlying data structure. + /// + public ObservableConcurrentCollection(IProducerConsumerCollection collection) : base(collection) + { + _context = AsyncOperationManager.SynchronizationContext; + } + + /// Event raised when the collection changes. + public event NotifyCollectionChangedEventHandler CollectionChanged; + /// Event raised when a property on the collection changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary. + /// + private void NotifyObserversOfChange() + { + var collectionHandler = CollectionChanged; + var propertyHandler = PropertyChanged; + if (collectionHandler != null || propertyHandler != null) + { + _context.Post(s => + { + if (collectionHandler != null) + { + collectionHandler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + if (propertyHandler != null) + { + propertyHandler(this, new PropertyChangedEventArgs("Count")); + } + }, null); + } + } + + protected override bool TryAdd(T item) + { + // Try to add the item to the underlying collection. If we were able to, + // notify any listeners. + bool result = base.TryAdd(item); + if (result) NotifyObserversOfChange(); + return result; + } + + + protected override bool TryTake(out T item) + { + // Try to remove an item from the underlying collection. If we were able to, + // notify any listeners. + bool result = base.TryTake(out item); + if (result) NotifyObserversOfChange(); + return result; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ObservableConcurrentDictionary.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ObservableConcurrentDictionary.cs new file mode 100644 index 0000000..916a3b2 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ObservableConcurrentDictionary.cs @@ -0,0 +1,199 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ObservableConcurrentDictionary.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Threading; +using System.Diagnostics; + +namespace System.Collections.Concurrent +{ + /// + /// Provides a thread-safe dictionary for use with data binding. + /// + /// Specifies the type of the keys in this collection. + /// Specifies the type of the values in this collection. + [DebuggerDisplay("Count={Count}")] + public class ObservableConcurrentDictionary : + ICollection>, IDictionary, + INotifyCollectionChanged, INotifyPropertyChanged + { + private readonly SynchronizationContext _context; + private readonly ConcurrentDictionary _dictionary; + + /// + /// Initializes an instance of the ObservableConcurrentDictionary class. + /// + public ObservableConcurrentDictionary() + { + _context = AsyncOperationManager.SynchronizationContext; + _dictionary = new ConcurrentDictionary(); + } + + /// Event raised when the collection changes. + public event NotifyCollectionChangedEventHandler CollectionChanged; + /// Event raised when a property on the collection changes. + public event PropertyChangedEventHandler PropertyChanged; + + /// + /// Notifies observers of CollectionChanged or PropertyChanged of an update to the dictionary. + /// + private void NotifyObserversOfChange() + { + var collectionHandler = CollectionChanged; + var propertyHandler = PropertyChanged; + if (collectionHandler != null || propertyHandler != null) + { + _context.Post(s => + { + if (collectionHandler != null) + { + collectionHandler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset)); + } + if (propertyHandler != null) + { + propertyHandler(this, new PropertyChangedEventArgs("Count")); + propertyHandler(this, new PropertyChangedEventArgs("Keys")); + propertyHandler(this, new PropertyChangedEventArgs("Values")); + } + }, null); + } + } + + /// Attempts to add an item to the dictionary, notifying observers of any changes. + /// The item to be added. + /// Whether the add was successful. + private bool TryAddWithNotification(KeyValuePair item) + { + return TryAddWithNotification(item.Key, item.Value); + } + + /// Attempts to add an item to the dictionary, notifying observers of any changes. + /// The key of the item to be added. + /// The value of the item to be added. + /// Whether the add was successful. + private bool TryAddWithNotification(TKey key, TValue value) + { + bool result = _dictionary.TryAdd(key, value); + if (result) NotifyObserversOfChange(); + return result; + } + + /// Attempts to remove an item from the dictionary, notifying observers of any changes. + /// The key of the item to be removed. + /// The value of the item removed. + /// Whether the removal was successful. + private bool TryRemoveWithNotification(TKey key, out TValue value) + { + bool result = _dictionary.TryRemove(key, out value); + if (result) NotifyObserversOfChange(); + return result; + } + + /// Attempts to add or update an item in the dictionary, notifying observers of any changes. + /// The key of the item to be updated. + /// The new value to set for the item. + /// Whether the update was successful. + private void UpdateWithNotification(TKey key, TValue value) + { + _dictionary[key] = value; + NotifyObserversOfChange(); + } + + #region ICollection> Members + void ICollection>.Add(KeyValuePair item) + { + TryAddWithNotification(item); + } + + void ICollection>.Clear() + { + ((ICollection>)_dictionary).Clear(); + NotifyObserversOfChange(); + } + + bool ICollection>.Contains(KeyValuePair item) + { + return ((ICollection>)_dictionary).Contains(item); + } + + void ICollection>.CopyTo(KeyValuePair[] array, int arrayIndex) + { + ((ICollection>)_dictionary).CopyTo(array, arrayIndex); + } + + int ICollection>.Count + { + get { return ((ICollection>)_dictionary).Count; } + } + + bool ICollection>.IsReadOnly + { + get { return ((ICollection>)_dictionary).IsReadOnly; } + } + + bool ICollection>.Remove(KeyValuePair item) + { + TValue temp; + return TryRemoveWithNotification(item.Key, out temp); + } + #endregion + + #region IEnumerable> Members + IEnumerator> IEnumerable>.GetEnumerator() + { + return ((ICollection>)_dictionary).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return ((ICollection>)_dictionary).GetEnumerator(); + } + #endregion + + #region IDictionary Members + public void Add(TKey key, TValue value) + { + TryAddWithNotification(key, value); + } + + public bool ContainsKey(TKey key) + { + return _dictionary.ContainsKey(key); + } + + public ICollection Keys + { + get { return _dictionary.Keys; } + } + + public bool Remove(TKey key) + { + TValue temp; + return TryRemoveWithNotification(key, out temp); + } + + public bool TryGetValue(TKey key, out TValue value) + { + return _dictionary.TryGetValue(key, out value); + } + + public ICollection Values + { + get { return _dictionary.Values; } + } + + public TValue this[TKey key] + { + get { return _dictionary[key]; } + set { UpdateWithNotification(key, value); } + } + #endregion + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/Pipeline.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/Pipeline.cs new file mode 100644 index 0000000..1352683 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/Pipeline.cs @@ -0,0 +1,182 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: Pipeline.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Threading.Tasks.Schedulers; + +namespace System.Threading +{ + /// Provides support for pipelined data processing. + public static class Pipeline + { + internal readonly static TaskScheduler Scheduler = new ThreadPerTaskScheduler(); + + /// Creates a new pipeline, with the specified function as the sole stage. + /// Specifies the type of the input data to the pipeline. + /// Specifies the type of the output data from this stage of the pipeline. + /// The function used to process input data into output data. + /// A pipeline for converting from input data to output data. + public static Pipeline Create(Func func) + { + return Create(func, 1); + } + + /// Creates a new pipeline, with the specified function as the sole stage. + /// Specifies the type of the input data to the pipeline. + /// Specifies the type of the output data from this stage of the pipeline. + /// The function used to process input data into output data. + /// The concurrency level for this stage of the pipeline. + /// A pipeline for converting from input data to output data. + public static Pipeline Create(Func func, int degreeOfParallelism) + { + if (func == null) throw new ArgumentNullException("func"); + if (degreeOfParallelism < 1) throw new ArgumentOutOfRangeException("degreeOfParallelism"); + return new Pipeline(func, degreeOfParallelism); + } + } + + /// Provides support for pipelined data processing. + /// Specifies the type of the input data to the pipeline. + /// Specifies the type of the output data from this stage of the pipeline. + public class Pipeline + { + private readonly Func _stageFunc; + private readonly int _degreeOfParallelism; + + internal Pipeline(int degreeOfParallelism) : this(null, degreeOfParallelism) { } + + internal Pipeline(Func func, int degreeOfParallelism) + { + _stageFunc = func; + _degreeOfParallelism = degreeOfParallelism; + } + + /// Creates a new pipeline that combines the current pipeline with a new stage. + /// Specifies the new output type of the pipeline. + /// + /// The function used to convert the output of the current pipeline into the new + /// output of the new pipeline. + /// + /// A new pipeline that combines the current pipeline with the new stage. + /// This overload creates a parallel pipeline stage. + public Pipeline Next(Func func) + { + return Next(func, 1); + } + + /// Creates a new pipeline that combines the current pipeline with a new stage. + /// Specifies the new output type of the pipeline. + /// + /// The function used to convert the output of the current pipeline into the new + /// output of the new pipeline. + /// + /// The concurrency level for this stage of the pipeline. + /// A new pipeline that combines the current pipeline with the new stage. + public Pipeline Next(Func func, int degreeOfParallelism) + { + if (func == null) throw new ArgumentNullException("func"); + if (degreeOfParallelism < 1) throw new ArgumentOutOfRangeException("degreeOfParallelism"); + return new InternalPipeline(this, func, degreeOfParallelism); + } + + /// Runs the pipeline and returns an enumerable over the results. + /// The source data to be processed by the pipeline. + /// An enumerable of the results of the pipeline. + public IEnumerable Process(IEnumerable source) + { + return Process(source, new CancellationToken()); + } + + /// Runs the pipeline and returns an enumerable over the results. + /// The source data to be processed by the pipeline. + /// The cancellation token used to signal cancellation of the pipelining. + /// An enumerable of the results of the pipeline. + public IEnumerable Process(IEnumerable source, CancellationToken cancellationToken) + { + // Validate arguments + if (source == null) throw new ArgumentNullException("source"); + return ProcessNoArgValidation(source, cancellationToken); + } + + /// Runs the pipeline and returns an enumerable over the results. + /// The source data to be processed by the pipeline. + /// The cancellation token used to signal cancellation of the pipelining. + /// An enumerable of the results of the pipeline. + private IEnumerable ProcessNoArgValidation(IEnumerable source, CancellationToken cancellationToken) + { + // Create a blocking collection for communication with the query running in a background task + using (var output = new BlockingCollection()) + { + // Start a task to run the core of the stage + var processingTask = Task.Factory.StartNew(() => + { + try { ProcessCore(source, cancellationToken, output); } + finally { output.CompleteAdding(); } + }, CancellationToken.None, TaskCreationOptions.None, Pipeline.Scheduler); + + // Enumerate and yield the results. This makes ProcessNoArgValidation + // lazy, in that processing won't start until enumeration begins. + foreach (var result in output.GetConsumingEnumerable(cancellationToken)) + { + yield return result; + } + + // Make sure the processing task has shut down, and propagate any exceptions that occurred + processingTask.Wait(); + } + } + + /// Implements the core processing for a pipeline stage. + /// The source data to be processed by the pipeline. + /// The cancellation token used to signal cancellation of the pipelining. + /// The collection into which to put the output. + protected virtual void ProcessCore(IEnumerable source, CancellationToken cancellationToken, BlockingCollection output) + { + var options = new ParallelOptions + { + CancellationToken = cancellationToken, + MaxDegreeOfParallelism = _degreeOfParallelism, + TaskScheduler = Pipeline.Scheduler + }; + Parallel.ForEach(source, options, item => output.Add(_stageFunc(item))); + } + + /// Helper used to add a new stage to a pipeline. + /// Specifies the type of the output for the new pipeline. + private sealed class InternalPipeline : Pipeline + { + private readonly Pipeline _beginningPipeline; + private readonly Func _lastStageFunc; + + public InternalPipeline(Pipeline beginningPipeline, Func func, int degreeOfParallelism) + : base(degreeOfParallelism) + { + _beginningPipeline = beginningPipeline; + _lastStageFunc = func; + } + + /// Implements the core processing for a pipeline stage. + /// The source data to be processed by the pipeline. + /// The cancellation token used to signal cancellation of the pipelining. + /// The collection into which to put the output. + protected override void ProcessCore( + IEnumerable source, CancellationToken cancellationToken, BlockingCollection output) + { + var options = new ParallelOptions + { + CancellationToken = cancellationToken, + MaxDegreeOfParallelism = _degreeOfParallelism, + TaskScheduler = Pipeline.Scheduler + }; + Parallel.ForEach(_beginningPipeline.Process(source, cancellationToken), options, item => output.Add(_lastStageFunc(item))); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ProducerConsumerCollectionBase.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ProducerConsumerCollectionBase.cs new file mode 100644 index 0000000..383a700 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ProducerConsumerCollectionBase.cs @@ -0,0 +1,111 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ProducerConsumerCollectionBase.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Collections.Concurrent +{ + /// Debug view for the IProducerConsumerCollection. + /// Specifies the type of the data being aggregated. + internal sealed class IProducerConsumerCollection_DebugView + { + private IProducerConsumerCollection _collection; + + public IProducerConsumerCollection_DebugView(IProducerConsumerCollection collection) + { + _collection = collection; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Values { get { return _collection.ToArray(); } } + } + + /// + /// Provides a base implementation for producer-consumer collections that wrap other + /// producer-consumer collections. + /// + /// Specifies the type of elements in the collection. + [Serializable] + public abstract class ProducerConsumerCollectionBase : IProducerConsumerCollection + { + private readonly IProducerConsumerCollection _contained; + + /// Initializes the ProducerConsumerCollectionBase instance. + /// The collection to be wrapped by this instance. + protected ProducerConsumerCollectionBase(IProducerConsumerCollection contained) + { + if (contained == null) throw new ArgumentNullException("contained"); + _contained = contained; + } + + /// Gets the contained collection. + protected IProducerConsumerCollection ContainedCollection { get { return _contained; } } + + /// Attempts to add the specified value to the end of the deque. + /// The item to add. + /// true if the item could be added; otherwise, false. + protected virtual bool TryAdd(T item) { return _contained.TryAdd(item); } + + /// Attempts to remove and return an item from the collection. + /// + /// When this method returns, if the operation was successful, item contains the item removed. If + /// no item was available to be removed, the value is unspecified. + /// + /// + /// true if an element was removed and returned from the collection; otherwise, false. + /// + protected virtual bool TryTake(out T item) { return _contained.TryTake(out item); } + + /// Attempts to add the specified value to the end of the deque. + /// The item to add. + /// true if the item could be added; otherwise, false. + bool IProducerConsumerCollection.TryAdd(T item) { return TryAdd(item); } + + /// Attempts to remove and return an item from the collection. + /// + /// When this method returns, if the operation was successful, item contains the item removed. If + /// no item was available to be removed, the value is unspecified. + /// + /// + /// true if an element was removed and returned from the collection; otherwise, false. + /// + bool IProducerConsumerCollection.TryTake(out T item) { return TryTake(out item); } + + /// Gets the number of elements contained in the collection. + public int Count { get { return _contained.Count; } } + + /// Creates an array containing the contents of the collection. + /// The array. + public T[] ToArray() { return _contained.ToArray(); } + + /// Copies the contents of the collection to an array. + /// The array to which the data should be copied. + /// The starting index at which data should be copied. + public void CopyTo(T[] array, int index) { _contained.CopyTo(array, index); } + + /// Copies the contents of the collection to an array. + /// The array to which the data should be copied. + /// The starting index at which data should be copied. + void ICollection.CopyTo(Array array, int index) { _contained.CopyTo(array, index); } + + /// Gets an enumerator for the collection. + /// An enumerator. + public IEnumerator GetEnumerator() { return _contained.GetEnumerator(); } + + /// Gets an enumerator for the collection. + /// An enumerator. + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + + /// Gets whether the collection is synchronized. + bool ICollection.IsSynchronized { get { return _contained.IsSynchronized; } } + + /// Gets the synchronization root object for the collection. + object ICollection.SyncRoot { get { return _contained.SyncRoot; } } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ReductionVariable.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ReductionVariable.cs new file mode 100644 index 0000000..6e4f297 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ReductionVariable.cs @@ -0,0 +1,99 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ReductionVariable.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace System.Threading +{ + /// Provides a reduction variable for aggregating data across multiple threads involved in a computation. + /// Specifies the type of the data being aggregated. + [DebuggerDisplay("Count={_values.Count}")] + [DebuggerTypeProxy(typeof(ReductionVariable_DebugView<>))] + public sealed class ReductionVariable + { + /// The factory used to initialize a value on a thread. + private readonly Func _seedFactory; + /// Thread-local storage for each thread's value. + private readonly ThreadLocal> _threadLocal; + /// The list of all thread-local values for later enumeration. + private readonly ConcurrentQueue> _values = new ConcurrentQueue>(); + + /// Initializes the instances. + public ReductionVariable() + { + _threadLocal = new ThreadLocal>(CreateValue); + } + + /// Initializes the instances. + /// + /// The function invoked to provide the initial value for a thread. + /// If null, the default value of T will be used as the seed. + /// + public ReductionVariable(Func seedFactory) : this() + { + _seedFactory = seedFactory; + } + + /// Creates a value for the current thread and stores it in the central list of values. + /// The boxed value. + private StrongBox CreateValue() + { + var s = new StrongBox(_seedFactory != null ? _seedFactory() : default(T)); + _values.Enqueue(s); + return s; + } + + /// Gets or sets the value for the current thread. + public T Value + { + get { return _threadLocal.Value.Value; } + set { _threadLocal.Value.Value = value; } + } + + /// Gets the values for all of the threads that have used this instance. + public IEnumerable Values { get { return _values.Select(s => s.Value); } } + + /// Applies an accumulator function over the values in this variable. + /// An accumulator function to be invoked on each value. + /// The accumulated value. + public T Reduce(Func function) + { + return Values.Aggregate(function); + } + + /// + /// Applies an accumulator function over the values in this variable. + /// The specified seed is used as the initial accumulator value. + /// + /// An accumulator function to be invoked on each value. + /// The accumulated value. + public TAccumulate Reduce(TAccumulate seed, Func function) + { + return Values.Aggregate(seed, function); + } + } + + /// Debug view for the reductino variable + /// Specifies the type of the data being aggregated. + internal sealed class ReductionVariable_DebugView + { + private ReductionVariable _variable; + + public ReductionVariable_DebugView(ReductionVariable variable) + { + _variable = variable; + } + + [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] + public T[] Values { get { return _variable.Values.ToArray(); } } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/SerialTaskQueue.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/SerialTaskQueue.cs new file mode 100644 index 0000000..27eea0f --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/SerialTaskQueue.cs @@ -0,0 +1,74 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: SerialTaskQueue.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Threading.Tasks +{ + /// Represents a queue of tasks to be started and executed serially. + public class SerialTaskQueue + { + /// The ordered queue of tasks to be executed. Also serves as a lock protecting all shared state. + private Queue _tasks = new Queue(); + /// The task currently executing, or null if there is none. + private Task _taskInFlight; + + /// Enqueues the task to be processed serially and in order. + /// The function that generates a non-started task. + public void Enqueue(Func taskGenerator) { EnqueueInternal(taskGenerator); } + + /// Enqueues the non-started task to be processed serially and in order. + /// The task. + public Task Enqueue(Task task) { EnqueueInternal(task); return task; } + + /// Gets a Task that represents the completion of all previously queued tasks. + public Task Completed() { return Enqueue(new Task(() => { })); } + + /// Enqueues the task to be processed serially and in order. + /// The task or functino that generates a task. + /// The task must not be started and must only be started by this instance. + private void EnqueueInternal(object taskOrFunction) + { + // Validate the task + if (taskOrFunction == null) throw new ArgumentNullException("task"); + lock(_tasks) + { + // If there is currently no task in flight, we'll start this one + if (_taskInFlight == null) StartTask_CallUnderLock(taskOrFunction); + // Otherwise, just queue the task to be started later + else _tasks.Enqueue(taskOrFunction); + } + } + + /// Called when a Task completes to potentially start the next in the queue. + /// The task that completed. + private void OnTaskCompletion(Task ignored) + { + lock (_tasks) + { + // The task completed, so nothing is currently in flight. + // If there are any tasks in the queue, start the next one. + _taskInFlight = null; + if (_tasks.Count > 0) StartTask_CallUnderLock(_tasks.Dequeue()); + } + } + + /// Starts the provided task (or function that returns a task). + /// The next task or function that returns a task. + private void StartTask_CallUnderLock(object nextItem) + { + Task next = nextItem as Task; + if (next == null) next = ((Func)nextItem)(); + + if (next.Status == TaskStatus.Created) next.Start(); + _taskInFlight = next; + next.ContinueWith(OnTaskCompletion); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/SpinLockClass.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/SpinLockClass.cs new file mode 100644 index 0000000..2693b76 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/SpinLockClass.cs @@ -0,0 +1,74 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: SpinLockClass.cs +// +//-------------------------------------------------------------------------- + +namespace System.Threading +{ + /// Provides a simple, reference type wrapper for SpinLock. + public class SpinLockClass + { + private SpinLock _spinLock; // NOTE: must *not* be readonly due to SpinLock being a mutable struct + + /// Initializes an instance of the SpinLockClass class. + public SpinLockClass() + { + _spinLock = new SpinLock(); + } + + /// Initializes an instance of the SpinLockClass class. + /// + /// Controls whether the SpinLockClass should track + /// thread-ownership fo the lock. + /// + public SpinLockClass(bool enableThreadOwnerTracking) + { + _spinLock = new SpinLock(enableThreadOwnerTracking); + } + + /// Runs the specified delegate under the lock. + /// The delegate to be executed while holding the lock. + public void Execute(Action runUnderLock) + { + bool lockTaken = false; + try + { + Enter(ref lockTaken); + runUnderLock(); + } + finally + { + if (lockTaken) Exit(); + } + } + + /// Enters the lock. + /// + /// Upon exit of the Enter method, specifies whether the lock was acquired. + /// The variable passed by reference must be initialized to false. + /// + public void Enter(ref bool lockTaken) + { + _spinLock.Enter(ref lockTaken); + } + + /// Exits the SpinLock. + public void Exit() + { + _spinLock.Exit(); + } + + /// Exits the SpinLock. + /// + /// A Boolean value that indicates whether a memory fence should be issued in + /// order to immediately publish the exit operation to other threads. + /// + public void Exit(bool useMemoryBarrier) + { + _spinLock.Exit(useMemoryBarrier); + } + } +} diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ThreadSafeRandom.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ThreadSafeRandom.cs new file mode 100644 index 0000000..64cbf9d --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/ThreadSafeRandom.cs @@ -0,0 +1,77 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ThreadSafeRandom.cs +// +//-------------------------------------------------------------------------- + +using System; +using System.Security.Cryptography; + +namespace System.Threading +{ + /// + /// Represents a thread-safe, pseudo-random number generator. + /// + public class ThreadSafeRandom : Random + { + /// Seed provider. + private static readonly RNGCryptoServiceProvider _global = new RNGCryptoServiceProvider(); + /// The underlyin provider of randomness, one instance per thread, initialized with _global. + private ThreadLocal _local = new ThreadLocal(() => + { + var buffer = new byte[4]; + _global.GetBytes(buffer); // RNGCryptoServiceProvider is thread-safe for use in this manner + return new Random(BitConverter.ToInt32(buffer, 0)); + }); + + /// Returns a nonnegative random number. + /// A 32-bit signed integer greater than or equal to zero and less than MaxValue. + public override int Next() + { + return _local.Value.Next(); + } + + /// Returns a nonnegative random number less than the specified maximum. + /// + /// The exclusive upper bound of the random number to be generated. maxValue must be greater than or equal to zero. + /// + /// + /// A 32-bit signed integer greater than or equal to zero, and less than maxValue; + /// that is, the range of return values ordinarily includes zero but not maxValue. However, + /// if maxValue equals zero, maxValue is returned. + /// + public override int Next(int maxValue) + { + return _local.Value.Next(maxValue); + } + + /// Returns a random number within a specified range. + /// The inclusive lower bound of the random number returned. + /// The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue. + /// + /// A 32-bit signed integer greater than or equal to minValue and less than maxValue; + /// that is, the range of return values includes minValue but not maxValue. + /// If minValue equals maxValue, minValue is returned. + /// + public override int Next(int minValue, int maxValue) + { + return _local.Value.Next(minValue, maxValue); + } + + /// Returns a random number between 0.0 and 1.0. + /// A double-precision floating point number greater than or equal to 0.0, and less than 1.0. + public override double NextDouble() + { + return _local.Value.NextDouble(); + } + + /// Fills the elements of a specified array of bytes with random numbers. + /// An array of bytes to contain random numbers. + public override void NextBytes(byte[] buffer) + { + _local.Value.NextBytes(buffer); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/TransferStream.cs b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/TransferStream.cs new file mode 100644 index 0000000..84cbb56 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/CoordinationDataStructures/TransferStream.cs @@ -0,0 +1,74 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TransferStream.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.IO; +using System.Threading.Tasks; + +namespace System.Threading +{ + /// Writeable stream for using a separate thread in a producer/consumer scenario. + public sealed class TransferStream : AbstractStreamBase + { + /// The underlying stream to target. + private Stream _writeableStream; + /// The collection of chunks to be written. + private BlockingCollection _chunks; + /// The Task to use for background writing. + private Task _processingTask; + + /// Initializes a new instance of the TransferStream. + /// The underlying stream to which to write. + public TransferStream(Stream writeableStream) + { + // Validate arguments + if (writeableStream == null) throw new ArgumentNullException("writeableStream"); + if (!writeableStream.CanWrite) throw new ArgumentException("Target stream is not writeable."); + + // Set up the producer/consumer relationship, including starting the consumer running + _writeableStream = writeableStream; + _chunks = new BlockingCollection(); + _processingTask = Task.Factory.StartNew(() => + { + // Write out all chunks to the underlying stream + foreach (var chunk in _chunks.GetConsumingEnumerable()) + _writeableStream.Write(chunk, 0, chunk.Length); + }, TaskCreationOptions.LongRunning); + } + + /// Determines whether data can be written to the stream. + public override bool CanWrite { get { return true; } } + + /// Writes a sequence of bytes to the stream. + /// An array of bytes. Write copies count bytes from buffer to the stream. + /// The zero-based byte offset in buffer at which to begin copying bytes to the stream. + /// The number of bytes to be written to the current stream. + public override void Write(byte[] buffer, int offset, int count) + { + // Validate all arguments + if (buffer == null) throw new ArgumentNullException("buffer"); + if (offset < 0 || offset >= buffer.Length) throw new ArgumentOutOfRangeException("offset"); + if (count < 0 || offset + count > buffer.Length) throw new ArgumentOutOfRangeException("count"); + if (count == 0) return; + + // Store the data to the collection + var chunk = new byte[count]; + Buffer.BlockCopy(buffer, offset, chunk, 0, count); + _chunks.Add(chunk); + } + + /// Closes the stream and releases all resources associated with it. + public override void Close() + { + // Complete the collection and waits for the consumer to process all of the data + _chunks.CompleteAdding(); + try { _processingTask.Wait(); } + finally { base.Close(); } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Drawing/FastBitmap.cs b/trunk/Libraries/ParallelExtensionsExtras/Drawing/FastBitmap.cs new file mode 100644 index 0000000..02037fe --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Drawing/FastBitmap.cs @@ -0,0 +1,104 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: FastBitmap.cs +// +//-------------------------------------------------------------------------- + +using System; +using System.Drawing; +using System.Drawing.Imaging; + +namespace Microsoft.Drawing +{ + public struct PixelData + { + public byte B; + public byte G; + public byte R; + } + + public unsafe class FastBitmap : IDisposable + { + private Bitmap _bitmap; + private int _width; + private BitmapData _bitmapData = null; + private byte* _pBase = null; + private PixelData* _pInitPixel = null; + private Point _size; + private bool _locked = false; + + public FastBitmap(Bitmap bmp) + { + if (bmp == null) throw new ArgumentNullException("bitmap"); + + _bitmap = bmp; + _size = new Point(bmp.Width, bmp.Height); + + LockBitmap(); + } + + public PixelData* GetInitialPixelForRow(int rowNumber) + { + return (PixelData*)(_pBase + rowNumber * _width); + } + + public PixelData* this[int x, int y] + { + get { return (PixelData*)(_pBase + y * _width + x * sizeof(PixelData)); } + } + + public Color GetColor(int x, int y) + { + PixelData* data = this[x, y]; + return Color.FromArgb(data->R, data->G, data->B); + } + + public void SetColor(int x, int y, Color c) + { + PixelData* data = this[x, y]; + data->R = c.R; + data->G = c.G; + data->B = c.B; + } + + private void LockBitmap() + { + if (_locked) throw new InvalidOperationException("Already locked"); + + Rectangle bounds = new Rectangle(0, 0, _bitmap.Width, _bitmap.Height); + + // Figure out the number of bytes in a row. This is rounded up to be a multiple + // of 4 bytes, since a scan line in an image must always be a multiple of 4 bytes + // in length. + _width = bounds.Width * sizeof(PixelData); + if (_width % 4 != 0) _width = 4 * (_width / 4 + 1); + + _bitmapData = _bitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); + + _pBase = (byte*)_bitmapData.Scan0.ToPointer(); + _locked = true; + } + + private void InitCurrentPixel() + { + _pInitPixel = (PixelData*)_pBase; + } + + private void UnlockBitmap() + { + if (!_locked) throw new InvalidOperationException("Not currently locked"); + + _bitmap.UnlockBits(_bitmapData); + _bitmapData = null; + _pBase = null; + _locked = false; + } + + public void Dispose() + { + if (_locked) UnlockBitmap(); + } + } +} diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/APM/FileAsync.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/APM/FileAsync.cs new file mode 100644 index 0000000..5fb9513 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/APM/FileAsync.cs @@ -0,0 +1,133 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: FileAsync.cs +// +//-------------------------------------------------------------------------- + +using System.Diagnostics; +using System.Text; +using System.Threading.Tasks; + +namespace System.IO +{ + /// Provides asynchronous counterparts to members of the File class. + public static class FileAsync + { + private const int BUFFER_SIZE = 0x2000; + + /// Opens an existing file for asynchronous reading. + /// The path to the file to be opened for reading. + /// A read-only FileStream on the specified path. + public static FileStream OpenRead(string path) + { + // Open a file stream for reading and that supports asynchronous I/O + return new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, BUFFER_SIZE, true); + } + + /// Opens an existing file for asynchronous writing. + /// The path to the file to be opened for writing. + /// An unshared FileStream on the specified path with access for writing. + public static FileStream OpenWrite(string path) + { + // Open a file stream for writing and that supports asynchronous I/O + return new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, BUFFER_SIZE, true); + } + + /// + /// Opens a binary file for asynchronosu operation, reads the contents of the file into a byte array, and then closes the file. + /// + /// The path to the file to be read. + /// A task that will contain the contents of the file. + public static Task ReadAllBytes(string path) + { + // Open the file for reading + var fs = OpenRead(path); + + // Read all of its contents + var asyncRead = fs.ReadAllBytesAsync(); + + // When we're done reading its contents, close the file and propagate the file's contents + var closedFile = asyncRead.ContinueWith(t => + { + fs.Close(); + return t.Result; + }, TaskContinuationOptions.ExecuteSynchronously); + + // Return the task that represents the entire operation being complete and that returns the + // file's contents + return closedFile; + } + + /// + /// Opens a binary file for asynchronous operation, writes the contents of the byte array into the file, and then closes the file. + /// + /// The path to the file to be written. + /// A task that will signal the completion of the operation. + public static Task WriteAllBytes(string path, byte[] bytes) + { + // Open the file for writing + var fs = OpenWrite(path); + + // Write the contents to the file + var asyncWrite = fs.WriteAsync(bytes, 0, bytes.Length); + + // When complete, close the file and propagate any exceptions + var closedFile = asyncWrite.ContinueWith(t => + { + var e = t.Exception; + fs.Close(); + if (e != null) throw e; + }, TaskContinuationOptions.ExecuteSynchronously); + + // Return a task that represents the operation having completed + return closedFile; + } + + /// + /// Opens a text file for asynchronosu operation, reads the contents of the file into a string, and then closes the file. + /// + /// The path to the file to be read. + /// A task that will contain the contents of the file. + public static Task ReadAllText(string path) + { + // Create a StringBuilder to store the text from the file and an encoding object to decode the + // contents of the file + var text = new StringBuilder(); + var encoding = new UTF8Encoding(); + + // Open the file for reading + var fs = OpenRead(path); + + // Continually read buffers from the file, decoding them and storing the results into the StringBuilder + var asyncRead = fs.ReadBuffersAsync(BUFFER_SIZE, (buffer, count) => text.Append(encoding.GetString(buffer, 0, count))); + + // When done, close the file, propagate any exceptions, and return the decoded text + return asyncRead.ContinueWith(t => + { + var e = t.Exception; + fs.Close(); + if (e != null) throw e; + return text.ToString(); + }, TaskContinuationOptions.ExecuteSynchronously); + } + + /// + /// Opens a text file for asynchronosu operation, writes a string into the file, and then closes the file. + /// + /// The path to the file to be written. + /// A task that will signal the completion of the operation. + public static Task WriteAllText(string path, string contents) + { + // First encode the string contents into a byte array + var encoded = Task.Factory.StartNew( + state => Encoding.UTF8.GetBytes((string)state), + contents); + + // When encoding is done, write all of the contents to the file. Return + // a task that represents the completion of that write. + return encoded.ContinueWith(t => WriteAllBytes(path, t.Result)).Unwrap(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/APM/StreamExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/APM/StreamExtensions.cs new file mode 100644 index 0000000..b056c56 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/APM/StreamExtensions.cs @@ -0,0 +1,196 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: StreamExtensions.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace System.IO +{ + /// Extension methods for asynchronously working with streams. + public static class StreamExtensions + { + private const int BUFFER_SIZE = 0x2000; + + /// Read from a stream asynchronously. + /// The stream. + /// An array of bytes to be filled by the read operation. + /// The offset at which data should be stored. + /// The number of bytes to be read. + /// A Task containing the number of bytes read. + public static Task ReadAsync( + this Stream stream, byte[] buffer, int offset, int count) + { + if (stream == null) throw new ArgumentNullException("stream"); + return Task.Factory.FromAsync( + stream.BeginRead, stream.EndRead, + buffer, offset, count, stream /* object state */); + } + + /// Write to a stream asynchronously. + /// The stream. + /// An array of bytes to be written. + /// The offset from which data should be read to be written. + /// The number of bytes to be written. + /// A Task representing the completion of the asynchronous operation. + public static Task WriteAsync( + this Stream stream, byte[] buffer, int offset, int count) + { + if (stream == null) throw new ArgumentNullException("stream"); + return Task.Factory.FromAsync( + stream.BeginWrite, stream.EndWrite, + buffer, offset, count, stream); + } + + /// Reads the contents of the stream asynchronously. + /// The stream. + /// A Task representing the contents of the file in bytes. + public static Task ReadAllBytesAsync(this Stream stream) + { + if (stream == null) throw new ArgumentNullException("stream"); + + // Create a MemoryStream to store the data read from the input stream + int initialCapacity = stream.CanSeek ? (int)stream.Length : 0; + var readData = new MemoryStream(initialCapacity); + + // Copy from the source stream to the memory stream and return the copied data + return stream.CopyStreamToStreamAsync(readData).ContinueWith(t => + { + t.PropagateExceptions(); + return readData.ToArray(); + }); + } + + /// Read the content of the stream, yielding its data in buffers to the provided delegate. + /// The stream. + /// The size of the buffers to use. + /// The delegate to be called when a new buffer is available. + /// A Task that represents the completion of the asynchronous operation. + public static Task ReadBuffersAsync(this Stream stream, int bufferSize, Action bufferAvailable) + { + if (stream == null) throw new ArgumentNullException("stream"); + if (bufferSize < 1) throw new ArgumentOutOfRangeException("bufferSize"); + if (bufferAvailable == null) throw new ArgumentNullException("bufferAvailable"); + + // Read from the stream over and over, handing the buffers off to the bufferAvailable delegate + // as they're available. Delegate invocation will be serialized. + return Task.Factory.Iterate( + ReadIterator(stream, bufferSize, bufferAvailable)); + } + + /// + /// Creates an enumerable to be used with TaskFactoryExtensions.Iterate that reads data + /// from an input stream and passes it to a user-provided delegate. + /// + /// The source stream. + /// The size of the buffers to be used. + /// + /// A delegate to be invoked when a buffer is available (provided the + /// buffer and the number of bytes in the buffer starting at offset 0. + /// + /// An enumerable containing yielded tasks from the operation. + private static IEnumerable ReadIterator(Stream input, int bufferSize, Action bufferAvailable) + { + // Create a buffer that will be used over and over + var buffer = new byte[bufferSize]; + + // Until there's no more data + while (true) + { + // Asynchronously read a buffer and yield until the operation completes + var readTask = input.ReadAsync(buffer, 0, buffer.Length); + yield return readTask; + + // If there's no more data in the stream, we're done. + if (readTask.Result <= 0) break; + + // Otherwise, hand the data off to the delegate + bufferAvailable(buffer, readTask.Result); + } + } + + /// Copies the contents of a stream to a file, asynchronously. + /// The source stream. + /// The path to the destination file. + /// A Task that represents the asynchronous operation. + public static Task CopyStreamToFileAsync(this Stream source, string destinationPath) + { + if (source == null) throw new ArgumentNullException("source"); + if (destinationPath == null) throw new ArgumentNullException("destinationPath"); + + // Open the output file for writing + var destinationStream = FileAsync.OpenWrite(destinationPath); + + // Copy the source to the destination stream, then close the output file. + return CopyStreamToStreamAsync(source, destinationStream).ContinueWith(t => + { + var e = t.Exception; + destinationStream.Close(); + if (e != null) throw e; + }, TaskContinuationOptions.ExecuteSynchronously); + } + + /// Copies the contents of one stream to another, asynchronously. + /// The source stream. + /// The destination stream. + /// A Task that represents the completion of the asynchronous operation. + public static Task CopyStreamToStreamAsync(this Stream source, Stream destination) + { + if (source == null) throw new ArgumentNullException("source"); + if (destination == null) throw new ArgumentNullException("destination"); + return Task.Factory.Iterate( + CopyStreamIterator(source, destination)); + } + + /// + /// Creates an enumerable to be used with TaskFactoryExtensions.Iterate that copies data from one + /// stream to another. + /// + /// The input stream. + /// The output stream. + /// An enumerable containing yielded tasks from the copy operation. + private static IEnumerable CopyStreamIterator(Stream input, Stream output) + { + // Create two buffers. One will be used for the current read operation and one for the current + // write operation. We'll continually swap back and forth between them. + byte[][] buffers = new byte[2][] { new byte[BUFFER_SIZE], new byte[BUFFER_SIZE] }; + int filledBufferNum = 0; + Task writeTask = null; + + // Until there's no more data to be read + while (true) + { + // Read from the input asynchronously + var readTask = input.ReadAsync(buffers[filledBufferNum], 0, buffers[filledBufferNum].Length); + + // If we have no pending write operations, just yield until the read operation has + // completed. If we have both a pending read and a pending write, yield until both the read + // and the write have completed. + if (writeTask == null) + { + yield return readTask; + readTask.Wait(); // propagate any exception that may have occurred + } + else + { + var tasks = new[] { readTask, writeTask }; + yield return Task.Factory.WhenAll(tasks); + Task.WaitAll(tasks); // propagate any exceptions that may have occurred + } + + // If no data was read, nothing more to do. + if (readTask.Result <= 0) break; + + // Otherwise, write the written data out to the file + writeTask = output.WriteAsync(buffers[filledBufferNum], 0, readTask.Result); + + // Swap buffers + filledBufferNum ^= 1; + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/APM/WebRequestExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/APM/WebRequestExtensions.cs new file mode 100644 index 0000000..c613b11 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/APM/WebRequestExtensions.cs @@ -0,0 +1,49 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: WebRequestExtensions.cs +// +//-------------------------------------------------------------------------- + +using System.IO; +using System.Threading.Tasks; + +namespace System.Net +{ + /// Extension methods for working with WebRequest asynchronously. + public static class WebRequestExtensions + { + /// Creates a Task that represents an asynchronous request to GetResponse. + /// The WebRequest. + /// A Task containing the retrieved WebResponse. + public static Task GetResponseAsync(this WebRequest webRequest) + { + if (webRequest == null) throw new ArgumentNullException("webRequest"); + return Task.Factory.FromAsync( + webRequest.BeginGetResponse, webRequest.EndGetResponse, webRequest /* object state for debugging */); + } + + /// Creates a Task that represents an asynchronous request to GetRequestStream. + /// The WebRequest. + /// A Task containing the retrieved Stream. + public static Task GetRequestStreamAsync(this WebRequest webRequest) + { + if (webRequest == null) throw new ArgumentNullException("webRequest"); + return Task.Factory.FromAsync( + webRequest.BeginGetRequestStream, webRequest.EndGetRequestStream, webRequest /* object state for debugging */); + } + + /// Creates a Task that respresents downloading all of the data from a WebRequest. + /// The WebRequest. + /// A Task containing the downloaded content. + public static Task DownloadDataAsync(this WebRequest webRequest) + { + // Asynchronously get the response. When that's done, asynchronously read the contents. + return webRequest.GetResponseAsync().ContinueWith(response => + { + return response.Result.GetResponseStream().ReadAllBytesAsync(); + }).Unwrap(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/AggregateExceptionExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/AggregateExceptionExtensions.cs new file mode 100644 index 0000000..6429712 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/AggregateExceptionExtensions.cs @@ -0,0 +1,77 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: AggregateExceptionExtensions.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; + +namespace System +{ + /// Extension methods for AggregateException. + public static class AggregateExceptionExtensions + { + /// Invokes a handler on each Exception contained by this AggregateException. + /// The AggregateException. + /// + /// The predicate to execute for each exception. The predicate accepts as an argument the Exception + /// to be processed and returns a Boolean to indicate whether the exception was handled. + /// + /// + /// Whether the rethrown AggregateException should maintain the same hierarchy as the original. + /// + public static void Handle( + this AggregateException aggregateException, + Func predicate, bool leaveStructureIntact) + { + if (aggregateException == null) throw new ArgumentNullException("aggregateException"); + if (predicate == null) throw new ArgumentNullException("predicate"); + + // If leaveStructureIntact, use this implementation + if (leaveStructureIntact) + { + var result = HandleRecursively(aggregateException, predicate); + if (result != null) throw result; + } + // Otherwise, default back to the implementation on AggregateException + else aggregateException.Handle(predicate); + } + + private static AggregateException HandleRecursively( + AggregateException aggregateException, Func predicate) + { + // Maintain a list of exceptions to be rethrown + List innerExceptions = null; + + // Loop over all of the inner exceptions + foreach(var inner in aggregateException.InnerExceptions) + { + // If the inner exception is itself an aggregate, process recursively + AggregateException innerAsAggregate = inner as AggregateException; + if (innerAsAggregate != null) + { + // Process recursively, and if we get back a new aggregate, store it + AggregateException newChildAggregate = HandleRecursively(innerAsAggregate, predicate); + if (newChildAggregate != null) + { + if (innerExceptions != null) innerExceptions = new List(); + innerExceptions.Add(newChildAggregate); + } + } + // Otherwise, if the exception does not match the filter, store it + else if (!predicate(inner)) + { + if (innerExceptions != null) innerExceptions = new List(); + innerExceptions.Add(inner); + } + } + + // If there are any remaining exceptions, return them in a new aggregate. + return innerExceptions.Count > 0 ? + new AggregateException(aggregateException.Message, innerExceptions) : + null; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/BlockingCollectionExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/BlockingCollectionExtensions.cs new file mode 100644 index 0000000..460aa93 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/BlockingCollectionExtensions.cs @@ -0,0 +1,210 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: BlockingCollectionExtensions.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Threading; +using System.Linq; + +namespace System.Collections.Concurrent +{ + /// Extension methods for BlockingCollection. + public static class BlockingCollectionExtensions + { + /// + /// Gets a partitioner for a BlockingCollection that consumes and yields the contents of the BlockingCollection. + /// Specifies the type of data in the collection. + /// The collection for which to create a partitioner. + /// A partitioner that completely consumes and enumerates the contents of the collection. + /// + /// Using this partitioner with a Parallel.ForEach loop or with PLINQ eliminates the need for those + /// constructs to do any additional locking. The only synchronization in place is that used by the + /// BlockingCollection internally. + /// + public static Partitioner GetConsumingPartitioner(this BlockingCollection collection) + { + return new BlockingCollectionPartitioner(collection); + } + + /// Provides a partitioner that consumes a blocking collection and yields its contents. + /// Specifies the type of data in the collection. + private class BlockingCollectionPartitioner : Partitioner + { + /// The target collection. + private BlockingCollection _collection; + + /// Initializes the partitioner. + /// The collection to be enumerated and consumed. + internal BlockingCollectionPartitioner(BlockingCollection collection) + { + if (collection == null) throw new ArgumentNullException("collection"); + _collection = collection; + } + + /// Gets whether additional partitions can be created dynamically. + public override bool SupportsDynamicPartitions { get { return true; } } + + /// Partitions the underlying collection into the given number of partitions. + /// The number of partitions to create. + /// A list containing partitionCount enumerators. + public override IList> GetPartitions(int partitionCount) + { + if (partitionCount < 1) throw new ArgumentOutOfRangeException("partitionCount"); + var dynamicPartitioner = GetDynamicPartitions(); + return Enumerable.Range(0, partitionCount).Select(_ => dynamicPartitioner.GetEnumerator()).ToArray(); + } + + /// + /// Creates an object that can partition the underlying collection into a variable number of partitions. + /// + /// An object that can create partitions over the underlying data source. + public override IEnumerable GetDynamicPartitions() + { + return _collection.GetConsumingEnumerable(); + } + } + + /// Adds the contents of an enumerable to the BlockingCollection. + /// Specifies the type of the elements in the collection. + /// The target BlockingCollection to be augmented. + /// The source enumerable containing the data to be added. + /// + /// Whether to mark the target BlockingCollection as complete for adding when + /// all elements of the source enumerable have been transfered. + /// + public static void AddFromEnumerable(this BlockingCollection target, IEnumerable source, bool completeAddingWhenDone) + { + try { foreach (var item in source) target.Add(item); } + finally { if (completeAddingWhenDone) target.CompleteAdding(); } + } + + /// Adds the contents of an observable to the BlockingCollection. + /// Specifies the type of the elements in the collection. + /// The target BlockingCollection to be augmented. + /// The source observable containing the data to be added. + /// + /// Whether to mark the target BlockingCollection as complete for adding when + /// all elements of the source observable have been transfered. + /// + /// An IDisposable that may be used to cancel the transfer. + public static IDisposable AddFromObservable(this BlockingCollection target, IObservable source, bool completeAddingWhenDone) + { + if (target == null) throw new ArgumentNullException("target"); + if (source == null) throw new ArgumentNullException("source"); + return source.Subscribe(new DelegateBasedObserver + ( + onNext: item => target.Add(item), + onError: error => { if (completeAddingWhenDone) target.CompleteAdding(); }, + onCompleted: () => { if (completeAddingWhenDone) target.CompleteAdding(); } + )); + } + + /// Creates an IProducerConsumerCollection-facade for a BlockingCollection instance. + /// Specifies the type of the elements in the collection. + /// The BlockingCollection. + /// + /// An IProducerConsumerCollection that wraps the provided BlockingCollection. + /// + public static IProducerConsumerCollection ToProducerConsumerCollection( + this BlockingCollection collection) + { + return ToProducerConsumerCollection(collection, Timeout.Infinite); + } + + /// Creates an IProducerConsumerCollection-facade for a BlockingCollection instance. + /// Specifies the type of the elements in the collection. + /// The BlockingCollection. + /// -1 for infinite blocking add and take operations. 0 for non-blocking, 1 or greater for blocking with timeout. + /// An IProducerConsumerCollection that wraps the provided BlockingCollection. + public static IProducerConsumerCollection ToProducerConsumerCollection( + this BlockingCollection collection, int millisecondsTimeout) + { + return new ProducerConsumerWrapper(collection, millisecondsTimeout, new CancellationToken()); + } + + /// Creates an IProducerConsumerCollection-facade for a BlockingCollection instance. + /// Specifies the type of the elements in the collection. + /// The BlockingCollection. + /// -1 for infinite blocking add and take operations. 0 for non-blocking, 1 or greater for blocking with timeout. + /// The CancellationToken to use for any blocking operations. + /// An IProducerConsumerCollection that wraps the provided BlockingCollection. + public static IProducerConsumerCollection ToProducerConsumerCollection( + this BlockingCollection collection, int millisecondsTimeout, CancellationToken cancellationToken) + { + return new ProducerConsumerWrapper(collection, millisecondsTimeout, cancellationToken); + } + + /// Provides a producer-consumer collection facade for a BlockingCollection. + /// Specifies the type of the elements in the collection. + internal sealed class ProducerConsumerWrapper : IProducerConsumerCollection + { + private readonly BlockingCollection _collection; + private readonly int _millisecondsTimeout; + private readonly CancellationToken _cancellationToken; + + public ProducerConsumerWrapper( + BlockingCollection collection, int millisecondsTimeout, CancellationToken cancellationToken) + { + if (collection == null) throw new ArgumentNullException("bc"); + if (millisecondsTimeout < -1) throw new ArgumentOutOfRangeException("millisecondsTimeout"); + _collection = collection; + _millisecondsTimeout = millisecondsTimeout; + _cancellationToken = cancellationToken; + } + + public void CopyTo(T[] array, int index) + { + _collection.CopyTo(array, index); + } + + public T[] ToArray() + { + return _collection.ToArray(); + } + + public bool TryAdd(T item) + { + return _collection.TryAdd(item, _millisecondsTimeout, _cancellationToken); + } + + public bool TryTake(out T item) + { + return _collection.TryTake(out item, _millisecondsTimeout, _cancellationToken); + } + + public IEnumerator GetEnumerator() + { + return ((IEnumerable)_collection).GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public void CopyTo(Array array, int index) + { + ((ICollection)_collection).CopyTo(array, index); + } + + public int Count + { + get { return _collection.Count; } + } + + public bool IsSynchronized + { + get { return ((ICollection)_collection).IsSynchronized; } + } + + public object SyncRoot + { + get { return ((ICollection)_collection).SyncRoot; } + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/CancellationTokenExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/CancellationTokenExtensions.cs new file mode 100644 index 0000000..5868d7f --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/CancellationTokenExtensions.cs @@ -0,0 +1,35 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: CancellationTokenExtensions.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent.Partitioners; +using System.Collections.Generic; + +namespace System.Threading +{ + /// Extension methods for CancellationToken. + public static class CancellationTokenExtensions + { + /// Cancels a CancellationTokenSource and throws a corresponding OperationCanceledException. + /// The source to be canceled. + public static void CancelAndThrow(this CancellationTokenSource source) + { + source.Cancel(); + source.Token.ThrowIfCancellationRequested(); + } + + /// + /// Creates a CancellationTokenSource that will be canceled when the specified token has cancellation requested. + /// + /// The token. + /// The created CancellationTokenSource. + public static CancellationTokenSource CreateLinkedSource(this CancellationToken token) + { + return CancellationTokenSource.CreateLinkedTokenSource(token, new CancellationToken()); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/CompletedTask.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/CompletedTask.cs new file mode 100644 index 0000000..67f94fb --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/CompletedTask.cs @@ -0,0 +1,34 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: CompletedTask.cs +// +//-------------------------------------------------------------------------- + +namespace System.Threading.Tasks +{ + /// Provides access to an already completed task. + /// A completed task can be useful for using ContinueWith overloads where there aren't StartNew equivalents. + public static class CompletedTask + { + /// Gets a completed Task. + public readonly static Task Default = CompletedTask.Default; + } + + /// Provides access to an already completed task. + /// A completed task can be useful for using ContinueWith overloads where there aren't StartNew equivalents. + public static class CompletedTask + { + /// Initializes a Task. + static CompletedTask() + { + var tcs = new TaskCompletionSource(); + tcs.TrySetResult(default(TResult)); + Default = tcs.Task; + } + + /// Gets a completed Task. + public readonly static Task Default; + } +} diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/DelegateBasedObserver.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/DelegateBasedObserver.cs new file mode 100644 index 0000000..d9497eb --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/DelegateBasedObserver.cs @@ -0,0 +1,33 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: IProducerConsumerCollectionExtensions.cs +// +//-------------------------------------------------------------------------- + +using System; + +namespace System +{ + internal class DelegateBasedObserver : IObserver + { + private Action _onNext; + private Action _onError; + private Action _onCompleted; + + internal DelegateBasedObserver(Action onNext, Action onError, Action onCompleted) + { + if (onNext == null) throw new ArgumentNullException("onNext"); + if (onError == null) throw new ArgumentNullException("onError"); + if (onCompleted == null) throw new ArgumentNullException("onCompleted"); + _onNext = onNext; + _onError = onError; + _onCompleted = onCompleted; + } + + public void OnCompleted() { _onCompleted(); } + public void OnError(Exception error) { _onError(error); } + public void OnNext(T value) { _onNext(value); } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/DelegateExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/DelegateExtensions.cs new file mode 100644 index 0000000..05655c0 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/DelegateExtensions.cs @@ -0,0 +1,68 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: DelegateExtensions.cs +// +//-------------------------------------------------------------------------- + +using System.Diagnostics; +using System.Linq; + +namespace System +{ + /// Parallel extensions for the Delegate class. + public static class DelegateExtensions + { + /// Dynamically invokes (late-bound) in parallel the methods represented by the delegate. + /// The delegate to be invoked. + /// An array of objects that are the arguments to pass to the delegates. + /// The return value of one of the delegate invocations. + public static object ParallelDynamicInvoke(this Delegate multicastDelegate, params object[] args) + { + if (multicastDelegate == null) throw new ArgumentNullException("multicastDelegate"); + if (args == null) throw new ArgumentNullException("args"); + return multicastDelegate.GetInvocationList() + .AsParallel().AsOrdered() + .Select(d => d.DynamicInvoke(args)) + .Last(); + } + + /// + /// Provides a delegate that runs the specified action and fails fast if the action throws an exception. + /// + /// The action to invoke. + /// The wrapper delegate. + public static Action WithFailFast(this Action action) + { + return () => + { + try { action(); } + catch (Exception exc) + { + if (Debugger.IsAttached) Debugger.Break(); + else Environment.FailFast("An unhandled exception occurred.", exc); + } + }; + } + + /// + /// Provides a delegate that runs the specified function and fails fast if the function throws an exception. + /// + /// The function to invoke. + /// The wrapper delegate. + public static Func WithFailFast(this Func function) + { + return () => + { + try { return function(); } + catch (Exception exc) + { + if (Debugger.IsAttached) Debugger.Break(); + else Environment.FailFast("An unhandled exception occurred.", exc); + } + throw new Exception("Will never get here"); + }; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/EAP/EAPCommon.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/EAP/EAPCommon.cs new file mode 100644 index 0000000..92b9298 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/EAP/EAPCommon.cs @@ -0,0 +1,31 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: EAPCommon.cs +// +//-------------------------------------------------------------------------- + +using System.ComponentModel; + +namespace System.Threading.Tasks +{ + internal class EAPCommon + { + internal static void HandleCompletion( + TaskCompletionSource tcs, AsyncCompletedEventArgs e, Func getResult, Action unregisterHandler) + { + // Transfers the results from the AsyncCompletedEventArgs and getResult() to the + // TaskCompletionSource, but only AsyncCompletedEventArg's UserState matches the TCS + // (this check is important if the same WebClient is used for multiple, asynchronous + // operations concurrently). Also unregisters the handler to avoid a leak. + if (e.UserState == tcs) + { + if (e.Cancelled) tcs.TrySetCanceled(); + else if (e.Error != null) tcs.TrySetException(e.Error); + else tcs.TrySetResult(getResult()); + unregisterHandler(); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/EAP/PingExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/EAP/PingExtensions.cs new file mode 100644 index 0000000..b9378e4 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/EAP/PingExtensions.cs @@ -0,0 +1,204 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: PingExtensions.cs +// +//-------------------------------------------------------------------------- + +using System.Threading.Tasks; + +namespace System.Net.NetworkInformation +{ + /// Extension methods for working with Ping asynchronously. + public static class PingExtensions + { + /// + /// Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. + /// + /// The Ping. + /// An IPAddress that identifies the computer that is the destination for the ICMP echo message. + /// A user-defined object stored in the resulting Task. + /// A task that represents the asynchronous operation. + public static Task SendTask(this Ping ping, IPAddress address, object userToken) + { + return SendTaskCore(ping, userToken, tcs => ping.SendAsync(address, tcs)); + } + + /// + /// Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. + /// + /// The Ping. + /// + /// A String that identifies the computer that is the destination for the ICMP echo message. + /// The value specified for this parameter can be a host name or a string representation of an IP address. + /// + /// A user-defined object stored in the resulting Task. + /// A task that represents the asynchronous operation. + public static Task SendTask(this Ping ping, string hostNameOrAddress, object userToken) + { + return SendTaskCore(ping, userToken, tcs => ping.SendAsync(hostNameOrAddress, tcs)); + } + + /// + /// Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. + /// + /// The Ping. + /// An IPAddress that identifies the computer that is the destination for the ICMP echo message. + /// + /// An Int32 value that specifies the maximum number of milliseconds (after sending the echo message) + /// to wait for the ICMP echo reply message. + /// + /// A user-defined object stored in the resulting Task. + /// A task that represents the asynchronous operation. + public static Task SendTask(this Ping ping, IPAddress address, int timeout, object userToken) + { + return SendTaskCore(ping, userToken, tcs => ping.SendAsync(address, timeout, tcs)); + } + + /// + /// Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. + /// + /// The Ping. + /// + /// A String that identifies the computer that is the destination for the ICMP echo message. + /// The value specified for this parameter can be a host name or a string representation of an IP address. + /// + /// + /// An Int32 value that specifies the maximum number of milliseconds (after sending the echo message) + /// to wait for the ICMP echo reply message. + /// + /// A user-defined object stored in the resulting Task. + /// A task that represents the asynchronous operation. + public static Task SendTask(this Ping ping, string hostNameOrAddress, int timeout, object userToken) + { + return SendTaskCore(ping, userToken, tcs => ping.SendAsync(hostNameOrAddress, timeout, tcs)); + } + + /// + /// Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. + /// + /// The Ping. + /// An IPAddress that identifies the computer that is the destination for the ICMP echo message. + /// + /// An Int32 value that specifies the maximum number of milliseconds (after sending the echo message) + /// to wait for the ICMP echo reply message. + /// + /// + /// A Byte array that contains data to be sent with the ICMP echo message and returned + /// in the ICMP echo reply message. The array cannot contain more than 65,500 bytes. + /// + /// A user-defined object stored in the resulting Task. + /// A task that represents the asynchronous operation. + public static Task SendTask(this Ping ping, IPAddress address, int timeout, byte[] buffer, object userToken) + { + return SendTaskCore(ping, userToken, tcs => ping.SendAsync(address, timeout, buffer, tcs)); + } + + /// + /// Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. + /// + /// The Ping. + /// + /// A String that identifies the computer that is the destination for the ICMP echo message. + /// The value specified for this parameter can be a host name or a string representation of an IP address. + /// + /// + /// An Int32 value that specifies the maximum number of milliseconds (after sending the echo message) + /// to wait for the ICMP echo reply message. + /// + /// + /// A Byte array that contains data to be sent with the ICMP echo message and returned + /// in the ICMP echo reply message. The array cannot contain more than 65,500 bytes. + /// + /// A user-defined object stored in the resulting Task. + /// A task that represents the asynchronous operation. + public static Task SendTask(this Ping ping, string hostNameOrAddress, int timeout, byte[] buffer, object userToken) + { + return SendTaskCore(ping, userToken, tcs => ping.SendAsync(hostNameOrAddress, timeout, buffer, tcs)); + } + + /// + /// Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. + /// + /// The Ping. + /// An IPAddress that identifies the computer that is the destination for the ICMP echo message. + /// + /// An Int32 value that specifies the maximum number of milliseconds (after sending the echo message) + /// to wait for the ICMP echo reply message. + /// + /// + /// A Byte array that contains data to be sent with the ICMP echo message and returned + /// in the ICMP echo reply message. The array cannot contain more than 65,500 bytes. + /// + /// A PingOptions object used to control fragmentation and Time-to-Live values for the ICMP echo message packet. + /// A user-defined object stored in the resulting Task. + /// A task that represents the asynchronous operation. + public static Task SendTask(this Ping ping, IPAddress address, int timeout, byte[] buffer, PingOptions options, object userToken) + { + return SendTaskCore(ping, userToken, tcs => ping.SendAsync(address, timeout, buffer, options, tcs)); + } + + /// + /// Asynchronously attempts to send an Internet Control Message Protocol (ICMP) echo message. + /// + /// The Ping. + /// + /// A String that identifies the computer that is the destination for the ICMP echo message. + /// The value specified for this parameter can be a host name or a string representation of an IP address. + /// + /// + /// An Int32 value that specifies the maximum number of milliseconds (after sending the echo message) + /// to wait for the ICMP echo reply message. + /// + /// + /// A Byte array that contains data to be sent with the ICMP echo message and returned + /// in the ICMP echo reply message. The array cannot contain more than 65,500 bytes. + /// + /// A PingOptions object used to control fragmentation and Time-to-Live values for the ICMP echo message packet. + /// A user-defined object stored in the resulting Task. + /// A task that represents the asynchronous operation. + public static Task SendTask(this Ping ping, string hostNameOrAddress, int timeout, byte[] buffer, PingOptions options, object userToken) + { + return SendTaskCore(ping, userToken, tcs => ping.SendAsync(hostNameOrAddress, timeout, buffer, options, tcs)); + } + + /// The core implementation of SendTask. + /// The Ping. + /// A user-defined object stored in the resulting Task. + /// + /// A delegate that initiates the asynchronous send. + /// The provided TaskCompletionSource must be passed as the user-supplied state to the actual Ping.SendAsync method. + /// + /// + private static Task SendTaskCore(Ping ping, object userToken, Action> sendAsync) + { + // Validate we're being used with a real smtpClient. The rest of the arg validation + // will happen in the call to sendAsync. + if (ping == null) throw new ArgumentNullException("ping"); + + // Create a TaskCompletionSource to represent the operation + var tcs = new TaskCompletionSource(userToken); + + // Register a handler that will transfer completion results to the TCS Task + PingCompletedEventHandler handler = null; + handler = (sender, e) => EAPCommon.HandleCompletion(tcs, e, () => e.Reply, () => ping.PingCompleted -= handler); + ping.PingCompleted += handler; + + // Try to start the async operation. If starting it fails (due to parameter validation) + // unregister the handler before allowing the exception to propagate. + try + { + sendAsync(tcs); + } + catch(Exception exc) + { + ping.PingCompleted -= handler; + tcs.TrySetException(exc); + } + + // Return the task to represent the asynchronous operation + return tcs.Task; + } + } +} diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/EAP/SmtpClientExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/EAP/SmtpClientExtensions.cs new file mode 100644 index 0000000..cba4b4a --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/EAP/SmtpClientExtensions.cs @@ -0,0 +1,79 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: SmtpClientExtensions.cs +// +//-------------------------------------------------------------------------- + +using System.Net.Mail; +using System.Threading.Tasks; + +namespace System.Net.NetworkInformation +{ + /// Extension methods for working with SmtpClient asynchronously. + public static class SmtpClientExtensions + { + /// Sends an e-mail message asynchronously. + /// The client. + /// A MailMessage that contains the message to send. + /// A user-defined object stored in the resulting Task. + /// A Task that represents the asynchronous send. + public static Task SendTask(this SmtpClient smtpClient, MailMessage message, object userToken) + { + return SendTaskCore(smtpClient, userToken, tcs => smtpClient.SendAsync(message, tcs)); + } + + /// Sends an e-mail message asynchronously. + /// The client. + /// A MailMessage that contains the message to send. + /// A String that contains the address information of the message sender. + /// A String that contains the address that the message is sent to. + /// A String that contains the subject line for the message. + /// A String that contains the message body. + /// A user-defined object stored in the resulting Task. + /// A Task that represents the asynchronous send. + public static Task SendTask(this SmtpClient smtpClient, string from, string recipients, string subject, string body, object userToken) + { + return SendTaskCore(smtpClient, userToken, tcs => smtpClient.SendAsync(from, recipients, subject, body, tcs)); + } + + /// The core implementation of SendTask. + /// The client. + /// The user-supplied state. + /// + /// A delegate that initiates the asynchronous send. + /// The provided TaskCompletionSource must be passed as the user-supplied state to the actual SmtpClient.SendAsync method. + /// + /// + private static Task SendTaskCore(SmtpClient smtpClient, object userToken, Action> sendAsync) + { + // Validate we're being used with a real smtpClient. The rest of the arg validation + // will happen in the call to sendAsync. + if (smtpClient == null) throw new ArgumentNullException("smtpClient"); + + // Create a TaskCompletionSource to represent the operation + var tcs = new TaskCompletionSource(userToken); + + // Register a handler that will transfer completion results to the TCS Task + SendCompletedEventHandler handler = null; + handler = (sender, e) => EAPCommon.HandleCompletion(tcs, e, () => null, () => smtpClient.SendCompleted -= handler); + smtpClient.SendCompleted += handler; + + // Try to start the async operation. If starting it fails (due to parameter validation) + // unregister the handler before allowing the exception to propagate. + try + { + sendAsync(tcs); + } + catch(Exception exc) + { + smtpClient.SendCompleted -= handler; + tcs.TrySetException(exc); + } + + // Return the task to represent the asynchronous operation + return tcs.Task; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/EAP/WebClientExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/EAP/WebClientExtensions.cs new file mode 100644 index 0000000..faa39d0 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/EAP/WebClientExtensions.cs @@ -0,0 +1,354 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: WebClientExtensions.cs +// +//-------------------------------------------------------------------------- + +using System.ComponentModel; +using System.IO; +using System.Threading.Tasks; + +namespace System.Net +{ + /// Extension methods for working with WebClient asynchronously. + public static class WebClientExtensions + { + /// Downloads the resource with the specified URI as a byte array, asynchronously. + /// The WebClient. + /// The URI from which to download data. + /// A Task that contains the downloaded data. + public static Task DownloadDataTask(this WebClient webClient, string address) + { + return DownloadDataTask(webClient, new Uri(address)); + } + + /// Downloads the resource with the specified URI as a byte array, asynchronously. + /// The WebClient. + /// The URI from which to download data. + /// A Task that contains the downloaded data. + public static Task DownloadDataTask(this WebClient webClient, Uri address) + { + // Create the task to be returned + var tcs = new TaskCompletionSource(address); + + // Setup the callback event handler + DownloadDataCompletedEventHandler handler = null; + handler = (sender, e) => EAPCommon.HandleCompletion(tcs, e, () => e.Result, () => webClient.DownloadDataCompleted -= handler); + webClient.DownloadDataCompleted += handler; + + // Start the async work + try + { + webClient.DownloadDataAsync(address, tcs); + } + catch(Exception exc) + { + // If something goes wrong kicking off the async work, + // unregister the callback and cancel the created task + webClient.DownloadDataCompleted -= handler; + tcs.TrySetException(exc); + } + + // Return the task that represents the async operation + return tcs.Task; + } + + /// Downloads the resource with the specified URI to a local file, asynchronously. + /// The WebClient. + /// The URI from which to download data. + /// The name of the local file that is to receive the data. + /// A Task that contains the downloaded data. + public static Task DownloadFileTask(this WebClient webClient, string address, string fileName) + { + return DownloadFileTask(webClient, new Uri(address), fileName); + } + + /// Downloads the resource with the specified URI to a local file, asynchronously. + /// The WebClient. + /// The URI from which to download data. + /// The name of the local file that is to receive the data. + /// A Task that contains the downloaded data. + public static Task DownloadFileTask(this WebClient webClient, Uri address, string fileName) + { + // Create the task to be returned + var tcs = new TaskCompletionSource(address); + + // Setup the callback event handler + AsyncCompletedEventHandler handler = null; + handler = (sender, e) => EAPCommon.HandleCompletion(tcs, e, () => null, () => webClient.DownloadFileCompleted -= handler); + webClient.DownloadFileCompleted += handler; + + // Start the async work + try + { + webClient.DownloadFileAsync(address, fileName, tcs); + } + catch(Exception exc) + { + // If something goes wrong kicking off the async work, + // unregister the callback and cancel the created task + webClient.DownloadFileCompleted -= handler; + tcs.TrySetException(exc); + } + + // Return the task that represents the async operation + return tcs.Task; + } + + /// Downloads the resource with the specified URI as a string, asynchronously. + /// The WebClient. + /// The URI from which to download data. + /// A Task that contains the downloaded string. + public static Task DownloadStringTask(this WebClient webClient, string address) + { + return DownloadStringTask(webClient, new Uri(address)); + } + + /// Downloads the resource with the specified URI as a string, asynchronously. + /// The WebClient. + /// The URI from which to download data. + /// A Task that contains the downloaded string. + public static Task DownloadStringTask(this WebClient webClient, Uri address) + { + // Create the task to be returned + var tcs = new TaskCompletionSource(address); + + // Setup the callback event handler + DownloadStringCompletedEventHandler handler = null; + handler = (sender, e) => EAPCommon.HandleCompletion(tcs, e, () => e.Result, () => webClient.DownloadStringCompleted -= handler); + webClient.DownloadStringCompleted += handler; + + // Start the async work + try + { + webClient.DownloadStringAsync(address, tcs); + } + catch(Exception exc) + { + // If something goes wrong kicking off the async work, + // unregister the callback and cancel the created task + webClient.DownloadStringCompleted -= handler; + tcs.TrySetException(exc); + } + + // Return the task that represents the async operation + return tcs.Task; + } + + /// Opens a readable stream for the data downloaded from a resource, asynchronously. + /// The WebClient. + /// The URI for which the stream should be opened. + /// A Task that contains the opened stream. + public static Task OpenReadTask(this WebClient webClient, string address) + { + return OpenReadTask(webClient, new Uri(address)); + } + + /// Opens a readable stream for the data downloaded from a resource, asynchronously. + /// The WebClient. + /// The URI for which the stream should be opened. + /// A Task that contains the opened stream. + public static Task OpenReadTask(this WebClient webClient, Uri address) + { + // Create the task to be returned + var tcs = new TaskCompletionSource(address); + + // Setup the callback event handler + OpenReadCompletedEventHandler handler = null; + handler = (sender, e) => EAPCommon.HandleCompletion(tcs, e, () => e.Result, () => webClient.OpenReadCompleted -= handler); + webClient.OpenReadCompleted += handler; + + // Start the async work + try + { + webClient.OpenReadAsync(address, tcs); + } + catch(Exception exc) + { + // If something goes wrong kicking off the async work, + // unregister the callback and cancel the created task + webClient.OpenReadCompleted -= handler; + tcs.TrySetException(exc); + } + + // Return the task that represents the async operation + return tcs.Task; + } + + /// Opens a writeable stream for uploading data to a resource, asynchronously. + /// The WebClient. + /// The URI for which the stream should be opened. + /// The HTTP method that should be used to open the stream. + /// A Task that contains the opened stream. + public static Task OpenWriteTask(this WebClient webClient, string address, string method) + { + return OpenWriteTask(webClient, new Uri(address), method); + } + + /// Opens a writeable stream for uploading data to a resource, asynchronously. + /// The WebClient. + /// The URI for which the stream should be opened. + /// The HTTP method that should be used to open the stream. + /// A Task that contains the opened stream. + public static Task OpenWriteTask(this WebClient webClient, Uri address, string method) + { + // Create the task to be returned + var tcs = new TaskCompletionSource(address); + + // Setup the callback event handler + OpenWriteCompletedEventHandler handler = null; + handler = (sender, e) => EAPCommon.HandleCompletion(tcs, e, () => e.Result, () => webClient.OpenWriteCompleted -= handler); + webClient.OpenWriteCompleted += handler; + + // Start the async work + try + { + webClient.OpenWriteAsync(address, method, tcs); + } + catch(Exception exc) + { + // If something goes wrong kicking off the async work, + // unregister the callback and cancel the created task + webClient.OpenWriteCompleted -= handler; + tcs.TrySetException(exc); + } + + // Return the task that represents the async operation + return tcs.Task; + } + + /// Uploads data to the specified resource, asynchronously. + /// The WebClient. + /// The URI to which the data should be uploaded. + /// The HTTP method that should be used to upload the data. + /// The data to upload. + /// A Task containing the data in the response from the upload. + public static Task UploadDataTask(this WebClient webClient, string address, string method, byte[] data) + { + return UploadDataTask(webClient, new Uri(address), method, data); + } + + /// Uploads data to the specified resource, asynchronously. + /// The WebClient. + /// The URI to which the data should be uploaded. + /// The HTTP method that should be used to upload the data. + /// The data to upload. + /// A Task containing the data in the response from the upload. + public static Task UploadDataTask(this WebClient webClient, Uri address, string method, byte [] data) + { + // Create the task to be returned + var tcs = new TaskCompletionSource(address); + + // Setup the callback event handler + UploadDataCompletedEventHandler handler = null; + handler = (sender, e) => EAPCommon.HandleCompletion(tcs, e, () => e.Result, () => webClient.UploadDataCompleted -= handler); + webClient.UploadDataCompleted += handler; + + // Start the async work + try + { + webClient.UploadDataAsync(address, method, data, tcs); + } + catch(Exception exc) + { + // If something goes wrong kicking off the async work, + // unregister the callback and cancel the created task + webClient.UploadDataCompleted -= handler; + tcs.TrySetException(exc); + } + + // Return the task that represents the async operation + return tcs.Task; + } + + /// Uploads a file to the specified resource, asynchronously. + /// The WebClient. + /// The URI to which the file should be uploaded. + /// The HTTP method that should be used to upload the file. + /// A path to the file to upload. + /// A Task containing the data in the response from the upload. + public static Task UploadFileTask(this WebClient webClient, string address, string method, string fileName) + { + return UploadFileTask(webClient, new Uri(address), method, fileName); + } + + /// Uploads a file to the specified resource, asynchronously. + /// The WebClient. + /// The URI to which the file should be uploaded. + /// The HTTP method that should be used to upload the file. + /// A path to the file to upload. + /// A Task containing the data in the response from the upload. + public static Task UploadFileTask(this WebClient webClient, Uri address, string method, string fileName) + { + // Create the task to be returned + var tcs = new TaskCompletionSource(address); + + // Setup the callback event handler + UploadFileCompletedEventHandler handler = null; + handler = (sender, e) => EAPCommon.HandleCompletion(tcs, e, () => e.Result, () => webClient.UploadFileCompleted -= handler); + webClient.UploadFileCompleted += handler; + + // Start the async work + try + { + webClient.UploadFileAsync(address, method, fileName, tcs); + } + catch(Exception exc) + { + // If something goes wrong kicking off the async work, + // unregister the callback and cancel the created task + webClient.UploadFileCompleted -= handler; + tcs.TrySetException(exc); + } + + // Return the task that represents the async operation + return tcs.Task; + } + + /// Uploads data in a string to the specified resource, asynchronously. + /// The WebClient. + /// The URI to which the data should be uploaded. + /// The HTTP method that should be used to upload the data. + /// The data to upload. + /// A Task containing the data in the response from the upload. + public static Task UploadStringTask(this WebClient webClient, string address, string method, string data) + { + return UploadStringTask(webClient, address, method, data); + } + + /// Uploads data in a string to the specified resource, asynchronously. + /// The WebClient. + /// The URI to which the data should be uploaded. + /// The HTTP method that should be used to upload the data. + /// The data to upload. + /// A Task containing the data in the response from the upload. + public static Task UploadStringTask(this WebClient webClient, Uri address, string method, string data) + { + // Create the task to be returned + var tcs = new TaskCompletionSource(address); + + // Setup the callback event handler + UploadStringCompletedEventHandler handler = null; + handler = (sender, e) => EAPCommon.HandleCompletion(tcs, e, () => e.Result, ()=> webClient.UploadStringCompleted -= handler); + webClient.UploadStringCompleted += handler; + + // Start the async work + try + { + webClient.UploadStringAsync(address, method, data, tcs); + } + catch(Exception exc) + { + // If something goes wrong kicking off the async work, + // unregister the callback and cancel the created task + webClient.UploadStringCompleted -= handler; + tcs.TrySetException(exc); + } + + // Return the task that represents the async operation + return tcs.Task; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/IProducerConsumerCollectionExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/IProducerConsumerCollectionExtensions.cs new file mode 100644 index 0000000..132a8b6 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/IProducerConsumerCollectionExtensions.cs @@ -0,0 +1,114 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: IProducerConsumerCollectionExtensions.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; + +namespace System.Collections.Concurrent +{ + /// Extension methods for IProducerConsumerCollection. + public static class ProducerConsumerCollectionExtensions + { + /// Clears the collection by repeatedly taking elements until it's empty. + /// Specifies the type of the elements in the collection. + /// The collection to be cleared. + public static void Clear(this IProducerConsumerCollection collection) + { + T ignored; + while (collection.TryTake(out ignored)); + } + + /// Creates an enumerable which will consume and return elements from the collection. + /// Specifies the type of the elements in the collection. + /// The collection to be consumed. + /// An enumerable that consumes elements from the collection and returns them. + public static IEnumerable GetConsumingEnumerable( + this IProducerConsumerCollection collection) + { + T item; + while (collection.TryTake(out item)) yield return item; + } + + /// Adds the contents of an enumerable to the collection. + /// Specifies the type of the elements in the collection. + /// The target collection to be augmented. + /// The source enumerable containing the data to be added. + public static void AddFromEnumerable(this IProducerConsumerCollection target, IEnumerable source) + { + foreach (var item in source) target.TryAdd(item); + } + + /// Adds the contents of an observable to the collection. + /// Specifies the type of the elements in the collection. + /// The target collection to be augmented. + /// The source observable containing the data to be added. + /// + /// Whether to mark the target collection as complete for adding when + /// all elements of the source observable have been transfered. + /// + /// An IDisposable that may be used to cancel the transfer. + public static IDisposable AddFromObservable(this IProducerConsumerCollection target, IObservable source) + { + if (target == null) throw new ArgumentNullException("target"); + if (source == null) throw new ArgumentNullException("source"); + return source.Subscribe(new DelegateBasedObserver + ( + onNext: item => target.TryAdd(item), + onError: error => {}, + onCompleted: () => {} + )); + } + + /// Creates an add-only facade for the collection. + /// Specifies the type of the elements in the collection. + /// The collection to be wrapped. + /// + /// An IProducerConsumerCollection that wraps the target collection and supports only add + /// functionality, not take. + /// + public static IProducerConsumerCollection ToProducerOnlyCollection(this IProducerConsumerCollection collection) + { + return new ProduceOrConsumeOnlyCollection(collection, true); + } + + /// Creates a take-only facade for the collection. + /// Specifies the type of the elements in the collection. + /// The collection to be wrapped. + /// + /// An IProducerConsumerCollection that wraps the target collection and supports only take + /// functionality, not add. + /// + public static IProducerConsumerCollection ToConsumerOnlyCollection(this IProducerConsumerCollection collection) + { + return new ProduceOrConsumeOnlyCollection(collection, false); + } + + // Internal wrapper that throws NotSupportedException when mutating methods (add/take) are used from the wrong mode + private sealed class ProduceOrConsumeOnlyCollection : ProducerConsumerCollectionBase + { + private readonly bool _produceOnly; // true for produce-only, false for consume-only + + public ProduceOrConsumeOnlyCollection(IProducerConsumerCollection contained, bool produceOnly) : + base(contained) + { + _produceOnly = produceOnly; + } + + protected override bool TryAdd(T item) + { + if (!_produceOnly) throw new NotSupportedException(); + return base.TryAdd(item); + } + + protected override bool TryTake(out T item) + { + if (_produceOnly) throw new NotSupportedException(); + return base.TryTake(out item); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/LazyExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/LazyExtensions.cs new file mode 100644 index 0000000..754a2cc --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/LazyExtensions.cs @@ -0,0 +1,45 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: LazyExtensions.cs +// +//-------------------------------------------------------------------------- + +using System.Threading; +using System.Threading.Tasks; + +namespace System +{ + /// Extension methods for Lazy. + public static class LazyExtensions + { + /// Forces value creation of a Lazy instance. + /// Specifies the type of the value being lazily initialized. + /// The Lazy instance. + /// The initialized Lazy instance. + public static Lazy Force(this Lazy lazy) + { + var ignored = lazy.Value; + return lazy; + } + + /// Retrieves the value of a Lazy asynchronously. + /// Specifies the type of the value being lazily initialized. + /// The Lazy instance. + /// A Task representing the Lazy's value. + public static Task GetValueAsync(this Lazy lazy) + { + return Task.Factory.StartNew(() => lazy.Value); + } + + /// Creates a Lazy that's already been initialized to a specified value. + /// The type of the data to be initialized. + /// The value with which to initialize the Lazy instance. + /// The initialized Lazy. + public static Lazy Create(T value) + { + return new Lazy(() => value, false).Force(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/LinqToTasks.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/LinqToTasks.cs new file mode 100644 index 0000000..1019bf2 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/LinqToTasks.cs @@ -0,0 +1,238 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: LinqToTasks.cs +// +//-------------------------------------------------------------------------- + +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace System.Linq +{ + /// + /// Provides LINQ support for Tasks by implementing the primary standard query operators. + /// + public static class LinqToTasks + { + public static Task Select(this Task source, Func selector) + { + // Validate arguments + if (source == null) throw new ArgumentNullException("source"); + if (selector == null) throw new ArgumentNullException("selector"); + + // Use a continuation to run the selector function + return source.ContinueWith(t => selector(t.Result), TaskContinuationOptions.NotOnCanceled); + } + + public static Task SelectMany(this Task source, Func> selector) + { + // Validate arguments + if (source == null) throw new ArgumentNullException("source"); + if (selector == null) throw new ArgumentNullException("selector"); + + // Use a continuation to run the selector function. + return source.ContinueWith(t => selector(t.Result), TaskContinuationOptions.NotOnCanceled).Unwrap(); + } + + public static Task SelectMany( + this Task source, + Func> collectionSelector, + Func resultSelector) + { + // Validate arguments + if (source == null) throw new ArgumentNullException("source"); + if (collectionSelector == null) throw new ArgumentNullException("collectionSelector"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + + // When the source completes, run the collectionSelector to get the next Task, + // and continue off of it to run the result selector + return source.ContinueWith(t => + { + return collectionSelector(t.Result). + ContinueWith(c => resultSelector(t.Result, c.Result), TaskContinuationOptions.NotOnCanceled); + }, TaskContinuationOptions.NotOnCanceled).Unwrap(); + } + + public static Task Where(this Task source, Func predicate) + { + // Validate arguments + if (source == null) throw new ArgumentNullException("source"); + if (predicate == null) throw new ArgumentNullException("predicate"); + + // Create a continuation to run the predicate and return the source's result. + // If the predicate returns false, cancel the returned Task. + var cts = new CancellationTokenSource(); + return source.ContinueWith(t => + { + var result = t.Result; + if (!predicate(result)) cts.CancelAndThrow(); + return result; + }, cts.Token, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default); + } + + public static Task Join( + this Task outer, Task inner, + Func outerKeySelector, + Func innerKeySelector, + Func resultSelector) + { + // Argument validation handled by delegated method call + return Join(outer, inner, outerKeySelector, innerKeySelector, resultSelector, EqualityComparer.Default); + } + + public static Task Join( + this Task outer, Task inner, + Func outerKeySelector, + Func innerKeySelector, + Func resultSelector, + IEqualityComparer comparer) + { + // Validate arguments + if (outer == null) throw new ArgumentNullException("outer"); + if (inner == null) throw new ArgumentNullException("inner"); + if (outerKeySelector == null) throw new ArgumentNullException("outerKeySelector"); + if (innerKeySelector == null) throw new ArgumentNullException("innerKeySelector"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + if (comparer == null) throw new ArgumentNullException("comparer"); + + // First continue off of the outer and then off of the inner. Two separate + // continuations are used so that each may be canceled easily using the NotOnCanceled option. + return outer.ContinueWith(delegate + { + var cts = new CancellationTokenSource(); + return inner.ContinueWith(delegate + { + // Propagate all exceptions + Task.WaitAll(outer, inner); + + // Both completed successfully, so if their keys are equal, return the result + if (comparer.Equals(outerKeySelector(outer.Result), innerKeySelector(inner.Result))) + { + return resultSelector(outer.Result, inner.Result); + } + // Otherwise, cancel this task. + else + { + cts.CancelAndThrow(); + return default(TResult); // won't be reached + } + }, cts.Token, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default); + }, TaskContinuationOptions.NotOnCanceled).Unwrap(); + } + + public static Task GroupJoin( + this Task outer, Task inner, + Func outerKeySelector, + Func innerKeySelector, + Func, TResult> resultSelector) + { + // Argument validation handled by delegated method call + return GroupJoin(outer, inner, outerKeySelector, innerKeySelector, resultSelector, EqualityComparer.Default); + } + + public static Task GroupJoin( + this Task outer, Task inner, + Func outerKeySelector, + Func innerKeySelector, + Func, TResult> resultSelector, + IEqualityComparer comparer) + { + // Validate arguments + if (outer == null) throw new ArgumentNullException("outer"); + if (inner == null) throw new ArgumentNullException("inner"); + if (outerKeySelector == null) throw new ArgumentNullException("outerKeySelector"); + if (innerKeySelector == null) throw new ArgumentNullException("innerKeySelector"); + if (resultSelector == null) throw new ArgumentNullException("resultSelector"); + if (comparer == null) throw new ArgumentNullException("comparer"); + + // First continue off of the outer and then off of the inner. Two separate + // continuations are used so that each may be canceled easily using the NotOnCanceled option. + return outer.ContinueWith(delegate + { + var cts = new CancellationTokenSource(); + return inner.ContinueWith(delegate + { + // Propagate all exceptions + Task.WaitAll(outer, inner); + + // Both completed successfully, so if their keys are equal, return the result + if (comparer.Equals(outerKeySelector(outer.Result), innerKeySelector(inner.Result))) + { + return resultSelector(outer.Result, inner); + } + // Otherwise, cancel this task. + else + { + cts.CancelAndThrow(); + return default(TResult); // won't be reached + } + }, cts.Token, TaskContinuationOptions.NotOnCanceled, TaskScheduler.Default); + }, TaskContinuationOptions.NotOnCanceled).Unwrap(); + } + + public static Task> GroupBy( + this Task source, Func keySelector, Func elementSelector) + { + // Validate arguments + if (source == null) throw new ArgumentNullException("source"); + if (keySelector == null) throw new ArgumentNullException("keySelector"); + if (elementSelector == null) throw new ArgumentNullException("elementSelector"); + + // When the source completes, return a grouping of just the one element + return source.ContinueWith(t => + { + var result = t.Result; + var key = keySelector(result); + var element = elementSelector(result); + return (IGrouping)new OneElementGrouping { Key = key, Element = element }; + }, TaskContinuationOptions.NotOnCanceled); + } + + /// Represents a grouping of one element. + /// The type of the key for the element. + /// The type of the element. + private class OneElementGrouping : IGrouping + { + public TKey Key { get; internal set; } + internal TElement Element { get; set; } + public IEnumerator GetEnumerator() { yield return Element; } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + } + + public static Task OrderBy(this Task source, Func keySelector) + { + // A single item is already in sorted order, no matter what the key selector is, so just + // return the original. + if (source == null) throw new ArgumentNullException("source"); + return source; + } + + public static Task OrderByDescending(this Task source, Func keySelector) + { + // A single item is already in sorted order, no matter what the key selector is, so just + // return the original. + if (source == null) throw new ArgumentNullException("source"); + return source; + } + + public static Task ThenBy(this Task source, Func keySelector) + { + // A single item is already in sorted order, no matter what the key selector is, so just + // return the original. + if (source == null) throw new ArgumentNullException("source"); + return source; + } + + public static Task ThenByDescending(this Task source, Func keySelector) + { + // A single item is already in sorted order, no matter what the key selector is, so just + // return the original. + if (source == null) throw new ArgumentNullException("source"); + return source; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/ParallelLinqOptions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/ParallelLinqOptions.cs new file mode 100644 index 0000000..fd8c36c --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/ParallelLinqOptions.cs @@ -0,0 +1,54 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelLinqOptions.cs +// +//-------------------------------------------------------------------------- + +using System.Threading; +using System.Threading.Tasks; + +namespace System.Linq +{ + /// Provides a grouping for common Parallel LINQ options. + public sealed class ParallelLinqOptions : ParallelOptions + { + private ParallelExecutionMode _executionMode = ParallelExecutionMode.Default; + private ParallelMergeOptions _mergeOptions = ParallelMergeOptions.Default; + private bool _ordered = false; + + /// Gets or sets the execution mode. + public ParallelExecutionMode ExecutionMode + { + get { return _executionMode; } + set + { + if (value != ParallelExecutionMode.Default && + value != ParallelExecutionMode.ForceParallelism) throw new ArgumentOutOfRangeException("ExecutionMode"); + _executionMode = value; + } + } + + /// Gets or sets the merge options. + public ParallelMergeOptions MergeOptions + { + get { return _mergeOptions; } + set + { + if (value != ParallelMergeOptions.AutoBuffered && + value != ParallelMergeOptions.Default && + value != ParallelMergeOptions.FullyBuffered && + value != ParallelMergeOptions.NotBuffered) throw new ArgumentOutOfRangeException("MergeOptions"); + _mergeOptions = value; + } + } + + /// Gets or sets whether the query should retain ordering. + public bool Ordered + { + get { return _ordered; } + set { _ordered = value; } + } + } +} diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/ParallelOptionsExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/ParallelOptionsExtensions.cs new file mode 100644 index 0000000..c0cab6b --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/ParallelOptionsExtensions.cs @@ -0,0 +1,27 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelOptionsExtensions.cs +// +//-------------------------------------------------------------------------- + +namespace System.Threading.Tasks +{ + /// Extension methods for ParallelOptions. + public static class ParallelOptionsExtensions + { + /// Copies a ParallelOptions instance to a shallow clone. + /// The options to be cloned. + /// The shallow clone. + public static ParallelOptions ShallowClone(this ParallelOptions options) + { + return new ParallelOptions() + { + CancellationToken = options.CancellationToken, + MaxDegreeOfParallelism = options.MaxDegreeOfParallelism, + TaskScheduler = options.TaskScheduler + }; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/PlinqExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/PlinqExtensions.cs new file mode 100644 index 0000000..82437ea --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/PlinqExtensions.cs @@ -0,0 +1,164 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: PlinqExtensions.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace System.Linq +{ + /// Extension methods for Parallel LINQ. + public static class ParallelLinqExtensions + { + /// Takes the top elements as if they were sorted. + /// Specifies the type of the elements. + /// Specifies the type of the keys used to compare elements. + /// The source elements. + /// A function used to extract a key from each element. + /// The number of elements to take. + /// + public static IEnumerable TakeTop(this ParallelQuery source, + Func keySelector, + int count) + { + // We want to sort in descending order, so we need the opposite of the default comparer + var comparer = new DescendingDefaultComparer(); + + // Aggregate, using a sorted list per thread to keep track of the best N elements, + // then merge those at the end. + return source.Aggregate( + () => new SortedTopN(count, comparer), + + (accum, item) => + { + accum.Add(keySelector(item), item); + return accum; + }, + + (accum1, accum2) => + { + foreach (var item in accum2) accum1.Add(item); + return accum1; + }, + + (accum) => accum.Values); + } + + /// A comparer that comparers using the inverse of the default comparer. + /// Specifies the type being compared. + private class DescendingDefaultComparer : IComparer + { + private static Comparer _defaultComparer = Comparer.Default; + public int Compare(T x, T y) { return _defaultComparer.Compare(y, x); } + } + + /// Implements a map-reduce operation. + /// Specifies the type of the source elements. + /// Specifies the type of the mapped elements. + /// Specifies the type of the element keys. + /// Specifies the type of the results. + /// The source elements. + /// A function used to get the target data from a source element. + /// A function used to get a key from the target data. + /// A function used to reduce a group of elements. + /// The result elements of the reductions. + public static ParallelQuery MapReduce( + this ParallelQuery source, + Func map, + Func keySelector, + Func, TResult> reduce) + { + return source. + Select(map). + GroupBy(keySelector). + Select(reduce); + } + + /// Implements a map-reduce operation. + /// Specifies the type of the source elements. + /// Specifies the type of the mapped elements. + /// Specifies the type of the element keys. + /// Specifies the type of the results. + /// The source elements. + /// A function used to get an enumerable of target data from a source element. + /// A function used to get a key from target data. + /// A function used to reduce a group of elements to an enumerable of results. + /// The result elements of the reductions. + public static ParallelQuery MapReduce( + this ParallelQuery source, + Func> map, + Func keySelector, + Func, IEnumerable> reduce) + { + return source. + SelectMany(map). + GroupBy(keySelector). + SelectMany(reduce); + } + + /// Runs the query and outputs its results into the target collection. + /// Specifies the type of elements output from the query. + /// The source query. + /// The target collection. + public static void OutputToProducerConsumerCollection( + this ParallelQuery source, + IProducerConsumerCollection target) + { + // Validate arguments + if (source == null) throw new ArgumentNullException("source"); + if (target == null) throw new ArgumentNullException("target"); + + // Store all results into the collection + source.ForAll(item => target.TryAdd(item)); + } + + /// This is the method to opt into Parallel LINQ. + /// Specifies the type of elements provided to the query. + /// The source query. + /// The options to use for query processing. + /// The source as a ParallelQuery to bind to ParallelEnumerable extension methods. + public static ParallelQuery AsParallel( + this IEnumerable source, + ParallelLinqOptions parallelOptions) + { + if (source == null) throw new ArgumentNullException("source"); + + // Validate unsupported options + if (parallelOptions.TaskScheduler != null && parallelOptions.TaskScheduler != TaskScheduler.Default) + { + throw new ArgumentException("Parallel LINQ only supports the default TaskScheduler."); + } + + // First convert to PLINQ + var result = source.AsParallel(); + + // Then apply all of the options as requested... + if (parallelOptions.Ordered) + { + result = result.AsOrdered(); + } + if (parallelOptions.CancellationToken.CanBeCanceled) + { + result = result.WithCancellation(parallelOptions.CancellationToken); + } + if (parallelOptions.MaxDegreeOfParallelism >= 1) + { + result = result.WithDegreeOfParallelism(parallelOptions.MaxDegreeOfParallelism); + } + if (parallelOptions.ExecutionMode != ParallelExecutionMode.Default) + { + result = result.WithExecutionMode(parallelOptions.ExecutionMode); + } + if (parallelOptions.MergeOptions != ParallelMergeOptions.Default) + { + result = result.WithMergeOptions(parallelOptions.MergeOptions); + } + return result; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskCompletionSourceExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskCompletionSourceExtensions.cs new file mode 100644 index 0000000..82bc1e8 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskCompletionSourceExtensions.cs @@ -0,0 +1,64 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TaskCompletionSourceExtensions.cs +// +//-------------------------------------------------------------------------- + +namespace System.Threading.Tasks +{ + /// Extension methods for TaskCompletionSource. + public static class TaskCompletionSourceExtensions + { + /// Transfers the result of a Task to the TaskCompletionSource. + /// Specifies the type of the result. + /// The TaskCompletionSource. + /// The task whose completion results should be transfered. + public static void SetFromTask(this TaskCompletionSource resultSetter, Task task) + { + switch (task.Status) + { + case TaskStatus.RanToCompletion: resultSetter.SetResult(task is Task ? ((Task)task).Result : default(TResult)); break; + case TaskStatus.Faulted: resultSetter.SetException(task.Exception.InnerExceptions); break; + case TaskStatus.Canceled: resultSetter.SetCanceled(); break; + default: throw new InvalidOperationException("The task was not completed."); + } + } + + /// Transfers the result of a Task to the TaskCompletionSource. + /// Specifies the type of the result. + /// The TaskCompletionSource. + /// The task whose completion results should be transfered. + public static void SetFromTask(this TaskCompletionSource resultSetter, Task task) + { + SetFromTask(resultSetter, (Task)task); + } + + /// Attempts to transfer the result of a Task to the TaskCompletionSource. + /// Specifies the type of the result. + /// The TaskCompletionSource. + /// The task whose completion results should be transfered. + /// Whether the transfer could be completed. + public static bool TrySetFromTask(this TaskCompletionSource resultSetter, Task task) + { + switch (task.Status) + { + case TaskStatus.RanToCompletion: return resultSetter.TrySetResult(task is Task ? ((Task)task).Result : default(TResult)); + case TaskStatus.Faulted: return resultSetter.TrySetException(task.Exception.InnerExceptions); + case TaskStatus.Canceled: return resultSetter.TrySetCanceled(); + default: throw new InvalidOperationException("The task was not completed."); + } + } + + /// Attempts to transfer the result of a Task to the TaskCompletionSource. + /// Specifies the type of the result. + /// The TaskCompletionSource. + /// The task whose completion results should be transfered. + /// Whether the transfer could be completed. + public static bool TrySetFromTask(this TaskCompletionSource resultSetter, Task task) + { + return TrySetFromTask(resultSetter, (Task)task); + } + } +} diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskExtrasExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskExtrasExtensions.cs new file mode 100644 index 0000000..767e61c --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskExtrasExtensions.cs @@ -0,0 +1,304 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TaskExtensions.cs +// +//-------------------------------------------------------------------------- + +using System.Linq; +using System.Windows.Threading; + +namespace System.Threading.Tasks +{ + /// Extensions methods for Task. + public static class TaskExtrasExtensions + { + #region ContinueWith accepting TaskFactory + /// Creates a continuation task using the specified TaskFactory. + /// The antecedent Task. + /// The continuation action. + /// The TaskFactory. + /// A continuation task. + public static Task ContinueWith( + this Task task, Action continuationAction, TaskFactory factory) + { + return task.ContinueWith(continuationAction, factory.CancellationToken, factory.ContinuationOptions, factory.Scheduler); + } + + /// Creates a continuation task using the specified TaskFactory. + /// The antecedent Task. + /// The continuation function. + /// The TaskFactory. + /// A continuation task. + public static Task ContinueWith( + this Task task, Func continuationFunction, TaskFactory factory) + { + return task.ContinueWith(continuationFunction, factory.CancellationToken, factory.ContinuationOptions, factory.Scheduler); + } + #endregion + + #region ContinueWith accepting TaskFactory + /// Creates a continuation task using the specified TaskFactory. + /// The antecedent Task. + /// The continuation action. + /// The TaskFactory. + /// A continuation task. + public static Task ContinueWith( + this Task task, Action> continuationAction, TaskFactory factory) + { + return task.ContinueWith(continuationAction, factory.CancellationToken, factory.ContinuationOptions, factory.Scheduler); + } + + /// Creates a continuation task using the specified TaskFactory. + /// The antecedent Task. + /// The continuation function. + /// The TaskFactory. + /// A continuation task. + public static Task ContinueWith( + this Task task, Func, TNewResult> continuationFunction, TaskFactory factory) + { + return task.ContinueWith(continuationFunction, factory.CancellationToken, factory.ContinuationOptions, factory.Scheduler); + } + #endregion + + #region ToAsync(AsyncCallback, object) + /// + /// Creates a Task that represents the completion of another Task, and + /// that schedules an AsyncCallback to run upon completion. + /// + /// The antecedent Task. + /// The AsyncCallback to run. + /// The object state to use with the AsyncCallback. + /// The new task. + public static Task ToAsync(this Task task, AsyncCallback callback, object state) + { + if (task == null) throw new ArgumentNullException("task"); + + var tcs = new TaskCompletionSource(state); + task.ContinueWith(_ => + { + tcs.SetFromTask(task); + if (callback != null) callback(tcs.Task); + }); + return tcs.Task; + } + + /// + /// Creates a Task that represents the completion of another Task, and + /// that schedules an AsyncCallback to run upon completion. + /// + /// The antecedent Task. + /// The AsyncCallback to run. + /// The object state to use with the AsyncCallback. + /// The new task. + public static Task ToAsync(this Task task, AsyncCallback callback, object state) + { + if (task == null) throw new ArgumentNullException("task"); + + var tcs = new TaskCompletionSource(state); + task.ContinueWith(_ => + { + tcs.SetFromTask(task); + if (callback != null) callback(tcs.Task); + }); + return tcs.Task; + } + #endregion + + #region Exception Handling + /// Suppresses default exception handling of a Task that would otherwise reraise the exception on the finalizer thread. + /// The Task to be monitored. + /// The original Task. + public static Task IgnoreExceptions(this Task task) + { + task.ContinueWith(t => { var ignored = t.Exception; }, + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted, + TaskScheduler.Default); + return task; + } + + /// Suppresses default exception handling of a Task that would otherwise reraise the exception on the finalizer thread. + /// The Task to be monitored. + /// The original Task. + public static Task IgnoreExceptions(this Task task) + { + return (Task)((Task)task).IgnoreExceptions(); + } + + /// Fails immediately when an exception is encountered. + /// The Task to be monitored. + /// The original Task. + public static Task FailFastOnException(this Task task) + { + task.ContinueWith(t => Environment.FailFast("A task faulted.", t.Exception), + CancellationToken.None, + TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnFaulted, + TaskScheduler.Default); + return task; + } + + /// Fails immediately when an exception is encountered. + /// The Task to be monitored. + /// The original Task. + public static Task FailFastOnException(this Task task) + { + return (Task)((Task)task).FailFastOnException(); + } + + /// Propagates any exceptions that occurred on the specified task. + /// The Task whose exceptions are to be propagated. + public static void PropagateExceptions(this Task task) + { + if (!task.IsCompleted) throw new InvalidOperationException("The task has not completed."); + if (task.IsFaulted) task.Wait(); + } + + /// Propagates any exceptions that occurred on the specified tasks. + /// The Tassk whose exceptions are to be propagated. + public static void PropagateExceptions(this Task [] tasks) + { + if (tasks == null) throw new ArgumentNullException("tasks"); + if (tasks.Any(t => t == null)) throw new ArgumentException("tasks"); + if (tasks.Any(t => !t.IsCompleted)) throw new InvalidOperationException("A task has not completed."); + Task.WaitAll(tasks); + } + #endregion + + #region Observables + /// Creates an IObservable that represents the completion of a Task. + /// Specifies the type of data returned by the Task. + /// The Task to be represented as an IObservable. + /// An IObservable that represents the completion of the Task. + public static IObservable ToObservable(this Task task) + { + if (task == null) throw new ArgumentNullException("task"); + return new TaskObservable { _task = task }; + } + + /// An implementation of IObservable that wraps a Task. + /// The type of data returned by the task. + private class TaskObservable : IObservable + { + internal Task _task; + + public IDisposable Subscribe(IObserver observer) + { + // Validate arguments + if (observer == null) throw new ArgumentNullException("observer"); + + // Support cancelling the continuation if the observer is unsubscribed + var cts = new CancellationTokenSource(); + + // Create a continuation to pass data along to the observer + _task.ContinueWith(t => + { + switch (t.Status) + { + case TaskStatus.RanToCompletion: + observer.OnNext(_task.Result); + observer.OnCompleted(); + break; + + case TaskStatus.Faulted: + observer.OnError(_task.Exception); + break; + + case TaskStatus.Canceled: + observer.OnError(new TaskCanceledException(t)); + break; + } + }, cts.Token); + + // Support unsubscribe simply by canceling the continuation if it hasn't yet run + return new CancelOnDispose { Source = cts }; + } + } + + /// Translate a call to IDisposable.Dispose to a CancellationTokenSource.Cancel. + private class CancelOnDispose : IDisposable + { + internal CancellationTokenSource Source; + void IDisposable.Dispose() { Source.Cancel(); } + } + #endregion + + #region Timeouts + /// Creates a new Task that mirrors the supplied task but that will be canceled after the specified timeout. + /// Specifies the type of data contained in the task. + /// The task. + /// The timeout. + /// The new Task that may time out. + public static Task WithTimeout(this Task task, TimeSpan timeout) + { + var result = new TaskCompletionSource(task.AsyncState); + var timer = new Timer(state => ((TaskCompletionSource)state).TrySetCanceled(), result, timeout, TimeSpan.FromMilliseconds(-1)); + task.ContinueWith(t => + { + timer.Dispose(); + result.TrySetFromTask(t); + }, TaskContinuationOptions.ExecuteSynchronously); + return result.Task; + } + + /// Creates a new Task that mirrors the supplied task but that will be canceled after the specified timeout. + /// Specifies the type of data contained in the task. + /// The task. + /// The timeout. + /// The new Task that may time out. + public static Task WithTimeout(this Task task, TimeSpan timeout) + { + var result = new TaskCompletionSource(task.AsyncState); + var timer = new Timer(state => ((TaskCompletionSource)state).TrySetCanceled(), result, timeout, TimeSpan.FromMilliseconds(-1)); + task.ContinueWith(t => + { + timer.Dispose(); + result.TrySetFromTask(t); + }, TaskContinuationOptions.ExecuteSynchronously); + return result.Task; + } + #endregion + + #region Children + /// + /// Ensures that a parent task can't transition into a completed state + /// until the specified task has also completed, even if it's not + /// already a child task. + /// + /// The task to attach to the current task as a child. + public static void AttachToParent(this Task task) + { + if (task == null) throw new ArgumentNullException("task"); + task.ContinueWith(t => t.Wait(), CancellationToken.None, + TaskContinuationOptions.AttachedToParent | + TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + } + #endregion + + #region Waiting + /// Waits for the task to complete execution, pumping in the meantime. + /// The task for which to wait. + /// This method is intended for usage with Windows Presentation Foundation. + public static void WaitWithPumping(this Task task) + { + if (task == null) throw new ArgumentNullException("task"); + var nestedFrame = new DispatcherFrame(); + task.ContinueWith(_ => nestedFrame.Continue = false); + Dispatcher.PushFrame(nestedFrame); + task.Wait(); + } + + /// Waits for the task to complete execution, returning the task's final status. + /// The task for which to wait. + /// The completion status of the task. + /// Unlike Wait, this method will not throw an exception if the task ends in the Faulted or Canceled state. + public static TaskStatus WaitForCompletionStatus(this Task task) + { + if (task == null) throw new ArgumentNullException("task"); + ((IAsyncResult)task).AsyncWaitHandle.WaitOne(); + return task.Status; + } + #endregion + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_Common.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_Common.cs new file mode 100644 index 0000000..543a83c --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_Common.cs @@ -0,0 +1,59 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TaskFactoryExtensions_Common.cs +// +//-------------------------------------------------------------------------- + +namespace System.Threading.Tasks +{ + /// Extensions for TaskFactory. + public static partial class TaskFactoryExtensions + { + /// Creates a generic TaskFactory from a non-generic one. + /// Specifies the type of Task results for the Tasks created by the new TaskFactory. + /// The TaskFactory to serve as a template. + /// The created TaskFactory. + public static TaskFactory ToGeneric(this TaskFactory factory) + { + return new TaskFactory( + factory.CancellationToken, factory.CreationOptions, factory.ContinuationOptions, factory.Scheduler); + } + + /// Creates a generic TaskFactory from a non-generic one. + /// Specifies the type of Task results for the Tasks created by the new TaskFactory. + /// The TaskFactory to serve as a template. + /// The created TaskFactory. + public static TaskFactory ToNonGeneric(this TaskFactory factory) + { + return new TaskFactory( + factory.CancellationToken, factory.CreationOptions, factory.ContinuationOptions, factory.Scheduler); + } + + /// Gets the TaskScheduler instance that should be used to schedule tasks. + public static TaskScheduler GetTargetScheduler(this TaskFactory factory) + { + if (factory == null) throw new ArgumentNullException("factory"); + return factory.Scheduler ?? TaskScheduler.Current; + } + + /// Gets the TaskScheduler instance that should be used to schedule tasks. + public static TaskScheduler GetTargetScheduler(this TaskFactory factory) + { + if (factory == null) throw new ArgumentNullException("factory"); + return factory.Scheduler != null ? factory.Scheduler : TaskScheduler.Current; + } + + /// Converts TaskCreationOptions into TaskContinuationOptions. + /// + /// + private static TaskContinuationOptions ContinuationOptionsFromCreationOptions(TaskCreationOptions creationOptions) + { + return (TaskContinuationOptions) + ((creationOptions & TaskCreationOptions.AttachedToParent) | + (creationOptions & TaskCreationOptions.PreferFairness) | + (creationOptions & TaskCreationOptions.LongRunning)); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_ContinueWhenAllAny.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_ContinueWhenAllAny.cs new file mode 100644 index 0000000..df8cafd --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_ContinueWhenAllAny.cs @@ -0,0 +1,67 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TaskFactoryExtensions_ContinueWhenAllAny.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; + +namespace System.Threading.Tasks +{ + public static partial class TaskFactoryExtensions + { + /// + /// Creates a continuation Task that will compplete upon + /// the completion of a set of provided Tasks. + /// + /// The TaskFactory to use to create the continuation task. + /// The array of tasks from which to continue. + /// A task that, when completed, will return the array of completed tasks. + public static Task WhenAll( + this TaskFactory factory, params Task[] tasks) + { + return factory.ContinueWhenAll(tasks, completedTasks => completedTasks); + } + + /// + /// Creates a continuation Task that will compplete upon + /// the completion of a set of provided Tasks. + /// + /// The TaskFactory to use to create the continuation task. + /// The array of tasks from which to continue. + /// A task that, when completed, will return the array of completed tasks. + public static Task[]> WhenAll( + this TaskFactory factory, params Task[] tasks) + { + return factory.ContinueWhenAll(tasks, completedTasks => completedTasks); + } + + /// + /// Creates a continuation Task that will complete upon + /// the completion of any one of a set of provided Tasks. + /// + /// The TaskFactory to use to create the continuation task. + /// The array of tasks from which to continue. + /// A task that, when completed, will return the completed task. + public static Task WhenAny( + this TaskFactory factory, params Task[] tasks) + { + return factory.ContinueWhenAny(tasks, completedTask => completedTask); + } + + /// + /// Creates a continuation Task that will complete upon + /// the completion of any one of a set of provided Tasks. + /// + /// The TaskFactory to use to create the continuation task. + /// The array of tasks from which to continue. + /// A task that, when completed, will return the completed task. + public static Task> WhenAny( + this TaskFactory factory, params Task[] tasks) + { + return factory.ContinueWhenAny(tasks, completedTask => completedTask); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_Create.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_Create.cs new file mode 100644 index 0000000..cc8cc46 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_Create.cs @@ -0,0 +1,157 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TaskFactoryExtensions_Create.cs +// +//-------------------------------------------------------------------------- + +namespace System.Threading.Tasks +{ + public static partial class TaskFactoryExtensions + { + #region TaskFactory with Action + /// Creates a Task using the TaskFactory. + /// The factory to use. + /// The delegate for the task. + /// The created task. The task has not been scheduled. + public static Task Create( + this TaskFactory factory, Action action) + { + if (factory == null) throw new ArgumentNullException("factory"); + return new Task(action, factory.CancellationToken, factory.CreationOptions); + } + + /// Creates a Task using the TaskFactory. + /// The factory to use. + /// The delegate for the task. + /// Options that control the task's behavior. + /// The created task. The task has not been scheduled. + public static Task Create( + this TaskFactory factory, Action action, TaskCreationOptions creationOptions) + { + return new Task(action, factory.CancellationToken, creationOptions); + } + + /// Creates a Task using the TaskFactory. + /// The factory to use. + /// The delegate for the task. + /// An object provided to the delegate. + /// The created task. The task has not been scheduled. + public static Task Create( + this TaskFactory factory, Action action, object state) + { + if (factory == null) throw new ArgumentNullException("factory"); + return new Task(action, state, factory.CancellationToken, factory.CreationOptions); + } + + /// Creates a Task using the TaskFactory. + /// The factory to use. + /// The delegate for the task. + /// An object provided to the delegate. + /// Options that control the task's behavior. + /// The created task. The task has not been scheduled. + public static Task Create( + this TaskFactory factory, Action action, object state, TaskCreationOptions creationOptions) + { + return new Task(action, state, factory.CancellationToken, creationOptions); + } + #endregion + + #region TaskFactory with Func + /// Creates a Task using the TaskFactory. + /// The factory to use. + /// The delegate for the task. + /// The created task. The task has not been scheduled. + public static Task Create( + this TaskFactory factory, Func function) + { + if (factory == null) throw new ArgumentNullException("factory"); + return new Task(function, factory.CancellationToken, factory.CreationOptions); + } + + /// Creates a Task using the TaskFactory. + /// The factory to use. + /// The delegate for the task. + /// Options that control the task's behavior. + /// The created task. The task has not been scheduled. + public static Task Create( + this TaskFactory factory, Func function, TaskCreationOptions creationOptions) + { + return new Task(function, factory.CancellationToken, creationOptions); + } + + /// Creates a Task using the TaskFactory. + /// The factory to use. + /// The delegate for the task. + /// An object provided to the delegate. + /// The created task. The task has not been scheduled. + public static Task Create( + this TaskFactory factory, Func function, object state) + { + if (factory == null) throw new ArgumentNullException("factory"); + return new Task(function, state, factory.CancellationToken, factory.CreationOptions); + } + + /// Creates a Task using the TaskFactory. + /// The factory to use. + /// The delegate for the task. + /// An object provided to the delegate. + /// Options that control the task's behavior. + /// The created task. The task has not been scheduled. + public static Task Create( + this TaskFactory factory, Func function, object state, TaskCreationOptions creationOptions) + { + return new Task(function, state, factory.CancellationToken, creationOptions); + } + #endregion + + #region TaskFactory with Func + /// Creates a Task using the TaskFactory. + /// The factory to use. + /// The delegate for the task. + /// The created task. The task has not been scheduled. + public static Task Create( + this TaskFactory factory, Func function) + { + if (factory == null) throw new ArgumentNullException("factory"); + return new Task(function, factory.CancellationToken, factory.CreationOptions); + } + + /// Creates a Task using the TaskFactory. + /// The factory to use. + /// The delegate for the task. + /// Options that control the task's behavior. + /// The created task. The task has not been scheduled. + public static Task Create( + this TaskFactory factory, Func function, TaskCreationOptions creationOptions) + { + return new Task(function, factory.CancellationToken, creationOptions); + } + + /// Creates a Task using the TaskFactory. + /// The factory to use. + /// The delegate for the task. + /// An object provided to the delegate. + /// The created task. The task has not been scheduled. + public static Task Create( + this TaskFactory factory, Func function, object state) + { + if (factory == null) throw new ArgumentNullException("factory"); + return new Task(function, state, factory.CancellationToken, factory.CreationOptions); + } + + /// Creates a Task using the TaskFactory. + /// The factory to use. + /// The delegate for the task. + /// An object provided to the delegate. + /// Options that control the task's behavior. + /// The created task. The task has not been scheduled. + public static Task Create( + this TaskFactory factory, Func function, object state, TaskCreationOptions creationOptions) + { + return new Task(function, state, factory.CancellationToken, creationOptions); + } + #endregion + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_Delayed.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_Delayed.cs new file mode 100644 index 0000000..ad80a12 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_Delayed.cs @@ -0,0 +1,386 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TaskFactoryExtensions_Delayed.cs +// +//-------------------------------------------------------------------------- + +namespace System.Threading.Tasks +{ + public static partial class TaskFactoryExtensions + { + #region TaskFactory No Action + /// Creates a Task that will complete after the specified delay. + /// The TaskFactory. + /// The delay after which the Task should transition to RanToCompletion. + /// A Task that will be completed after the specified duration. + public static Task StartNewDelayed( + this TaskFactory factory, int millisecondsDelay) + { + return StartNewDelayed(factory, millisecondsDelay, CancellationToken.None); + } + + /// Creates a Task that will complete after the specified delay. + /// The TaskFactory. + /// The delay after which the Task should transition to RanToCompletion. + /// The cancellation token that can be used to cancel the timed task. + /// A Task that will be completed after the specified duration and that's cancelable with the specified token. + public static Task StartNewDelayed(this TaskFactory factory, int millisecondsDelay, CancellationToken cancellationToken) + { + // Validate arguments + if (factory == null) throw new ArgumentNullException("factory"); + if (millisecondsDelay < 0) throw new ArgumentOutOfRangeException("millisecondsDelay"); + + // Create the timed task + var tcs = new TaskCompletionSource(factory.CreationOptions); + var ctr = default(CancellationTokenRegistration); + + // Create the timer but don't start it yet. If we start it now, + // it might fire before ctr has been set to the right registration. + var timer = new Timer(self => + { + // Clean up both the cancellation token and the timer, and try to transition to completed + ctr.Dispose(); + ((Timer)self).Dispose(); + tcs.TrySetResult(null); + }); + + // Register with the cancellation token. + if (cancellationToken.CanBeCanceled) + { + // When cancellation occurs, cancel the timer and try to transition to canceled. + // There could be a race, but it's benign. + ctr = cancellationToken.Register(() => + { + timer.Dispose(); + tcs.TrySetCanceled(); + }); + } + + // Start the timer and hand back the task... + timer.Change(millisecondsDelay, Timeout.Infinite); + return tcs.Task; + } + #endregion + + #region TaskFactory with Action + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Action action) + { + if (factory == null) throw new ArgumentNullException("factory"); + return StartNewDelayed(factory, millisecondsDelay, action, factory.CancellationToken, factory.CreationOptions, factory.GetTargetScheduler()); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// Options that control the task's behavior. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Action action, + TaskCreationOptions creationOptions) + { + if (factory == null) throw new ArgumentNullException("factory"); + return StartNewDelayed(factory, millisecondsDelay, action, factory.CancellationToken, creationOptions, factory.GetTargetScheduler()); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// The cancellation token to assign to the created Task. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Action action, + CancellationToken cancellationToken) + { + if (factory == null) throw new ArgumentNullException("factory"); + return StartNewDelayed(factory, millisecondsDelay, action, cancellationToken, factory.CreationOptions, factory.GetTargetScheduler()); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// The cancellation token to assign to the created Task. + /// Options that control the task's behavior. + /// The scheduler to which the Task will be scheduled. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Action action, + CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) + { + if (factory == null) throw new ArgumentNullException("factory"); + if (millisecondsDelay < 0) throw new ArgumentOutOfRangeException("millisecondsDelay"); + if (action == null) throw new ArgumentNullException("action"); + if (scheduler == null) throw new ArgumentNullException("scheduler"); + + return factory + .StartNewDelayed(millisecondsDelay, cancellationToken) + .ContinueWith(_ => action(), cancellationToken, TaskContinuationOptions.OnlyOnRanToCompletion, scheduler); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// An object provided to the delegate. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Action action, object state) + { + if (factory == null) throw new ArgumentNullException("factory"); + return StartNewDelayed(factory, millisecondsDelay, action, state, factory.CancellationToken, factory.CreationOptions, factory.GetTargetScheduler()); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// An object provided to the delegate. + /// Options that control the task's behavior. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Action action, object state, + TaskCreationOptions creationOptions) + { + if (factory == null) throw new ArgumentNullException("factory"); + return StartNewDelayed(factory, millisecondsDelay, action, state, factory.CancellationToken, creationOptions, factory.GetTargetScheduler()); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// An object provided to the delegate. + /// The cancellation token to assign to the created Task. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Action action, object state, + CancellationToken cancellationToken) + { + if (factory == null) throw new ArgumentNullException("factory"); + return StartNewDelayed(factory, millisecondsDelay, action, state, cancellationToken, factory.CreationOptions, factory.GetTargetScheduler()); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// An object provided to the delegate. + /// The cancellation token to assign to the created Task. + /// Options that control the task's behavior. + /// The scheduler to which the Task will be scheduled. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Action action, object state, + CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) + { + if (factory == null) throw new ArgumentNullException("factory"); + if (millisecondsDelay < 0) throw new ArgumentOutOfRangeException("millisecondsDelay"); + if (action == null) throw new ArgumentNullException("action"); + if (scheduler == null) throw new ArgumentNullException("scheduler"); + + // Create the task that will be returned; workaround for no ContinueWith(..., state) overload. + var result = new TaskCompletionSource(state); + + // Delay a continuation to run the action + factory + .StartNewDelayed(millisecondsDelay, cancellationToken) + .ContinueWith(t => + { + if (t.IsCanceled) result.TrySetCanceled(); + else + { + try + { + action(state); + result.TrySetResult(null); + } + catch (Exception exc) { result.TrySetException(exc); } + } + }, scheduler); + + // Return the task + return result.Task; + } + #endregion + + #region TaskFactory with Func + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Func function) + { + if (factory == null) throw new ArgumentNullException("factory"); + return StartNewDelayed(factory, millisecondsDelay, function, factory.CancellationToken, factory.CreationOptions, factory.GetTargetScheduler()); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// Options that control the task's behavior. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Func function, + TaskCreationOptions creationOptions) + { + if (factory == null) throw new ArgumentNullException("factory"); + return StartNewDelayed(factory, millisecondsDelay, function, factory.CancellationToken, creationOptions, factory.GetTargetScheduler()); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// The CancellationToken to assign to the Task. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Func function, + CancellationToken cancellationToken) + { + if (factory == null) throw new ArgumentNullException("factory"); + return StartNewDelayed(factory, millisecondsDelay, function, cancellationToken, factory.CreationOptions, factory.GetTargetScheduler()); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// The CancellationToken to assign to the Task. + /// Options that control the task's behavior. + /// The scheduler to which the Task will be scheduled. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Func function, + CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) + { + if (factory == null) throw new ArgumentNullException("factory"); + if (millisecondsDelay < 0) throw new ArgumentOutOfRangeException("millisecondsDelay"); + if (function == null) throw new ArgumentNullException("function"); + if (scheduler == null) throw new ArgumentNullException("scheduler"); + + // Create the trigger and the timer to start it + var tcs = new TaskCompletionSource(); + var timer = new Timer(obj => ((TaskCompletionSource)obj).SetResult(null), + tcs, millisecondsDelay, Timeout.Infinite); + + // Return a task that executes the function when the trigger fires + return tcs.Task.ContinueWith(_ => + { + timer.Dispose(); + return function(); + }, cancellationToken, ContinuationOptionsFromCreationOptions(creationOptions), scheduler); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// An object provided to the delegate. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Func function, object state) + { + if (factory == null) throw new ArgumentNullException("factory"); + return StartNewDelayed(factory, millisecondsDelay, function, state, factory.CancellationToken, factory.CreationOptions, factory.GetTargetScheduler()); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// An object provided to the delegate. + /// The CancellationToken to assign to the Task. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Func function, object state, + CancellationToken cancellationToken) + { + if (factory == null) throw new ArgumentNullException("factory"); + return StartNewDelayed(factory, millisecondsDelay, function, state, cancellationToken, factory.CreationOptions, factory.GetTargetScheduler()); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// An object provided to the delegate. + /// Options that control the task's behavior. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Func function, object state, + TaskCreationOptions creationOptions) + { + if (factory == null) throw new ArgumentNullException("factory"); + return StartNewDelayed(factory, millisecondsDelay, function, state, factory.CancellationToken, creationOptions, factory.GetTargetScheduler()); + } + + /// Creates and schedules a task for execution after the specified time delay. + /// The factory to use to create the task. + /// The delay after which the task will be scheduled. + /// The delegate executed by the task. + /// An object provided to the delegate. + /// The CancellationToken to assign to the Task. + /// Options that control the task's behavior. + /// The scheduler to which the Task will be scheduled. + /// The created Task. + public static Task StartNewDelayed( + this TaskFactory factory, + int millisecondsDelay, Func function, object state, + CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) + { + if (factory == null) throw new ArgumentNullException("factory"); + if (millisecondsDelay < 0) throw new ArgumentOutOfRangeException("millisecondsDelay"); + if (function == null) throw new ArgumentNullException("action"); + if (scheduler == null) throw new ArgumentNullException("scheduler"); + + // Create the task that will be returned + var result = new TaskCompletionSource(state); + Timer timer = null; + + // Create the task that will run the user's function + var functionTask = new Task(function, state, creationOptions); + + // When the function task completes, transfer the results to the returned task + functionTask.ContinueWith(t => + { + result.SetFromTask(t); + timer.Dispose(); + }, cancellationToken, ContinuationOptionsFromCreationOptions(creationOptions) | TaskContinuationOptions.ExecuteSynchronously, scheduler); + + // Start the timer for the trigger + timer = new Timer(obj => ((Task)obj).Start(scheduler), + functionTask, millisecondsDelay, Timeout.Infinite); + + return result.Task; + } + #endregion + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_From.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_From.cs new file mode 100644 index 0000000..0837365 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_From.cs @@ -0,0 +1,76 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TaskFactoryExtensions_From.cs +// +//-------------------------------------------------------------------------- + +namespace System.Threading.Tasks +{ + /// Extensions for TaskFactory. + public static partial class TaskFactoryExtensions + { + #region TaskFactory + /// Creates a Task that has completed in the Faulted state with the specified exception. + /// The target TaskFactory. + /// The exception with which the Task should fault. + /// The completed Task. + public static Task FromException(this TaskFactory factory, Exception exception) + { + var tcs = new TaskCompletionSource(factory.CreationOptions); + tcs.SetException(exception); + return tcs.Task; + } + + /// Creates a Task that has completed in the Faulted state with the specified exception. + /// Specifies the type of payload for the new Task. + /// The target TaskFactory. + /// The exception with which the Task should fault. + /// The completed Task. + public static Task FromException(this TaskFactory factory, Exception exception) + { + var tcs = new TaskCompletionSource(factory.CreationOptions); + tcs.SetException(exception); + return tcs.Task; + } + + /// Creates a Task that has completed in the RanToCompletion state with the specified result. + /// Specifies the type of payload for the new Task. + /// The target TaskFactory. + /// The result with which the Task should complete. + /// The completed Task. + public static Task FromResult(this TaskFactory factory, TResult result) + { + var tcs = new TaskCompletionSource(factory.CreationOptions); + tcs.SetResult(result); + return tcs.Task; + } + #endregion + + #region TaskFactory + /// Creates a Task that has completed in the Faulted state with the specified exception. + /// The target TaskFactory. + /// The exception with which the Task should fault. + /// The completed Task. + public static Task FromException(this TaskFactory factory, Exception exception) + { + var tcs = new TaskCompletionSource(factory.CreationOptions); + tcs.SetException(exception); + return tcs.Task; + } + + /// Creates a Task that has completed in the RanToCompletion state with the specified result. + /// Specifies the type of payload for the new Task. + /// The target TaskFactory. + /// The result with which the Task should complete. + /// The completed Task. + public static Task FromResult(this TaskFactory factory, TResult result) + { + var tcs = new TaskCompletionSource(factory.CreationOptions); + tcs.SetResult(result); + return tcs.Task; + } + #endregion + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_FromAsync.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_FromAsync.cs new file mode 100644 index 0000000..c825df2 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_FromAsync.cs @@ -0,0 +1,30 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TaskFactoryExtensions_FromAsync.cs +// +//-------------------------------------------------------------------------- + +namespace System.Threading.Tasks +{ + /// Extensions for TaskFactory. + public static partial class TaskFactoryExtensions + { + /// Creates a Task that will be completed when the specified WaitHandle is signaled. + /// The target factory. + /// The WaitHandle. + /// The created Task. + public static Task FromAsync(this TaskFactory factory, WaitHandle waitHandle) + { + if (factory == null) throw new ArgumentNullException("factory"); + if (waitHandle == null) throw new ArgumentNullException("waitHandle"); + + var tcs = new TaskCompletionSource(); + var rwh = ThreadPool.RegisterWaitForSingleObject(waitHandle, delegate { tcs.TrySetResult(null); }, null, -1, true); + var t = tcs.Task; + t.ContinueWith(_ => rwh.Unregister(null), TaskContinuationOptions.ExecuteSynchronously); + return t; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_Iterate.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_Iterate.cs new file mode 100644 index 0000000..e6ae72b --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_Iterate.cs @@ -0,0 +1,228 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TaskFactoryExtensions_Iterate.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace System.Threading.Tasks +{ + public static partial class TaskFactoryExtensions + { + #region No Object State Overloads + /// Asynchronously iterates through an enumerable of tasks. + /// The target factory. + /// The enumerable containing the tasks to be iterated through. + /// A Task that represents the complete asynchronous operation. + public static Task Iterate( + this TaskFactory factory, + IEnumerable source) + { + if (factory == null) throw new ArgumentNullException("factory"); + return Iterate(factory, source, null, factory.CancellationToken, factory.CreationOptions, factory.GetTargetScheduler()); + } + + /// Asynchronously iterates through an enumerable of tasks. + /// The target factory. + /// The enumerable containing the tasks to be iterated through. + /// The cancellation token used to cancel the iteration. + /// A Task that represents the complete asynchronous operation. + public static Task Iterate( + this TaskFactory factory, + IEnumerable source, + CancellationToken cancellationToken) + { + if (factory == null) throw new ArgumentNullException("factory"); + return Iterate(factory, source, null, cancellationToken, factory.CreationOptions, factory.GetTargetScheduler()); + } + + /// Asynchronously iterates through an enumerable of tasks. + /// The target factory. + /// The enumerable containing the tasks to be iterated through. + /// Options that control the task's behavior. + /// A Task that represents the complete asynchronous operation. + public static Task Iterate( + this TaskFactory factory, + IEnumerable source, + TaskCreationOptions creationOptions) + { + if (factory == null) throw new ArgumentNullException("factory"); + return Iterate(factory, source, null, factory.CancellationToken, creationOptions, factory.GetTargetScheduler()); + } + + /// Asynchronously iterates through an enumerable of tasks. + /// The target factory. + /// The enumerable containing the tasks to be iterated through. + /// The scheduler to which tasks will be scheduled. + /// A Task that represents the complete asynchronous operation. + public static Task Iterate( + this TaskFactory factory, + IEnumerable source, + TaskScheduler scheduler) + { + if (factory == null) throw new ArgumentNullException("factory"); + return Iterate(factory, source, null, factory.CancellationToken, factory.CreationOptions, scheduler); + } + + /// Asynchronously iterates through an enumerable of tasks. + /// The target factory. + /// The enumerable containing the tasks to be iterated through. + /// The cancellation token used to cancel the iteration. + /// Options that control the task's behavior. + /// The scheduler to which tasks will be scheduled. + /// A Task that represents the complete asynchronous operation. + public static Task Iterate( + this TaskFactory factory, + IEnumerable source, + CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) + { + return Iterate(factory, source, null, cancellationToken, creationOptions, scheduler); + } + #endregion + + #region Object State Overloads and Full Implementation + /// Asynchronously iterates through an enumerable of tasks. + /// The target factory. + /// The enumerable containing the tasks to be iterated through. + /// The asynchronous state for the returned Task. + /// A Task that represents the complete asynchronous operation. + public static Task Iterate( + this TaskFactory factory, + IEnumerable source, object state) + { + if (factory == null) throw new ArgumentNullException("factory"); + return Iterate(factory, source, state, factory.CancellationToken, factory.CreationOptions, factory.GetTargetScheduler()); + } + + /// Asynchronously iterates through an enumerable of tasks. + /// The target factory. + /// The enumerable containing the tasks to be iterated through. + /// The asynchronous state for the returned Task. + /// The cancellation token used to cancel the iteration. + /// A Task that represents the complete asynchronous operation. + public static Task Iterate( + this TaskFactory factory, + IEnumerable source, object state, + CancellationToken cancellationToken) + { + if (factory == null) throw new ArgumentNullException("factory"); + return Iterate(factory, source, state, cancellationToken, factory.CreationOptions, factory.GetTargetScheduler()); + } + + /// Asynchronously iterates through an enumerable of tasks. + /// The target factory. + /// The enumerable containing the tasks to be iterated through. + /// The asynchronous state for the returned Task. + /// Options that control the task's behavior. + /// A Task that represents the complete asynchronous operation. + public static Task Iterate( + this TaskFactory factory, + IEnumerable source, object state, + TaskCreationOptions creationOptions) + { + if (factory == null) throw new ArgumentNullException("factory"); + return Iterate(factory, source, state, factory.CancellationToken, creationOptions, factory.GetTargetScheduler()); + } + + /// Asynchronously iterates through an enumerable of tasks. + /// The target factory. + /// The enumerable containing the tasks to be iterated through. + /// The asynchronous state for the returned Task. + /// The scheduler to which tasks will be scheduled. + /// A Task that represents the complete asynchronous operation. + public static Task Iterate( + this TaskFactory factory, + IEnumerable source, object state, + TaskScheduler scheduler) + { + if (factory == null) throw new ArgumentNullException("factory"); + return Iterate(factory, source, state, factory.CancellationToken, factory.CreationOptions, scheduler); + } + + /// Asynchronously iterates through an enumerable of tasks. + /// The target factory. + /// The enumerable containing the tasks to be iterated through. + /// The asynchronous state for the returned Task. + /// The cancellation token used to cancel the iteration. + /// Options that control the task's behavior. + /// The scheduler to which tasks will be scheduled. + /// A Task that represents the complete asynchronous operation. + public static Task Iterate( + this TaskFactory factory, + IEnumerable source, object state, + CancellationToken cancellationToken, TaskCreationOptions creationOptions, TaskScheduler scheduler) + { + // Validate/update parameters + if (factory == null) throw new ArgumentNullException("factory"); + if (source == null) throw new ArgumentNullException("asyncIterator"); + if (scheduler == null) throw new ArgumentNullException("scheduler"); + + // Get an enumerator from the enumerable + var enumerator = source.GetEnumerator(); + if (enumerator == null) throw new InvalidOperationException("Invalid enumerable - GetEnumerator returned null"); + + // Create the task to be returned to the caller. And ensure + // that when everything is done, the enumerator is cleaned up. + var trs = new TaskCompletionSource(state, creationOptions); + trs.Task.ContinueWith(_ => enumerator.Dispose(), CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); + + // This will be called every time more work can be done. + Action recursiveBody = null; + recursiveBody = antecedent => + { + try + { + // If we should continue iterating and there's more to iterate + // over, create a continuation to continue processing. We only + // want to continue processing once the current Task (as yielded + // from the enumerator) is complete. + if (enumerator.MoveNext()) + { + var nextItem = enumerator.Current; + + // If we got a Task, continue from it to continue iterating + if (nextItem is Task) + { + var nextTask = (Task)nextItem; + /**/ nextTask.IgnoreExceptions(); // TODO: Is this a good idea? + nextTask.ContinueWith(recursiveBody).IgnoreExceptions(); + } + // If we got a scheduler, continue iterating under the new scheduler, + // enabling hopping between contexts. + else if (nextItem is TaskScheduler) + { + Task.Factory.StartNew(() => recursiveBody(null), CancellationToken.None, TaskCreationOptions.None, (TaskScheduler)nextItem).IgnoreExceptions(); + } + // Anything else is invalid + else trs.TrySetException(new InvalidOperationException("Task or TaskScheduler object expected in Iterate")); + } + + // Otherwise, we're done! + else trs.TrySetResult(null); + } + // If MoveNext throws an exception, propagate that to the user, + // either as cancellation or as a fault + catch (Exception exc) + { + var oce = exc as OperationCanceledException; + if (oce != null && oce.CancellationToken == cancellationToken) + { + trs.TrySetCanceled(); + } + else trs.TrySetException(exc); + } + }; + + // Get things started by launching the first task + factory.StartNew(() => recursiveBody(null), CancellationToken.None, TaskCreationOptions.None, scheduler).IgnoreExceptions(); + + // Return the representative task to the user + return trs.Task; + } + #endregion + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_TrackedSequence.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_TrackedSequence.cs new file mode 100644 index 0000000..bbfb316 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskFactoryExtensions/TaskFactoryExtensions_TrackedSequence.cs @@ -0,0 +1,63 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TaskFactoryExtensions_TrackedSequence.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; + +namespace System.Threading.Tasks +{ + public static partial class TaskFactoryExtensions + { + /// Asynchronously executes a sequence of tasks, maintaining a list of all tasks processed. + /// The TaskFactory to use to create the task. + /// + /// The functions that generate the tasks through which to iterate sequentially. + /// Iteration will cease if a task faults. + /// + /// A Task that will return the list of tracked tasks iterated. + public static Task> TrackedSequence(this TaskFactory factory, params Func [] functions) + { + var tcs = new TaskCompletionSource>(); + factory.Iterate(TrackedSequenceInternal(functions, tcs)); + return tcs.Task; + } + + /// Creates the enumerable to iterate through with Iterate. + /// + /// The functions that generate the tasks through which to iterate sequentially. + /// Iteration will cease if a task faults. + /// + /// The TaskCompletionSource to resolve with the asynchronous results. + /// The enumerable through which to iterate. + private static IEnumerable TrackedSequenceInternal( + IEnumerable> functions, TaskCompletionSource> tcs) + { + // Store a list of all tasks iterated through. This will be provided + // to the resulting task when we're done. + var tasks = new List(); + + // Run seqeuentially through all of the provided functions. + foreach (var func in functions) + { + // Get the next task. If we get an exception while trying to do so, + // an invalid function was provided. Fault the TCS and break out. + Task nextTask = null; + try { nextTask = func(); } catch (Exception exc) { tcs.TrySetException(exc); } + if (nextTask == null) yield break; + + // Store the task that was generated and yield it from the sequence. If the task + // faults, break out of the loop so that no more tasks are processed. + tasks.Add(nextTask); + yield return nextTask; + if (nextTask.IsFaulted) break; + } + + // We're done. Transfer all tasks we iterated through. + tcs.TrySetResult(tasks); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskSchedulerExtensions.cs b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskSchedulerExtensions.cs new file mode 100644 index 0000000..1861895 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Extensions/TaskSchedulerExtensions.cs @@ -0,0 +1,55 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: TaskSchedulerExtensions.cs +// +//-------------------------------------------------------------------------- + +namespace System.Threading.Tasks +{ + /// Extension methods for TaskScheduler. + public static class TaskSchedulerExtensions + { + /// Gets a SynchronizationContext that targets this TaskScheduler. + /// The target scheduler. + /// A SynchronizationContext that targets this scheduler. + public static SynchronizationContext ToSynchronizationContext(this TaskScheduler scheduler) + { + return new TaskSchedulerSynchronizationContext(scheduler); + } + + /// Provides a SynchronizationContext wrapper for a TaskScheduler. + private sealed class TaskSchedulerSynchronizationContext : SynchronizationContext + { + /// The scheduler. + private TaskScheduler _scheduler; + + /// Initializes the context with the specified scheduler. + /// The scheduler to target. + internal TaskSchedulerSynchronizationContext(TaskScheduler scheduler) + { + if (scheduler == null) throw new ArgumentNullException("scheduler"); + _scheduler = scheduler; + } + + /// Dispatches an asynchronous message to the synchronization context. + /// The System.Threading.SendOrPostCallback delegate to call. + /// The object passed to the delegate. + public override void Post(SendOrPostCallback d, object state) + { + Task.Factory.StartNew(() => d(state), CancellationToken.None, TaskCreationOptions.None, _scheduler); + } + + /// Dispatches a synchronous message to the synchronization context. + /// The System.Threading.SendOrPostCallback delegate to call. + /// The object passed to the delegate. + public override void Send(SendOrPostCallback d, object state) + { + Task t = new Task(() => d(state)); + t.RunSynchronously(_scheduler); + t.Wait(); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Common.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Common.cs new file mode 100644 index 0000000..50ddaf4 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Common.cs @@ -0,0 +1,21 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_Common.cs +// +//-------------------------------------------------------------------------- + +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + /// + /// Provides parallelized algorithms for common operations. + /// + public static partial class ParallelAlgorithms + { + // Default, shared instance of the ParallelOptions class. This should not be modified. + private static ParallelOptions s_defaultParallelOptions = new ParallelOptions(); + } +} diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Filter.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Filter.cs new file mode 100644 index 0000000..a50bc63 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Filter.cs @@ -0,0 +1,49 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_Filter.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + public static partial class ParallelAlgorithms + { + /// Filters an input list, running a predicate over each element of the input. + /// Specifies the type of data in the list. + /// The list to be filtered. + /// The predicate to use to determine which elements pass. + /// A new list containing all those elements from the input that passed the filter. + public static IList Filter(IList input, Func predicate) + { + return Filter(input, s_defaultParallelOptions, predicate); + } + + /// Filters an input list, running a predicate over each element of the input. + /// Specifies the type of data in the list. + /// The list to be filtered. + /// Options to use for the execution of this filter. + /// The predicate to use to determine which elements pass. + /// A new list containing all those elements from the input that passed the filter. + public static IList Filter(IList input, ParallelOptions parallelOptions, Func predicate) + { + if (input == null) throw new ArgumentNullException("input"); + if (parallelOptions == null) throw new ArgumentNullException("parallelOptions"); + if (predicate == null) throw new ArgumentNullException("predicate"); + + var results = new List(input.Count); + Parallel.For(0, input.Count, parallelOptions, () => new List(input.Count), (i, loop, localList) => + { + var item = input[i]; + if (predicate(item)) localList.Add(item); + return localList; + }, + localList => { lock (results) results.AddRange(localList); }); + return results; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_For.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_For.cs new file mode 100644 index 0000000..4351520 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_For.cs @@ -0,0 +1,68 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_For.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Numerics; +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + public static partial class ParallelAlgorithms + { + /// Executes a for loop in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per iteration. + public static void For(BigInteger fromInclusive, BigInteger toExclusive, Action body) + { + For(fromInclusive, toExclusive, s_defaultParallelOptions, body); + } + + /// Executes a for loop in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// A System.Threading.Tasks.ParallelOptions instance that configures the behavior of this operation. + /// The delegate that is invoked once per iteration. + public static void For(BigInteger fromInclusive, BigInteger toExclusive, ParallelOptions options, Action body) + { + // Determine how many iterations to run... + var range = toExclusive - fromInclusive; + + // ... and run them. + if (range <= 0) + { + // If there's nothing to do, bail + return; + } + // Fast path + else if (range <= Int64.MaxValue) + { + // If the range is within the realm of Int64, we'll delegate to Parallel.For's Int64 overloads. + // Iterate from 0 to range, and then call the user-provided body with the scaled-back value. + Parallel.For(0, (long)range, options, i => body(i + fromInclusive)); + } + // Slower path + else + { + // For a range larger than Int64.MaxValue, we'll rely on an enumerable of BigInteger. + // We create a C# iterator that yields all of the BigInteger values in the requested range + // and then ForEach over that range. + Parallel.ForEach(Range(fromInclusive, toExclusive), options, body); + } + } + + /// Creates an enumerable that iterates the range [fromInclusive, toExclusive). + /// The lower bound, inclusive. + /// The upper bound, exclusive. + /// The enumerable of the range. + private static IEnumerable Range(BigInteger fromInclusive, BigInteger toExclusive) + { + for (var i = fromInclusive; i < toExclusive; i++) yield return i; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_ForRange.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_ForRange.cs new file mode 100644 index 0000000..5933a40 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_ForRange.cs @@ -0,0 +1,236 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_ForRange.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + public static partial class ParallelAlgorithms + { + #region Int32, No Options + /// Executes a for loop over ranges in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per range. + /// A ParallelLoopResult structure that contains information on what portion of the loop completed. + public static ParallelLoopResult ForRange( + int fromInclusive, int toExclusive, + Action body) + { + return ForRange(fromInclusive, toExclusive, s_defaultParallelOptions, body); + } + + /// Executes a for loop over ranges in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per range. + /// A ParallelLoopResult structure that contains information on what portion of the loop completed. + public static ParallelLoopResult ForRange( + int fromInclusive, int toExclusive, + Action body) + { + return ForRange(fromInclusive, toExclusive, s_defaultParallelOptions, body); + } + + /// Executes a for loop over ranges in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// The function delegate that returns the initial state of the local data for each thread. + /// The delegate that is invoked once per range. + /// The delegate that performs a final action on the local state of each thread. + /// A ParallelLoopResult structure that contains information on what portion of the loop completed. + public static ParallelLoopResult ForRange( + int fromInclusive, int toExclusive, + Func localInit, + Func body, + Action localFinally) + { + return ForRange(fromInclusive, toExclusive, s_defaultParallelOptions, + localInit, body, localFinally); + } + #endregion + + #region Int64, No Options + /// Executes a for loop over ranges in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per range. + /// A ParallelLoopResult structure that contains information on what portion of the loop completed. + public static ParallelLoopResult ForRange( + long fromInclusive, long toExclusive, + Action body) + { + return ForRange(fromInclusive, toExclusive, s_defaultParallelOptions, body); + } + + /// Executes a for loop over ranges in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// The delegate that is invoked once per range. + /// A ParallelLoopResult structure that contains information on what portion of the loop completed. + public static ParallelLoopResult ForRange( + long fromInclusive, long toExclusive, + Action body) + { + return ForRange(fromInclusive, toExclusive, s_defaultParallelOptions, body); + } + + /// Executes a for loop over ranges in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// The function delegate that returns the initial state of the local data for each thread. + /// The delegate that is invoked once per range. + /// The delegate that performs a final action on the local state of each thread. + /// A ParallelLoopResult structure that contains information on what portion of the loop completed. + public static ParallelLoopResult ForRange( + long fromInclusive, long toExclusive, + Func localInit, + Func body, + Action localFinally) + { + return ForRange(fromInclusive, toExclusive, s_defaultParallelOptions, + localInit, body, localFinally); + } + #endregion + + #region Int32, Parallel Options + /// Executes a for loop over ranges in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// A ParallelOptions instance that configures the behavior of this operation. + /// The delegate that is invoked once per range. + /// A ParallelLoopResult structure that contains information on what portion of the loop completed. + public static ParallelLoopResult ForRange( + int fromInclusive, int toExclusive, + ParallelOptions parallelOptions, + Action body) + { + if (parallelOptions == null) throw new ArgumentNullException("parallelOptions"); + if (body == null) throw new ArgumentNullException("body"); + + return Parallel.ForEach(Partitioner.Create(fromInclusive, toExclusive), parallelOptions, range => + { + body(range.Item1, range.Item2); + }); + } + + /// Executes a for loop over ranges in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// A ParallelOptions instance that configures the behavior of this operation. + /// The delegate that is invoked once per range. + /// A ParallelLoopResult structure that contains information on what portion of the loop completed. + public static ParallelLoopResult ForRange( + int fromInclusive, int toExclusive, + ParallelOptions parallelOptions, + Action body) + { + if (parallelOptions == null) throw new ArgumentNullException("parallelOptions"); + if (body == null) throw new ArgumentNullException("body"); + + return Parallel.ForEach(Partitioner.Create(fromInclusive, toExclusive), parallelOptions, (range, loopState) => + { + body(range.Item1, range.Item2, loopState); + }); + } + + /// Executes a for loop over ranges in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// The function delegate that returns the initial state of the local data for each thread. + /// The delegate that is invoked once per range. + /// The delegate that performs a final action on the local state of each thread. + /// A ParallelLoopResult structure that contains information on what portion of the loop completed. + public static ParallelLoopResult ForRange( + int fromInclusive, int toExclusive, + ParallelOptions parallelOptions, + Func localInit, + Func body, + Action localFinally) + { + if (parallelOptions == null) throw new ArgumentNullException("parallelOptions"); + if (localInit == null) throw new ArgumentNullException("localInit"); + if (body == null) throw new ArgumentNullException("body"); + if (localFinally == null) throw new ArgumentNullException("localFinally"); + + return Parallel.ForEach(Partitioner.Create(fromInclusive, toExclusive), parallelOptions, localInit, (range, loopState, x) => + { + return body(range.Item1, range.Item2, loopState, x); + }, localFinally); + } + #endregion + + #region Int64, Parallel Options + /// Executes a for loop over ranges in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// A ParallelOptions instance that configures the behavior of this operation. + /// The delegate that is invoked once per range. + /// A ParallelLoopResult structure that contains information on what portion of the loop completed. + public static ParallelLoopResult ForRange( + long fromInclusive, long toExclusive, + ParallelOptions parallelOptions, + Action body) + { + if (parallelOptions == null) throw new ArgumentNullException("parallelOptions"); + if (body == null) throw new ArgumentNullException("body"); + + return Parallel.ForEach(Partitioner.Create(fromInclusive, toExclusive), parallelOptions, range => + { + body(range.Item1, range.Item2); + }); + } + + /// Executes a for loop over ranges in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// A ParallelOptions instance that configures the behavior of this operation. + /// The delegate that is invoked once per range. + /// A ParallelLoopResult structure that contains information on what portion of the loop completed. + public static ParallelLoopResult ForRange( + long fromInclusive, long toExclusive, + ParallelOptions parallelOptions, + Action body) + { + if (parallelOptions == null) throw new ArgumentNullException("parallelOptions"); + if (body == null) throw new ArgumentNullException("body"); + + return Parallel.ForEach(Partitioner.Create(fromInclusive, toExclusive), parallelOptions, (range, loopState) => + { + body(range.Item1, range.Item2, loopState); + }); + } + + /// Executes a for loop over ranges in which iterations may run in parallel. + /// The start index, inclusive. + /// The end index, exclusive. + /// The function delegate that returns the initial state of the local data for each thread. + /// The delegate that is invoked once per range. + /// The delegate that performs a final action on the local state of each thread. + /// A ParallelLoopResult structure that contains information on what portion of the loop completed. + public static ParallelLoopResult ForRange( + long fromInclusive, long toExclusive, + ParallelOptions parallelOptions, + Func localInit, + Func body, + Action localFinally) + { + if (parallelOptions == null) throw new ArgumentNullException("parallelOptions"); + if (localInit == null) throw new ArgumentNullException("localInit"); + if (body == null) throw new ArgumentNullException("body"); + if (localFinally == null) throw new ArgumentNullException("localFinally"); + + return Parallel.ForEach(Partitioner.Create(fromInclusive, toExclusive), parallelOptions, localInit, (range, loopState, x) => + { + return body(range.Item1, range.Item2, loopState, x); + }, localFinally); + } + #endregion + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Map.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Map.cs new file mode 100644 index 0000000..722d95b --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Map.cs @@ -0,0 +1,45 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_Map.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + public static partial class ParallelAlgorithms + { + /// Executes a map operation, converting an input list into an output list, in parallel. + /// Specifies the type of the input data. + /// Specifies the type of the output data. + /// The input list to be mapped used the transform function. + /// The transform function to use to map the input data to the output data. + /// The output data, transformed using the transform function. + public static TOutput[] Map(IList input, Func transform) + { + return Map(input, s_defaultParallelOptions, transform); + } + + /// Executes a map operation, converting an input list into an output list, in parallel. + /// Specifies the type of the input data. + /// Specifies the type of the output data. + /// The input list to be mapped used the transform function. + /// A ParallelOptions instance that configures the behavior of this operation. + /// The transform function to use to map the input data to the output data. + /// The output data, transformed using the transform function. + public static TOutput[] Map(IList input, ParallelOptions parallelOptions, Func transform) + { + if (input == null) throw new ArgumentNullException("input"); + if (parallelOptions == null) throw new ArgumentNullException("parallelOptions"); + if (transform == null) throw new ArgumentNullException("transform"); + + var output = new TOutput[input.Count]; + Parallel.For(0, input.Count, parallelOptions, i => output[i] = transform(input[i])); + return output; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Reduce.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Reduce.cs new file mode 100644 index 0000000..649a99f --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Reduce.cs @@ -0,0 +1,93 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_Reduce.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + public static partial class ParallelAlgorithms + { + /// Reduces the input data using the specified aggregation operation. + /// Specifies the type of data being aggregated. + /// The input data to be reduced. + /// The seed to use to initialize the operation; this seed may be used multiple times. + /// The reduction operation. + /// The reduced value. + public static T Reduce( + IList input, T seed, + Func associativeCommutativeOperation) + { + return Reduce(input, s_defaultParallelOptions, seed, associativeCommutativeOperation); + } + + /// Reduces the input data using the specified aggregation operation. + /// Specifies the type of data being aggregated. + /// The input data to be reduced. + /// A ParallelOptions instance that configures the behavior of this operation. + /// The seed to use to initialize the operation; this seed may be used multiple times. + /// The reduction operation. + /// The reduced value. + public static T Reduce( + IList input, ParallelOptions parallelOptions, + T seed, Func associativeCommutativeOperation) + { + if (input == null) throw new ArgumentNullException("input"); + return Reduce(0, input.Count, parallelOptions, i => input[i], seed, associativeCommutativeOperation); + } + + /// Reduces the input range using the specified aggregation operation. + /// Specifies the type of data being aggregated. + /// The start index, inclusive. + /// The end index, exclusive. + /// The function used to retrieve the data to be reduced for a given index. + /// The seed to use to initialize the operation; this seed may be used multiple times. + /// The reduction operation. + /// The reduced value. + public static T Reduce( + int fromInclusive, int toExclusive, + Func mapOperation, T seed, Func associativeCommutativeOperation) + { + return Reduce(fromInclusive, toExclusive, s_defaultParallelOptions, mapOperation, seed, associativeCommutativeOperation); + } + + /// Reduces the input range using the specified aggregation operation. + /// Specifies the type of data being aggregated. + /// The start index, inclusive. + /// The end index, exclusive. + /// A ParallelOptions instance that configures the behavior of this operation. + /// The function used to retrieve the data to be reduced for a given index. + /// The seed to use to initialize the operation; this seed may be used multiple times. + /// The reduction operation. + /// The reduced value. + public static T Reduce( + int fromInclusive, int toExclusive, ParallelOptions parallelOptions, + Func mapOperation, T seed, Func associativeCommutativeOperation) + { + if (parallelOptions == null) throw new ArgumentNullException("parallelOptions"); + if (mapOperation == null) throw new ArgumentNullException("mapOperation"); + if (associativeCommutativeOperation == null) throw new ArgumentNullException("associativeCommutativeOperation"); + if (toExclusive < fromInclusive) throw new ArgumentOutOfRangeException("toExclusive"); + + object obj = new object(); // used as a monitor for the final reduction + T result = seed; // accumulator for final reduction + + // Reduce in parallel + Parallel.For(fromInclusive, toExclusive, parallelOptions, + // Initialize each thread with the user-specified seed + () => seed, + // Map the current index to a value and aggregate that value into the local reduction + (i, loop, localResult) => associativeCommutativeOperation(mapOperation(i), localResult), + // Combine all of the local reductions + localResult => { lock (obj) result = associativeCommutativeOperation(localResult, result); }); + + // Return the final result + return result; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Scan.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Scan.cs new file mode 100644 index 0000000..caf908d --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Scan.cs @@ -0,0 +1,219 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_Scan.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + public static partial class ParallelAlgorithms + { + /// Computes a parallel prefix scan over the source enumerable using the specified function. + /// The type of the data in the source. + /// The source data over which a prefix scan should be computed. + /// The function to use for the scan. + /// The results of the scan operation. + /// + /// For very small functions, such as additions, an implementation targeted + /// at the relevant type and operation will perform significantly better than + /// this generalized implementation. + /// + public static T[] Scan(IEnumerable source, Func function) + { + return Scan(source, function, loadBalance: false); + } + + /// Computes a parallel prefix scan over the source enumerable using the specified function. + /// The type of the data in the source. + /// The source data over which a prefix scan should be computed. + /// The function to use for the scan. + /// Whether to load-balance during process. + /// The results of the scan operation. + /// + /// For very small functions, such as additions, an implementation targeted + /// at the relevant type and operation will perform significantly better than + /// this generalized implementation. + /// + public static T[] Scan(IEnumerable source, Func function, bool loadBalance) + { + // Validate arguments + if (source == null) throw new ArgumentNullException("source"); + + // Create output copy + var output = source.ToArray(); + + // Do the prefix scan in-place on the copy and return the results + ScanInPlace(output, function, loadBalance); + return output; + } + + /// Computes a parallel prefix scan in-place on an array using the specified function. + /// The type of the data in the source. + /// The data over which a prefix scan should be computed. Upon exit, stores the results. + /// The function to use for the scan. + /// The results of the scan operation. + /// + /// For very small functions, such as additions, an implementation targeted + /// at the relevant type and operation will perform significantly better than + /// this generalized implementation. + /// + public static void ScanInPlace(T[] data, Func function) + { + ScanInPlace(data, function, loadBalance:false); + } + + /// Computes a parallel prefix scan in-place on an array using the specified function. + /// The type of the data in the source. + /// The data over which a prefix scan should be computed. Upon exit, stores the results. + /// The function to use for the scan. + /// Whether to load-balance during process. + /// The results of the scan operation. + /// + /// For very small functions, such as additions, an implementation targeted + /// at the relevant type and operation will perform significantly better than + /// this generalized implementation. + /// + public static void ScanInPlace(T [] data, Func function, bool loadBalance) + { + // Validate arguments + if (data == null) throw new ArgumentNullException("data"); + if (function == null) throw new ArgumentNullException("function"); + + // Do the prefix scan in-place and return the results. This implementation + // of parallel prefix scan ends up executing the function twice as many + // times as the sequential implementation. Thus, only if we have more than 2 cores + // will the parallel version have a chance of running faster than sequential. + if (Environment.ProcessorCount <= 2) + { + InclusiveScanInPlaceSerial(data, function, 0, data.Length, 1); + } + else if (loadBalance) + { + InclusiveScanInPlaceWithLoadBalancingParallel(data, function, 0, data.Length, 1); + } + else // parallel, non-loadbalance + { + InclusiveScanInPlaceParallel(data, function); + } + } + + /// Computes a sequential prefix scan over the array using the specified function. + /// The type of the data in the array. + /// The data, which will be overwritten with the computed prefix scan. + /// The function to use for the scan. + /// The start of the data in arr over which the scan is being computed. + /// The length of the data in arr over which the scan is being computed. + /// The inclusive distance between elements over which the scan is being computed. + /// No parameter validation is performed. + private static void InclusiveScanInPlaceSerial(T[] arr, Func function, int arrStart, int arrLength, int skip) + { + for (int i = arrStart; i + skip < arrLength; i += skip) + { + arr[i + skip] = function(arr[i], arr[i + skip]); + } + } + + /// Computes a sequential exclusive prefix scan over the array using the specified function. + /// The data, which will be overwritten with the computed prefix scan. + /// The function to use for the scan. + /// The inclusive lower bound of the array at which to start the scan. + /// The exclusive upper bound of the array at which to end the scan. + public static void ExclusiveScanInPlaceSerial(T[] arr, Func function, int lowerBoundInclusive, int upperBoundExclusive) + { + T total = arr[lowerBoundInclusive]; + arr[lowerBoundInclusive] = default(T); + for (int i = lowerBoundInclusive + 1; i < upperBoundExclusive; i++) + { + T prevTotal = total; + total = function(total, arr[i]); + arr[i] = prevTotal; + } + } + + /// Computes a parallel prefix scan over the array using the specified function. + /// The type of the data in the array. + /// The data, which will be overwritten with the computed prefix scan. + /// The function to use for the scan. + /// The start of the data in arr over which the scan is being computed. + /// The length of the data in arr over which the scan is being computed. + /// The inclusive distance between elements over which the scan is being computed. + /// No parameter validation is performed. + private static void InclusiveScanInPlaceWithLoadBalancingParallel(T[] arr, Func function, + int arrStart, int arrLength, int skip) + { + // If the length is 0 or 1, just return a copy of the original array. + if (arrLength <= 1) return; + int halfInputLength = arrLength / 2; + + // Pairwise combine. Use static partitioning, as the function + // is likely to be very small. + Parallel.For(0, halfInputLength, i => + { + int loc = arrStart + (i * 2 * skip); + arr[loc + skip] = function(arr[loc], arr[loc + skip]); + }); + + // Recursively prefix scan the pairwise computations. + InclusiveScanInPlaceWithLoadBalancingParallel(arr, function, arrStart + skip, halfInputLength, skip * 2); + + // Generate output. As before, use static partitioning. + Parallel.For(0, (arrLength % 2) == 0 ? halfInputLength - 1 : halfInputLength, i => + { + int loc = arrStart + (i * 2 * skip) + skip; + arr[loc + skip] = function(arr[loc], arr[loc + skip]); + }); + } + + /// Computes a parallel inclusive prefix scan over the array using the specified function. + public static void InclusiveScanInPlaceParallel(T[] arr, Func function) + { + int procCount = Environment.ProcessorCount; + T[] intermediatePartials = new T[procCount]; + using (var phaseBarrier = new Barrier(procCount, + _ => ExclusiveScanInPlaceSerial(intermediatePartials, function, 0, intermediatePartials.Length))) + { + // Compute the size of each range + int rangeSize = arr.Length / procCount; + int nextRangeStart = 0; + + // Create, store, and wait on all of the tasks + var tasks = new Task[procCount]; + for (int i = 0; i < procCount; i++, nextRangeStart += rangeSize) + { + // Get the range for each task, then start it + int rangeNum = i; + int lowerRangeInclusive = nextRangeStart; + int upperRangeExclusive = i < procCount - 1 ? nextRangeStart + rangeSize : arr.Length; + tasks[rangeNum] = Task.Factory.StartNew(() => + { + // Phase 1: Prefix scan assigned range, and copy upper bound to intermediate partials + InclusiveScanInPlaceSerial(arr, function, lowerRangeInclusive, upperRangeExclusive, 1); + intermediatePartials[rangeNum] = arr[upperRangeExclusive - 1]; + + // Phase 2: One thread only should prefix scan the intermediaries... done implicitly by the barrier + phaseBarrier.SignalAndWait(); + + // Phase 3: Incorporate partials + if (rangeNum != 0) + { + for (int j = lowerRangeInclusive; j < upperRangeExclusive; j++) + { + arr[j] = function(intermediatePartials[rangeNum], arr[j]); + } + } + }); + } + + // Wait for all of the tasks to complete + Task.WaitAll(tasks); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Sort.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Sort.cs new file mode 100644 index 0000000..123bbc8 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Sort.cs @@ -0,0 +1,240 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_Sort.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + public static partial class ParallelAlgorithms + { + /// Sorts an array in parallel. + /// Specifies the type of data in the array. + /// The array to be sorted. + public static void Sort(T [] array) + { + Sort(array, (IComparer)null); + } + + /// Sorts an array in parallel. + /// Specifies the type of data in the array. + /// The array to be sorted. + /// The comparer used to compare two elements during the sort operation. + public static void Sort(T[] array, IComparer comparer) + { + if (array == null) throw new ArgumentNullException("array"); + Sort(array, null, 0, array.Length, comparer); + } + + /// Sorts an array in parallel. + /// Specifies the type of data in the array. + /// The array to be sorted. + /// The index at which to start the sort, inclusive. + /// The number of elements to be sorted, starting at the start index. + public static void Sort(T [] array, Int32 index, Int32 length) + { + Sort(array, null, index, length, (IComparer)null); + } + + /// Sorts an array in parallel. + /// Specifies the type of data in the array. + /// The array to be sorted. + /// The index at which to start the sort, inclusive. + /// The number of elements to be sorted, starting at the start index. + /// The comparer used to compare two elements during the sort operation. + public static void Sort(T[] array, Int32 index, Int32 length, IComparer comparer) + { + Sort(array, null, index, length, comparer); + } + + /// Sorts key/value arrays in parallel. + /// Specifies the type of the data in the keys array. + /// Specifies the type of the data in the items array. + /// The keys to be sorted. + /// The items to be sorted based on the corresponding keys. + public static void Sort(TKey[] keys, TValue[] items) + { + Sort(keys, items, 0, keys.Length, (IComparer)null); + } + + /// Sorts key/value arrays in parallel. + /// Specifies the type of the data in the keys array. + /// Specifies the type of the data in the items array. + /// The keys to be sorted. + /// The items to be sorted based on the corresponding keys. + /// The comparer used to compare two elements during the sort operation. + public static void Sort(TKey[] keys, TValue[] items, IComparer comparer) + { + if (keys == null) throw new ArgumentNullException("keys"); + Sort(keys, items, 0, keys.Length, comparer); + } + + /// Sorts key/value arrays in parallel. + /// Specifies the type of the data in the keys array. + /// Specifies the type of the data in the items array. + /// The keys to be sorted. + /// The items to be sorted based on the corresponding keys. + /// The index at which to start the sort, inclusive. + /// The number of elements to be sorted, starting at the start index. + public static void Sort(TKey[] keys, TValue[] items, Int32 index, Int32 length) + { + Sort(keys, items, index, length, (IComparer)null); + } + + /// Sorts key/value arrays in parallel. + /// Specifies the type of the data in the keys array. + /// Specifies the type of the data in the items array. + /// The keys to be sorted. + /// The items to be sorted based on the corresponding keys. + /// The index at which to start the sort, inclusive. + /// The number of elements to be sorted, starting at the start index. + /// The comparer used to compare two elements during the sort operation. + public static void Sort(TKey [] keys, TValue [] items, Int32 index, Int32 length, IComparer comparer) + { + if (keys == null) throw new ArgumentNullException("keys"); + if ((index < 0) || (length < 0)) throw new ArgumentOutOfRangeException(length < 0 ? "length" : "index"); + if (((keys.Length - index) < length) || ((items != null) && (index > (items.Length - length)))) throw new ArgumentException("index"); + + // Run the core sort operation + new Sorter(keys, items, comparer).QuickSort(index, index + length - 1); + } + + // Stores the data necessary for the sort, and provides the core sorting method + private sealed class Sorter + { + private TKey[] _keys; + private TItem[] _items; + private IComparer _comparer; + + public Sorter(TKey[] keys, TItem[] items, IComparer comparer) + { + if (comparer == null) comparer = Comparer.Default; + _keys = keys; + _items = items; + _comparer = comparer; + } + + // Gets a recommended depth for recursion. This assumes that every level will + // spawn two child tasks, which isn't actually the case with the algorithm, but + // it's a "good enough" approximation. + private static int GetMaxDepth() + { + return (int)Math.Log(Environment.ProcessorCount, 2); + } + + // Swaps the items at the two specified indexes if they need to be swapped + internal void SwapIfGreaterWithItems(int a, int b) + { + if (a != b) + { + if (_comparer.Compare(_keys[a], _keys[b]) > 0) + { + TKey temp = _keys[a]; + _keys[a] = _keys[b]; + _keys[b] = temp; + if (_items != null) + { + TItem item = _items[a]; + _items[a] = _items[b]; + _items[b] = item; + } + } + } + } + + // Gets the middle value between the provided low and high + private static int GetMiddle(int low, int high) { return low + ((high - low) >> 1); } + + // Does a quicksort of the stored data, between the positions (inclusive specified by left and right) + internal void QuickSort(int left, int right) + { + QuickSort(left, right, 0, GetMaxDepth()); + } + + // Does a quicksort of the stored data, between the positions (inclusive specified by left and right). + // Depth specifies the current recursion depth, while maxDepth specifies the maximum depth + // we should recur to until we switch over to sequential. + internal void QuickSort(int left, int right, int depth, int maxDepth) + { + const int SEQUENTIAL_THRESHOLD = 0x1000; + + // If the max depth has been reached or if we've hit the sequential + // threshold for the input array size, run sequential. + if (depth >= maxDepth || (right - left + 1) <= SEQUENTIAL_THRESHOLD) + { + Array.Sort(_keys, _items, left, right - left + 1, _comparer); + return; + } + + // Store all tasks generated to process subarrays + List tasks = new List(); + + // Run the same basic algorithm used by Array.Sort, but spawning Tasks for all recursive calls + do + { + int i = left; + int j = right; + + // Pre-sort the low, middle (pivot), and high values in place. + int middle = GetMiddle(i, j); + SwapIfGreaterWithItems(i, middle); // swap the low with the mid point + SwapIfGreaterWithItems(i, j); // swap the low with the high + SwapIfGreaterWithItems(middle, j); // swap the middle with the high + + // Get the pivot + TKey x = _keys[middle]; + + // Move all data around the pivot value + do + { + while (_comparer.Compare(_keys[i], x) < 0) i++; + while (_comparer.Compare(x, _keys[j]) < 0) j--; + + if (i > j) break; + if (i < j) + { + TKey key = _keys[i]; + _keys[i] = _keys[j]; + _keys[j] = key; + if (_items != null) + { + TItem item = _items[i]; + _items[i] = _items[j]; + _items[j] = item; + } + } + i++; + j--; + } while (i <= j); + + if (j - left <= right - i) + { + if (left < j) + { + int leftcopy = left, jcopy = j; + tasks.Add(Task.Factory.StartNew(() => QuickSort(leftcopy, jcopy, depth + 1, maxDepth))); + } + left = i; + } + else + { + if (i < right) + { + int icopy = i, rightcopy = right; + tasks.Add(Task.Factory.StartNew(() => QuickSort(icopy, rightcopy, depth + 1, maxDepth))); + } + right = j; + } + } while (left < right); + + // Wait for all of this level's tasks to complete + Task.WaitAll(tasks.ToArray()); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_SpeculativeFor.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_SpeculativeFor.cs new file mode 100644 index 0000000..8beeffc --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_SpeculativeFor.cs @@ -0,0 +1,59 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_SpeculativeFor.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + public static partial class ParallelAlgorithms + { + /// Executes a function for each value in a range, returning the first result achieved and ceasing execution. + /// The type of the data returned. + /// The start of the range, inclusive. + /// The end of the range, exclusive. + /// The options to use for processing the loop. + /// The function to execute for each element. + /// The result computed. + public static TResult SpeculativeFor( + int fromInclusive, int toExclusive, Func body) + { + return SpeculativeFor(fromInclusive, toExclusive, s_defaultParallelOptions, body); + } + + /// Executes a function for each value in a range, returning the first result achieved and ceasing execution. + /// The type of the data returned. + /// The start of the range, inclusive. + /// The end of the range, exclusive. + /// The options to use for processing the loop. + /// The function to execute for each element. + /// The result computed. + public static TResult SpeculativeFor( + int fromInclusive, int toExclusive, ParallelOptions options, Func body) + { + // Validate parameters; the Parallel.For we delegate to will validate the rest + if (body == null) throw new ArgumentNullException("body"); + + // Store one result. We box it if it's a value type to avoid torn writes and enable + // CompareExchange even for value types. + object result = null; + + // Run all bodies in parallel, stopping as soon as one has completed. + Parallel.For(fromInclusive, toExclusive, options, (i, loopState) => + { + // Run an iteration. When it completes, store (box) + // the result, and cancel the rest + Interlocked.CompareExchange(ref result, (object)body(i), null); + loopState.Stop(); + }); + + // Return the computed result + return (TResult)result; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_SpeculativeForEach.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_SpeculativeForEach.cs new file mode 100644 index 0000000..2c6cc54 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_SpeculativeForEach.cs @@ -0,0 +1,58 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_SpeculativeForEach.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + public static partial class ParallelAlgorithms + { + /// Executes a function for each element in a source, returning the first result achieved and ceasing execution. + /// The type of the data in the source. + /// The type of the data returned. + /// The input elements to be processed. + /// The function to execute for each element. + /// The result computed. + public static TResult SpeculativeForEach(IEnumerable source, Func body) + { + // Run with default options + return SpeculativeForEach(source, s_defaultParallelOptions, body); + } + + /// Executes a function for each element in a source, returning the first result achieved and ceasing execution. + /// The type of the data in the source. + /// The type of the data returned. + /// The input elements to be processed. + /// The options to use for processing the loop. + /// The function to execute for each element. + /// The result computed. + public static TResult SpeculativeForEach( + IEnumerable source, ParallelOptions options, Func body) + { + // Validate parameters; the Parallel.ForEach we delegate to will validate the rest + if (body == null) throw new ArgumentNullException("body"); + + // Store one result. We box it if it's a value type to avoid torn writes and enable + // CompareExchange even for value types. + object result = null; + + // Run all bodies in parallel, stopping as soon as one has completed. + Parallel.ForEach(source, options, (item, loopState) => + { + // Run an iteration. When it completes, store (box) + // the result, and cancel the rest + Interlocked.CompareExchange(ref result, (object)body(item), null); + loopState.Stop(); + }); + + // Return the computed result + return (TResult)result; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_SpeculativeInvoke.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_SpeculativeInvoke.cs new file mode 100644 index 0000000..f8e1a8e --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_SpeculativeInvoke.cs @@ -0,0 +1,41 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_SpeculativeInvoke.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent.Partitioners; +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + public static partial class ParallelAlgorithms + { + /// Invokes the specified functions, potentially in parallel, canceling outstanding invocations once one completes. + /// Specifies the type of data returned by the functions. + /// The functions to be executed. + /// A result from executing one of the functions. + public static T SpeculativeInvoke(params Func[] functions) + { + // Run with default options + return SpeculativeInvoke(s_defaultParallelOptions, functions); + } + + /// Invokes the specified functions, potentially in parallel, canceling outstanding invocations once one completes. + /// Specifies the type of data returned by the functions. + /// The options to use for the execution. + /// The functions to be executed. + /// A result from executing one of the functions. + public static T SpeculativeInvoke(ParallelOptions options, params Func[] functions) + { + // Validate parameters + if (options == null) throw new ArgumentNullException("options"); + if (functions == null) throw new ArgumentNullException("functions"); + + // Speculatively invoke each function + return ParallelAlgorithms.SpeculativeForEach(functions, options, function => function()); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Wavefront.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Wavefront.cs new file mode 100644 index 0000000..1870465 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_Wavefront.cs @@ -0,0 +1,119 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_Wavefront.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + public static partial class ParallelAlgorithms + { + /// Process in parallel a matrix where every cell has a dependency on the cell above it and to its left. + /// The number of rows in the matrix. + /// The number of columns in the matrix. + /// Partition the matrix into this number of blocks along the rows. + /// Partition the matrix into this number of blocks along the columns. + /// The action to invoke for every block, supplied with the start and end indices of the rows and columns. + public static void Wavefront( + int numRows, int numColumns, + int numBlocksPerRow, int numBlocksPerColumn, + Action processBlock) + { + // Validate parameters + if (numRows <= 0) throw new ArgumentOutOfRangeException("numRows"); + if (numColumns <= 0) throw new ArgumentOutOfRangeException("numColumns"); + if (numBlocksPerRow <= 0 || numBlocksPerRow > numRows) + throw new ArgumentOutOfRangeException("numBlocksPerRow"); + if (numBlocksPerColumn <= 0 || numBlocksPerColumn > numColumns) + throw new ArgumentOutOfRangeException("numBlocksPerColumn"); + if (processBlock == null) + throw new ArgumentNullException("processRowColumnCell"); + + // Compute the size of each block + int rowBlockSize = numRows / numBlocksPerRow; + int columnBlockSize = numColumns / numBlocksPerColumn; + + Wavefront(numBlocksPerRow, numBlocksPerColumn, (row, column) => + { + int start_i = row * rowBlockSize; + int end_i = row < numBlocksPerRow - 1 ? + start_i + rowBlockSize : numRows; + + int start_j = column * columnBlockSize; + int end_j = column < numBlocksPerColumn - 1 ? + start_j + columnBlockSize : numColumns; + + processBlock(start_i, end_i, start_j, end_j); + }); + } + + /// Process in parallel a matrix where every cell has a dependency on the cell above it and to its left. + /// The number of rows in the matrix. + /// The number of columns in the matrix. + /// The action to invoke for every cell, supplied with the row and column indices. + public static void Wavefront(int numRows, int numColumns, Action processRowColumnCell) + { + // Validate parameters + if (numRows <= 0) throw new ArgumentOutOfRangeException("numRows"); + if (numColumns <= 0) throw new ArgumentOutOfRangeException("numColumns"); + if (processRowColumnCell == null) throw new ArgumentNullException("processRowColumnCell"); + + // Store the previous row of tasks as well as the previous task in the current row + Task[] prevTaskRow = new Task[numColumns]; + Task prevTaskInCurrentRow = null; + var dependencies = new Task[2]; + + // Create a task for each cell + for (int row = 0; row < numRows; row++) + { + prevTaskInCurrentRow = null; + for (int column = 0; column < numColumns; column++) + { + // In-scope locals for being captured in the task closures + int j = row, i = column; + + // Create a task with the appropriate dependencies. + Task curTask; + if (row == 0 && column == 0) + { + // Upper-left task kicks everything off, having no dependencies + curTask = Task.Factory.StartNew(() => processRowColumnCell(j, i)); + } + else if (row == 0 || column == 0) + { + // Tasks in the left-most column depend only on the task above them, and + // tasks in the top row depend only on the task to their left + var antecedent = column == 0 ? prevTaskRow[0] : prevTaskInCurrentRow; + curTask = antecedent.ContinueWith(p => + { + p.Wait(); // Necessary only to propagate exceptions + processRowColumnCell(j, i); + }); + } + else // row > 0 && column > 0 + { + // All other tasks depend on both the tasks above and to the left + dependencies[0] = prevTaskInCurrentRow; + dependencies[1] = prevTaskRow[column]; + curTask = Task.Factory.ContinueWhenAll(dependencies, ps => + { + Task.WaitAll(ps); // Necessary only to propagate exceptions + processRowColumnCell(j, i); + }); + } + + // Keep track of the task just created for future iterations + prevTaskRow[column] = prevTaskInCurrentRow = curTask; + } + } + + // Wait for the last task to be done. + prevTaskInCurrentRow.Wait(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_While.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_While.cs new file mode 100644 index 0000000..c3f8d0f --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_While.cs @@ -0,0 +1,46 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_While.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent.Partitioners; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + public static partial class ParallelAlgorithms + { + /// Repeatedly executes an operation in parallel while the specified condition evaluates to true. + /// The condition to evaluate. + /// The loop body. + public static void ParallelWhile(Func condition, Action body) + { + // Just delegate to the overload that accepts a ParallelOptions + ParallelWhile(s_defaultParallelOptions, condition, body); + } + + /// Repeatedly executes an operation in parallel while the specified condition evaluates to true. + /// A ParallelOptions instance that configures the behavior of this operation. + /// The condition to evaluate. + /// The loop body. + public static void ParallelWhile( + ParallelOptions parallelOptions, Func condition, Action body) + { + if (parallelOptions == null) throw new ArgumentNullException("parallelOptions"); + if (condition == null) throw new ArgumentNullException("condition"); + if (body == null) throw new ArgumentNullException("body"); + + Parallel.ForEach(SingleItemPartitioner.Create(IterateUntilFalse(condition)), parallelOptions, ignored => body()); + } + + // Continually yield values until condition returns false + private static IEnumerable IterateUntilFalse(Func condition) + { + while (condition()) yield return true; + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_WhileNotEmpty.cs b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_WhileNotEmpty.cs new file mode 100644 index 0000000..e227543 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelAlgorithms/ParallelAlgorithms_WhileNotEmpty.cs @@ -0,0 +1,64 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ParallelAlgorithms_WhileNotEmpty.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace System.Threading.Algorithms +{ + public static partial class ParallelAlgorithms + { + /// Processes data in parallel, allowing the processing function to add more data to be processed. + /// Specifies the type of data being processed. + /// The initial set of data to be processed. + /// The operation to execute for each value. + public static void WhileNotEmpty(IEnumerable initialValues, Action> body) + { + WhileNotEmpty(s_defaultParallelOptions, initialValues, body); + } + + /// Processes data in parallel, allowing the processing function to add more data to be processed. + /// Specifies the type of data being processed. + /// A ParallelOptions instance that configures the behavior of this operation. + /// The initial set of data to be processed. + /// The operation to execute for each value. + public static void WhileNotEmpty( + ParallelOptions parallelOptions, + IEnumerable initialValues, + Action> body) + { + // Validate arguments + if (parallelOptions == null) throw new ArgumentNullException("parallelOptions"); + if (initialValues == null) throw new ArgumentNullException("initialValues"); + if (body == null) throw new ArgumentNullException("body"); + + // Create two lists to alternate between as source and destination. + var lists = new[] { new ConcurrentStack(initialValues), new ConcurrentStack() }; + + // Iterate until no more items to be processed + for (int i = 0; ; i++) + { + // Determine which list is the source and which is the destination + int fromIndex = i % 2; + var from = lists[fromIndex]; + var to = lists[fromIndex ^ 1]; + + // If the source is empty, we're done + if (from.IsEmpty) break; + + // Otherwise, process all source items in parallel, adding any new items into the destination + Action adder = newItem => to.Push(newItem); + Parallel.ForEach(from, parallelOptions, e => body(e, adder)); + + // Clear out the source as it's now been fully processed + from.Clear(); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelExtensionsExtras.csproj b/trunk/Libraries/ParallelExtensionsExtras/ParallelExtensionsExtras.csproj new file mode 100644 index 0000000..b2bf97d --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelExtensionsExtras.csproj @@ -0,0 +1,164 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3} + Library + Properties + ParallelExtensionsExtras + ParallelExtensionsExtras + v4.0 + 512 + 1 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + False + False + True + False + False + False + False + False + False + False + False + True + False + False + True + + + + + + + False + Full + Build + 0 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/ParallelExtensionsExtras.sln b/trunk/Libraries/ParallelExtensionsExtras/ParallelExtensionsExtras.sln new file mode 100644 index 0000000..2a3a098 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/ParallelExtensionsExtras.sln @@ -0,0 +1,20 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParallelExtensionsExtras", "ParallelExtensionsExtras.csproj", "{C45218F8-09E7-4F57-85BC-5D8D2AC736A3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/trunk/Libraries/ParallelExtensionsExtras/Partitioners/ChunkPartitioner.cs b/trunk/Libraries/ParallelExtensionsExtras/Partitioners/ChunkPartitioner.cs new file mode 100644 index 0000000..744f54b --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Partitioners/ChunkPartitioner.cs @@ -0,0 +1,290 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ChunkPartitioner.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Threading; + +namespace System.Collections.Concurrent.Partitioners +{ + /// + /// Partitions an enumerable into chunks based on user-supplied criteria. + /// + public static class ChunkPartitioner + { + /// Creates a partitioner that chooses the next chunk size based on a user-supplied function. + /// The type of the data being partitioned. + /// The data being partitioned. + /// A function that determines the next chunk size based on the + /// previous chunk size. + /// A partitioner. + public static OrderablePartitioner Create( + IEnumerable source, Func nextChunkSizeFunc) + { + return new ChunkPartitioner(source, nextChunkSizeFunc); + } + + /// Creates a partitioner that always uses a user-specified chunk size. + /// The type of the data being partitioned. + /// The data being partitioned. + /// The chunk size to be used. + /// A partitioner. + public static OrderablePartitioner Create( + IEnumerable source, int chunkSize) + { + return new ChunkPartitioner(source, chunkSize); + } + + /// Creates a partitioner that chooses chunk sizes between the user-specified min and max. + /// The type of the data being partitioned. + /// The data being partitioned. + /// The minimum chunk size to use. + /// The maximum chunk size to use. + /// A partitioner. + public static OrderablePartitioner Create( + IEnumerable source, int minChunkSize, int maxChunkSize) + { + return new ChunkPartitioner(source, minChunkSize, maxChunkSize); + } + } + + /// + /// Partitions an enumerable into chunks based on user-supplied criteria. + /// + internal sealed class ChunkPartitioner : OrderablePartitioner + { + private readonly IEnumerable _source; + private readonly Func _nextChunkSizeFunc; + + public ChunkPartitioner(IEnumerable source, Func nextChunkSizeFunc) + // The keys will be ordered across both individual partitions and across partitions, + // and they will be normalized. + : base(true, true, true) + { + // Validate and store the enumerable and function (used to determine how big + // to make the next chunk given the current chunk size) + if (source == null) throw new ArgumentNullException("source"); + if (nextChunkSizeFunc == null) throw new ArgumentNullException("nextChunkSizeFunc"); + _source = source; + _nextChunkSizeFunc = nextChunkSizeFunc; + } + + public ChunkPartitioner(IEnumerable source, int chunkSize) + : this(source, prev => chunkSize) // uses a function that always returns the specified chunk size + { + if (chunkSize <= 0) throw new ArgumentOutOfRangeException("chunkSize"); + } + + public ChunkPartitioner(IEnumerable source, int minChunkSize, int maxChunkSize) : + this(source, CreateFuncFromMinAndMax(minChunkSize, maxChunkSize)) // uses a function that grows from min to max + { + if (minChunkSize <= 0 || + minChunkSize > maxChunkSize) throw new ArgumentOutOfRangeException("minChunkSize"); + } + + private static Func CreateFuncFromMinAndMax(int minChunkSize, int maxChunkSize) + { + // Create a function that returns exponentially growing chunk sizes between minChunkSize and maxChunkSize + return delegate(int prev) + { + if (prev < minChunkSize) return minChunkSize; + if (prev >= maxChunkSize) return maxChunkSize; + int next = prev * 2; + if (next >= maxChunkSize || next < 0) return maxChunkSize; + return next; + }; + } + + /// + /// Partitions the underlying collection into the specified number of orderable partitions. + /// + /// The number of partitions to create. + /// An object that can create partitions over the underlying data source. + public override IList>> GetOrderablePartitions(int partitionCount) + { + // Validate parameters + if (partitionCount <= 0) throw new ArgumentOutOfRangeException("partitionCount"); + + // Create an array of dynamic partitions and return them + var partitions = new IEnumerator>[partitionCount]; + var dynamicPartitions = GetOrderableDynamicPartitions(true); + for (int i = 0; i < partitionCount; i++) + { + partitions[i] = dynamicPartitions.GetEnumerator(); // Create and store the next partition + } + return partitions; + } + + /// Gets whether additional partitions can be created dynamically. + public override bool SupportsDynamicPartitions { get { return true; } } + + /// + /// Creates an object that can partition the underlying collection into a variable number of + /// partitions. + /// + /// + /// An object that can create partitions over the underlying data source. + /// + public override IEnumerable> GetOrderableDynamicPartitions() + { + return new EnumerableOfEnumerators(this, false); + } + + private IEnumerable> GetOrderableDynamicPartitions(bool referenceCountForDisposal) + { + return new EnumerableOfEnumerators(this, referenceCountForDisposal); + } + + // The object used to dynamically create partitions + private class EnumerableOfEnumerators : IEnumerable>, IDisposable + { + private readonly ChunkPartitioner _parentPartitioner; + private readonly object _sharedLock = new object(); + private readonly IEnumerator _sharedEnumerator; + private long _nextSharedIndex; + private int _activeEnumerators; + private bool _noMoreElements; + private bool _disposed; + private bool _referenceCountForDisposal; + + public EnumerableOfEnumerators(ChunkPartitioner parentPartitioner, bool referenceCountForDisposal) + { + // Validate parameters + if (parentPartitioner == null) throw new ArgumentNullException("parentPartitioner"); + + // Store the data, including creating an enumerator from the underlying data source + _parentPartitioner = parentPartitioner; + _sharedEnumerator = parentPartitioner._source.GetEnumerator(); + _nextSharedIndex = -1; + _referenceCountForDisposal = referenceCountForDisposal; + } + + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + public IEnumerator> GetEnumerator() + { + if (_referenceCountForDisposal) + { + Interlocked.Increment(ref _activeEnumerators); + } + return new Enumerator(this); + } + + private void DisposeEnumerator(Enumerator enumerator) + { + if (_referenceCountForDisposal) + { + if (Interlocked.Decrement(ref _activeEnumerators) == 0) + { + _sharedEnumerator.Dispose(); + } + } + } + + private class Enumerator : IEnumerator> + { + private EnumerableOfEnumerators _parentEnumerable; + private List> _currentChunk = new List>(); + private int _currentChunkCurrentIndex; + private int _lastRequestedChunkSize; + private bool _disposed; + + public Enumerator(EnumerableOfEnumerators parentEnumerable) + { + if (parentEnumerable == null) throw new ArgumentNullException("parentEnumerable"); + _parentEnumerable = parentEnumerable; + } + + public bool MoveNext() + { + if (_disposed) throw new ObjectDisposedException(GetType().Name); + + // Move to the next cached element. If we already retrieved a chunk and if there's still + // data left in it, just use the next item from it. + ++_currentChunkCurrentIndex; + if (_currentChunkCurrentIndex >= 0 && + _currentChunkCurrentIndex < _currentChunk.Count) return true; + + // First, figure out how much new data we want. The previous requested chunk size is used + // as input to figure out how much data the user now wants. The initial chunk size + // supplied is 0 so that the user delegate is made aware that this is the initial request + // such that it can select the initial chunk size on first request. + int nextChunkSize = _parentEnumerable._parentPartitioner._nextChunkSizeFunc(_lastRequestedChunkSize); + if (nextChunkSize <= 0) throw new InvalidOperationException( + "Invalid chunk size requested: chunk sizes must be positive."); + _lastRequestedChunkSize = nextChunkSize; + + // Reset the list + _currentChunk.Clear(); + _currentChunkCurrentIndex = 0; + if (nextChunkSize > _currentChunk.Capacity) _currentChunk.Capacity = nextChunkSize; + + // Try to grab the next chunk of data + lock (_parentEnumerable._sharedEnumerator) + { + // If we've already discovered that no more elements exist (and we've gotten this + // far, which means we don't have any elements cached), we're done. + if (_parentEnumerable._noMoreElements) return false; + + // Get another chunk + for (int i = 0; i < nextChunkSize; i++) + { + // If there are no more elements to be retrieved from the shared enumerator, mark + // that so that other partitions don't have to check again. Return whether we + // were able to retrieve any data at all. + if (!_parentEnumerable._sharedEnumerator.MoveNext()) + { + _parentEnumerable._noMoreElements = true; + return _currentChunk.Count > 0; + } + + ++_parentEnumerable._nextSharedIndex; + _currentChunk.Add(new KeyValuePair( + _parentEnumerable._nextSharedIndex, + _parentEnumerable._sharedEnumerator.Current)); + } + } + + // We got at least some data + return true; + } + + public KeyValuePair Current + { + get + { + if (_currentChunkCurrentIndex >= _currentChunk.Count) + { + throw new InvalidOperationException("There is no current item."); + } + return _currentChunk[_currentChunkCurrentIndex]; + } + } + + public void Dispose() + { + if (!_disposed) + { + _parentEnumerable.DisposeEnumerator(this); + _disposed = true; + } + } + + object IEnumerator.Current { get { return Current; } } + public void Reset() { throw new NotSupportedException(); } + } + + public void Dispose() + { + if (!_disposed) + { + if (!_referenceCountForDisposal) _sharedEnumerator.Dispose(); + _disposed = true; + } + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Partitioners/SingleItemPartitioner.cs b/trunk/Libraries/ParallelExtensionsExtras/Partitioners/SingleItemPartitioner.cs new file mode 100644 index 0000000..48a469d --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Partitioners/SingleItemPartitioner.cs @@ -0,0 +1,173 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: SingleItemPartitioner.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace System.Collections.Concurrent.Partitioners +{ + /// Partitions a data source one item at a time. + public static class SingleItemPartitioner + { + /// Creates a partitioner for an enumerable that partitions it one item at a time. + /// Specifies the type of data contained in the enumerable. + /// The source enumerable to be partitioned. + /// The partitioner. + public static OrderablePartitioner Create(IEnumerable source) + { + if (source == null) throw new ArgumentNullException("source"); + else if (source is IList) return new SingleItemIListPartitioner((IList)source); + else return new SingleItemEnumerablePartitioner(source); + } + + /// Partitions an enumerable one item at a time. + /// Specifies the type of data contained in the list. + private sealed class SingleItemEnumerablePartitioner : OrderablePartitioner + { + /// The enumerable to be partitioned. + private readonly IEnumerable _source; + + /// Initializes the partitioner. + /// The enumerable to be partitioned. + internal SingleItemEnumerablePartitioner(IEnumerable source) : base(true, false, true) { _source = source; } + + /// Gets whether this partitioner supports dynamic partitioning (it does). + public override bool SupportsDynamicPartitions { get { return true; } } + + public override IList>> GetOrderablePartitions(int partitionCount) + { + if (partitionCount < 1) throw new ArgumentOutOfRangeException("partitionCount"); + var dynamicPartitioner = new DynamicGenerator(_source.GetEnumerator(), false); + return (from i in Enumerable.Range(0, partitionCount) select dynamicPartitioner.GetEnumerator()).ToList(); + } + + /// Gets a list of the specified static number of partitions. + /// The static number of partitions to create. + /// The list of created partitions ready to be iterated. + public override IEnumerable> GetOrderableDynamicPartitions() + { + return new DynamicGenerator(_source.GetEnumerator(), true); + } + + /// Dynamically generates a partitions on a shared enumerator. + private class DynamicGenerator : IEnumerable>, IDisposable + { + /// The source enumerator shared amongst all partitions. + private readonly IEnumerator _sharedEnumerator; + /// The next available position to be yielded. + private long _nextAvailablePosition; + /// The number of partitions remaining to be disposed, potentially including this dynamic generator. + private int _remainingPartitions; + /// Whether this dynamic partitioner has been disposed. + private bool _disposed; + + /// Initializes the dynamic generator. + /// The enumerator shared by all partitions. + /// Whether this generator will be disposed. + public DynamicGenerator(IEnumerator sharedEnumerator, bool requiresDisposal) + { + _sharedEnumerator = sharedEnumerator; + _nextAvailablePosition = -1; + _remainingPartitions = requiresDisposal ? 1 : 0; + } + + /// Closes the shared enumerator if all other partitions have completed. + void IDisposable.Dispose() + { + if (!_disposed && Interlocked.Decrement(ref _remainingPartitions) == 0) + { + _disposed = true; + _sharedEnumerator.Dispose(); + } + } + + /// Increments the number of partitions in use and returns a new partition. + /// The new partition. + public IEnumerator> GetEnumerator() + { + Interlocked.Increment(ref _remainingPartitions); + return GetEnumeratorCore(); + } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + + /// Creates a partition. + /// The new partition. + private IEnumerator> GetEnumeratorCore() + { + try + { + while (true) + { + T nextItem; + long position; + lock (_sharedEnumerator) + { + if (_sharedEnumerator.MoveNext()) + { + position = _nextAvailablePosition++; + nextItem = _sharedEnumerator.Current; + } + else yield break; + } + yield return new KeyValuePair(position, nextItem); + } + } + finally { if (Interlocked.Decrement(ref _remainingPartitions) == 0) _sharedEnumerator.Dispose(); } + } + } + } + + /// Partitions a list one item at a time. + /// Specifies the type of data contained in the list. + private sealed class SingleItemIListPartitioner : OrderablePartitioner + { + /// The list to be partitioned. + private readonly IList _source; + + /// Initializes the partitioner. + /// The list to be partitioned. + internal SingleItemIListPartitioner(IList source) : base(true, false, true) { _source = source; } + + /// Gets whether this partitioner supports dynamic partitioning (it does). + public override bool SupportsDynamicPartitions { get { return true; } } + + /// Gets a list of the specified static number of partitions. + /// The static number of partitions to create. + /// The list of created partitions ready to be iterated. + public override IList>> GetOrderablePartitions(int partitionCount) + { + if (partitionCount < 1) throw new ArgumentOutOfRangeException("partitionCount"); + var dynamicPartitioner = GetOrderableDynamicPartitions(); + return (from i in Enumerable.Range(0, partitionCount) select dynamicPartitioner.GetEnumerator()).ToList(); + } + + /// Creates a dynamic partitioner for creating a dynamic number of partitions. + /// The dynamic partitioner. + public override IEnumerable> GetOrderableDynamicPartitions() + { + return GetOrderableDynamicPartitionsCore(_source, new StrongBox(0)); + } + + /// An enumerable that creates individual enumerators that all work together to partition the list. + /// The list being partitioned. + /// An integer shared between partitions denoting the next available index in the source. + /// An enumerable that generates enumerators which participate in partitioning the list. + private static IEnumerable> GetOrderableDynamicPartitionsCore(IList source, StrongBox nextIteration) + { + while (true) + { + var iteration = Interlocked.Increment(ref nextIteration.Value) - 1; + if (iteration >= 0 && iteration < source.Count) yield return new KeyValuePair(iteration, source[iteration]); + else yield break; + } + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Properties/AssemblyInfo.cs b/trunk/Libraries/ParallelExtensionsExtras/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..c6af0fd --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Properties/AssemblyInfo.cs @@ -0,0 +1,24 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: AssemblyInfo.cs +// +//-------------------------------------------------------------------------- + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Parallel Extensions Extras")] +[assembly: AssemblyDescription("Samples and extra functionality for use with Parallel Extensions to the .NET Framework")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Parallel Extensions Extras")] +[assembly: AssemblyCopyright("Copyright © Microsoft Corporation. All rights reserved.")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] +[assembly: Guid("ae914514-52ad-4769-a503-654f1a708dcc")] +[assembly: AssemblyVersion("1.2.0.0")] +[assembly: AssemblyFileVersion("1.2.0.0")] \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/ConcurrentExclusiveInterleave.cs b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/ConcurrentExclusiveInterleave.cs new file mode 100644 index 0000000..cafb815 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/ConcurrentExclusiveInterleave.cs @@ -0,0 +1,282 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ConcurrentExclusiveInterleave.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Diagnostics; + +namespace System.Threading.Tasks.Schedulers +{ + /// Provides concurrent and exclusive task schedulers that coordinate. + [DebuggerDisplay("ConcurrentTasksWaiting={ConcurrentTaskCount}, ExclusiveTasksWaiting={ExclusiveTaskCount}")] + [DebuggerTypeProxy(typeof(ConcurrentExclusiveInterleaveDebugView))] + public sealed class ConcurrentExclusiveInterleave + { + /// Provides a debug view for ConcurrentExclusiveInterleave. + internal class ConcurrentExclusiveInterleaveDebugView + { + /// The interleave being debugged. + private ConcurrentExclusiveInterleave _interleave; + + /// Initializes the debug view. + /// The interleave being debugged. + public ConcurrentExclusiveInterleaveDebugView(ConcurrentExclusiveInterleave interleave) + { + if (interleave == null) throw new ArgumentNullException("interleave"); + _interleave = interleave; + } + + public IEnumerable ExclusiveTasksWaiting { get { return _interleave._exclusiveTaskScheduler.Tasks; } } + /// Gets the number of tasks waiting to run concurrently. + public IEnumerable ConcurrentTasksWaiting { get { return _interleave._concurrentTaskScheduler.Tasks; } } + /// Gets a description of the processing task for debugging purposes. + public Task InterleaveTask { get { return _interleave._taskExecuting; } } + } + + /// Synchronizes all activity in this type and its generated schedulers. + private readonly object _internalLock; + /// The parallel options used by the asynchronous task and parallel loops. + private ParallelOptions _parallelOptions; + /// The scheduler used to queue and execute "reader" tasks that may run concurrently with other readers. + private ConcurrentExclusiveTaskScheduler _concurrentTaskScheduler; + /// The scheduler used to queue and execute "writer" tasks that must run exclusively while no other tasks for this interleave are running. + private ConcurrentExclusiveTaskScheduler _exclusiveTaskScheduler; + /// Whether this interleave has queued its processing task. + private Task _taskExecuting; + /// Whether the exclusive processing of a task should include all of its children as well. + private bool _exclusiveProcessingIncludesChildren; + + /// Initialies the ConcurrentExclusiveInterleave. + public ConcurrentExclusiveInterleave() : + this(TaskScheduler.Current, false) {} + + /// Initialies the ConcurrentExclusiveInterleave. + /// Whether the exclusive processing of a task should include all of its children as well. + public ConcurrentExclusiveInterleave(bool exclusiveProcessingIncludesChildren) : + this(TaskScheduler.Current, exclusiveProcessingIncludesChildren) {} + + /// Initialies the ConcurrentExclusiveInterleave. + /// The target scheduler on which this interleave should execute. + public ConcurrentExclusiveInterleave(TaskScheduler targetScheduler) : + this(targetScheduler, false) {} + + /// Initialies the ConcurrentExclusiveInterleave. + /// The target scheduler on which this interleave should execute. + /// Whether the exclusive processing of a task should include all of its children as well. + public ConcurrentExclusiveInterleave(TaskScheduler targetScheduler, bool exclusiveProcessingIncludesChildren) + { + // A scheduler must be provided + if (targetScheduler == null) throw new ArgumentNullException("targetScheduler"); + + // Create the state for this interleave + _internalLock = new object(); + _exclusiveProcessingIncludesChildren = exclusiveProcessingIncludesChildren; + _parallelOptions = new ParallelOptions() { TaskScheduler = targetScheduler }; + _concurrentTaskScheduler = new ConcurrentExclusiveTaskScheduler(this, new Queue(), targetScheduler.MaximumConcurrencyLevel); + _exclusiveTaskScheduler = new ConcurrentExclusiveTaskScheduler(this, new Queue(), 1); + } + + /// + /// Gets a TaskScheduler that can be used to schedule tasks to this interleave + /// that may run concurrently with other tasks on this interleave. + /// + public TaskScheduler ConcurrentTaskScheduler { get { return _concurrentTaskScheduler; } } + /// + /// Gets a TaskScheduler that can be used to schedule tasks to this interleave + /// that must run exclusively with regards to other tasks on this interleave. + /// + public TaskScheduler ExclusiveTaskScheduler { get { return _exclusiveTaskScheduler; } } + + /// Gets the number of tasks waiting to run exclusively. + private int ExclusiveTaskCount { get { lock (_internalLock) return _exclusiveTaskScheduler.Tasks.Count; } } + /// Gets the number of tasks waiting to run concurrently. + private int ConcurrentTaskCount { get { lock (_internalLock) return _concurrentTaskScheduler.Tasks.Count; } } + + /// Notifies the interleave that new work has arrived to be processed. + /// Must only be called while holding the lock. + internal void NotifyOfNewWork() + { + // If a task is already running, bail. + if (_taskExecuting != null) return; + + // Otherwise, run the processor. Store the task and then start it to ensure that + // the assignment happens before the body of the task runs. + _taskExecuting = new Task(ConcurrentExclusiveInterleaveProcessor, CancellationToken.None, TaskCreationOptions.None); + _taskExecuting.Start(_parallelOptions.TaskScheduler); + } + + /// The body of the async processor to be run in a Task. Only one should be running at a time. + /// This has been separated out into its own method to improve the Parallel Tasks window experience. + private void ConcurrentExclusiveInterleaveProcessor() + { + // Run while there are more tasks to be processed. We assume that the first time through, + // there are tasks. If they aren't, worst case is we try to process and find none. + bool runTasks = true; + bool cleanupOnExit = true; + while (runTasks) + { + try + { + // Process all waiting exclusive tasks + foreach (var task in GetExclusiveTasks()) + { + _exclusiveTaskScheduler.ExecuteTask(task); + + // Just because we executed the task doesn't mean it's "complete", + // if it has child tasks that have not yet completed + // and will complete later asynchronously. To account for this, + // if a task isn't yet completed, leave the interleave processor + // but leave it still in a running state. When the task completes, + // we'll come back in and keep going. Note that the children + // must not be scheduled to this interleave, or this will deadlock. + if (_exclusiveProcessingIncludesChildren && !task.IsCompleted) + { + cleanupOnExit = false; + task.ContinueWith(_ => ConcurrentExclusiveInterleaveProcessor(), _parallelOptions.TaskScheduler); + return; + } + } + + // Process all waiting concurrent tasks *until* any exclusive tasks show up, in which + // case we want to switch over to processing those (by looping around again). + Parallel.ForEach(GetConcurrentTasksUntilExclusiveExists(), _parallelOptions, + ExecuteConcurrentTask); + } + finally + { + if (cleanupOnExit) + { + lock (_internalLock) + { + // If there are no tasks remaining, we're done. If there are, loop around and go again. + if (_concurrentTaskScheduler.Tasks.Count == 0 && _exclusiveTaskScheduler.Tasks.Count == 0) + { + _taskExecuting = null; + runTasks = false; + } + } + } + } + } + } + + /// Runs a concurrent task. + /// The task to execute. + /// This has been separated out into its own method to improve the Parallel Tasks window experience. + private void ExecuteConcurrentTask(Task task) { _concurrentTaskScheduler.ExecuteTask(task); } + + /// + /// Gets an enumerable that yields waiting concurrent tasks one at a time until + /// either there are no more concurrent tasks or there are any exclusive tasks. + /// + private IEnumerable GetConcurrentTasksUntilExclusiveExists() + { + while (true) + { + Task foundTask = null; + lock (_internalLock) + { + if (_exclusiveTaskScheduler.Tasks.Count == 0 && + _concurrentTaskScheduler.Tasks.Count > 0) + { + foundTask = _concurrentTaskScheduler.Tasks.Dequeue(); + } + } + if (foundTask != null) yield return foundTask; + else yield break; + } + } + + /// + /// Gets an enumerable that yields all of the exclusive tasks one at a time. + /// + private IEnumerable GetExclusiveTasks() + { + while (true) + { + Task foundTask = null; + lock (_internalLock) + { + if (_exclusiveTaskScheduler.Tasks.Count > 0) foundTask = _exclusiveTaskScheduler.Tasks.Dequeue(); + } + if (foundTask != null) yield return foundTask; + else yield break; + } + } + + /// + /// A scheduler shim used to queue tasks to the interleave and execute those tasks on request of the interleave. + /// + private class ConcurrentExclusiveTaskScheduler : TaskScheduler + { + /// The parent interleave. + private readonly ConcurrentExclusiveInterleave _interleave; + /// The maximum concurrency level for the scheduler. + private readonly int _maximumConcurrencyLevel; + /// Whether a Task is currently being processed on this thread. + private ThreadLocal _processingTaskOnCurrentThread = new ThreadLocal(); + + /// Initializes the scheduler. + /// The parent interleave. + /// The queue to store queued tasks into. + internal ConcurrentExclusiveTaskScheduler(ConcurrentExclusiveInterleave interleave, Queue tasks, int maximumConcurrencyLevel) + { + if (interleave == null) throw new ArgumentNullException("interleave"); + if (tasks == null) throw new ArgumentNullException("tasks"); + _interleave = interleave; + _maximumConcurrencyLevel = maximumConcurrencyLevel; + Tasks = tasks; + } + + /// Gets the maximum concurrency level this scheduler is able to support. + public override int MaximumConcurrencyLevel { get { return _maximumConcurrencyLevel; } } + + /// Gets the queue of tasks for this scheduler. + internal Queue Tasks { get; private set; } + + /// Queues a task to the scheduler. + /// The task to be queued. + protected override void QueueTask(Task task) + { + lock (_interleave._internalLock) + { + Tasks.Enqueue(task); + _interleave.NotifyOfNewWork(); + } + } + + /// Executes a task on this scheduler. + /// The task to be executed. + internal void ExecuteTask(Task task) + { + var processingTaskOnCurrentThread = _processingTaskOnCurrentThread.Value; + if (!processingTaskOnCurrentThread) _processingTaskOnCurrentThread.Value = true; + base.TryExecuteTask(task); + if (!processingTaskOnCurrentThread) _processingTaskOnCurrentThread.Value = false; + } + + /// Tries to execute the task synchronously on this scheduler. + /// The task to execute. + /// Whether the task was previously queued to the scheduler. + /// true if the task could be executed; otherwise, false. + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + if (_processingTaskOnCurrentThread.Value) + { + var t = new Task(state => TryExecuteTask((Task)state), task); + t.RunSynchronously(_interleave._parallelOptions.TaskScheduler); + return t.Result; + } + return false; + } + + /// Gets for debugging purposes the tasks scheduled to this scheduler. + /// An enumerable of the tasks queued. + protected override IEnumerable GetScheduledTasks() { return Tasks; } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/CurrentThreadTaskScheduler.cs b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/CurrentThreadTaskScheduler.cs new file mode 100644 index 0000000..e773afa --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/CurrentThreadTaskScheduler.cs @@ -0,0 +1,43 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: CurrentThreadTaskScheduler.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Linq; + +namespace System.Threading.Tasks.Schedulers +{ + /// Provides a task scheduler that runs tasks on the current thread. + public sealed class CurrentThreadTaskScheduler : TaskScheduler + { + /// Runs the provided Task synchronously on the current thread. + /// The task to be executed. + protected override void QueueTask(Task task) + { + TryExecuteTask(task); + } + + /// Runs the provided Task synchronously on the current thread. + /// The task to be executed. + /// Whether the Task was previously queued to the scheduler. + /// True if the Task was successfully executed; otherwise, false. + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + return TryExecuteTask(task); + } + + /// Gets the Tasks currently scheduled to this scheduler. + /// An empty enumerable, as Tasks are never queued, only executed. + protected override IEnumerable GetScheduledTasks() + { + return Enumerable.Empty(); + } + + /// Gets the maximum degree of parallelism for this scheduler. + public override int MaximumConcurrencyLevel { get { return 1; } } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/IOCompletionPortTaskScheduler.cs b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/IOCompletionPortTaskScheduler.cs new file mode 100644 index 0000000..f3aa6f9 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/IOCompletionPortTaskScheduler.cs @@ -0,0 +1,192 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: IOCompletionPortTaskScheduler.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.ComponentModel; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +namespace System.Threading.Tasks.Schedulers +{ + /// Provides a TaskScheduler that uses an I/O completion port for concurrency control. + public sealed class IOCompletionPortTaskScheduler : TaskScheduler, IDisposable + { + /// The queue of tasks to be scheduled. + private readonly ConcurrentQueue m_tasks; + /// The I/O completion port to use for concurrency control. + private readonly IOCompletionPort m_iocp; + /// Whether the current thread is a scheduler thread. + private ThreadLocal m_schedulerThread; + /// Event used to wait for all threads to shutdown. + private CountdownEvent m_remainingThreadsToShutdown; + + /// Initializes the IOCompletionPortTaskScheduler. + /// The maximum number of threads in the scheduler to be executing concurrently. + /// The number of threads to have available in the scheduler for executing tasks. + public IOCompletionPortTaskScheduler(int maxConcurrencyLevel, int numAvailableThreads) + { + // Validate arguments + if (maxConcurrencyLevel < 1) throw new ArgumentNullException("maxConcurrencyLevel"); + if (numAvailableThreads < 1) throw new ArgumentNullException("numAvailableThreads"); + + m_tasks = new ConcurrentQueue(); + m_iocp = new IOCompletionPort(maxConcurrencyLevel); + m_schedulerThread = new ThreadLocal(); + m_remainingThreadsToShutdown = new CountdownEvent(numAvailableThreads); + + // Create and start the threads + for (int i = 0; i < numAvailableThreads; i++) + { + new Thread(() => + { + try + { + // Note that this is a scheduler thread. Used for inlining checks. + m_schedulerThread.Value = true; + + // Continually wait on the I/O completion port until + // there's a work item, then process it. + while (m_iocp.WaitOne()) + { + Task next; + if (m_tasks.TryDequeue(out next)) TryExecuteTask(next); + } + } + finally { m_remainingThreadsToShutdown.Signal(); } + }) { IsBackground = true }.Start(); + } + } + + /// Dispose of the scheduler. + public void Dispose() + { + // Close the I/O completion port. This will cause any threads blocked + // waiting for items to wake up. + m_iocp.Dispose(); + + // Wait for all threads to shutdown. This could cause deadlock + // if the current thread is calling Dispose or is part of such a cycle. + m_remainingThreadsToShutdown.Wait(); + m_remainingThreadsToShutdown.Dispose(); + + // Clean up remaining state + m_schedulerThread.Dispose(); + } + + /// Gets a list of all tasks scheduled to this scheduler. + /// An enumerable of all scheduled tasks. + protected override IEnumerable GetScheduledTasks() { return m_tasks.ToArray(); } + + /// Queues a task to this scheduler for execution. + /// The task to be executed. + protected override void QueueTask(Task task) + { + // Store the task and let the I/O completion port know that more work has arrived. + m_tasks.Enqueue(task); + m_iocp.NotifyOne(); + } + + /// Try to execute a task on the current thread. + /// The task to execute. + /// Whether the task was previously queued to this scheduler. + /// Whether the task was executed. + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + // Only inline from scheduler threads. This is to ensure concurrency control + // is able to handle inlining as well. + return m_schedulerThread.Value && TryExecuteTask(task); + } + + /// Provides a simple managed wrapper for an I/O completion port. + private sealed class IOCompletionPort : IDisposable + { + /// Infinite timeout value to use for GetQueuedCompletedStatus. + private UInt32 INFINITE_TIMEOUT = unchecked((UInt32)Timeout.Infinite); + /// An invalid file handle value. + private IntPtr INVALID_FILE_HANDLE = unchecked((IntPtr)(-1)); + /// An invalid I/O completion port handle value. + private IntPtr INVALID_IOCP_HANDLE = IntPtr.Zero; + + /// The I/O completion porth handle. + private SafeFileHandle m_handle; + + /// Initializes the I/O completion port. + /// The maximum concurrency level allowed by the I/O completion port. + public IOCompletionPort(Int32 maxConcurrencyLevel) + { + // Validate the argument and create the port. + if (maxConcurrencyLevel < 1) throw new ArgumentOutOfRangeException("maxConcurrencyLevel"); + m_handle = CreateIoCompletionPort(INVALID_FILE_HANDLE, INVALID_IOCP_HANDLE, UIntPtr.Zero, (UInt32)maxConcurrencyLevel); + } + + /// Clean up. + public void Dispose() { m_handle.Dispose(); } + + /// Notify that I/O completion port that new work is available. + public void NotifyOne() + { + if (!PostQueuedCompletionStatus(m_handle, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero)) + throw new Win32Exception(); + } + + /// Waits for an item on the I/O completion port. + /// true if an item was available; false if the completion port closed before an item could be retrieved. + public bool WaitOne() + { + // Wait for an item to be posted. + // DangerousGetHandle is used so that the safe handle can be closed even while blocked in the call to GetQueuedCompletionStatus. + UInt32 lpNumberOfBytes; + IntPtr lpCompletionKey, lpOverlapped; + if (!GetQueuedCompletionStatus(m_handle.DangerousGetHandle(), out lpNumberOfBytes, out lpCompletionKey, out lpOverlapped, INFINITE_TIMEOUT)) + { + int errorCode = Marshal.GetLastWin32Error(); + if (errorCode == 735 /*ERROR_ABANDONED_WAIT_0*/ || errorCode == 6 /*INVALID_HANDLE*/) + return false; + else + throw new Win32Exception(errorCode); + } + return true; + } + + /// + /// Creates an input/output (I/O) completion port and associates it with a specified file handle, + /// or creates an I/O completion port that is not yet associated with a file handle, allowing association at a later time. + /// + /// An open file handle or INVALID_HANDLE_VALUE. + /// A handle to an existing I/O completion port or NULL. + /// The per-handle user-defined completion key that is included in every I/O completion packet for the specified file handle. + /// The maximum number of threads that the operating system can allow to concurrently process I/O completion packets for the I/O completion port. + /// If the function succeeds, the return value is the handle to an I/O completion port. If the function fails, the return value is NULL. + [DllImport("kernel32.dll", SetLastError = true)] + private static extern SafeFileHandle CreateIoCompletionPort( + IntPtr fileHandle, IntPtr existingCompletionPort, UIntPtr completionKey, UInt32 numberOfConcurrentThreads); + + /// Attempts to dequeue an I/O completion packet from the specified I/O completion port. + /// A handle to the completion port. + /// A pointer to a variable that receives the number of bytes transferred during an I/O operation that has completed. + /// A pointer to a variable that receives the completion key value associated with the file handle whose I/O operation has completed. + /// A pointer to a variable that receives the address of the OVERLAPPED structure that was specified when the completed I/O operation was started. + /// The number of milliseconds that the caller is willing to wait for a completion packet to appear at the completion port. + /// Returns nonzero (TRUE) if successful or zero (FALSE) otherwise. + [DllImport("kernel32.dll", SetLastError = true)] + private static extern Boolean GetQueuedCompletionStatus( + IntPtr completionPort, out UInt32 lpNumberOfBytes, out IntPtr lpCompletionKey, out IntPtr lpOverlapped, UInt32 dwMilliseconds); + + /// Posts an I/O completion packet to an I/O completion port. + /// A handle to the completion port. + /// The value to be returned through the lpNumberOfBytesTransferred parameter of the GetQueuedCompletionStatus function. + /// The value to be returned through the lpCompletionKey parameter of the GetQueuedCompletionStatus function. + /// The value to be returned through the lpOverlapped parameter of the GetQueuedCompletionStatus function. + /// If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. + [DllImport("kernel32.dll", SetLastError = true)] + private static extern Boolean PostQueuedCompletionStatus( + SafeFileHandle completionPort, IntPtr dwNumberOfBytesTransferred, IntPtr dwCompletionKey, IntPtr lpOverlapped); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/IOTaskScheduler.cs b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/IOTaskScheduler.cs new file mode 100644 index 0000000..0667e01 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/IOTaskScheduler.cs @@ -0,0 +1,88 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: IOTaskScheduler.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace System.Threading.Tasks.Schedulers +{ + /// Provides a task scheduler that targets the I/O ThreadPool. + public sealed class IOTaskScheduler : TaskScheduler, IDisposable + { + /// Represents a task queued to the I/O pool. + private unsafe class WorkItem + { + internal IOTaskScheduler _scheduler; + internal NativeOverlapped* _pNOlap; + internal Task _task; + + internal void Callback(uint errorCode, uint numBytes, NativeOverlapped* pNOlap) + { + // Execute the task + _scheduler.TryExecuteTask(_task); + + // Put this item back into the pool for someone else to use + var pool = _scheduler._availableWorkItems; + if (pool != null) pool.PutObject(this); + else Overlapped.Free(pNOlap); + } + } + + // A pool of available WorkItem instances that can be used to schedule tasks + private ObjectPool _availableWorkItems; + + /// Initializes a new instance of the IOTaskScheduler class. + public unsafe IOTaskScheduler() + { + // Configure the object pool of work items + _availableWorkItems = new ObjectPool(() => + { + var wi = new WorkItem { _scheduler = this }; + wi._pNOlap = new Overlapped().UnsafePack(wi.Callback, null); + return wi; + }, new ConcurrentStack()); + } + + /// Queues a task to the scheduler for execution on the I/O ThreadPool. + /// The Task to queue. + protected override unsafe void QueueTask(Task task) + { + var pool = _availableWorkItems; + if (pool == null) throw new ObjectDisposedException(GetType().Name); + var wi = pool.GetObject(); + wi._task = task; + ThreadPool.UnsafeQueueNativeOverlapped(wi._pNOlap); + } + + /// Executes a task on the current thread. + /// The task to be executed. + /// Ignored. + /// Whether the task could be executed. + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + return TryExecuteTask(task); + } + + /// Disposes of resources used by the scheduler. + public unsafe void Dispose() + { + var pool = _availableWorkItems; + _availableWorkItems = null; + var workItems = pool.ToArrayAndClear(); + foreach (WorkItem wi in workItems) Overlapped.Free(wi._pNOlap); + // NOTE: A window exists where some number of NativeOverlapped ptrs could + // be leaked, if the call to Dispose races with work items completing. + } + + /// Gets an enumerable of tasks queued to the scheduler. + /// An enumerable of tasks queued to the scheduler. + /// This implementation will always return an empty enumerable. + protected override IEnumerable GetScheduledTasks() { return Enumerable.Empty(); } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/LimitedConcurrencyLevelTaskScheduler.cs b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/LimitedConcurrencyLevelTaskScheduler.cs new file mode 100644 index 0000000..757e204 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/LimitedConcurrencyLevelTaskScheduler.cs @@ -0,0 +1,142 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: LimitedConcurrencyTaskScheduler.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Linq; + +namespace System.Threading.Tasks.Schedulers +{ + /// + /// Provides a task scheduler that ensures a maximum concurrency level while + /// running on top of the ThreadPool. + /// + public class LimitedConcurrencyLevelTaskScheduler : TaskScheduler + { + /// Whether the current thread is processing work items. + [ThreadStatic] + private static bool _currentThreadIsProcessingItems; + /// The list of tasks to be executed. + private readonly LinkedList _tasks = new LinkedList(); // protected by lock(_tasks) + /// The maximum concurrency level allowed by this scheduler. + private readonly int _maxDegreeOfParallelism; + /// Whether the scheduler is currently processing work items. + private int _delegatesQueuedOrRunning = 0; // protected by lock(_tasks) + + /// + /// Initializes an instance of the LimitedConcurrencyLevelTaskScheduler class with the + /// specified degree of parallelism. + /// + /// The maximum degree of parallelism provided by this scheduler. + public LimitedConcurrencyLevelTaskScheduler(int maxDegreeOfParallelism) + { + if (maxDegreeOfParallelism < 1) throw new ArgumentOutOfRangeException("maxDegreeOfParallelism"); + _maxDegreeOfParallelism = maxDegreeOfParallelism; + } + + /// Queues a task to the scheduler. + /// The task to be queued. + protected sealed override void QueueTask(Task task) + { + // Add the task to the list of tasks to be processed. If there aren't enough + // delegates currently queued or running to process tasks, schedule another. + lock (_tasks) + { + _tasks.AddLast(task); + if (_delegatesQueuedOrRunning < _maxDegreeOfParallelism) + { + ++_delegatesQueuedOrRunning; + NotifyThreadPoolOfPendingWork(); + } + } + } + + /// + /// Informs the ThreadPool that there's work to be executed for this scheduler. + /// + private void NotifyThreadPoolOfPendingWork() + { + ThreadPool.UnsafeQueueUserWorkItem(_ => + { + // Note that the current thread is now processing work items. + // This is necessary to enable inlining of tasks into this thread. + _currentThreadIsProcessingItems = true; + try + { + // Process all available items in the queue. + while (true) + { + Task item; + lock (_tasks) + { + // When there are no more items to be processed, + // note that we're done processing, and get out. + if (_tasks.Count == 0) + { + --_delegatesQueuedOrRunning; + break; + } + + // Get the next item from the queue + item = _tasks.First.Value; + _tasks.RemoveFirst(); + } + + // Execute the task we pulled out of the queue + base.TryExecuteTask(item); + } + } + // We're done processing items on the current thread + finally { _currentThreadIsProcessingItems = false; } + }, null); + } + + /// Attempts to execute the specified task on the current thread. + /// The task to be executed. + /// + /// Whether the task could be executed on the current thread. + protected sealed override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + // If this thread isn't already processing a task, we don't support inlining + if (!_currentThreadIsProcessingItems) return false; + + // If the task was previously queued, remove it from the queue + if (taskWasPreviouslyQueued) TryDequeue(task); + + // Try to run the task. + return base.TryExecuteTask(task); + } + + /// Attempts to remove a previously scheduled task from the scheduler. + /// The task to be removed. + /// Whether the task could be found and removed. + protected sealed override bool TryDequeue(Task task) + { + lock (_tasks) return _tasks.Remove(task); + } + + /// Gets the maximum concurrency level supported by this scheduler. + public sealed override int MaximumConcurrencyLevel { get { return _maxDegreeOfParallelism; } } + + /// Gets an enumerable of the tasks currently scheduled on this scheduler. + /// An enumerable of the tasks currently scheduled. + protected sealed override IEnumerable GetScheduledTasks() + { + bool lockTaken = false; + try + { + Monitor.TryEnter(_tasks, ref lockTaken); + if (lockTaken) return _tasks.ToArray(); + else throw new NotSupportedException(); + } + finally + { + if (lockTaken) Monitor.Exit(_tasks); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/OrderedTaskScheduler.cs b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/OrderedTaskScheduler.cs new file mode 100644 index 0000000..44ea908 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/OrderedTaskScheduler.cs @@ -0,0 +1,20 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: OrderedTaskScheduler.cs +// +//-------------------------------------------------------------------------- + +namespace System.Threading.Tasks.Schedulers +{ + /// + /// Provides a task scheduler that ensures only one task is executing at a time, and that tasks + /// execute in the order that they were queued. + /// + public sealed class OrderedTaskScheduler : LimitedConcurrencyLevelTaskScheduler + { + /// Initializes an instance of the OrderedTaskScheduler class. + public OrderedTaskScheduler() : base(1) { } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/QueuedTaskScheduler.cs b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/QueuedTaskScheduler.cs new file mode 100644 index 0000000..8701955 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/QueuedTaskScheduler.cs @@ -0,0 +1,612 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: QueuedTaskScheduler.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace System.Threading.Tasks.Schedulers +{ + /// + /// Provides a TaskScheduler that provides control over priorities, fairness, and the underlying threads utilized. + /// + [DebuggerTypeProxy(typeof(QueuedTaskSchedulerDebugView))] + [DebuggerDisplay("Id={Id}, Queues={DebugQueueCount}, ScheduledTasks = {DebugTaskCount}")] + public sealed class QueuedTaskScheduler : TaskScheduler, IDisposable + { + /// Debug view for the QueuedTaskScheduler. + private class QueuedTaskSchedulerDebugView + { + /// The scheduler. + private QueuedTaskScheduler _scheduler; + + /// Initializes the debug view. + /// The scheduler. + public QueuedTaskSchedulerDebugView(QueuedTaskScheduler scheduler) + { + if (scheduler == null) throw new ArgumentNullException("scheduler"); + _scheduler = scheduler; + } + + /// Gets all of the Tasks queued to the scheduler directly. + public IEnumerable ScheduledTasks + { + get + { + var tasks = (_scheduler._targetScheduler != null) ? + (IEnumerable)_scheduler._nonthreadsafeTaskQueue : + (IEnumerable)_scheduler._blockingTaskQueue; + return tasks.Where(t => t != null).ToList(); + } + } + + /// Gets the prioritized and fair queues. + public IEnumerable Queues + { + get + { + List queues = new List(); + foreach (var group in _scheduler._queueGroups) queues.AddRange(group.Value); + return queues; + } + } + } + + /// + /// A sorted list of round-robin queue lists. Tasks with the smallest priority value + /// are preferred. Priority groups are round-robin'd through in order of priority. + /// + private readonly SortedList _queueGroups = new SortedList(); + /// Cancellation token used for disposal. + private readonly CancellationTokenSource _disposeCancellation = new CancellationTokenSource(); + /// + /// The maximum allowed concurrency level of this scheduler. If custom threads are + /// used, this represents the number of created threads. + /// + private readonly int _concurrencyLevel; + /// Whether we're processing tasks on the current thread. + private static ThreadLocal _taskProcessingThread = new ThreadLocal(); + + // *** + // *** For when using a target scheduler + // *** + + /// The scheduler onto which actual work is scheduled. + private readonly TaskScheduler _targetScheduler; + /// The queue of tasks to process when using an underlying target scheduler. + private readonly Queue _nonthreadsafeTaskQueue; + /// The number of Tasks that have been queued or that are running whiel using an underlying scheduler. + private int _delegatesQueuedOrRunning = 0; + + // *** + // *** For when using our own threads + // *** + + /// The threads used by the scheduler to process work. + private readonly Thread[] _threads; + /// The collection of tasks to be executed on our custom threads. + private readonly BlockingCollection _blockingTaskQueue; + + // *** + + /// Initializes the scheduler. + public QueuedTaskScheduler() : this(TaskScheduler.Default, 0) { } + + /// Initializes the scheduler. + /// The target underlying scheduler onto which this sceduler's work is queued. + public QueuedTaskScheduler(TaskScheduler targetScheduler) : this(targetScheduler, 0) { } + + /// Initializes the scheduler. + /// The target underlying scheduler onto which this sceduler's work is queued. + /// The maximum degree of concurrency allowed for this scheduler's work. + public QueuedTaskScheduler( + TaskScheduler targetScheduler, + int maxConcurrencyLevel) + { + // Validate arguments + if (targetScheduler == null) throw new ArgumentNullException("underlyingScheduler"); + if (maxConcurrencyLevel < 0) throw new ArgumentOutOfRangeException("concurrencyLevel"); + + // Initialize only those fields relevant to use an underlying scheduler. We don't + // initialize the fields relevant to using our own custom threads. + _targetScheduler = targetScheduler; + _nonthreadsafeTaskQueue = new Queue(); + + // If 0, use the number of logical processors. But make sure whatever value we pick + // is not greater than the degree of parallelism allowed by the underlying scheduler. + _concurrencyLevel = maxConcurrencyLevel != 0 ? maxConcurrencyLevel : Environment.ProcessorCount; + if (targetScheduler.MaximumConcurrencyLevel > 0 && + targetScheduler.MaximumConcurrencyLevel < _concurrencyLevel) + { + _concurrencyLevel = targetScheduler.MaximumConcurrencyLevel; + } + } + + /// Initializes the scheduler. + /// The number of threads to create and use for processing work items. + public QueuedTaskScheduler(int threadCount) : this(threadCount, string.Empty, false, ThreadPriority.Normal, ApartmentState.MTA, 0, null, null) { } + + /// Initializes the scheduler. + /// The number of threads to create and use for processing work items. + /// The name to use for each of the created threads. + /// A Boolean value that indicates whether to use foreground threads instead of background. + /// The priority to assign to each thread. + /// The apartment state to use for each thread. + /// The stack size to use for each thread. + /// An initialization routine to run on each thread. + /// A finalization routine to run on each thread. + public QueuedTaskScheduler( + int threadCount, + string threadName = "", + bool useForegroundThreads = false, + ThreadPriority threadPriority = ThreadPriority.Normal, + ApartmentState threadApartmentState = ApartmentState.MTA, + int threadMaxStackSize = 0, + Action threadInit = null, + Action threadFinally = null) + { + // Validates arguments (some validation is left up to the Thread type itself). + // If the thread count is 0, default to the number of logical processors. + if (threadCount < 0) throw new ArgumentOutOfRangeException("concurrencyLevel"); + else if (threadCount == 0) _concurrencyLevel = Environment.ProcessorCount; + else _concurrencyLevel = threadCount; + + // Initialize the queue used for storing tasks + _blockingTaskQueue = new BlockingCollection(); + + // Create all of the threads + _threads = new Thread[threadCount]; + for (int i = 0; i < threadCount; i++) + { + _threads[i] = new Thread(() => ThreadBasedDispatchLoop(threadInit, threadFinally), threadMaxStackSize) + { + Priority = threadPriority, + IsBackground = !useForegroundThreads, + }; + if (threadName != null) _threads[i].Name = threadName + " (" + i + ")"; + _threads[i].SetApartmentState(threadApartmentState); + } + + // Start all of the threads + foreach (var thread in _threads) thread.Start(); + } + + /// The dispatch loop run by all threads in this scheduler. + /// An initialization routine to run when the thread begins. + /// A finalization routine to run before the thread ends. + private void ThreadBasedDispatchLoop(Action threadInit, Action threadFinally) + { + _taskProcessingThread.Value = true; + if (threadInit != null) threadInit(); + try + { + // If the scheduler is disposed, the cancellation token will be set and + // we'll receive an OperationCanceledException. That OCE should not crash the process. + try + { + // If a thread abort occurs, we'll try to reset it and continue running. + while (true) + { + try + { + // For each task queued to the scheduler, try to execute it. + foreach (var task in _blockingTaskQueue.GetConsumingEnumerable(_disposeCancellation.Token)) + { + // If the task is not null, that means it was queued to this scheduler directly. + // Run it. + if (task != null) + { + TryExecuteTask(task); + } + // If the task is null, that means it's just a placeholder for a task + // queued to one of the subschedulers. Find the next task based on + // priority and fairness and run it. + else + { + // Find the next task based on our ordering rules... + Task targetTask; + QueuedTaskSchedulerQueue queueForTargetTask; + lock (_queueGroups) FindNextTask_NeedsLock(out targetTask, out queueForTargetTask); + + // ... and if we found one, run it + if (targetTask != null) queueForTargetTask.ExecuteTask(targetTask); + } + } + } + catch (ThreadAbortException) + { + // If we received a thread abort, and that thread abort was due to shutting down + // or unloading, let it pass through. Otherwise, reset the abort so we can + // continue processing work items. + if (!Environment.HasShutdownStarted && !AppDomain.CurrentDomain.IsFinalizingForUnload()) + { + Thread.ResetAbort(); + } + } + } + } + catch (OperationCanceledException) { } + } + finally + { + // Run a cleanup routine if there was one + if (threadFinally != null) threadFinally(); + _taskProcessingThread.Value = false; + } + } + + /// Gets the number of queues currently activated. + private int DebugQueueCount + { + get + { + int count = 0; + foreach (var group in _queueGroups) count += group.Value.Count; + return count; + } + } + + /// Gets the number of tasks currently scheduled. + private int DebugTaskCount + { + get + { + return (_targetScheduler != null ? + (IEnumerable)_nonthreadsafeTaskQueue : (IEnumerable)_blockingTaskQueue) + .Where(t => t != null).Count(); + } + } + + /// Find the next task that should be executed, based on priorities and fairness and the like. + /// The found task, or null if none was found. + /// + /// The scheduler associated with the found task. Due to security checks inside of TPL, + /// this scheduler needs to be used to execute that task. + /// + private void FindNextTask_NeedsLock(out Task targetTask, out QueuedTaskSchedulerQueue queueForTargetTask) + { + targetTask = null; + queueForTargetTask = null; + + // Look through each of our queue groups in sorted order. + // This ordering is based on the priority of the queues. + foreach (var queueGroup in _queueGroups) + { + var queues = queueGroup.Value; + + // Within each group, iterate through the queues in a round-robin + // fashion. Every time we iterate again and successfully find a task, + // we'll start in the next location in the group. + foreach (int i in queues.CreateSearchOrder()) + { + queueForTargetTask = queues[i]; + var items = queueForTargetTask._workItems; + if (items.Count > 0) + { + targetTask = items.Dequeue(); + if (queueForTargetTask._disposed && items.Count == 0) + { + RemoveQueue_NeedsLock(queueForTargetTask); + } + queues.NextQueueIndex = (queues.NextQueueIndex + 1) % queueGroup.Value.Count; + return; + } + } + } + } + + /// Queues a task to the scheduler. + /// The task to be queued. + protected override void QueueTask(Task task) + { + // If we've been disposed, no one should be queueing + if (_disposeCancellation.IsCancellationRequested) throw new ObjectDisposedException(GetType().Name); + + // If the target scheduler is null (meaning we're using our own threads), + // add the task to the blocking queue + if (_targetScheduler == null) + { + _blockingTaskQueue.Add(task); + } + // Otherwise, add the task to the non-blocking queue, + // and if there isn't already an executing processing task, + // start one up + else + { + // Queue the task and check whether we should launch a processing + // task (noting it if we do, so that other threads don't result + // in queueing up too many). + bool launchTask = false; + lock (_nonthreadsafeTaskQueue) + { + _nonthreadsafeTaskQueue.Enqueue(task); + if (_delegatesQueuedOrRunning < _concurrencyLevel) + { + ++_delegatesQueuedOrRunning; + launchTask = true; + } + } + + // If necessary, start processing asynchronously + if (launchTask) + { + Task.Factory.StartNew(ProcessPrioritizedAndBatchedTasks, + CancellationToken.None, TaskCreationOptions.None, _targetScheduler); + } + } + } + + /// + /// Process tasks one at a time in the best order. + /// This should be run in a Task generated by QueueTask. + /// It's been separated out into its own method to show up better in Parallel Tasks. + /// + private void ProcessPrioritizedAndBatchedTasks() + { + bool continueProcessing = true; + while (!_disposeCancellation.IsCancellationRequested && continueProcessing) + { + try + { + // Note that we're processing tasks on this thread + _taskProcessingThread.Value = true; + + // Until there are no more tasks to process + while (!_disposeCancellation.IsCancellationRequested) + { + // Try to get the next task. If there aren't any more, we're done. + Task targetTask; + lock (_nonthreadsafeTaskQueue) + { + if (_nonthreadsafeTaskQueue.Count == 0) return; + targetTask = _nonthreadsafeTaskQueue.Dequeue(); + } + + // If the task is null, it's a placeholder for a task in the round-robin queues. + // Find the next one that should be processed. + QueuedTaskSchedulerQueue queueForTargetTask = null; + if (targetTask == null) + { + lock (_queueGroups) FindNextTask_NeedsLock(out targetTask, out queueForTargetTask); + } + + // Now if we finally have a task, run it. If the task + // was associated with one of the round-robin schedulers, we need to use it + // as a thunk to execute its task. + if (targetTask != null) + { + if (queueForTargetTask != null) queueForTargetTask.ExecuteTask(targetTask); + else TryExecuteTask(targetTask); + } + } + } + finally + { + // Now that we think we're done, verify that there really is + // no more work to do. If there's not, highlight + // that we're now less parallel than we were a moment ago. + lock (_nonthreadsafeTaskQueue) + { + if (_nonthreadsafeTaskQueue.Count == 0) + { + _delegatesQueuedOrRunning--; + continueProcessing = false; + _taskProcessingThread.Value = false; + } + } + } + } + } + + /// Notifies the pool that there's a new item to be executed in one of the round-robin queues. + private void NotifyNewWorkItem() { QueueTask(null); } + + /// Tries to execute a task synchronously on the current thread. + /// The task to execute. + /// Whether the task was previously queued. + /// true if the task was executed; otherwise, false. + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + // If we're already running tasks on this threads, enable inlining + return _taskProcessingThread.Value && TryExecuteTask(task); + } + + /// Gets the tasks scheduled to this scheduler. + /// An enumerable of all tasks queued to this scheduler. + /// This does not include the tasks on sub-schedulers. Those will be retrieved by the debugger separately. + protected override IEnumerable GetScheduledTasks() + { + // If we're running on our own threads, get the tasks from the blocking queue... + if (_targetScheduler == null) + { + // Get all of the tasks, filtering out nulls, which are just placeholders + // for tasks in other sub-schedulers + return _blockingTaskQueue.Where(t => t != null).ToList(); + } + // otherwise get them from the non-blocking queue... + else + { + return _nonthreadsafeTaskQueue.Where(t => t != null).ToList(); + } + } + + /// Gets the maximum concurrency level to use when processing tasks. + public override int MaximumConcurrencyLevel { get { return _concurrencyLevel; } } + + /// Initiates shutdown of the scheduler. + public void Dispose() + { + _disposeCancellation.Cancel(); + } + + /// Creates and activates a new scheduling queue for this scheduler. + /// The newly created and activated queue at priority 0. + public TaskScheduler ActivateNewQueue() { return ActivateNewQueue(0); } + + /// Creates and activates a new scheduling queue for this scheduler. + /// The priority level for the new queue. + /// The newly created and activated queue at the specified priority. + public TaskScheduler ActivateNewQueue(int priority) + { + // Create the queue + var createdQueue = new QueuedTaskSchedulerQueue(priority, this); + + // Add the queue to the appropriate queue group based on priority + lock (_queueGroups) + { + QueueGroup list; + if (!_queueGroups.TryGetValue(priority, out list)) + { + list = new QueueGroup(); + _queueGroups.Add(priority, list); + } + list.Add(createdQueue); + } + + // Hand the new queue back + return createdQueue; + } + + /// Removes a scheduler from the group. + /// The scheduler to be removed. + private void RemoveQueue_NeedsLock(QueuedTaskSchedulerQueue queue) + { + // Find the group that contains the queue and the queue's index within the group + var queueGroup = _queueGroups[queue._priority]; + int index = queueGroup.IndexOf(queue); + + // We're about to remove the queue, so adjust the index of the next + // round-robin starting location if it'll be affected by the removal + if (queueGroup.NextQueueIndex >= index) queueGroup.NextQueueIndex--; + + // Remove it + queueGroup.RemoveAt(index); + } + + /// A group of queues a the same priority level. + private class QueueGroup : List + { + /// The starting index for the next round-robin traversal. + public int NextQueueIndex = 0; + + /// Creates a search order through this group. + /// An enumerable of indices for this group. + public IEnumerable CreateSearchOrder() + { + for (int i = NextQueueIndex; i < Count; i++) yield return i; + for (int i = 0; i < NextQueueIndex; i++) yield return i; + } + } + + /// Provides a scheduling queue associatd with a QueuedTaskScheduler. + [DebuggerDisplay("QueuePriority = {_priority}, WaitingTasks = {WaitingTasks}")] + [DebuggerTypeProxy(typeof(QueuedTaskSchedulerQueueDebugView))] + private sealed class QueuedTaskSchedulerQueue : TaskScheduler, IDisposable + { + /// A debug view for the queue. + private sealed class QueuedTaskSchedulerQueueDebugView + { + /// The queue. + private readonly QueuedTaskSchedulerQueue _queue; + + /// Initializes the debug view. + /// The queue to be debugged. + public QueuedTaskSchedulerQueueDebugView(QueuedTaskSchedulerQueue queue) + { + if (queue == null) throw new ArgumentNullException("queue"); + _queue = queue; + } + + /// Gets the priority of this queue in its associated scheduler. + public int Priority { get { return _queue._priority; } } + /// Gets the ID of this scheduler. + public int Id { get { return _queue.Id; } } + /// Gets all of the tasks scheduled to this queue. + public IEnumerable ScheduledTasks { get { return _queue.GetScheduledTasks(); } } + /// Gets the QueuedTaskScheduler with which this queue is associated. + public QueuedTaskScheduler AssociatedScheduler { get { return _queue._pool; } } + } + + /// The scheduler with which this pool is associated. + private readonly QueuedTaskScheduler _pool; + /// The work items stored in this queue. + internal readonly Queue _workItems; + /// Whether this queue has been disposed. + internal bool _disposed; + /// Gets the priority for this queue. + internal int _priority; + + /// Initializes the queue. + /// The priority associated with this queue. + /// The scheduler with which this queue is associated. + internal QueuedTaskSchedulerQueue(int priority, QueuedTaskScheduler pool) + { + _priority = priority; + _pool = pool; + _workItems = new Queue(); + } + + /// Gets the number of tasks waiting in this scheduler. + internal int WaitingTasks { get { return _workItems.Count; } } + + /// Gets the tasks scheduled to this scheduler. + /// An enumerable of all tasks queued to this scheduler. + protected override IEnumerable GetScheduledTasks() { return _workItems.ToList(); } + + /// Queues a task to the scheduler. + /// The task to be queued. + protected override void QueueTask(Task task) + { + if (_disposed) throw new ObjectDisposedException(GetType().Name); + + // Queue up the task locally to this queue, and then notify + // the parent scheduler that there's work available + lock (_pool._queueGroups) _workItems.Enqueue(task); + _pool.NotifyNewWorkItem(); + } + + /// Tries to execute a task synchronously on the current thread. + /// The task to execute. + /// Whether the task was previously queued. + /// true if the task was executed; otherwise, false. + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + // If we're using our own threads and if this is being called from one of them, + // or if we're currently processing another task on this thread, try running it inline. + return _taskProcessingThread.Value && TryExecuteTask(task); + } + + /// Runs the specified ask. + /// The task to execute. + internal void ExecuteTask(Task task) { TryExecuteTask(task); } + + /// Gets the maximum concurrency level to use when processing tasks. + public override int MaximumConcurrencyLevel { get { return _pool.MaximumConcurrencyLevel; } } + + /// Signals that the queue should be removed from the scheduler as soon as the queue is empty. + public void Dispose() + { + if (!_disposed) + { + lock (_pool._queueGroups) + { + // We only remove the queue if it's empty. If it's not empty, + // we still mark it as disposed, and the associated QueuedTaskScheduler + // will remove the queue when its count hits 0 and its _disposed is true. + if (_workItems.Count == 0) + { + _pool.RemoveQueue_NeedsLock(this); + } + } + _disposed = true; + } + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/ReprioritizableTaskScheduler.cs b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/ReprioritizableTaskScheduler.cs new file mode 100644 index 0000000..3ca161b --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/ReprioritizableTaskScheduler.cs @@ -0,0 +1,115 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: PrioritizingTaskScheduler.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Linq; + +namespace System.Threading.Tasks.Schedulers +{ + /// Provides a task scheduler that supports reprioritizing previously queued tasks. + public sealed class ReprioritizableTaskScheduler : TaskScheduler + { + private readonly LinkedList _tasks = new LinkedList(); // protected by lock(_tasks) + + /// Queues a task to the scheduler. + /// The task to be queued. + protected override void QueueTask(Task task) + { + // Store the task, and notify the ThreadPool of work to be processed + lock (_tasks) _tasks.AddLast(task); + ThreadPool.UnsafeQueueUserWorkItem(ProcessNextQueuedItem, null); + } + + /// Reprioritizes a previously queued task to the front of the queue. + /// The task to be reprioritized. + /// Whether the task could be found and moved to the front of the queue. + public bool Prioritize(Task task) + { + lock (_tasks) + { + var node = _tasks.Find(task); + if (node != null) + { + _tasks.Remove(node); + _tasks.AddFirst(node); + return true; + } + } + return false; + } + + /// Reprioritizes a previously queued task to the back of the queue. + /// The task to be reprioritized. + /// Whether the task could be found and moved to the back of the queue. + public bool Deprioritize(Task task) + { + lock (_tasks) + { + var node = _tasks.Find(task); + if (node != null) + { + _tasks.Remove(node); + _tasks.AddLast(node); + return true; + } + } + return false; + } + + /// Removes a previously queued item from the scheduler. + /// The task to be removed. + /// Whether the task could be removed from the scheduler. + protected override bool TryDequeue(Task task) + { + lock (_tasks) return _tasks.Remove(task); + } + + /// Picks up and executes the next item in the queue. + /// Ignored. + private void ProcessNextQueuedItem(object ignored) + { + Task t; + lock (_tasks) + { + if (_tasks.Count > 0) + { + t = _tasks.First.Value; + _tasks.RemoveFirst(); + } + else return; + } + base.TryExecuteTask(t); + } + + /// Executes the specified task inline. + /// The task to be executed. + /// Whether the task was previously queued. + /// Whether the task could be executed inline. + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + return TryExecuteTask(task); + } + + /// Gets all of the tasks currently queued to the scheduler. + /// An enumerable of the tasks currently queued to the scheduler. + protected override IEnumerable GetScheduledTasks() + { + bool lockTaken = false; + try + { + Monitor.TryEnter(_tasks, ref lockTaken); + if (lockTaken) return _tasks.ToArray(); + else throw new NotSupportedException(); + } + finally + { + if (lockTaken) Monitor.Exit(_tasks); + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/RoundRobinTaskScheduler.cs b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/RoundRobinTaskScheduler.cs new file mode 100644 index 0000000..ff1c443 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/RoundRobinTaskScheduler.cs @@ -0,0 +1,134 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: RoundRobinTaskScheduler.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; + +namespace System.Threading.Tasks.Schedulers +{ + /// Enables the creation of a group of schedulers that support round-robin scheduling for fairness. + public sealed class RoundRobinSchedulerGroup + { + private readonly List _queues = new List(); + private int _nextQueue = 0; + + /// Creates a new scheduler as part of this group. + /// The new scheduler. + public TaskScheduler CreateScheduler() + { + var createdQueue = new RoundRobinTaskSchedulerQueue(this); + lock (_queues) _queues.Add(createdQueue); + return createdQueue; + } + + /// Gets a collection of all schedulers in this group. + public ReadOnlyCollection Schedulers + { + get { lock(_queues) return new ReadOnlyCollection(_queues.Cast().ToArray()); } + } + + /// Removes a scheduler from the group. + /// The scheduler to be removed. + private void RemoveQueue_NeedsLock(RoundRobinTaskSchedulerQueue queue) + { + int index = _queues.IndexOf(queue); + if (_nextQueue >= index) _nextQueue--; + _queues.RemoveAt(index); + } + + /// Notifies the ThreadPool that there's a new item to be executed. + private void NotifyNewWorkItem() + { + // Queue a processing delegate to the ThreadPool + ThreadPool.UnsafeQueueUserWorkItem(_ => + { + Task targetTask = null; + RoundRobinTaskSchedulerQueue queueForTargetTask = null; + lock (_queues) + { + // Determine the order in which we'll search the schedulers for work + var searchOrder = Enumerable.Range(_nextQueue, _queues.Count - _nextQueue).Concat(Enumerable.Range(0, _nextQueue)); + + // Look for the next item to process + foreach (int i in searchOrder) + { + queueForTargetTask = _queues[i]; + var items = queueForTargetTask._workItems; + if (items.Count > 0) + { + targetTask = items.Dequeue(); + _nextQueue = i; + if (queueForTargetTask._disposed && items.Count == 0) + { + RemoveQueue_NeedsLock(queueForTargetTask); + } + break; + } + } + _nextQueue = (_nextQueue + 1) % _queues.Count; + } + + // If we found an item, run it + if (targetTask != null) queueForTargetTask.RunQueuedTask(targetTask); + }, null); + } + + /// A scheduler that participates in round-robin scheduling. + private sealed class RoundRobinTaskSchedulerQueue : TaskScheduler, IDisposable + { + internal RoundRobinTaskSchedulerQueue(RoundRobinSchedulerGroup pool) { _pool = pool; } + + private RoundRobinSchedulerGroup _pool; + internal Queue _workItems = new Queue(); + internal bool _disposed; + + protected override IEnumerable GetScheduledTasks() + { + object obj = _pool._queues; + bool lockTaken = false; + try + { + Monitor.TryEnter(obj, ref lockTaken); + if (lockTaken) return _workItems.ToArray(); + else throw new NotSupportedException(); + } + finally + { + if (lockTaken) Monitor.Exit(obj); + } + } + + protected override void QueueTask(Task task) + { + if (_disposed) throw new ObjectDisposedException(GetType().Name); + lock (_pool._queues) _workItems.Enqueue(task); + _pool.NotifyNewWorkItem(); + } + + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + return base.TryExecuteTask(task); + } + + internal void RunQueuedTask(Task task) { TryExecuteTask(task); } + + void IDisposable.Dispose() + { + if (!_disposed) + { + lock (_pool._queues) + { + if (_workItems.Count == 0) _pool.RemoveQueue_NeedsLock(this); + _disposed = true; + } + } + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/StaTaskScheduler.cs b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/StaTaskScheduler.cs new file mode 100644 index 0000000..8bfdf20 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/StaTaskScheduler.cs @@ -0,0 +1,108 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: StaTaskScheduler.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace System.Threading.Tasks.Schedulers +{ + /// Provides a scheduler that uses STA threads. + public sealed class StaTaskScheduler : TaskScheduler, IDisposable + { + /// Stores the queued tasks to be executed by our pool of STA threads. + private BlockingCollection _tasks; + /// The STA threads used by the scheduler. + private readonly List _threads; + + /// Initializes a new instance of the StaTaskScheduler class with the specified concurrency level. + /// The number of threads that should be created and used by this scheduler. + public StaTaskScheduler(int numberOfThreads) + { + // Validate arguments + if (numberOfThreads < 1) throw new ArgumentOutOfRangeException("concurrencyLevel"); + + // Initialize the tasks collection + _tasks = new BlockingCollection(); + + // Create the threads to be used by this scheduler + _threads = Enumerable.Range(0, numberOfThreads).Select(i => + { + var thread = new Thread(() => + { + // Continually get the next task and try to execute it. + // This will continue until the scheduler is disposed and no more tasks remain. + foreach (var t in _tasks.GetConsumingEnumerable()) + { + TryExecuteTask(t); + } + }); + thread.IsBackground = true; + thread.SetApartmentState(ApartmentState.STA); + return thread; + }).ToList(); + + // Start all of the threads + _threads.ForEach(t => t.Start()); + } + + /// Queues a Task to be executed by this scheduler. + /// The task to be executed. + protected override void QueueTask(Task task) + { + // Push it into the blocking collection of tasks + _tasks.Add(task); + } + + /// Provides a list of the scheduled tasks for the debugger to consume. + /// An enumerable of all tasks currently scheduled. + protected override IEnumerable GetScheduledTasks() + { + // Serialize the contents of the blocking collection of tasks for the debugger + return _tasks.ToArray(); + } + + /// Determines whether a Task may be inlined. + /// The task to be executed. + /// Whether the task was previously queued. + /// true if the task was successfully inlined; otherwise, false. + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + // Try to inline if the current thread is STA + return + Thread.CurrentThread.GetApartmentState() == ApartmentState.STA && + TryExecuteTask(task); + } + + /// Gets the maximum concurrency level supported by this scheduler. + public override int MaximumConcurrencyLevel + { + get { return _threads.Count; } + } + + /// + /// Cleans up the scheduler by indicating that no more tasks will be queued. + /// This method blocks until all threads successfully shutdown. + /// + public void Dispose() + { + if (_tasks != null) + { + // Indicate that no new tasks will be coming in + _tasks.CompleteAdding(); + + // Wait for all threads to finish processing tasks + foreach (var thread in _threads) thread.Join(); + + // Cleanup + _tasks.Dispose(); + _tasks = null; + } + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/SynchronizationContextTaskScheduler.cs b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/SynchronizationContextTaskScheduler.cs new file mode 100644 index 0000000..7e9d7a9 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/SynchronizationContextTaskScheduler.cs @@ -0,0 +1,73 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: SynchronizationContextTaskScheduler.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.ComponentModel; + +namespace System.Threading.Tasks.Schedulers +{ + /// Provides a task scheduler that targets a specific SynchronizationContext. + public sealed class SynchronizationContextTaskScheduler : TaskScheduler + { + /// The queue of tasks to execute, maintained for debugging purposes. + private readonly ConcurrentQueue _tasks; + /// The target context under which to execute the queued tasks. + private readonly SynchronizationContext _context; + + /// Initializes an instance of the SynchronizationContextTaskScheduler class. + public SynchronizationContextTaskScheduler() : + this(SynchronizationContext.Current) + { + } + + /// + /// Initializes an instance of the SynchronizationContextTaskScheduler class + /// with the specified SynchronizationContext. + /// + /// The SynchronizationContext under which to execute tasks. + public SynchronizationContextTaskScheduler(SynchronizationContext context) + { + if (context == null) throw new ArgumentNullException("context"); + _context = context; + _tasks = new ConcurrentQueue(); + } + + /// Queues a task to the scheduler for execution on the I/O ThreadPool. + /// The Task to queue. + protected override void QueueTask(Task task) + { + _tasks.Enqueue(task); + _context.Post(delegate + { + Task nextTask; + if (_tasks.TryDequeue(out nextTask)) TryExecuteTask(nextTask); + }, null); + } + + /// Tries to execute a task on the current thread. + /// The task to be executed. + /// Ignored. + /// Whether the task could be executed. + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + return _context != SynchronizationContext.Current && TryExecuteTask(task); + } + + /// Gets an enumerable of tasks queued to the scheduler. + /// An enumerable of tasks queued to the scheduler. + protected override IEnumerable GetScheduledTasks() + { + return _tasks.ToArray(); + } + + /// Gets the maximum concurrency level supported by this scheduler. + public override int MaximumConcurrencyLevel { get { return 1; } } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/ThreadPerTaskkScheduler.cs b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/ThreadPerTaskkScheduler.cs new file mode 100644 index 0000000..83df527 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/ThreadPerTaskkScheduler.cs @@ -0,0 +1,37 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: ThreadPerTaskScheduler.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; +using System.Linq; + +namespace System.Threading.Tasks.Schedulers +{ + /// Provides a task scheduler that dedicates a thread per task. + public class ThreadPerTaskScheduler : TaskScheduler + { + /// Gets the tasks currently scheduled to this scheduler. + /// This will always return an empty enumerable, as tasks are launched as soon as they're queued. + protected override IEnumerable GetScheduledTasks() { return Enumerable.Empty(); } + + /// Starts a new thread to process the provided task. + /// The task to be executed. + protected override void QueueTask(Task task) + { + new Thread(() => TryExecuteTask(task)) { IsBackground = true }.Start(); + } + + /// Runs the provided task on the current thread. + /// The task to be executed. + /// Ignored. + /// Whether the task could be executed on the current thread. + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + return TryExecuteTask(task); + } + } +} diff --git a/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/WorkStealingTaskScheduler.cs b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/WorkStealingTaskScheduler.cs new file mode 100644 index 0000000..c172892 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/TaskSchedulers/WorkStealingTaskScheduler.cs @@ -0,0 +1,488 @@ +//-------------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// File: WorkStealingTaskScheduler.cs +// +//-------------------------------------------------------------------------- + +using System.Collections.Generic; + +namespace System.Threading.Tasks.Schedulers +{ + /// Provides a work-stealing scheduler. + public class WorkStealingTaskScheduler : TaskScheduler, IDisposable + { + private readonly int m_concurrencyLevel; + private readonly Queue m_queue = new Queue(); + private WorkStealingQueue[] m_wsQueues = new WorkStealingQueue[Environment.ProcessorCount]; + private Lazy m_threads; + private int m_threadsWaiting; + private bool m_shutdown; + [ThreadStatic] + private static WorkStealingQueue m_wsq; + + /// Initializes a new instance of the WorkStealingTaskScheduler class. + /// This constructors defaults to using twice as many threads as there are processors. + public WorkStealingTaskScheduler() : this(Environment.ProcessorCount * 2) { } + + /// Initializes a new instance of the WorkStealingTaskScheduler class. + /// The number of threads to use in the scheduler. + public WorkStealingTaskScheduler(int concurrencyLevel) + { + // Store the concurrency level + if (concurrencyLevel <= 0) throw new ArgumentOutOfRangeException("concurrencyLevel"); + m_concurrencyLevel = concurrencyLevel; + + // Set up threads + m_threads = new Lazy(() => + { + var threads = new Thread[m_concurrencyLevel]; + for (int i = 0; i < threads.Length; i++) + { + threads[i] = new Thread(DispatchLoop) { IsBackground = true }; + threads[i].Start(); + } + return threads; + }); + } + + /// Queues a task to the scheduler. + /// The task to be scheduled. + protected override void QueueTask(Task task) + { + // Make sure the pool is started, e.g. that all threads have been created. + m_threads.Force(); + + // If the task is marked as long-running, give it its own dedicated thread + // rather than queueing it. + if ((task.CreationOptions & TaskCreationOptions.LongRunning) != 0) + { + new Thread(state => base.TryExecuteTask((Task)state)) { IsBackground = true }.Start(task); + } + else + { + // Otherwise, insert the work item into a queue, possibly waking a thread. + // If there's a local queue and the task does not prefer to be in the global queue, + // add it to the local queue. + WorkStealingQueue wsq = m_wsq; + if (wsq != null && ((task.CreationOptions & TaskCreationOptions.PreferFairness) == 0)) + { + // Add to the local queue and notify any waiting threads that work is available. + // Races may occur which result in missed event notifications, but they're benign in that + // this thread will eventually pick up the work item anyway, as will other threads when another + // work item notification is received. + wsq.LocalPush(task); + if (m_threadsWaiting > 0) // OK to read lock-free. + { + lock (m_queue) { Monitor.Pulse(m_queue); } + } + } + // Otherwise, add the work item to the global queue + else + { + lock (m_queue) + { + m_queue.Enqueue(task); + if (m_threadsWaiting > 0) Monitor.Pulse(m_queue); + } + } + } + } + + /// Executes a task on the current thread. + /// The task to be executed. + /// Ignored. + /// Whether the task could be executed. + protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued) + { + return TryExecuteTask(task); + + // // Optional replacement: Instead of always trying to execute the task (which could + // // benignly leave a task in the queue that's already been executed), we + // // can search the current work-stealing queue and remove the task, + // // executing it inline only if it's found. + // WorkStealingQueue wsq = m_wsq; + // return wsq != null && wsq.TryFindAndPop(task) && TryExecuteTask(task); + } + + /// Gets the maximum concurrency level supported by this scheduler. + public override int MaximumConcurrencyLevel + { + get { return m_concurrencyLevel; } + } + + /// Gets all of the tasks currently scheduled to this scheduler. + /// An enumerable containing all of the scheduled tasks. + protected override IEnumerable GetScheduledTasks() + { + // Keep track of all of the tasks we find + List tasks = new List(); + + // Get all of the global tasks. We use TryEnter so as not to hang + // a debugger if the lock is held by a frozen thread. + bool lockTaken = false; + try + { + Monitor.TryEnter(m_queue, ref lockTaken); + if (lockTaken) tasks.AddRange(m_queue.ToArray()); + else throw new NotSupportedException(); + } + finally + { + if (lockTaken) Monitor.Exit(m_queue); + } + + // Now get all of the tasks from the work-stealing queues + WorkStealingQueue[] queues = m_wsQueues; + for (int i = 0; i < queues.Length; i++) + { + WorkStealingQueue wsq = queues[i]; + if (wsq != null) tasks.AddRange(wsq.ToArray()); + } + + // Return to the debugger all of the collected task instances + return tasks; + } + + /// Adds a work-stealing queue to the set of queues. + /// The queue to be added. + private void AddWsq(WorkStealingQueue wsq) + { + lock (m_wsQueues) + { + // Find the next open slot in the array. If we find one, + // store the queue and we're done. + int i; + for (i = 0; i < m_wsQueues.Length; i++) + { + if (m_wsQueues[i] == null) + { + m_wsQueues[i] = wsq; + return; + } + } + + // We couldn't find an open slot, so double the length + // of the array by creating a new one, copying over, + // and storing the new one. Here, i == m_wsQueues.Length. + WorkStealingQueue[] queues = new WorkStealingQueue[i * 2]; + Array.Copy(m_wsQueues, queues, i); + queues[i] = wsq; + m_wsQueues = queues; + } + } + + /// Remove a work-stealing queue from the set of queues. + /// The work-stealing queue to remove. + private void RemoveWsq(WorkStealingQueue wsq) + { + lock (m_wsQueues) + { + // Find the queue, and if/when we find it, null out its array slot + for (int i = 0; i < m_wsQueues.Length; i++) + { + if (m_wsQueues[i] == wsq) + { + m_wsQueues[i] = null; + } + } + } + } + + /// + /// The dispatch loop run by each thread in the scheduler. + /// + private void DispatchLoop() + { + // Create a new queue for this thread, store it in TLS for later retrieval, + // and add it to the set of queues for this scheduler. + WorkStealingQueue wsq = new WorkStealingQueue(); + m_wsq = wsq; + AddWsq(wsq); + + try + { + // Until there's no more work to do... + while (true) + { + Task wi = null; + + // Search order: (1) local WSQ, (2) global Q, (3) steals from other queues. + if (!wsq.LocalPop(ref wi)) + { + // We weren't able to get a task from the local WSQ + bool searchedForSteals = false; + while (true) + { + lock (m_queue) + { + // If shutdown was requested, exit the thread. + if (m_shutdown) + return; + + // (2) try the global queue. + if (m_queue.Count != 0) + { + // We found a work item! Grab it ... + wi = m_queue.Dequeue(); + break; + } + else if (searchedForSteals) + { + // Note that we're not waiting for work, and then wait + m_threadsWaiting++; + try { Monitor.Wait(m_queue); } + finally { m_threadsWaiting--; } + + // If we were signaled due to shutdown, exit the thread. + if (m_shutdown) + return; + + searchedForSteals = false; + continue; + } + } + + // (3) try to steal. + WorkStealingQueue[] wsQueues = m_wsQueues; + int i; + for (i = 0; i < wsQueues.Length; i++) + { + WorkStealingQueue q = wsQueues[i]; + if (q != null && q != wsq && q.TrySteal(ref wi)) break; + } + + if (i != wsQueues.Length) break; + + searchedForSteals = true; + } + } + + // ...and Invoke it. + TryExecuteTask(wi); + } + } + finally + { + RemoveWsq(wsq); + } + } + + /// Signal the scheduler to shutdown and wait for all threads to finish. + public void Dispose() + { + m_shutdown = true; + if (m_queue != null && m_threads.IsValueCreated) + { + var threads = m_threads.Value; + lock (m_queue) Monitor.PulseAll(m_queue); + for (int i = 0; i < threads.Length; i++) threads[i].Join(); + } + } + } + + /// A work-stealing queue. + /// Specifies the type of data stored in the queue. + internal class WorkStealingQueue where T : class + { + private const int INITIAL_SIZE = 32; + private T[] m_array = new T[INITIAL_SIZE]; + private int m_mask = INITIAL_SIZE - 1; + private volatile int m_headIndex = 0; + private volatile int m_tailIndex = 0; + + private object m_foreignLock = new object(); + + internal void LocalPush(T obj) + { + int tail = m_tailIndex; + + // When there are at least 2 elements' worth of space, we can take the fast path. + if (tail < m_headIndex + m_mask) + { + m_array[tail & m_mask] = obj; + m_tailIndex = tail + 1; + } + else + { + // We need to contend with foreign pops, so we lock. + lock (m_foreignLock) + { + int head = m_headIndex; + int count = m_tailIndex - m_headIndex; + + // If there is still space (one left), just add the element. + if (count >= m_mask) + { + // We're full; expand the queue by doubling its size. + T[] newArray = new T[m_array.Length << 1]; + for (int i = 0; i < m_array.Length; i++) + newArray[i] = m_array[(i + head) & m_mask]; + + // Reset the field values, incl. the mask. + m_array = newArray; + m_headIndex = 0; + m_tailIndex = tail = count; + m_mask = (m_mask << 1) | 1; + } + + m_array[tail & m_mask] = obj; + m_tailIndex = tail + 1; + } + } + } + + internal bool LocalPop(ref T obj) + { + while (true) + { + // Decrement the tail using a fence to ensure subsequent read doesn't come before. + int tail = m_tailIndex; + if (m_headIndex >= tail) + { + obj = null; + return false; + } + + tail -= 1; +#pragma warning disable 0420 + Interlocked.Exchange(ref m_tailIndex, tail); +#pragma warning restore 0420 + + // If there is no interaction with a take, we can head down the fast path. + if (m_headIndex <= tail) + { + int idx = tail & m_mask; + obj = m_array[idx]; + + // Check for nulls in the array. + if (obj == null) continue; + + m_array[idx] = null; + return true; + } + else + { + // Interaction with takes: 0 or 1 elements left. + lock (m_foreignLock) + { + if (m_headIndex <= tail) + { + // Element still available. Take it. + int idx = tail & m_mask; + obj = m_array[idx]; + + // Check for nulls in the array. + if (obj == null) continue; + + m_array[idx] = null; + return true; + } + else + { + // We lost the race, element was stolen, restore the tail. + m_tailIndex = tail + 1; + obj = null; + return false; + } + } + } + } + } + + internal bool TrySteal(ref T obj) + { + obj = null; + + while (true) + { + if (m_headIndex >= m_tailIndex) + return false; + + lock (m_foreignLock) + { + // Increment head, and ensure read of tail doesn't move before it (fence). + int head = m_headIndex; +#pragma warning disable 0420 + Interlocked.Exchange(ref m_headIndex, head + 1); +#pragma warning restore 0420 + + if (head < m_tailIndex) + { + int idx = head & m_mask; + obj = m_array[idx]; + + // Check for nulls in the array. + if (obj == null) continue; + + m_array[idx] = null; + return true; + } + else + { + // Failed, restore head. + m_headIndex = head; + obj = null; + } + } + + return false; + } + } + + internal bool TryFindAndPop(T obj) + { + // We do an O(N) search for the work item. The theory of work stealing and our + // inlining logic is that most waits will happen on recently queued work. And + // since recently queued work will be close to the tail end (which is where we + // begin our search), we will likely find it quickly. In the worst case, we + // will traverse the whole local queue; this is typically not going to be a + // problem (although degenerate cases are clearly an issue) because local work + // queues tend to be somewhat shallow in length, and because if we fail to find + // the work item, we are about to block anyway (which is very expensive). + + for (int i = m_tailIndex - 1; i >= m_headIndex; i--) + { + if (m_array[i & m_mask] == obj) + { + // If we found the element, block out steals to avoid interference. + lock (m_foreignLock) + { + // If we lost the race, bail. + if (m_array[i & m_mask] == null) + { + return false; + } + + // Otherwise, null out the element. + m_array[i & m_mask] = null; + + // And then check to see if we can fix up the indexes (if we're at + // the edge). If we can't, we just leave nulls in the array and they'll + // get filtered out eventually (but may lead to superflous resizing). + if (i == m_tailIndex) + m_tailIndex -= 1; + else if (i == m_headIndex) + m_headIndex += 1; + + return true; + } + } + } + + return false; + } + + internal T[] ToArray() + { + List list = new List(); + for (int i = m_tailIndex - 1; i >= m_headIndex; i--) + { + T obj = m_array[i & m_mask]; + if (obj != null) list.Add(obj); + } + return list.ToArray(); + } + } +} \ No newline at end of file diff --git a/trunk/Libraries/ParallelExtensionsExtras/Utils/SortedTopN.cs b/trunk/Libraries/ParallelExtensionsExtras/Utils/SortedTopN.cs new file mode 100644 index 0000000..0732769 --- /dev/null +++ b/trunk/Libraries/ParallelExtensionsExtras/Utils/SortedTopN.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Collections; + +namespace System.Linq +{ + internal class SortedTopN : IEnumerable> + { + private int _n; + private List _topNKeys; + private List _topNValues; + private IComparer _comparer; + + public SortedTopN(int count, IComparer comparer) + { + if (count < 1) throw new ArgumentOutOfRangeException("count"); + if (comparer == null) throw new ArgumentNullException("comparer"); + _n = count; + _topNKeys = new List(count); + _topNValues = new List(count); + _comparer = comparer; + } + + public bool Add(KeyValuePair item) + { + return Add(item.Key, item.Value); + } + + public bool Add(TKey key, TValue value) + { + int position = _topNKeys.BinarySearch(key, _comparer); + if (position < 0) position = ~position; + if (_topNKeys.Count < _n || position != 0) + { + // Empty out an item if we're already full and we need to + // add another + if (_topNKeys.Count == _n) + { + _topNKeys.RemoveAt(0); + _topNValues.RemoveAt(0); + position--; + } + + // Insert or add based on where we're adding + if (position < _n) + { + _topNKeys.Insert(position, key); + _topNValues.Insert(position, value); + } + else + { + _topNKeys.Add(key); + _topNValues.Add(value); + } + return true; + } + + // No room for this item + return false; + } + + public IEnumerable Values + { + get + { + for (int i = _topNKeys.Count - 1; i >= 0; i--) + { + yield return _topNValues[i]; + } + } + } + + public IEnumerator> GetEnumerator() + { + for (int i = _topNKeys.Count - 1; i>=0; i--) + { + yield return new KeyValuePair(_topNKeys[i], _topNValues[i]); + } + } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + } +} diff --git a/trunk/Pithos.Client.Test/Pithos.Client.Test.csproj b/trunk/Pithos.Client.Test/Pithos.Client.Test.csproj new file mode 100644 index 0000000..0b64b84 --- /dev/null +++ b/trunk/Pithos.Client.Test/Pithos.Client.Test.csproj @@ -0,0 +1,64 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {822F885B-83E8-4A9A-B02E-0FEAE444D960} + Library + Properties + Pithos.Client.Test + Pithos.Client.Test + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + {5AC90E5E-60C6-4F53-9444-6088BD7BC929} + Pithos.Client + + + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4} + Pithos.Interfaces + + + + + \ No newline at end of file diff --git a/trunk/Pithos.Client.Test/Properties/AssemblyInfo.cs b/trunk/Pithos.Client.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..6054dd6 --- /dev/null +++ b/trunk/Pithos.Client.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Pithos.Client.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Pithos.Client.Test")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("0d5e9779-d13e-4024-b945-71e083b7b3f8")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/trunk/Pithos.Client/AccountSettings.cs b/trunk/Pithos.Client/AccountSettings.cs new file mode 100644 index 0000000..258c6bd --- /dev/null +++ b/trunk/Pithos.Client/AccountSettings.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Pithos.Client +{ + public class AccountSettings + { + public string AccountName { get; set; } + public string ApiKey { get; set; } + + private List _selectiveFolders=new List(); + public List SelectiveFolders + { + get { return _selectiveFolders; } + set { _selectiveFolders = value; } + } + + + } +} diff --git a/trunk/Pithos.Client/App_Code/LICENSE.txt b/trunk/Pithos.Client/App_Code/LICENSE.txt new file mode 100644 index 0000000..f90aa76 --- /dev/null +++ b/trunk/Pithos.Client/App_Code/LICENSE.txt @@ -0,0 +1,11 @@ +New BSD License +http://www.opensource.org/licenses/bsd-license.php +Copyright (c) 2009, Rob Conery (robconery@gmail.com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of the SubSonic nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/trunk/Pithos.Client/App_Code/Massive.cs b/trunk/Pithos.Client/App_Code/Massive.cs new file mode 100644 index 0000000..af02d0c --- /dev/null +++ b/trunk/Pithos.Client/App_Code/Massive.cs @@ -0,0 +1,400 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration; +using System.Data; +using System.Data.Common; +using System.Dynamic; +using System.Linq; +using System.Text; + +namespace Massive { + public static class ObjectExtensions { + /// + /// Extension method for adding in a bunch of parameters + /// + public static void AddParams(this DbCommand cmd, object[] args) { + foreach (var item in args) { + AddParam(cmd, item); + } + } + /// + /// Extension for adding single parameter + /// + public static void AddParam(this DbCommand cmd, object item) { + var p = cmd.CreateParameter(); + p.ParameterName = string.Format("@{0}", cmd.Parameters.Count); + //fix for NULLs as parameter values + if (item == null) { + p.Value = DBNull.Value; + } else { + //fix for Guids + if (item.GetType() == typeof(Guid)) { + p.Value = item.ToString(); + p.DbType = DbType.String; + } else { + p.Value = item; + } + } + cmd.Parameters.Add(p); + } + /// + /// Turns an IDataReader to a Dynamic list of things + /// + public static List ToExpandoList(this IDataReader rdr) { + var result = new List(); + //work with the Expando as a Dictionary + while (rdr.Read()) { + dynamic e = new ExpandoObject(); + var d = e as IDictionary; + for (int i = 0; i < rdr.FieldCount; i++) + d.Add(rdr.GetName(i), rdr[i]); + result.Add(e); + } + return result; + } + /// + /// Turns the object into an ExpandoObject + /// + /// + /// + public static dynamic ToExpando(this object o) { + var result = new ExpandoObject(); + var d = result as IDictionary; //work with the Expando as a Dictionary + if (o.GetType() == typeof(ExpandoObject)) return o; //shouldn't have to... but just in case + //special for form submissions + if (o.GetType() == typeof(NameValueCollection)) { + var nv = (NameValueCollection)o; + nv.Cast().Select(key => new KeyValuePair(key, nv[key])).ToList().ForEach(i => d.Add(i)); + } else { + //assume it's a regular lovely object + var props = o.GetType().GetProperties(); + foreach (var item in props) { + d.Add(item.Name, item.GetValue(o, null)); + } + } + return result; + } + /// + /// Turns the object into a Dictionary + /// + /// + /// + public static IDictionary ToDictionary(this object thingy) { + return (IDictionary)thingy.ToExpando(); + } + } + /// + /// A class that wraps your database table in Dynamic Funtime + /// + public abstract class DynamicModel : DynamicObject { + DbProviderFactory _factory; + string _connectionStringName; + string _connectionString; + + public IList Query(string sql, params object[] args) { + var result = new List(); + using (var conn = OpenConnection()) { + using (var cmd = CreateCommand(sql, args)) { + cmd.Connection = conn; + using (var rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection)) { + result = rdr.ToExpandoList(); + } + } + } + return result; + } + /// + /// Creates a DBCommand that you can use for loving your database. + /// + DbCommand CreateCommand(string sql, params object[] args) { + DbCommand result = null; + result = _factory.CreateCommand(); + result.CommandText = sql; + if (args.Length > 0) + result.AddParams(args); + return result; + } + DbConnection GetConnection() { + var connection = _factory.CreateConnection(); + connection.ConnectionString = _connectionString; + return connection; + } + DbConnection OpenConnection() { + var conn = GetConnection(); + conn.Open(); + return conn; + } + /// + /// Creates a slick, groovy little wrapper for your action + /// + /// + public DynamicModel(string connectionStringName) { + //can be overridden by property setting + TableName = this.GetType().Name; + _connectionStringName = connectionStringName; + + var providerName = "System.Data.SqlClient"; + if (ConfigurationManager.ConnectionStrings[_connectionStringName] != null) { + providerName = ConfigurationManager.ConnectionStrings[_connectionStringName].ProviderName ?? "System.Data.SqlClient"; + } else { + throw new InvalidOperationException("Can't find a connection string with the name '" + _connectionStringName + "'"); + } + _factory = DbProviderFactories.GetFactory(providerName); + _connectionString = ConfigurationManager.ConnectionStrings[_connectionStringName].ConnectionString; + } + string _primaryKeyField; + /// + /// Conventionally returns a PK field. The default is "ID" if you don't set one + /// + public string PrimaryKeyField { + get { return string.IsNullOrEmpty(_primaryKeyField) ? /*a bit of convention here*/ "ID" : /*oh well - did our best*/ _primaryKeyField; } + set { _primaryKeyField = value; } + } + /// + /// Conventionally introspects the object passed in for a field that + /// looks like a PK. If you've named your PrimaryKeyField, this becomes easy + /// + public bool HasPrimaryKey(object o) { + var result = o.ToDictionary().ContainsKey(PrimaryKeyField); + return result; + } + /// + /// If the object passed in has a property with the same name as your PrimaryKeyField + /// it is returned here. + /// + public object GetPrimaryKey(object o) { + var d = o.ToDictionary(); + object result = null; + d.TryGetValue(PrimaryKeyField, out result); + return result; + } + /// + /// The name of the Database table we're working with. This defaults to + /// the class name - set this value if it's different + /// + public string TableName { get; set; } + /// + /// Adds a record to the database. You can pass in an Anonymous object, an ExpandoObject, + /// A regular old POCO, or a NameValueColletion from a Request.Form or Request.QueryString + /// + public dynamic Insert(object o) { + dynamic result = 0; + if (BeforeInsert(o)) { + using (var conn = OpenConnection()) { + using (var cmd = CreateInsertCommand(o)) { + cmd.Connection = conn; + result = cmd.ExecuteScalar(); + } + AfterInsert(o); + } + } + return result; + } + + /// + /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with + /// + public DbCommand CreateInsertCommand(object o) { + DbCommand result = null; + //turn this into an expando - we'll need that for the validators + var expando = o.ToExpando(); + var settings = (IDictionary)expando; + var sbKeys = new StringBuilder(); + var sbVals = new StringBuilder(); + var stub = "INSERT INTO {0} ({1}) \r\n VALUES ({2}); \r\nSELECT SCOPE_IDENTITY()"; + result = CreateCommand(stub); + + int counter = 0; + foreach (var item in settings) { + sbKeys.AppendFormat("{0},", item.Key); + sbVals.AppendFormat("@{0},", counter.ToString()); + result.AddParam(item.Value); + counter++; + } + if (counter > 0) { + //strip off trailing commas + var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 1); + var vals = sbVals.ToString().Substring(0, sbVals.Length - 1); + var sql = string.Format(stub, TableName, keys, vals); + result.CommandText = sql; + } else throw new InvalidOperationException("Can't parse this object to the database - there are no properties set"); + return result; + } + + /// + /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with + /// + public DbCommand CreateUpdateCommand(object o, object key) { + var expando = o.ToExpando(); + var settings = (IDictionary)expando; + var sbKeys = new StringBuilder(); + var stub = "UPDATE {0} SET {1} WHERE {2} = @{3}"; + var args = new List(); + var result = CreateCommand(stub); + int counter = 0; + foreach (var item in settings) { + var val = item.Value; + if (!item.Key.Equals(PrimaryKeyField, StringComparison.CurrentCultureIgnoreCase) && item.Value != null) { + result.AddParam(val); + sbKeys.AppendFormat("{0} = @{1}, \r\n", item.Key, counter.ToString()); + counter++; + } + } + if (counter > 0) { + //add the key + result.AddParam(key); + //strip the last commas + var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 4); + result.CommandText = string.Format(stub, TableName, keys, PrimaryKeyField, counter); + } else throw new InvalidOperationException("No parsable object was sent in - could not divine any name/value pairs"); + return result; + } + /// + /// Updates a record in the database. You can pass in an Anonymous object, an ExpandoObject, + /// A regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString + /// + public int Update(object o, object key) { + //turn this into an expando - we'll need that for the validators + int result = 0; + if (BeforeUpdate(o)) { + using (var conn = OpenConnection()) { + using (var cmd = CreateUpdateCommand(o, key)) { + result = cmd.ExecuteNonQuery(); + AfterUpdate(o); + } + } + } + return result; + } + /// + /// Updates a bunch of records in the database within a transaction. You can pass Anonymous objects, ExpandoObjects, + /// Regular old POCOs - these all have to have a PK set + /// + public int InsertMany(IEnumerable things) { + int result = 0; + using (var conn = OpenConnection()) { + using (var tx = conn.BeginTransaction()) { + foreach (var item in things) { + if (BeforeInsert(item)) { + using (var cmd = CreateInsertCommand(item)) { + cmd.Connection = conn; + cmd.Transaction = tx; + cmd.ExecuteNonQuery(); + } + AfterInsert(item); + } + result++; + } + tx.Commit(); + } + } + return result; + } + /// + /// Updates a bunch of records in the database within a transaction. You can pass Anonymous objects, ExpandoObjects, + /// Regular old POCOs - these all have to have a PK set + /// + public int UpdateMany(IEnumerable things) { + int result = 0; + using (var conn = OpenConnection()) { + using (var tx = conn.BeginTransaction()) { + foreach (var item in things) { + var pk = GetPrimaryKey(item); + if (pk == null) + throw new InvalidOperationException("Please be sure to set a value for the primary key"); + if (BeforeUpdate(item)) { + using (var cmd = CreateUpdateCommand(item, pk)) { + cmd.Connection = conn; + cmd.Transaction = tx; + cmd.ExecuteNonQuery(); + } + AfterUpdate(item); + } + result++; + } + tx.Commit(); + } + } + return result; + } + /// + /// If you're feeling lazy, or are just unsure about whether to use Update or Insert you can use + /// this method. It will look for a PrimaryKeyField with a set value to determine if this should + /// be an Insert or Save. You can pass in an Anonymous object, an ExpandoObject, + /// A regular old POCO, or a NameValueColletion from a Request.Form or Request.QueryString + /// + public dynamic Save(object o) { + dynamic result = 0; + if (BeforeSave(o)) { + var expando = o.ToExpando(); + //decide insert or update + result = HasPrimaryKey(expando) ? Update(expando, GetPrimaryKey(o)) : Insert(expando); + AfterSave(o); + } + return result; + } + /// + /// Removes a record from the database + /// + public int Delete(object key) { + //execute + var sql = string.Format("DELETE FROM {0} WHERE {1} = @0", TableName, PrimaryKeyField); + var result = 0; + using (var conn = OpenConnection()) { + using (var cmd = CreateCommand(sql, key)) { + cmd.Connection = conn; + result = cmd.ExecuteNonQuery(); + } + } + return result; + } + /// + /// Removes one or more records from the DB according to the passed-in WHERE + /// + public dynamic Delete(string where, params object[] args) { + //execute + var sql = string.Format("DELETE FROM {0} ", TableName); + sql += where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase) ? where : "WHERE " + where; + var result = 0; + using (var conn = OpenConnection()) { + using (var cmd = CreateCommand(sql, args)) { + cmd.Connection = conn; + result = cmd.ExecuteNonQuery(); + } + } + return result; + } + /// + /// Returns all records complying with the passed-in WHERE clause and arguments, + /// ordered as specified, limited (TOP) by limit. + /// + public IEnumerable All(string where = "", string orderBy = "", int limit = 0, params object[] args) { + string sql = limit > 0 ? "SELECT TOP " + limit + " * FROM {0} " : "SELECT * FROM {0} "; + if (!string.IsNullOrEmpty(where)) + sql += where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase) ? where : "WHERE " + where; + if (!String.IsNullOrEmpty(orderBy)) + sql += orderBy.Trim().StartsWith("order by", StringComparison.CurrentCultureIgnoreCase) ? orderBy : " ORDER BY " + orderBy; + return Query(string.Format(sql, TableName), args); + } + /// + /// Returns a single row from the database + /// + /// ExpandoObject + public dynamic Single(object key) { + var sql = string.Format("SELECT * FROM {0} WHERE {1} = @0", TableName, PrimaryKeyField); + return Query(sql, key).FirstOrDefault(); + } + #region hooks + //hooks for save routines + public virtual bool BeforeInsert(object o) { return true; } + public virtual bool BeforeUpdate(object o) { return true; } + public virtual bool BeforeSave(object o) { return true; } + public virtual bool BeforeDelete(object key) { return true; } + public virtual void AfterInsert(object o) { } + public virtual void AfterUpdate(object o) { } + public virtual void AfterSave(object o) { } + public virtual void AfterDelete(object key) { } + #endregion + } +} \ No newline at end of file diff --git a/trunk/Pithos.Client/IoC.cs b/trunk/Pithos.Client/IoC.cs new file mode 100644 index 0000000..97d8eb9 --- /dev/null +++ b/trunk/Pithos.Client/IoC.cs @@ -0,0 +1,56 @@ +using System; +using System.ComponentModel.Composition.Hosting; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.ComponentModel.Composition; +using Pithos.Core; + +namespace Pithos.Client +{ + public class IoC + { + public CompositionContainer Container; + + static readonly Lazy Instance=new Lazy(); + + public static IoC Current + { + get { return Instance.Value; } + } + + public IoC() + { + var catalog = new AggregateCatalog(); + var executingAssembly = Assembly.GetExecutingAssembly(); + catalog.Catalogs.Add(new AssemblyCatalog(executingAssembly)); + + Type[] types = {typeof (PithosMonitor), typeof (Pithos.Network.CloudFilesClient)}; + foreach (var type in types) + { + catalog.Catalogs.Add(new AssemblyCatalog(type.Assembly)); + } + + + + Container=new CompositionContainer(catalog); + } + + + + public T Compose(T target) + { + try + { + Container.ComposeParts(target); + return target; + } + catch (Exception exc) + { + Trace.TraceError(exc.ToString()); + throw; + } + } + } +} diff --git a/trunk/Pithos.Client/Pithos.Client.csproj b/trunk/Pithos.Client/Pithos.Client.csproj new file mode 100644 index 0000000..20af9f8 --- /dev/null +++ b/trunk/Pithos.Client/Pithos.Client.csproj @@ -0,0 +1,199 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {5AC90E5E-60C6-4F53-9444-6088BD7BC929} + WinExe + Properties + Pithos.Client + Pithos.Client + v4.0 + + + 512 + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + false + true + 1 + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + True + False + True + False + False + True + False + False + False + False + False + True + False + False + True + + + + + + + False + Full + Build + 0 + + + x86 + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + Form + + + Preferences.cs + + + + + Preferences.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + + + + + + + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3} + ParallelExtensionsExtras + + + {142AF135-DF30-4563-B0AC-B604235AE874} + Pithos.Core + + + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4} + Pithos.Interfaces + + + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD} + Pithos.Network + + + + + + PreserveNewest + + + + + False + Microsoft .NET Framework 4 Client Profile %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + SQL Server Compact 3.5 SP2 + true + + + False + Windows Installer 3.1 + true + + + + + \ No newline at end of file diff --git a/trunk/Pithos.Client/PithosFiles.db b/trunk/Pithos.Client/PithosFiles.db new file mode 100644 index 0000000..4ca1146 Binary files /dev/null and b/trunk/Pithos.Client/PithosFiles.db differ diff --git a/trunk/Pithos.Client/PithosSettings.cs b/trunk/Pithos.Client/PithosSettings.cs new file mode 100644 index 0000000..95e3e0c --- /dev/null +++ b/trunk/Pithos.Client/PithosSettings.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using System.Text; +using Pithos.Client.Properties; +using Pithos.Interfaces; + +namespace Pithos.Client +{ + [Export(typeof(IPithosSettings))] + public class PithosSettings:IPithosSettings + { + public string PithosPath + { + get { return Settings.Default.PithosPath; } + set { Settings.Default.PithosPath=value; } + } + + public string IconsPath + { + get { return Settings.Default.IconPath; } + set { Settings.Default.IconPath=value; } + } + + public string UserName + { + get { return Settings.Default.UserName; } + set { Settings.Default.UserName=value; } + } + + public string ApiKey + { + get { return Settings.Default.ApiKey; } + set { Settings.Default.ApiKey=value; } + } + + public void Save() + { + Settings.Default.Save(); + } + + public void Reload() + { + Settings.Default.Reload(); + } + } +} diff --git a/trunk/Pithos.Client/Preferences.Designer.cs b/trunk/Pithos.Client/Preferences.Designer.cs new file mode 100644 index 0000000..8e2503d --- /dev/null +++ b/trunk/Pithos.Client/Preferences.Designer.cs @@ -0,0 +1,430 @@ +namespace Pithos.Client +{ + partial class Preferences + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Preferences)); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabAccount = new System.Windows.Forms.TabPage(); + this.btnAccountVerify = new System.Windows.Forms.Button(); + this.txtApiKey = new System.Windows.Forms.TextBox(); + this.label2 = new System.Windows.Forms.Label(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.btnRemoveAccount = new System.Windows.Forms.Button(); + this.btnAddAccount = new System.Windows.Forms.Button(); + this.listBox1 = new System.Windows.Forms.ListBox(); + this.tabAdvanced = new System.Windows.Forms.TabPage(); + this.btnBrowsePithosPath = new System.Windows.Forms.Button(); + this.label3 = new System.Windows.Forms.Label(); + this.txtPithosPath = new System.Windows.Forms.TextBox(); + this.notifyIcon = new System.Windows.Forms.NotifyIcon(this.components); + this.systemMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components); + this.openPithosFolderToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.launchPithosWebSiteToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.recentlyChangedFilesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this.spaceUserToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.statusToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator3 = new System.Windows.Forms.ToolStripSeparator(); + this.pauseSynchingToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.preferencesToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator4 = new System.Windows.Forms.ToolStripSeparator(); + this.exitToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.panel1 = new System.Windows.Forms.Panel(); + this.btnApply = new System.Windows.Forms.Button(); + this.btnCancel = new System.Windows.Forms.Button(); + this.btnOK = new System.Windows.Forms.Button(); + this.pithosFolderBrowser = new System.Windows.Forms.FolderBrowserDialog(); + this.tabControl1.SuspendLayout(); + this.tabAccount.SuspendLayout(); + this.tabAdvanced.SuspendLayout(); + this.systemMenuStrip.SuspendLayout(); + this.panel1.SuspendLayout(); + this.SuspendLayout(); + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.tabAccount); + this.tabControl1.Controls.Add(this.tabAdvanced); + this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabControl1.Location = new System.Drawing.Point(0, 0); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(462, 243); + this.tabControl1.TabIndex = 0; + // + // tabAccount + // + this.tabAccount.Controls.Add(this.btnAccountVerify); + this.tabAccount.Controls.Add(this.txtApiKey); + this.tabAccount.Controls.Add(this.label2); + this.tabAccount.Controls.Add(this.textBox1); + this.tabAccount.Controls.Add(this.label1); + this.tabAccount.Controls.Add(this.btnRemoveAccount); + this.tabAccount.Controls.Add(this.btnAddAccount); + this.tabAccount.Controls.Add(this.listBox1); + this.tabAccount.Location = new System.Drawing.Point(4, 22); + this.tabAccount.Name = "tabAccount"; + this.tabAccount.Padding = new System.Windows.Forms.Padding(3); + this.tabAccount.Size = new System.Drawing.Size(454, 217); + this.tabAccount.TabIndex = 0; + this.tabAccount.Text = "Accounts"; + this.tabAccount.UseVisualStyleBackColor = true; + this.tabAccount.Click += new System.EventHandler(this.tabAccount_Click); + // + // btnAccountVerify + // + this.btnAccountVerify.Location = new System.Drawing.Point(367, 89); + this.btnAccountVerify.Name = "btnAccountVerify"; + this.btnAccountVerify.Size = new System.Drawing.Size(75, 23); + this.btnAccountVerify.TabIndex = 7; + this.btnAccountVerify.Text = "Verify"; + this.btnAccountVerify.UseVisualStyleBackColor = true; + // + // txtApiKey + // + this.txtApiKey.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.txtApiKey.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::Pithos.Client.Properties.Settings.Default, "ApiKey", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.txtApiKey.Location = new System.Drawing.Point(208, 63); + this.txtApiKey.Name = "txtApiKey"; + this.txtApiKey.Size = new System.Drawing.Size(234, 20); + this.txtApiKey.TabIndex = 6; + this.txtApiKey.Text = global::Pithos.Client.Properties.Settings.Default.ApiKey; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(205, 47); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(45, 13); + this.label2.TabIndex = 5; + this.label2.Text = "API Key"; + // + // textBox1 + // + this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBox1.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::Pithos.Client.Properties.Settings.Default, "UserName", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.textBox1.Location = new System.Drawing.Point(208, 22); + this.textBox1.Name = "textBox1"; + this.textBox1.Size = new System.Drawing.Size(234, 20); + this.textBox1.TabIndex = 4; + this.textBox1.Text = global::Pithos.Client.Properties.Settings.Default.UserName; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(205, 6); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(78, 13); + this.label1.TabIndex = 3; + this.label1.Text = "Account Name"; + // + // btnRemoveAccount + // + this.btnRemoveAccount.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnRemoveAccount.Location = new System.Drawing.Point(113, 188); + this.btnRemoveAccount.Name = "btnRemoveAccount"; + this.btnRemoveAccount.Size = new System.Drawing.Size(75, 23); + this.btnRemoveAccount.TabIndex = 2; + this.btnRemoveAccount.Text = "Remove"; + this.btnRemoveAccount.UseVisualStyleBackColor = true; + // + // btnAddAccount + // + this.btnAddAccount.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.btnAddAccount.Location = new System.Drawing.Point(6, 188); + this.btnAddAccount.Name = "btnAddAccount"; + this.btnAddAccount.Size = new System.Drawing.Size(75, 23); + this.btnAddAccount.TabIndex = 1; + this.btnAddAccount.Text = "Add"; + this.btnAddAccount.UseVisualStyleBackColor = true; + // + // listBox1 + // + this.listBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.listBox1.FormattingEnabled = true; + this.listBox1.Location = new System.Drawing.Point(6, 6); + this.listBox1.Name = "listBox1"; + this.listBox1.Size = new System.Drawing.Size(184, 173); + this.listBox1.TabIndex = 0; + // + // tabAdvanced + // + this.tabAdvanced.Controls.Add(this.btnBrowsePithosPath); + this.tabAdvanced.Controls.Add(this.label3); + this.tabAdvanced.Controls.Add(this.txtPithosPath); + this.tabAdvanced.Location = new System.Drawing.Point(4, 22); + this.tabAdvanced.Name = "tabAdvanced"; + this.tabAdvanced.Padding = new System.Windows.Forms.Padding(3); + this.tabAdvanced.Size = new System.Drawing.Size(454, 217); + this.tabAdvanced.TabIndex = 2; + this.tabAdvanced.Text = "Advanced"; + this.tabAdvanced.UseVisualStyleBackColor = true; + // + // btnBrowsePithosPath + // + this.btnBrowsePithosPath.Location = new System.Drawing.Point(296, 28); + this.btnBrowsePithosPath.Name = "btnBrowsePithosPath"; + this.btnBrowsePithosPath.Size = new System.Drawing.Size(75, 23); + this.btnBrowsePithosPath.TabIndex = 2; + this.btnBrowsePithosPath.Text = "Browse"; + this.btnBrowsePithosPath.UseVisualStyleBackColor = true; + this.btnBrowsePithosPath.Click += new System.EventHandler(this.btnBrowsePithosPath_Click); + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(8, 12); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(68, 13); + this.label3.TabIndex = 0; + this.label3.Text = "Pithos Folder"; + // + // txtPithosPath + // + this.txtPithosPath.DataBindings.Add(new System.Windows.Forms.Binding("Text", global::Pithos.Client.Properties.Settings.Default, "PithosPath", true, System.Windows.Forms.DataSourceUpdateMode.OnPropertyChanged)); + this.txtPithosPath.Location = new System.Drawing.Point(11, 28); + this.txtPithosPath.Name = "txtPithosPath"; + this.txtPithosPath.Size = new System.Drawing.Size(279, 20); + this.txtPithosPath.TabIndex = 1; + this.txtPithosPath.Text = global::Pithos.Client.Properties.Settings.Default.PithosPath; + // + // notifyIcon + // + this.notifyIcon.BalloonTipText = "All Files Up to Date"; + this.notifyIcon.BalloonTipTitle = "Pithos 1.0"; + this.notifyIcon.ContextMenuStrip = this.systemMenuStrip; + this.notifyIcon.Icon = ((System.Drawing.Icon)(resources.GetObject("notifyIcon.Icon"))); + this.notifyIcon.Text = "Pithos 1.0"; + this.notifyIcon.Visible = true; + this.notifyIcon.DoubleClick += new System.EventHandler(this.notifyIcon_DoubleClick); + // + // systemMenuStrip + // + this.systemMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.openPithosFolderToolStripMenuItem, + this.launchPithosWebSiteToolStripMenuItem, + this.recentlyChangedFilesToolStripMenuItem, + this.toolStripSeparator1, + this.spaceUserToolStripMenuItem, + this.toolStripSeparator2, + this.statusToolStripMenuItem, + this.toolStripSeparator3, + this.pauseSynchingToolStripMenuItem, + this.preferencesToolStripMenuItem, + this.toolStripSeparator4, + this.exitToolStripMenuItem}); + this.systemMenuStrip.Name = "systemMenuStrip"; + this.systemMenuStrip.Size = new System.Drawing.Size(187, 204); + // + // openPithosFolderToolStripMenuItem + // + this.openPithosFolderToolStripMenuItem.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Bold); + this.openPithosFolderToolStripMenuItem.Name = "openPithosFolderToolStripMenuItem"; + this.openPithosFolderToolStripMenuItem.Size = new System.Drawing.Size(186, 22); + this.openPithosFolderToolStripMenuItem.Text = "Open Pithos folder"; + this.openPithosFolderToolStripMenuItem.Click += new System.EventHandler(this.openPithosFolderToolStripMenuItem_Click); + // + // launchPithosWebSiteToolStripMenuItem + // + this.launchPithosWebSiteToolStripMenuItem.Name = "launchPithosWebSiteToolStripMenuItem"; + this.launchPithosWebSiteToolStripMenuItem.Size = new System.Drawing.Size(186, 22); + this.launchPithosWebSiteToolStripMenuItem.Text = "Launch Pithos Web Site"; + // + // recentlyChangedFilesToolStripMenuItem + // + this.recentlyChangedFilesToolStripMenuItem.Name = "recentlyChangedFilesToolStripMenuItem"; + this.recentlyChangedFilesToolStripMenuItem.Size = new System.Drawing.Size(186, 22); + this.recentlyChangedFilesToolStripMenuItem.Text = "Recently Changed Files"; + // + // toolStripSeparator1 + // + this.toolStripSeparator1.Name = "toolStripSeparator1"; + this.toolStripSeparator1.Size = new System.Drawing.Size(183, 6); + // + // spaceUserToolStripMenuItem + // + this.spaceUserToolStripMenuItem.Enabled = false; + this.spaceUserToolStripMenuItem.Name = "spaceUserToolStripMenuItem"; + this.spaceUserToolStripMenuItem.Size = new System.Drawing.Size(186, 22); + this.spaceUserToolStripMenuItem.Text = "x % of 5 GB used"; + // + // toolStripSeparator2 + // + this.toolStripSeparator2.Name = "toolStripSeparator2"; + this.toolStripSeparator2.Size = new System.Drawing.Size(183, 6); + // + // statusToolStripMenuItem + // + this.statusToolStripMenuItem.Enabled = false; + this.statusToolStripMenuItem.Name = "statusToolStripMenuItem"; + this.statusToolStripMenuItem.Size = new System.Drawing.Size(186, 22); + this.statusToolStripMenuItem.Text = "All Files Up to Date"; + // + // toolStripSeparator3 + // + this.toolStripSeparator3.Name = "toolStripSeparator3"; + this.toolStripSeparator3.Size = new System.Drawing.Size(183, 6); + // + // pauseSynchingToolStripMenuItem + // + this.pauseSynchingToolStripMenuItem.Name = "pauseSynchingToolStripMenuItem"; + this.pauseSynchingToolStripMenuItem.Size = new System.Drawing.Size(186, 22); + this.pauseSynchingToolStripMenuItem.Text = "Pause Synching"; + this.pauseSynchingToolStripMenuItem.Click += new System.EventHandler(this.pauseSynchingToolStripMenuItem_Click); + // + // preferencesToolStripMenuItem + // + this.preferencesToolStripMenuItem.Name = "preferencesToolStripMenuItem"; + this.preferencesToolStripMenuItem.Size = new System.Drawing.Size(186, 22); + this.preferencesToolStripMenuItem.Text = "Preferences"; + this.preferencesToolStripMenuItem.Click += new System.EventHandler(this.preferencesToolStripMenuItem_Click); + // + // toolStripSeparator4 + // + this.toolStripSeparator4.Name = "toolStripSeparator4"; + this.toolStripSeparator4.Size = new System.Drawing.Size(183, 6); + // + // exitToolStripMenuItem + // + this.exitToolStripMenuItem.Name = "exitToolStripMenuItem"; + this.exitToolStripMenuItem.Size = new System.Drawing.Size(186, 22); + this.exitToolStripMenuItem.Text = "Exit"; + this.exitToolStripMenuItem.Click += new System.EventHandler(this.exitToolStripMenuItem_Click); + // + // panel1 + // + this.panel1.Controls.Add(this.btnApply); + this.panel1.Controls.Add(this.btnCancel); + this.panel1.Controls.Add(this.btnOK); + this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom; + this.panel1.Location = new System.Drawing.Point(0, 243); + this.panel1.Name = "panel1"; + this.panel1.Size = new System.Drawing.Size(462, 43); + this.panel1.TabIndex = 1; + // + // btnApply + // + this.btnApply.Location = new System.Drawing.Point(371, 8); + this.btnApply.Name = "btnApply"; + this.btnApply.Size = new System.Drawing.Size(75, 23); + this.btnApply.TabIndex = 2; + this.btnApply.Text = "Apply"; + this.btnApply.UseVisualStyleBackColor = true; + this.btnApply.Click += new System.EventHandler(this.btnApply_Click); + // + // btnCancel + // + this.btnCancel.Location = new System.Drawing.Point(261, 8); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.Size = new System.Drawing.Size(75, 23); + this.btnCancel.TabIndex = 1; + this.btnCancel.Text = "Cancel"; + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // btnOK + // + this.btnOK.Location = new System.Drawing.Point(169, 8); + this.btnOK.Name = "btnOK"; + this.btnOK.Size = new System.Drawing.Size(75, 23); + this.btnOK.TabIndex = 0; + this.btnOK.Text = "OK"; + this.btnOK.UseVisualStyleBackColor = true; + this.btnOK.Click += new System.EventHandler(this.btnOK_Click); + // + // pithosFolderBrowser + // + this.pithosFolderBrowser.Description = "Select a folder to synchronize with Pithos"; + this.pithosFolderBrowser.RootFolder = System.Environment.SpecialFolder.Personal; + // + // Preferences + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(462, 286); + this.Controls.Add(this.tabControl1); + this.Controls.Add(this.panel1); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "Preferences"; + this.Text = "Preferences"; + this.tabControl1.ResumeLayout(false); + this.tabAccount.ResumeLayout(false); + this.tabAccount.PerformLayout(); + this.tabAdvanced.ResumeLayout(false); + this.tabAdvanced.PerformLayout(); + this.systemMenuStrip.ResumeLayout(false); + this.panel1.ResumeLayout(false); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabAccount; + private System.Windows.Forms.TabPage tabAdvanced; + private System.Windows.Forms.NotifyIcon notifyIcon; + private System.Windows.Forms.ContextMenuStrip systemMenuStrip; + private System.Windows.Forms.ToolStripMenuItem openPithosFolderToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem launchPithosWebSiteToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem recentlyChangedFilesToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + private System.Windows.Forms.ToolStripMenuItem spaceUserToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; + private System.Windows.Forms.ToolStripMenuItem statusToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator3; + private System.Windows.Forms.ToolStripMenuItem pauseSynchingToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem preferencesToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator4; + private System.Windows.Forms.ToolStripMenuItem exitToolStripMenuItem; + private System.Windows.Forms.Button btnRemoveAccount; + private System.Windows.Forms.Button btnAddAccount; + private System.Windows.Forms.ListBox listBox1; + private System.Windows.Forms.Panel panel1; + private System.Windows.Forms.Button btnApply; + private System.Windows.Forms.Button btnCancel; + private System.Windows.Forms.Button btnOK; + private System.Windows.Forms.Button btnAccountVerify; + private System.Windows.Forms.TextBox txtApiKey; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Button btnBrowsePithosPath; + private System.Windows.Forms.TextBox txtPithosPath; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.FolderBrowserDialog pithosFolderBrowser; + } +} \ No newline at end of file diff --git a/trunk/Pithos.Client/Preferences.cs b/trunk/Pithos.Client/Preferences.cs new file mode 100644 index 0000000..be6ce55 --- /dev/null +++ b/trunk/Pithos.Client/Preferences.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.Composition; +using System.Data; +using System.Diagnostics; +using System.Drawing; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Windows.Forms; +using Pithos.Client.Properties; +using Pithos.Core; +using Pithos.Interfaces; + +namespace Pithos.Client +{ + public partial class Preferences : Form + { + [Import] + public IStatusChecker StatusChecker; + + [Import] + public IPithosSettings Settings; + + + bool _allowVisible; // ContextMenu's Show command used + bool _allowClose; // ContextMenu's Exit command used + bool _loadFired; // Form was shown once + + + [Import] + public PithosMonitor Monitor; + + public Preferences() + { + InitializeComponent(); + } + + protected override void SetVisibleCore(bool value) + { + if (!_allowVisible) + value = false; + base.SetVisibleCore(value); + } + + protected override void OnFormClosing(FormClosingEventArgs e) + { + if (!_allowClose) + { + this.Hide(); + e.Cancel = true; + } + base.OnFormClosing(e); + } + private void openPithosFolderToolStripMenuItem_Click(object sender, EventArgs e) + { + OpenPithosPath(); + } + + private void notifyIcon_DoubleClick(object sender, EventArgs e) + { + OpenPithosPath(); + } + + private void OpenPithosPath() + { + Process.Start(Settings.PithosPath); + } + + + private Dictionary iconNames =new List + { + new StatusInfo(PithosStatus.InSynch, "All files up to date", "TrayInSynch"), + new StatusInfo(PithosStatus.Synching, "Synching Files", "TraySynching") + }.ToDictionary(s => s.Status); + + public void UpdateStatus() + { + var pithosStatus=StatusChecker.GetPithosStatus(); + + if (iconNames.ContainsKey(pithosStatus)) + { + var info= iconNames[pithosStatus]; + + + notifyIcon.Icon = (Icon) Resources.ResourceManager.GetObject(info.IconName); + notifyIcon.Text = String.Format("Pithos 1.0\r\n{0}", info.StatusText); + notifyIcon.BalloonTipText = info.StatusText; + statusToolStripMenuItem.Text = info.StatusText; + } + if (!String.IsNullOrWhiteSpace(Settings.UserName )&& + !String.IsNullOrWhiteSpace(Settings.ApiKey)) + Monitor.Start(); + + } + + private void btnBrowsePithosPath_Click(object sender, EventArgs e) + { + pithosFolderBrowser.SelectedPath = txtPithosPath.Text; + var result = pithosFolderBrowser.ShowDialog(); + if (result == DialogResult.OK) + { + var newPath = pithosFolderBrowser.SelectedPath; + txtPithosPath.Text = newPath; + Settings.PithosPath = newPath; + Settings.Save(); + } + } + + private void preferencesToolStripMenuItem_Click(object sender, EventArgs e) + { + _allowVisible = true; + _loadFired = true; + Show(); + } + + private void exitToolStripMenuItem_Click(object sender, EventArgs e) + { + _allowClose = _allowVisible = true; + Monitor.Stop(); + if (!_loadFired) + Show(); + Close(); + } + + private void btnCancel_Click(object sender, EventArgs e) + { + Settings.Reload(); + Hide(); + } + + private void btnOK_Click(object sender, EventArgs e) + { + DoSave(); + Hide(); + } + + private void btnApply_Click(object sender, EventArgs e) + { + DoSave(); + } + + private void DoSave() + { + Settings.Save(); + Monitor.Start(); + } + + private void pauseSynchingToolStripMenuItem_Click(object sender, EventArgs e) + { + Monitor.Pause = !Monitor.Pause; + pauseSynchingToolStripMenuItem.Text = Monitor.Pause?"Resume Synching":"Pause Synching"; + } + + private void tabAccount_Click(object sender, EventArgs e) + { + + } + } +} diff --git a/trunk/Pithos.Client/Preferences.resx b/trunk/Pithos.Client/Preferences.resx new file mode 100644 index 0000000..47455af --- /dev/null +++ b/trunk/Pithos.Client/Preferences.resx @@ -0,0 +1,152 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 119, 13 + + + + + AAABAAIAEBAQAAAABAAoAQAAJgAAACAgEAAAAAQA6AIAAE4BAAAoAAAAEAAAACAAAAABAAQAAAAAAIAA + AAAAAAAAAAAAABAAAAAQAAAAAAAAAAAAgAAAgAAAAICAAIAAAACAAIAAgIAAAICAgADAwMAAAAD/AAD/ + AAAA//8A/wAAAP8A/wD//wAA////AAAAAAAAAAAAAAAMzMzAAAAAAAAAAAAAAAAMzMzMzMAAAAAAAAAA + AAAAzMzMzMzMAAzMzMzMzMzAAAzMzMzMwAAMzMzMzMzMAAAADMzMwAAAAATMzMzMwAAAAAAAAAAAAAAA + DMzMwAAAAAAAzMzAAAAAAAAAAAAAAAAMzMzMzMAA//8AAPgfAAD//wAA4AcAAP//AADAAwAAgAEAAOAH + AACAAwAA+B8AAOAHAAD//wAA+B8AAPwfAAD//wAA4AcAACgAAAAgAAAAQAAAAAEABAAAAAAAAAIAAAAA + AAAAAAAAEAAAABAAAAAAAAAAAACAAACAAAAAgIAAgAAAAIAAgACAgAAAgICAAMDAwAAAAP8AAP8AAAD/ + /wD/AAAA/wD/AP//AAD///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzMzAAAAAAAAAAAAAAAzMzMzM + zAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzMzMzMAAAAAAAAAAAMzM + zMzMzMzMwAAAAAAAAAzMzMzMzMzMzMwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAzMzMzMzMzMAAAAAAAA + DMzMzMzMzMzMzMzAAAAADMTMzMzMzMzMzMzMzMAAAAzMzMzMzMzMzMzMzMwAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAzMzMzMzMzMzAAAAAAAAMzMzMzMzMzMzMzMAAAAAAzMzMzMzMzMzMzMzAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAMzMzMzMwAAAAAAAAAAADMzMzMzMzMzMAAAAAAAAAMTMzMzMzMzMzAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMzMzMzMzAAAAAAAAAAAAADMzMzMzMAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAADMzMzMwAAAAAAAAAAAAAAMzMzMzMwAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAADMzMzMzMzMwAAAAAAAAAAMzMzMzMzMzMAAAAAAAAAAAAAAAAAAAAAAAAAAD///////AH///g + A//////////////AAf//AAB//gAAP///////gAD/+AAAH+AAAAfgAAAP//////8AAH/4AAAf8AAAH/// + ////4AP//wAAf/4AAH/////////////AA///wAf////////wB///4AP///////+AAP//AAD//////w== + + + + 256, 13 + + \ No newline at end of file diff --git a/trunk/Pithos.Client/Program.cs b/trunk/Pithos.Client/Program.cs new file mode 100644 index 0000000..453e014 --- /dev/null +++ b/trunk/Pithos.Client/Program.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace Pithos.Client +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + var mainForm = new Preferences(); + + IoC.Current.Compose(mainForm); + mainForm.UpdateStatus(); + Application.Run(mainForm); + } + } +} diff --git a/trunk/Pithos.Client/Properties/AssemblyInfo.cs b/trunk/Pithos.Client/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..df77cb9 --- /dev/null +++ b/trunk/Pithos.Client/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Pithos.Client")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Pithos.Client")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("087876a7-cbbd-4fa9-8930-93cef5841e6c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/trunk/Pithos.Client/Properties/Resources.Designer.cs b/trunk/Pithos.Client/Properties/Resources.Designer.cs new file mode 100644 index 0000000..2aae16b --- /dev/null +++ b/trunk/Pithos.Client/Properties/Resources.Designer.cs @@ -0,0 +1,84 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.225 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Pithos.Client.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Pithos.Client.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + internal static System.Drawing.Icon Tray { + get { + object obj = ResourceManager.GetObject("Tray", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon TrayInSynch { + get { + object obj = ResourceManager.GetObject("TrayInSynch", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + + internal static System.Drawing.Icon TraySynching { + get { + object obj = ResourceManager.GetObject("TraySynching", resourceCulture); + return ((System.Drawing.Icon)(obj)); + } + } + } +} diff --git a/trunk/Pithos.Client/Properties/Resources.resx b/trunk/Pithos.Client/Properties/Resources.resx new file mode 100644 index 0000000..a397191 --- /dev/null +++ b/trunk/Pithos.Client/Properties/Resources.resx @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\Resources\Tray.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\TrayInSynch.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + ..\Resources\TraySynching.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + \ No newline at end of file diff --git a/trunk/Pithos.Client/Properties/Settings.Designer.cs b/trunk/Pithos.Client/Properties/Settings.Designer.cs new file mode 100644 index 0000000..0ee2936 --- /dev/null +++ b/trunk/Pithos.Client/Properties/Settings.Designer.cs @@ -0,0 +1,158 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.225 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Pithos.Client.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("e:\\Pithos")] + public string PithosPath { + get { + return ((string)(this["PithosPath"])); + } + set { + this["PithosPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("C:\\Program Files\\Common Files\\TortoiseOverlays\\icons\\XPStyle")] + public string IconPath { + get { + return ((string)(this["IconPath"])); + } + set { + this["IconPath"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ProxyServer { + get { + return ((string)(this["ProxyServer"])); + } + set { + this["ProxyServer"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ProxyPort { + get { + return ((string)(this["ProxyPort"])); + } + set { + this["ProxyPort"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ProxyType { + get { + return ((string)(this["ProxyType"])); + } + set { + this["ProxyType"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ProxyUsername { + get { + return ((string)(this["ProxyUsername"])); + } + set { + this["ProxyUsername"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ProxyPassword { + get { + return ((string)(this["ProxyPassword"])); + } + set { + this["ProxyPassword"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool ProxyAuthentication { + get { + return ((bool)(this["ProxyAuthentication"])); + } + set { + this["ProxyAuthentication"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string Setting { + get { + return ((string)(this["Setting"])); + } + set { + this["Setting"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string UserName { + get { + return ((string)(this["UserName"])); + } + set { + this["UserName"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("")] + public string ApiKey { + get { + return ((string)(this["ApiKey"])); + } + set { + this["ApiKey"] = value; + } + } + } +} diff --git a/trunk/Pithos.Client/Properties/Settings.settings b/trunk/Pithos.Client/Properties/Settings.settings new file mode 100644 index 0000000..9962330 --- /dev/null +++ b/trunk/Pithos.Client/Properties/Settings.settings @@ -0,0 +1,39 @@ + + + + + + e:\Pithos + + + C:\Program Files\Common Files\TortoiseOverlays\icons\XPStyle + + + + + + + + + + + + + + + + + + False + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Pithos.Client/Resources/Tray.ico b/trunk/Pithos.Client/Resources/Tray.ico new file mode 100644 index 0000000..19ab02c Binary files /dev/null and b/trunk/Pithos.Client/Resources/Tray.ico differ diff --git a/trunk/Pithos.Client/Resources/TrayInSynch.ico b/trunk/Pithos.Client/Resources/TrayInSynch.ico new file mode 100644 index 0000000..5bf2b2d Binary files /dev/null and b/trunk/Pithos.Client/Resources/TrayInSynch.ico differ diff --git a/trunk/Pithos.Client/Resources/TraySynching.ico b/trunk/Pithos.Client/Resources/TraySynching.ico new file mode 100644 index 0000000..d246fbd Binary files /dev/null and b/trunk/Pithos.Client/Resources/TraySynching.ico differ diff --git a/trunk/Pithos.Client/app.config b/trunk/Pithos.Client/app.config new file mode 100644 index 0000000..c8fcef3 --- /dev/null +++ b/trunk/Pithos.Client/app.config @@ -0,0 +1,45 @@ + + + + +
+ + + + + + e:\Pithos + + + C:\Program Files\Common Files\TortoiseOverlays\icons\XPStyle + + + + + + + + + + + + + + + + + + False + + + + + + + + + + + + + diff --git a/trunk/Pithos.Client/header.jpg b/trunk/Pithos.Client/header.jpg new file mode 100644 index 0000000..f5a4936 Binary files /dev/null and b/trunk/Pithos.Client/header.jpg differ diff --git a/trunk/Pithos.Client/packages.config b/trunk/Pithos.Client/packages.config new file mode 100644 index 0000000..edf7d1d --- /dev/null +++ b/trunk/Pithos.Client/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/trunk/Pithos.Core.Test/MockSettings.cs b/trunk/Pithos.Core.Test/MockSettings.cs new file mode 100644 index 0000000..5584a9a --- /dev/null +++ b/trunk/Pithos.Core.Test/MockSettings.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Pithos.Interfaces; + +namespace Pithos.Core.Test +{ + class MockSettings:IPithosSettings + { + public string PithosPath { get; set; } + + public string IconsPath { get; set; } + public string UserName { get; set; } + public string ApiKey { get; set; } + + public void Save() + { + + } + + public void Reload() + { + + } + } +} diff --git a/trunk/Pithos.Core.Test/MockStatusKeeper.cs b/trunk/Pithos.Core.Test/MockStatusKeeper.cs new file mode 100644 index 0000000..577b7fc --- /dev/null +++ b/trunk/Pithos.Core.Test/MockStatusKeeper.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Text; +using Pithos.Interfaces; + +namespace Pithos.Core.Test +{ + public class MockStatusChecker : IStatusChecker, IStatusKeeper + { + public IPithosSettings Settings { get; set; } + + private readonly string[] _states = { "Normal", "Modified", "Conflict", "Synch" }; + + Dictionary _overlayCache = new Dictionary(); + Dictionary _statusCache = new Dictionary(); + Dictionary _checksums = new Dictionary(); + + public FileOverlayStatus GetFileOverlayStatus(string path) + { + Contract.Requires(!String.IsNullOrWhiteSpace(path)); + if (!_overlayCache.ContainsKey(path)) + return FileOverlayStatus.NA; + + var pithosPath = Settings.PithosPath; + if (path.StartsWith(pithosPath, true, null)) + { + var status = _overlayCache[path]; + return status; + } + return FileOverlayStatus.NA; + } + + public PithosStatus GetPithosStatus() + { + return PithosStatus.InSynch; + } + + public void SetFileOverlayStatus(string path, FileOverlayStatus overlayStatus) + { + _overlayCache[path] = overlayStatus; + } + + public void RemoveFileOverlayStatus(string path) + { + _overlayCache.Remove(path); + } + + public void RenameFileOverlayStatus(string oldPath, string newPath) + { + var status = _overlayCache[oldPath]; + _overlayCache[newPath] = status; + _overlayCache.Remove(oldPath); + } + + public void UpdateFileChecksum(string path, string checksum) + { + _checksums[path] = checksum; + } + + public void SetFileStatus(string path, FileStatus status) + { + _statusCache[path] = status; + } + + public FileStatus GetFileStatus(string path) + { + if (!_statusCache.ContainsKey(path)) + return FileStatus.Missing; + return _statusCache[path]; + } + + public void ClearFileStatus(string path) + { + _statusCache.Remove(path); + } + } +} diff --git a/trunk/Pithos.Core.Test/Pithos.Core.Test.csproj b/trunk/Pithos.Core.Test/Pithos.Core.Test.csproj new file mode 100644 index 0000000..7c63eac --- /dev/null +++ b/trunk/Pithos.Core.Test/Pithos.Core.Test.csproj @@ -0,0 +1,95 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA} + Library + Properties + Pithos.Core.Test + Pithos.Core.Test + v4.0 + 512 + 0 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + True + False + True + False + False + False + False + False + False + False + False + True + False + False + False + + + + + + + False + Full + %28none%29 + 0 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + {142AF135-DF30-4563-B0AC-B604235AE874} + Pithos.Core + + + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4} + Pithos.Interfaces + + + + + \ No newline at end of file diff --git a/trunk/Pithos.Core.Test/PithosWorkflowTest.cs b/trunk/Pithos.Core.Test/PithosWorkflowTest.cs new file mode 100644 index 0000000..7714054 --- /dev/null +++ b/trunk/Pithos.Core.Test/PithosWorkflowTest.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Pithos.Interfaces; + +namespace Pithos.Core.Test +{ + [TestFixture] + public class PithosWorkflowTest + { + private MockSettings _settings; + private MockStatusChecker _statusChecker; + + [SetUp] + public void SetUp() + { + _settings = new MockSettings + { + PithosPath = @"e:\Pithos", + IconsPath = @"C:\Program Files\Common Files\TortoiseOverlays\icons\XPStyle" + }; + _statusChecker = new MockStatusChecker {Settings = _settings}; + } + + [Test] + public void TestSendNotificationForFile() + { + IPithosWorkflow workflow = new PithosWorkflow {Settings = _settings, StatusKeeper = _statusChecker}; + var path = @"e:\pithos\0File.txt"; + + Assert.DoesNotThrow(() => + { + workflow.RaiseChangeNotification(path); + }); + path = @"e:\pithos\01New folder"; + Assert.DoesNotThrow(() => + { + workflow.RaiseChangeNotification(path); + }); + + } + + + } +} diff --git a/trunk/Pithos.Core.Test/Properties/AssemblyInfo.cs b/trunk/Pithos.Core.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d08db46 --- /dev/null +++ b/trunk/Pithos.Core.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Pithos.Core.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Pithos.Core.Test")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6b7b9383-b929-45ab-a633-eea0fc85bccb")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/trunk/Pithos.Core.Test/StatusCheckerTest.cs b/trunk/Pithos.Core.Test/StatusCheckerTest.cs new file mode 100644 index 0000000..adbdcd8 --- /dev/null +++ b/trunk/Pithos.Core.Test/StatusCheckerTest.cs @@ -0,0 +1,85 @@ +using System; +using NUnit.Framework; +using Pithos.Interfaces; + +namespace Pithos.Core.Test +{ + [TestFixture] + public class StatusCheckerTest + { + [Test] + public void TestFileCheck() + { + + var files = new[] {Tuple.Create(@"e:\pithos\0File1.txt", FileOverlayStatus.Normal), + Tuple.Create(@"e:\pithos\0File2.txt", FileOverlayStatus.Conflict), + Tuple.Create(@"e:\pithos\0File3.txt", FileOverlayStatus.Modified), + Tuple.Create(@"e:\pithos\0File4.txt", FileOverlayStatus.Synch) + }; + + var checker = new StatusChecker(); + + foreach (var file in files) + { + checker.SetFileOverlayStatus(file.Item1,file.Item2); + } + + foreach (var file in files) + { + + var status = checker.GetFileOverlayStatus(file.Item1); + Assert.AreEqual(file.Item2,status); + } + } + + [Test] + public void TestFileRemoval() + { + + var files = new[] {Tuple.Create(@"e:\pithos\0File1.txt", FileOverlayStatus.Normal), + Tuple.Create(@"e:\pithos\0File2.txt", FileOverlayStatus.Conflict), + Tuple.Create(@"e:\pithos\0File3.txt", FileOverlayStatus.Modified), + Tuple.Create(@"e:\pithos\0File4.txt", FileOverlayStatus.Synch) + }; + + var checker = new StatusChecker(); + + foreach (var file in files) + { + checker.SetFileOverlayStatus(file.Item1,file.Item2); + } + + checker.RemoveFileOverlayStatus(@"e:\pithos\0File3.txt"); + var status = checker.GetFileOverlayStatus(@"e:\pithos\0File3.txt"); + Assert.AreEqual(FileOverlayStatus.NA,status); + + } + + + + [Test] + public void TestNonExistent() + { + var files = new[] + { + Tuple.Create(@"e:\pithos\0File1.txt", FileOverlayStatus.Normal), + Tuple.Create(@"e:\pithos\0File2.txt", FileOverlayStatus.Conflict), + Tuple.Create(@"e:\pithos\0File3.txt", FileOverlayStatus.Modified), + Tuple.Create(@"e:\pithos\0File4.txt", FileOverlayStatus.Synch) + }; + + var checker = new StatusChecker(); + + foreach (var file in files) + { + checker.SetFileOverlayStatus(file.Item1, file.Item2); + } + + + var status = checker.GetFileOverlayStatus(@"e:\pithos\notexisting"); + Assert.AreEqual(FileOverlayStatus.NA,status); + + + } + } +} diff --git a/trunk/Pithos.Core.Test/WorkflowFileStatusTest.cs b/trunk/Pithos.Core.Test/WorkflowFileStatusTest.cs new file mode 100644 index 0000000..2764aae --- /dev/null +++ b/trunk/Pithos.Core.Test/WorkflowFileStatusTest.cs @@ -0,0 +1,156 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework; + + +namespace Pithos.Core.Test +{ + [TestFixture] + public class WorkflowFileStatusTest + { + private MockSettings _settings; + private MockStatusChecker _statusChecker; + + [SetUp] + public void SetUp() + { + _settings = new MockSettings + { + PithosPath = @"e:\Pithos", + IconsPath = @"C:\Program Files\Common Files\TortoiseOverlays\icons\XPStyle" + }; + _statusChecker = new MockStatusChecker { Settings = _settings }; + } + + [Test] + public void TestSetFileStatusCreated() + { + PithosWorkflow workflow = new PithosWorkflow { Settings = _settings, StatusKeeper = _statusChecker }; + + var path = @"e:\pithos\0File1.txt"; + workflow.SetFileStatus(path, FileStatus.Created); + var status = _statusChecker.GetFileStatus(path); + Assert.AreEqual(FileStatus.Created, status); + + } + + [Test] + public void TestSetFileStatusCreatedTransitions() + { + IPithosWorkflow workflow = new PithosWorkflow { Settings = _settings, StatusKeeper = _statusChecker }; + + var path = @"e:\pithos\0File1.txt"; + + var initialStatus = FileStatus.Created; + FileStatus[,] transitions = { { FileStatus.Modified, FileStatus.Created }, + { FileStatus.Created, FileStatus.Created }, + { FileStatus.Deleted, FileStatus.Deleted }, + { FileStatus.Renamed, FileStatus.Renamed }, + { FileStatus.Unchanged, FileStatus.Unchanged }, + }; + + + TestTransitions(transitions, workflow, path, initialStatus); + } + + [Test] + public void TestSetFileStatusModifiedTransitions() + { + IPithosWorkflow workflow = new PithosWorkflow { Settings = _settings, StatusKeeper = _statusChecker }; + + var path = @"e:\pithos\0File1.txt"; + + var initialStatus = FileStatus.Modified; + FileStatus[,] transitions = { { FileStatus.Modified, FileStatus.Modified }, + { FileStatus.Created, FileStatus.Modified }, + { FileStatus.Deleted, FileStatus.Deleted }, + { FileStatus.Renamed, FileStatus.Renamed }, + { FileStatus.Unchanged, FileStatus.Unchanged }, + }; + + + TestTransitions(transitions, workflow, path, initialStatus); + } + + [Test] + public void TestSetFileStatusUnchangedTransitions() + { + IPithosWorkflow workflow = new PithosWorkflow { Settings = _settings, StatusKeeper = _statusChecker }; + + var path = @"e:\pithos\0File1.txt"; + + var initialStatus = FileStatus.Unchanged; + FileStatus[,] transitions = { { FileStatus.Modified, FileStatus.Modified }, + { FileStatus.Created, FileStatus.Created }, + { FileStatus.Deleted, FileStatus.Deleted }, + { FileStatus.Renamed, FileStatus.Renamed }, + { FileStatus.Unchanged, FileStatus.Unchanged }, + }; + + + TestTransitions(transitions, workflow, path, initialStatus); + } + + [Test] + public void TestSetFileStatusDeletedTransitions() + { + IPithosWorkflow workflow = new PithosWorkflow { Settings = _settings, StatusKeeper = _statusChecker }; + + var path = @"e:\pithos\0File1.txt"; + + var initialStatus = FileStatus.Deleted; + FileStatus[,] transitions = { { FileStatus.Modified, FileStatus.Deleted }, + { FileStatus.Created, FileStatus.Deleted }, + { FileStatus.Deleted, FileStatus.Deleted }, + { FileStatus.Renamed, FileStatus.Deleted }, + { FileStatus.Unchanged, FileStatus.Deleted }, + }; + + + TestTransitions(transitions, workflow, path, initialStatus); + } + + [Test] + public void TestSetFileStatusRenamedTransitions() + { + IPithosWorkflow workflow = new PithosWorkflow { Settings = _settings, StatusKeeper = _statusChecker }; + + var path = @"e:\pithos\0File1.txt"; + + var initialStatus = FileStatus.Renamed; + FileStatus[,] transitions = { { FileStatus.Modified, FileStatus.Modified }, + { FileStatus.Created, FileStatus.Renamed }, + { FileStatus.Deleted, FileStatus.Deleted }, + { FileStatus.Renamed, FileStatus.Renamed }, + { FileStatus.Unchanged, FileStatus.Unchanged }, + }; + + + TestTransitions(transitions, workflow, path, initialStatus); + } + + private void TestTransitions(FileStatus[,] transitions, IPithosWorkflow workflow, string path, FileStatus initialStatus) + { + workflow.SetFileStatus(path, initialStatus); + var status = _statusChecker.GetFileStatus(path); + Assert.AreEqual(initialStatus, status, "Setting new file to {0}", status); + for (int i = 0; i < transitions.GetLength(0); i++) + { + workflow.ClearFileStatus(path); + status = _statusChecker.GetFileStatus(path); + Assert.AreEqual(FileStatus.Missing, status, "Clear status"); + + workflow.SetFileStatus(path, initialStatus); + + var transition = transitions[i, 0]; + var result = transitions[i, 1]; + workflow.SetFileStatus(path, transition); + status = _statusChecker.GetFileStatus(path); + Assert.AreEqual(result, status, "Marking {0} as {1}", initialStatus, transition); + + } + } + } +} diff --git a/trunk/Pithos.Core/IPithosWorkflow.cs b/trunk/Pithos.Core/IPithosWorkflow.cs new file mode 100644 index 0000000..87bc50e --- /dev/null +++ b/trunk/Pithos.Core/IPithosWorkflow.cs @@ -0,0 +1,25 @@ +using Pithos.Interfaces; + +namespace Pithos.Core +{ + public interface IPithosWorkflow + { + IPithosSettings Settings { get; set; } + IStatusKeeper StatusKeeper { get; set; } + FileStatus SetFileStatus(string path,FileStatus status); + void ClearFileStatus(string path); + void RaiseChangeNotification(string path); + + + } + + public enum FileStatus + { + Missing, + Unchanged, + Created, + Modified, + Renamed, + Deleted + } +} \ No newline at end of file diff --git a/trunk/Pithos.Core/IStatusKeeper.cs b/trunk/Pithos.Core/IStatusKeeper.cs new file mode 100644 index 0000000..16145b6 --- /dev/null +++ b/trunk/Pithos.Core/IStatusKeeper.cs @@ -0,0 +1,61 @@ +using System; +using System.Diagnostics.Contracts; +using Pithos.Interfaces; + +namespace Pithos.Core +{ + [ContractClass(typeof(IStatusKeeperContract))] + public interface IStatusKeeper + { + void SetFileOverlayStatus(string path,FileOverlayStatus status); + void UpdateFileChecksum(string path, string checksum); + void RemoveFileOverlayStatus(string path); + void SetFileStatus(string path, FileStatus status); + FileStatus GetFileStatus(string path); + void ClearFileStatus(string path); + } + + [ContractClassFor(typeof(IStatusKeeper))] + public abstract class IStatusKeeperContract : IStatusKeeper + { + public void SetFileOverlayStatus(string path, FileOverlayStatus status) + { + Contract.Requires(!String.IsNullOrWhiteSpace(path)); + } + + public void UpdateFileChecksum(string path, string checksum) + { + Contract.Requires(!String.IsNullOrWhiteSpace(path)); + Contract.Requires(checksum!=null); + } + + public void RemoveFileOverlayStatus(string path) + { + Contract.Requires(!String.IsNullOrWhiteSpace(path)); + } + + public void RenameFileOverlayStatus(string oldPath, string newPath) + { + Contract.Requires(!String.IsNullOrWhiteSpace(oldPath)); + Contract.Requires(!String.IsNullOrWhiteSpace(newPath)); + + } + + public void SetFileStatus(string path, FileStatus status) + { + Contract.Requires(!String.IsNullOrWhiteSpace(path)); + } + + public FileStatus GetFileStatus(string path) + { + Contract.Requires(!String.IsNullOrWhiteSpace(path)); + + return default(FileStatus); + } + + public void ClearFileStatus(string path) + { + Contract.Requires(!String.IsNullOrWhiteSpace(path)); + } + } +} diff --git a/trunk/Pithos.Core/NativeMethods.cs b/trunk/Pithos.Core/NativeMethods.cs new file mode 100644 index 0000000..8ed34cf --- /dev/null +++ b/trunk/Pithos.Core/NativeMethods.cs @@ -0,0 +1,281 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace Pithos.Core +{ + #region Enums & Structs + + #region enum HChangeNotifyEventID + /// + /// Describes the event that has occurred. + /// Typically, only one event is specified at a time. + /// If more than one event is specified, the values contained + /// in the dwItem1 and dwItem2 + /// parameters must be the same, respectively, for all specified events. + /// This parameter can be one or more of the following values. + /// + /// + /// Windows NT/2000/XP: dwItem2 contains the index + /// in the system image list that has changed. + /// dwItem1 is not used and should be . + /// Windows 95/98: dwItem1 contains the index + /// in the system image list that has changed. + /// dwItem2 is not used and should be . + /// + [Flags] + enum HChangeNotifyEventID + { + /// + /// All events have occurred. + /// + SHCNE_ALLEVENTS = 0x7FFFFFFF, + + /// + /// A file type association has changed. + /// must be specified in the uFlags parameter. + /// dwItem1 and dwItem2 are not used and must be . + /// + SHCNE_ASSOCCHANGED = 0x08000000, + + /// + /// The attributes of an item or folder have changed. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the item or folder that has changed. + /// dwItem2 is not used and should be . + /// + SHCNE_ATTRIBUTES = 0x00000800, + + /// + /// A nonfolder item has been created. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the item that was created. + /// dwItem2 is not used and should be . + /// + SHCNE_CREATE = 0x00000002, + + /// + /// A nonfolder item has been deleted. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the item that was deleted. + /// dwItem2 is not used and should be . + /// + SHCNE_DELETE = 0x00000004, + + /// + /// A drive has been added. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the root of the drive that was added. + /// dwItem2 is not used and should be . + /// + SHCNE_DRIVEADD = 0x00000100, + + /// + /// A drive has been added and the Shell should create a new window for the drive. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the root of the drive that was added. + /// dwItem2 is not used and should be . + /// + SHCNE_DRIVEADDGUI = 0x00010000, + + /// + /// A drive has been removed. or + /// must be specified in uFlags. + /// dwItem1 contains the root of the drive that was removed. + /// dwItem2 is not used and should be . + /// + SHCNE_DRIVEREMOVED = 0x00000080, + + /// + /// Not currently used. + /// + SHCNE_EXTENDED_EVENT = 0x04000000, + + /// + /// The amount of free space on a drive has changed. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the root of the drive on which the free space changed. + /// dwItem2 is not used and should be . + /// + SHCNE_FREESPACE = 0x00040000, + + /// + /// Storage media has been inserted into a drive. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the root of the drive that contains the new media. + /// dwItem2 is not used and should be . + /// + SHCNE_MEDIAINSERTED = 0x00000020, + + /// + /// Storage media has been removed from a drive. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the root of the drive from which the media was removed. + /// dwItem2 is not used and should be . + /// + SHCNE_MEDIAREMOVED = 0x00000040, + + /// + /// A folder has been created. + /// or must be specified in uFlags. + /// dwItem1 contains the folder that was created. + /// dwItem2 is not used and should be . + /// + SHCNE_MKDIR = 0x00000008, + + /// + /// A folder on the local computer is being shared via the network. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the folder that is being shared. + /// dwItem2 is not used and should be . + /// + SHCNE_NETSHARE = 0x00000200, + + /// + /// A folder on the local computer is no longer being shared via the network. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the folder that is no longer being shared. + /// dwItem2 is not used and should be . + /// + SHCNE_NETUNSHARE = 0x00000400, + + /// + /// The name of a folder has changed. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the previous pointer to an item identifier list (PIDL) or name of the folder. + /// dwItem2 contains the new PIDL or name of the folder. + /// + SHCNE_RENAMEFOLDER = 0x00020000, + + /// + /// The name of a nonfolder item has changed. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the previous PIDL or name of the item. + /// dwItem2 contains the new PIDL or name of the item. + /// + SHCNE_RENAMEITEM = 0x00000001, + + /// + /// A folder has been removed. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the folder that was removed. + /// dwItem2 is not used and should be . + /// + SHCNE_RMDIR = 0x00000010, + + /// + /// The computer has disconnected from a server. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the server from which the computer was disconnected. + /// dwItem2 is not used and should be . + /// + SHCNE_SERVERDISCONNECT = 0x00004000, + + /// + /// The contents of an existing folder have changed, + /// but the folder still exists and has not been renamed. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the folder that has changed. + /// dwItem2 is not used and should be . + /// If a folder has been created, deleted, or renamed, use SHCNE_MKDIR, SHCNE_RMDIR, or + /// SHCNE_RENAMEFOLDER, respectively, instead. + /// + SHCNE_UPDATEDIR = 0x00001000, + + SHCNE_UPDATEITEM = 0x00002000, + + /// + /// An image in the system image list has changed. + /// must be specified in uFlags. + /// + SHCNE_UPDATEIMAGE = 0x00008000, + + } + #endregion // enum HChangeNotifyEventID + + #region public enum HChangeNotifyFlags + /// + /// Flags that indicate the meaning of the dwItem1 and dwItem2 parameters. + /// The uFlags parameter must be one of the following values. + /// + [Flags] + public enum HChangeNotifyFlags + { + /// + /// The dwItem1 and dwItem2 parameters are DWORD values. + /// + SHCNF_DWORD = 0x0003, + /// + /// dwItem1 and dwItem2 are the addresses of ITEMIDLIST structures that + /// represent the item(s) affected by the change. + /// Each ITEMIDLIST must be relative to the desktop folder. + /// + SHCNF_IDLIST = 0x0000, + /// + /// dwItem1 and dwItem2 are the addresses of null-terminated strings of + /// maximum length MAX_PATH that contain the full path names + /// of the items affected by the change. + /// + SHCNF_PATHA = 0x0001, + /// + /// dwItem1 and dwItem2 are the addresses of null-terminated strings of + /// maximum length MAX_PATH that contain the full path names + /// of the items affected by the change. + /// + SHCNF_PATHW = 0x0005, + /// + /// dwItem1 and dwItem2 are the addresses of null-terminated strings that + /// represent the friendly names of the printer(s) affected by the change. + /// + SHCNF_PRINTERA = 0x0002, + /// + /// dwItem1 and dwItem2 are the addresses of null-terminated strings that + /// represent the friendly names of the printer(s) affected by the change. + /// + SHCNF_PRINTERW = 0x0006, + /// + /// The function should not return until the notification + /// has been delivered to all affected components. + /// As this flag modifies other data-type flags, it cannot by used by itself. + /// + SHCNF_FLUSH = 0x1000, + /// + /// The function should begin delivering notifications to all affected components + /// but should return as soon as the notification process has begun. + /// As this flag modifies other data-type flags, it cannot by used by itself. + /// + SHCNF_FLUSHNOWAIT = 0x2000 + } + #endregion // enum HChangeNotifyFlags + + + #endregion + + + internal static class NativeMethods + { + [DllImport("shell32.dll")] + public static extern void SHChangeNotify(HChangeNotifyEventID wEventId, + HChangeNotifyFlags uFlags, + IntPtr dwItem1, + IntPtr dwItem2); + + } +} diff --git a/trunk/Pithos.Core/Pithos.Core.csproj b/trunk/Pithos.Core/Pithos.Core.csproj new file mode 100644 index 0000000..9b436b9 --- /dev/null +++ b/trunk/Pithos.Core/Pithos.Core.csproj @@ -0,0 +1,102 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {142AF135-DF30-4563-B0AC-B604235AE874} + Library + Properties + Pithos.Core + Pithos.Core + v4.0 + 512 + 1 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + True + False + True + False + False + False + False + False + False + False + False + True + False + False + True + + + + + + + False + Full + Build + 0 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3} + ParallelExtensionsExtras + + + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4} + Pithos.Interfaces + + + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD} + Pithos.Network + + + + + \ No newline at end of file diff --git a/trunk/Pithos.Core/PithosMonitor.cs b/trunk/Pithos.Core/PithosMonitor.cs new file mode 100644 index 0000000..c58c264 --- /dev/null +++ b/trunk/Pithos.Core/PithosMonitor.cs @@ -0,0 +1,469 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Pithos.Interfaces; + +namespace Pithos.Core +{ + [Export(typeof(PithosMonitor))] + public class PithosMonitor:IDisposable + { + + [Import] + public IPithosSettings Settings{get;set;} + + [Import] + public IStatusKeeper StatusKeeper { get; set; } + + [Import] + public IPithosWorkflow Workflow { get; set; } + + [Import] + public ICloudClient CloudClient { get; set; } + + [Import] + public ICloudClient CloudListeningClient { get; set; } + + private FileSystemWatcher _watcher; + + public bool Pause + { + get { return _watcher == null || !_watcher.EnableRaisingEvents; } + set + { + if (_watcher!=null) + _watcher.EnableRaisingEvents = !value; + } + } + + + CancellationTokenSource _cancellationSource; + + BlockingCollection _fileEvents = new BlockingCollection(); + + + + public void Start() + { + string path = Settings.PithosPath; + + CloudClient.Authenticate(Settings.UserName,Settings.ApiKey); + + if (_cancellationSource != null) + { + if (!_cancellationSource.IsCancellationRequested) + return; + } + _cancellationSource=new CancellationTokenSource(); + StartListening(); + StartSending(); + + + _watcher = new FileSystemWatcher(path); + _watcher.Changed += OnFileEvent; + _watcher.Created += OnFileEvent; + _watcher.Deleted += OnFileEvent; + _watcher.Renamed += OnRenameEvent; + _watcher.EnableRaisingEvents = true; + } + + internal enum CloudActionType + { + Upload=0, + Download, + UploadUnconditional, + DownloadUnconditional, + DeleteLocal, + DeleteCloud + } + + internal class ListenerAction + { + public CloudActionType Action { get; set; } + public FileInfo LocalFile { get; set; } + public ObjectInfo CloudFile { get; set; } + + public Lazy LocalHash { get; set; } + + public ListenerAction(CloudActionType action, FileInfo localFile, ObjectInfo cloudFile) + { + Action = action; + LocalFile = localFile; + CloudFile = cloudFile; + LocalHash=new Lazy(()=>CalculateHash(LocalFile.FullName),LazyThreadSafetyMode.ExecutionAndPublication); + } + + } + + internal class LocalFileComparer:EqualityComparer + { + public override bool Equals(ListenerAction x, ListenerAction y) + { + if (x.Action != y.Action) + return false; + if (x.LocalFile != null && y.LocalFile != null && !x.LocalFile.FullName.Equals(y.LocalFile.FullName)) + return false; + if (x.CloudFile != null && y.CloudFile != null && !x.CloudFile.Hash.Equals(y.CloudFile.Hash)) + return false; + if (x.CloudFile == null ^ y.CloudFile == null || + x.LocalFile == null ^ y.LocalFile == null) + return false; + return true; + } + + public override int GetHashCode(ListenerAction obj) + { + var hash1 = (obj.LocalFile == null) ? int.MaxValue : obj.LocalFile.FullName.GetHashCode(); + var hash2 = (obj.CloudFile == null) ? int.MaxValue : obj.CloudFile.Hash.GetHashCode(); + var hash3 = obj.Action.GetHashCode(); + return hash1 ^ hash2 & hash3; + } + } + + private BlockingCollection _listenerActions=new BlockingCollection(); + + private Timer timer; + + private void StartListening() + { + + Func listener = ()=>Task.Factory.StartNew(()=>CloudClient.ListObjects("PITHOS")) + .ContinueWith(task => + { + + var objects = task.Result; + if (objects.Count == 0) + return; + + var pithosDir = new DirectoryInfo(Settings.PithosPath); + + var upFiles = from info in objects + select info.Name; + + var onlyLocal = from localFile in pithosDir.EnumerateFiles() + where !upFiles.Contains(localFile.Name) + select new ListenerAction(CloudActionType.UploadUnconditional, localFile,null); + + + + + var localNames =pithosDir.EnumerateFiles().Select(info => info.Name); + var onlyRemote = from upFile in objects + where !localNames.Contains(upFile.Name) + select new ListenerAction(CloudActionType.DownloadUnconditional,null,upFile); + + + var existingObjects = from upFile in objects + join localFile in pithosDir.EnumerateFiles() + on upFile.Name equals localFile.Name + select new ListenerAction(CloudActionType.Download, localFile, upFile); + + var uniques = + onlyLocal.Union(onlyRemote).Union(existingObjects) + .Except(_listenerActions,new LocalFileComparer()); + + _listenerActions.AddFromEnumerable(uniques, false); + + } + ); + + Task.Factory.StartNew(() => + { + foreach (var action in _listenerActions.GetConsumingEnumerable()) + { + var localFile = action.LocalFile; + var cloudFile = action.CloudFile; + var downloadPath = (cloudFile==null)? String.Empty:Path.Combine(Settings.PithosPath,cloudFile.Name); + + switch(action.Action) + { + case CloudActionType.UploadUnconditional: + + UploadCloudFile(localFile.Name, localFile.Length, localFile.FullName, action.LocalHash.Value); + break; + case CloudActionType.DownloadUnconditional: + DownloadCloudFile("PITHOS", cloudFile.Name, downloadPath); + break; + case CloudActionType.Download: + if (File.Exists(downloadPath)) + { + if (cloudFile.Hash != action.LocalHash.Value) + { + var lastLocalTime=localFile.LastWriteTime; + var lastUpTime=cloudFile.Last_Modified; + if(lastUpTime<=lastLocalTime) + { + //Files in conflict + StatusKeeper.SetFileOverlayStatus(downloadPath,FileOverlayStatus.Conflict); + } + else + DownloadCloudFile("PITHOS", action.CloudFile.Name, downloadPath); + } + } + else + DownloadCloudFile("PITHOS", action.CloudFile.Name, downloadPath); + break; + } + } + } + ); + + timer = new Timer(o => listener(), null, TimeSpan.Zero, TimeSpan.FromSeconds(10)); + + } + + private void DownloadCloudFile(string container, string fileName, string localPath) + { + using (var upstream = CloudClient.GetObject(container, fileName)) + using (var fileStream = File.OpenWrite(localPath)) + { + upstream.CopyTo(fileStream); + } + } + + private void StartSending() + { + Task.Factory.StartNew(() => + { + foreach (var state in _fileEvents.GetConsumingEnumerable()) + { + try + { + UpdateFileStatus(state); + UpdateOverlayStatus(state); + UpdateFileChecksum(state); + SynchToCloud(state); + } + catch (OperationCanceledException) + { + throw; + } + catch(Exception ex) + {} + } + + },_cancellationSource.Token); + } + + + private WorkflowState SynchToCloud(WorkflowState state) + { + if (state.Skip) + return state; + string path = state.Path; + string fileName = Path.GetFileName(path); + + switch(state.Status) + { + case FileStatus.Created: + case FileStatus.Modified: + var info = new FileInfo(path); + long fileSize = info.Length; + UploadCloudFile(fileName, fileSize, path,state.Hash); + break; + case FileStatus.Deleted: + DeleteCloudFile(fileName); + break; + case FileStatus.Renamed: + RenameCloudFile(state); + break; + } + return state; + } + + private void RenameCloudFile(WorkflowState state) + { + this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Synch); + + + + CloudClient.MoveObject("PITHOS", state.OldFileName, state.FileName); + + this.StatusKeeper.SetFileStatus(state.Path, FileStatus.Unchanged); + this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Normal); + Workflow.RaiseChangeNotification(state.Path); + } + + private void DeleteCloudFile(string fileName) + { + Contract.Requires(!Path.IsPathRooted(fileName)); + + this.StatusKeeper.SetFileOverlayStatus(fileName, FileOverlayStatus.Synch); + CloudClient.DeleteObject("PITHOS", fileName); + this.StatusKeeper.ClearFileStatus(fileName); + this.StatusKeeper.RemoveFileOverlayStatus(fileName); + } + + private void UploadCloudFile(string fileName, long fileSize, string path,string hash) + { + Contract.Requires(!Path.IsPathRooted(fileName)); + //Even if GetObjectInfo times out, we can proceed with the upload + var info=CloudClient.GetObjectInfo("PITHOS", fileName); + if ( hash != info.Hash) + { + this.StatusKeeper.SetFileOverlayStatus(path, FileOverlayStatus.Synch); + using (var stream = File.OpenRead(path)) + { + CloudClient.PutObject("PITHOS", fileName, stream, fileSize); + } + } + this.StatusKeeper.SetFileStatus(path,FileStatus.Unchanged); + this.StatusKeeper.SetFileOverlayStatus(path,FileOverlayStatus.Normal); + Workflow.RaiseChangeNotification(path); + } + + private Dictionary _statusDict = new Dictionary + { + {WatcherChangeTypes.Created,FileStatus.Created}, + {WatcherChangeTypes.Changed,FileStatus.Modified}, + {WatcherChangeTypes.Deleted,FileStatus.Deleted}, + {WatcherChangeTypes.Renamed,FileStatus.Renamed} + }; + + private WorkflowState UpdateFileStatus(WorkflowState state) + { + string path = state.Path; + FileStatus status = _statusDict[state.TriggeringChange]; + var oldStatus = Workflow.StatusKeeper.GetFileStatus(path); + if (status == oldStatus) + { + state.Status = status; + state.Skip = true; + return state; + } + if (state.Status == FileStatus.Renamed) + Workflow.ClearFileStatus(path); + + state.Status = Workflow.SetFileStatus(path, status); + return state; + } + + private WorkflowState UpdateOverlayStatus(WorkflowState state) + { + if (state.Skip) + return state; + + switch (state.Status) + { + case FileStatus.Created: + case FileStatus.Modified: + this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified); + break; + case FileStatus.Deleted: + this.StatusKeeper.RemoveFileOverlayStatus(state.Path); + break; + case FileStatus.Renamed: + this.StatusKeeper.RemoveFileOverlayStatus(state.OldPath); + this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Modified); + break; + case FileStatus.Unchanged: + this.StatusKeeper.SetFileOverlayStatus(state.Path, FileOverlayStatus.Normal); + break; + } + + if (state.Status==FileStatus.Deleted) + Workflow.RaiseChangeNotification(Path.GetDirectoryName(state.Path)); + else + Workflow.RaiseChangeNotification(state.Path); + return state; + } + + + private WorkflowState UpdateFileChecksum(WorkflowState state) + { + if (state.Skip) + return state; + + if (state.Status == FileStatus.Deleted) + return state; + + string path = state.Path; + string hash = CalculateHash(path); + + StatusKeeper.UpdateFileChecksum(path, hash); + + state.Hash = hash; + return state; + } + + private static string CalculateHash(string path) + { + string hash; + using (var hasher = MD5.Create()) + using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, 4096, true)) + { + var hashBytes = hasher.ComputeHash(stream); + var hashBuilder = new StringBuilder(); + foreach (byte b in hasher.ComputeHash(stream)) + hashBuilder.Append(b.ToString("x2").ToLower()); + hash = hashBuilder.ToString(); + + } + return hash; + } + + private FileSystemEventArgs CalculateSignature(FileSystemEventArgs arg) + { + Debug.WriteLine(String.Format("{0} {1} {2}", arg.ChangeType, arg.Name, arg.FullPath), "INFO"); + return arg; + } + + void OnFileEvent(object sender, FileSystemEventArgs e) + { + _fileEvents.Add(new WorkflowState{Path=e.FullPath,FileName = e.Name,TriggeringChange=e.ChangeType}); + } + + void OnRenameEvent(object sender, RenamedEventArgs e) + { + _fileEvents.Add(new WorkflowState { OldPath=e.OldFullPath,OldFileName=e.OldName, + Path = e.FullPath, FileName = e.Name, TriggeringChange = e.ChangeType }); + } + + public void Stop() + { + if (_watcher != null) + { + _watcher.Changed -= OnFileEvent; + _watcher.Created -= OnFileEvent; + _watcher.Deleted -= OnFileEvent; + _watcher.Renamed -= OnRenameEvent; + _watcher.Dispose(); + } + _watcher = null; + _fileEvents.CompleteAdding(); + if (timer != null) + timer.Dispose(); + timer = null; + } + + ~PithosMonitor() + { + Dispose(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + Stop(); + } + } + + + } +} diff --git a/trunk/Pithos.Core/PithosWorkflow.cs b/trunk/Pithos.Core/PithosWorkflow.cs new file mode 100644 index 0000000..8f8313a --- /dev/null +++ b/trunk/Pithos.Core/PithosWorkflow.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Pithos.Interfaces; +using System.IO; + +namespace Pithos.Core +{ + [Export(typeof(IPithosWorkflow))] + public class PithosWorkflow:IPithosWorkflow + { + [Import] + public IPithosSettings Settings { get; set; } + + [Import] + public IStatusKeeper StatusKeeper { get; set; } + + public FileStatus SetFileStatus(string path, FileStatus status) + { + if (String.IsNullOrWhiteSpace(path)) + throw new ArgumentNullException("path", "The path parameter must not be emtpy"); + + var oldStatus=StatusKeeper.GetFileStatus(path); + + if (oldStatus == status) + return oldStatus; + + switch(oldStatus) + { + case FileStatus.Unchanged : + break; + case FileStatus.Created: + if (status == FileStatus.Modified) + return oldStatus; + break; + case FileStatus.Modified: + case FileStatus.Renamed: + if (status == FileStatus.Created) + return oldStatus; + break; + case FileStatus.Deleted: + return oldStatus; + } + StatusKeeper.SetFileStatus(path, status); + return status; + } + + public void ClearFileStatus(string path) + { + if (String.IsNullOrWhiteSpace(path)) + throw new ArgumentNullException("path", "The path parameter must not be emtpy"); + + StatusKeeper.ClearFileStatus(path); + } + + public void RaiseChangeNotification(string path) + { + if (String.IsNullOrWhiteSpace(path)) + throw new ArgumentNullException("path", "The path parameter must not be emtpy"); + + if (!Directory.Exists(path ) && !File.Exists(path)) + throw new FileNotFoundException("The specified file or path does not exist",path); + + + IntPtr pathPointer = Marshal.StringToCoTaskMemAuto(path); + + try + { + NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_UPDATEITEM, + HChangeNotifyFlags.SHCNF_PATHW | HChangeNotifyFlags.SHCNF_FLUSHNOWAIT, + pathPointer, IntPtr.Zero); + } + finally + { + Marshal.FreeHGlobal(pathPointer); + } + + } + + public Task OpenStreamWithWaiting(string path) + { + if (String.IsNullOrWhiteSpace(path)) + throw new ArgumentNullException("path","The path parameter must not be emtpy"); + + if (!File.Exists(path)) + throw new FileNotFoundException("The specified file or path does not exist", path); + + return new Task(() => + { + int counter = 0; + while (true) + { + try + { + var stream=File.OpenRead(path); + return stream; + } + catch (Exception ex) + { + Thread.Sleep(500); + if (++counter > 10) + throw; + } + } + }); + } + } +} diff --git a/trunk/Pithos.Core/Properties/AssemblyInfo.cs b/trunk/Pithos.Core/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..2467066 --- /dev/null +++ b/trunk/Pithos.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Pithos.Core")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Pithos.Core")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("42686253-9460-441a-b2fb-5976957744df")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/trunk/Pithos.Core/StatusChecker.cs b/trunk/Pithos.Core/StatusChecker.cs new file mode 100644 index 0000000..7319ac8 --- /dev/null +++ b/trunk/Pithos.Core/StatusChecker.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics.Contracts; +using Pithos.Interfaces; + +namespace Pithos.Core +{ + [Export(typeof(IStatusChecker)),Export(typeof(IStatusKeeper))] + public class StatusChecker:IStatusChecker,IStatusKeeper + { + [Import] + public IPithosSettings Settings { get; set; } + + private readonly string[] _states = {"Normal", "Modified", "Conflict","Synch"}; + + Dictionary _overlayCache=new Dictionary(); + Dictionary _statusCache= new Dictionary(); + Dictionary _checksums = new Dictionary(); + + public FileOverlayStatus GetFileOverlayStatus(string path) + { + if (!_overlayCache.ContainsKey(path)) + return FileOverlayStatus.NA; + + var pithosPath = Settings.PithosPath; + if (path.StartsWith(pithosPath,true,null)) + { + var status = _overlayCache[path]; + return status; + } + return FileOverlayStatus.NA; + } + + public PithosStatus GetPithosStatus() + { + return PithosStatus.InSynch; + } + + public void SetFileOverlayStatus(string path, FileOverlayStatus overlayStatus) + { + _overlayCache[path] = overlayStatus; + } + + public void RemoveFileOverlayStatus(string path) + { + _overlayCache.Remove(path); + } + + public void RenameFileOverlayStatus(string oldPath, string newPath) + { + var status=_overlayCache[oldPath]; + _overlayCache[newPath] = status; + _overlayCache.Remove(oldPath); + } + + public void SetFileStatus(string path, FileStatus status) + { + _statusCache[path] = status; + } + + public FileStatus GetFileStatus(string path) + { + if (!_statusCache.ContainsKey(path)) + return FileStatus.Missing; + return _statusCache[path]; + } + + public void ClearFileStatus(string path) + { + _statusCache.Remove(path); + } + + public void UpdateFileChecksum(string path, string checksum) + { + _checksums[path] = checksum; + } + } +} diff --git a/trunk/Pithos.Core/StatusInfo.cs b/trunk/Pithos.Core/StatusInfo.cs new file mode 100644 index 0000000..4d9639a --- /dev/null +++ b/trunk/Pithos.Core/StatusInfo.cs @@ -0,0 +1,20 @@ +using Pithos.Interfaces; + +namespace Pithos.Core +{ + public class StatusInfo + { + public StatusInfo(PithosStatus status, string statusText, string iconName) + { + Status = status; + StatusText = statusText; + IconName = iconName; + } + + public PithosStatus Status { get; set; } + public string StatusText { get; set; } + public string IconName { get; set; } + + + } +} diff --git a/trunk/Pithos.Core/WorkflowState.cs b/trunk/Pithos.Core/WorkflowState.cs new file mode 100644 index 0000000..0300da7 --- /dev/null +++ b/trunk/Pithos.Core/WorkflowState.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Pithos.Core +{ + class WorkflowState + { + public string Path { get; set; } + public string FileName { get; set; } + + public string OldPath { get; set; } + public string OldFileName{ get; set; } + + public WatcherChangeTypes TriggeringChange { get; set; } + public FileStatus Status { get; set; } + + public bool Skip { get; set; } + + public string Hash { get; set; } + public string LastUpdateHash { get; set; } + } +} diff --git a/trunk/Pithos.Interfaces/ICloudClient.cs b/trunk/Pithos.Interfaces/ICloudClient.cs new file mode 100644 index 0000000..6d0770e --- /dev/null +++ b/trunk/Pithos.Interfaces/ICloudClient.cs @@ -0,0 +1,175 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.IO; +using System.Linq; +using System.Text; + +namespace Pithos.Interfaces +{ + [ContractClass(typeof(ICloudClientContract))] + public interface ICloudClient + { + string ApiKey { get; set; } + string UserName { get; set; } + string StorageUrl { get; set; } + string Token { get; set; } + void Authenticate(string userName,string apiKey); + + IList ListContainers(); + IList ListObjects(string container); + bool ContainerExists(string container); + ContainerInfo GetContainerInfo(string container); + void CreateContainer(string container); + + Stream GetObject(string container, string objectName); + void PutObject(string container, string objectName, Stream file,long fileSize); + void DeleteObject(string container, string objectName); + void MoveObject(string container, string oldObjectName, string newObjectName); + bool ObjectExists(string container,string objectName); + ObjectInfo GetObjectInfo(string container, string objectName); + } + + + [ContractClassFor(typeof(ICloudClient))] + public abstract class ICloudClientContract:ICloudClient + { + public string ApiKey { get; set; } + public string UserName { get; set; } + public string StorageUrl { get; set; } + public string Token { get; set; } + + public void Authenticate(string userName, string apiKey) + { + Contract.Requires(!String.IsNullOrWhiteSpace(apiKey), "ApiKey must be filled before calling Authenticate"); + Contract.Requires(!String.IsNullOrWhiteSpace(userName), "UserName must be filled before calling Authenticate"); + + + Contract.Ensures(apiKey==ApiKey); + Contract.Ensures(userName==UserName); + Contract.Ensures(!String.IsNullOrWhiteSpace(StorageUrl)); + Contract.Ensures(!String.IsNullOrWhiteSpace(Token)); + + } + + public IList ListContainers() + { + Contract.Requires(!String.IsNullOrWhiteSpace(Token)); + Contract.Requires(!String.IsNullOrWhiteSpace(StorageUrl)); + + return default(IList); + } + + public IList ListObjects(string container) + { + Contract.Requires(!String.IsNullOrWhiteSpace(Token)); + Contract.Requires(!String.IsNullOrWhiteSpace(StorageUrl)); + Contract.Requires(!String.IsNullOrWhiteSpace(container)); + + return default(IList); + } + + public bool ContainerExists(string container) + { + Contract.Requires(!String.IsNullOrWhiteSpace(Token)); + Contract.Requires(!String.IsNullOrWhiteSpace(StorageUrl)); + Contract.Requires(!String.IsNullOrWhiteSpace(container)); + + return default(bool); + } + + public ContainerInfo GetContainerInfo(string container) + { + Contract.Requires(!String.IsNullOrWhiteSpace(Token)); + Contract.Requires(!String.IsNullOrWhiteSpace(StorageUrl)); + Contract.Requires(!String.IsNullOrWhiteSpace(container)); + + return default(ContainerInfo); + } + + public void CreateContainer(string container) + { + Contract.Requires(!String.IsNullOrWhiteSpace(Token)); + Contract.Requires(!String.IsNullOrWhiteSpace(StorageUrl)); + Contract.Requires(!String.IsNullOrWhiteSpace(container)); + } + + public Stream GetObject(string container, string objectName) + { + Contract.Requires(!String.IsNullOrWhiteSpace(Token)); + Contract.Requires(!String.IsNullOrWhiteSpace(StorageUrl)); + Contract.Requires(!String.IsNullOrWhiteSpace(container)); + Contract.Requires(!String.IsNullOrWhiteSpace(objectName)); + + return default(Stream); + } + + public void PutObject(string container, string objectName, Stream file,long fileSize) + { + Contract.Requires(!String.IsNullOrWhiteSpace(Token)); + Contract.Requires(!String.IsNullOrWhiteSpace(StorageUrl)); + Contract.Requires(!String.IsNullOrWhiteSpace(container)); + Contract.Requires(file!=null); + Contract.Requires(file.CanRead); + Contract.Requires(fileSize>=0); + Contract.Requires(!String.IsNullOrWhiteSpace(objectName)); + } + + public void DeleteObject(string container, string objectName) + { + Contract.Requires(!String.IsNullOrWhiteSpace(Token)); + Contract.Requires(!String.IsNullOrWhiteSpace(StorageUrl)); + Contract.Requires(!String.IsNullOrWhiteSpace(container)); + Contract.Requires(!String.IsNullOrWhiteSpace(objectName)); + } + + public void MoveObject(string container, string oldObjectName, string newObjectName) + { + Contract.Requires(!String.IsNullOrWhiteSpace(Token)); + Contract.Requires(!String.IsNullOrWhiteSpace(StorageUrl)); + Contract.Requires(!String.IsNullOrWhiteSpace(container)); + Contract.Requires(!String.IsNullOrWhiteSpace(oldObjectName)); + Contract.Requires(!String.IsNullOrWhiteSpace(newObjectName)); + } + + public bool ObjectExists(string container,string objectName) + { + Contract.Requires(!String.IsNullOrWhiteSpace(Token)); + Contract.Requires(!String.IsNullOrWhiteSpace(StorageUrl)); + Contract.Requires(!String.IsNullOrWhiteSpace(container)); + Contract.Requires(!String.IsNullOrWhiteSpace(objectName)); + + return default(bool); + } + + public ObjectInfo GetObjectInfo(string container,string objectName) + { + Contract.Requires(!String.IsNullOrWhiteSpace(Token)); + Contract.Requires(!String.IsNullOrWhiteSpace(StorageUrl)); + Contract.Requires(!String.IsNullOrWhiteSpace(container)); + Contract.Requires(!String.IsNullOrWhiteSpace(objectName)); + + return default(ObjectInfo); + } + } + + public class ContainerInfo + { + public string Name { get; set; } + public long Count { get; set; } + public long Bytes { get; set; } + + public static ContainerInfo Empty=new ContainerInfo(); + } + + public class ObjectInfo + { + public string Name { get; set; } + public string Hash { get; set; } + public long Bytes { get; set; } + public string Content_Type { get; set; } + public DateTime Last_Modified { get; set; } + + public static ObjectInfo Empty=new ObjectInfo {Name=String.Empty,Hash=String.Empty,Bytes=0,Content_Type=String.Empty,Last_Modified=DateTime.MinValue}; + } +} diff --git a/trunk/Pithos.Interfaces/IPithosSettings.cs b/trunk/Pithos.Interfaces/IPithosSettings.cs new file mode 100644 index 0000000..e421700 --- /dev/null +++ b/trunk/Pithos.Interfaces/IPithosSettings.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Pithos.Interfaces +{ + public interface IPithosSettings + { + string PithosPath { get; set; } + string IconsPath { get; set; } + string UserName { get; set; } + string ApiKey { get; set; } + + void Save(); + void Reload(); + } +} diff --git a/trunk/Pithos.Interfaces/IStatusChecker.cs b/trunk/Pithos.Interfaces/IStatusChecker.cs new file mode 100644 index 0000000..08c14e6 --- /dev/null +++ b/trunk/Pithos.Interfaces/IStatusChecker.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Text; + +namespace Pithos.Interfaces +{ + [ContractClass(typeof(IStatusCheckerContract))] + public interface IStatusChecker + { + FileOverlayStatus GetFileOverlayStatus(string path); + + PithosStatus GetPithosStatus(); + + } + + [ContractClassFor(typeof(IStatusChecker))] + public abstract class IStatusCheckerContract:IStatusChecker + { + public FileOverlayStatus GetFileOverlayStatus(string path) + { + Contract.Requires(!String.IsNullOrWhiteSpace(path),"Null or empty paths not allowed"); + + return default(FileOverlayStatus); + } + + public PithosStatus GetPithosStatus() + { + return default(PithosStatus); + } + } + + public enum FileOverlayStatus + { + Deleted=-2, + NA=-1, + Normal=0, + Modified, + Conflict, + Synch + } + + public enum PithosStatus + { + InSynch, + Synching, + HasConflicts, + Disconnected + } +} diff --git a/trunk/Pithos.Interfaces/Pithos.Interfaces.csproj b/trunk/Pithos.Interfaces/Pithos.Interfaces.csproj new file mode 100644 index 0000000..545ca85 --- /dev/null +++ b/trunk/Pithos.Interfaces/Pithos.Interfaces.csproj @@ -0,0 +1,91 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4} + Library + Properties + Pithos.Interfaces + Pithos.Interfaces + v4.0 + 512 + 1 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + True + False + True + False + False + True + False + False + False + False + False + True + True + False + True + + + + + + + False + Full + Build + 0 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + pithos.snk + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/trunk/Pithos.Interfaces/Properties/AssemblyInfo.cs b/trunk/Pithos.Interfaces/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..90bdcf7 --- /dev/null +++ b/trunk/Pithos.Interfaces/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Pithos.Interfaces")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Pithos.Interfaces")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3683c574-ab30-4a94-aaff-9a3b262da7e6")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/trunk/Pithos.Interfaces/pithos.snk b/trunk/Pithos.Interfaces/pithos.snk new file mode 100644 index 0000000..2069d32 Binary files /dev/null and b/trunk/Pithos.Interfaces/pithos.snk differ diff --git a/trunk/Pithos.Network.Test/ChecksumTest.cs b/trunk/Pithos.Network.Test/ChecksumTest.cs new file mode 100644 index 0000000..42030f3 --- /dev/null +++ b/trunk/Pithos.Network.Test/ChecksumTest.cs @@ -0,0 +1,66 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Text; +using NUnit.Framework; +using Pithos.Interfaces; + + +namespace Pithos.Network.Test +{ + [TestFixture] + class ChecksumTest + { + private ICloudClient client; + [SetUp] + public void Setup() + { + client = new CloudFilesClient(); + client.Authenticate("", ""); + + } + + [Test] + public void TestChecksum() + { + Assert.DoesNotThrow(() => + { + + var filePath = @"e:\DeveloperGuide.pdf"; + var info=new FileInfo(filePath); + + using (var file = File.OpenRead(filePath)) + { + var hash = CalculateHash(file); + file.Seek(0, 0); + client.PutObject("Shares", info.Name, file, info.Length); + + + var meta = client.GetObjectInfo("Shares", "DeveloperGuide.pdf"); + Assert.IsNotEmpty(meta.Hash); + + + Assert.AreEqual(hash,meta.Hash,String.Format("The hashes don't match, expected {0} but got {1}",hash,meta.Hash)); + } + + }); + + + } + + private static string CalculateHash(FileStream file) + { + string hash; + using (var hasher = MD5.Create()) + { + var hashBuilder = new StringBuilder(); + foreach (byte b in hasher.ComputeHash(file)) + hashBuilder.Append(b.ToString("x2").ToLower()); + hash = hashBuilder.ToString(); + } + return hash; + } + } +} diff --git a/trunk/Pithos.Network.Test/NetworkOpsTest.cs b/trunk/Pithos.Network.Test/NetworkOpsTest.cs new file mode 100644 index 0000000..088b71b --- /dev/null +++ b/trunk/Pithos.Network.Test/NetworkOpsTest.cs @@ -0,0 +1,269 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Text; +using NUnit.Framework; +using Pithos.Interfaces; +using System.IO; + +namespace Pithos.Network.Test +{ + [TestFixture] + class NetworkOpsTest + { + [Test] + public void TestAuthenticate() + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate("", ""); + string storageUrl=client.StorageUrl; + string token = client.Token; + + Assert.IsNotEmpty(storageUrl, "Storage Url was empty"); + Assert.IsNotEmpty(token, "Token was empty"); + } + + [Test] + public void TestListContainers() + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate("", ""); + + IList containers=client.ListContainers(); + Assert.IsTrue(containers.Count()>1); + } + + [Test] + public void TestListObjects() + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate("", ""); + + IList objects=client.ListObjects("PITHOS"); + Assert.IsTrue(objects.Count()>=1); + } + + [Test] + public void TestContainerExists() + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate("", ""); + + bool dnzExists=client.ContainerExists("DotNetZone"); + bool pithosExists = client.ContainerExists("PITHOS"); + bool mooExists = client.ContainerExists("Moo"); + Assert.IsTrue(dnzExists); + Assert.IsTrue(pithosExists); + Assert.IsFalse(mooExists); + } + + [Test] + public void TestGetContainerInfo() + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate("", ""); + + var dnzInfo =client.GetContainerInfo("DotNetZone"); + Assert.AreNotEqual(ContainerInfo.Empty, dnzInfo); + + var pithosInfo = client.GetContainerInfo("PITHOS"); + Assert.AreNotEqual(ContainerInfo.Empty, pithosInfo); + + var mooInfo = client.GetContainerInfo("moo"); + Assert.AreEqual(ContainerInfo.Empty, mooInfo); + } + + [Test] + public void TestCreateContainer() + { + Assert.DoesNotThrow(() => + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate("", ""); + + client.CreateContainer("Shares"); + Assert.IsTrue(client.ContainerExists("Shares")); + client.CreateContainer("DotNetZone"); + Assert.IsTrue(client.ContainerExists("DotNetZone")); + }); + } + + [Test] + public void TestGetObject() + { + Assert.DoesNotThrow(() => + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate("", ""); + + client.CreateContainer("Shares"); + Assert.IsTrue(client.ContainerExists("Shares")); + using (var stream = client.GetObject("DotNetZone", "OData and WCF Data Services.pptx")) + using(var file=File.Create(@"e:\test.pptx",4096,FileOptions.DeleteOnClose)) + { + stream.CopyTo(file); + Assert.IsTrue(File.Exists(@"e:\test.pptx")); + } + + }); + + } + + [Test] + public void TestPutObject() + { + Assert.DoesNotThrow(() => + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate("", ""); + + client.CreateContainer("Shares"); + Assert.IsTrue(client.ContainerExists("Shares")); + + var filePath = @"e:\DeveloperGuide.pdf"; + FileInfo info=new FileInfo(filePath); + + using (var file = File.OpenRead(filePath)) + { + client.PutObject("Shares",info.Name, file,info.Length ); + } + + }); + + } + + [Test] + public void TestGetObjectMetadata() + { + Assert.DoesNotThrow(() => + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate("", ""); + + var filePath = "DeveloperGuide.pdf"; + var meta=client.GetObjectInfo("Shares", filePath); + Assert.IsNotEmpty(meta.Hash); + Assert.AreEqual(meta.Name,filePath); + + }); + + } + + [Test] + public void TestDeleteObject() + { + Assert.DoesNotThrow(() => + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate("", ""); + + client.CreateContainer("Shares"); + Assert.IsTrue(client.ContainerExists("Shares"),"Container Exists"); + + var filePath = @"e:\DeveloperGuide.pdf"; + FileInfo info=new FileInfo(filePath); + + using (var file = File.OpenRead(filePath)) + { + client.PutObject("Shares",info.Name, file,info.Length ); + } + + Assert.IsTrue(client.ObjectExists("Shares",info.Name),"File Created"); + + client.DeleteObject("Shares",info.Name); + Assert.IsFalse(client.ObjectExists("Shares", info.Name),"File Deleted"); + + + + + }); + + } + + [Test] + public void TestFilesWithSpaces() + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate("", ""); + + var testName = "Name with spaces.txt"; + + using(var stream=new MemoryStream()) + using (var writer = new StreamWriter(stream)) + { + + writer.WriteLine("This is a test line"); + stream.Seek(0, 0); + + client.PutObject("PITHOS",testName,stream,stream.Length); + } + + Assert.DoesNotThrow(() => + { + var info = client.GetObjectInfo("PITHOS", testName); + Assert.AreEqual(testName, info.Name); + }); + Assert.DoesNotThrow(() => + { + client.DeleteObject("PITHOS", testName); + }); + } + + [Test] + public void TestMoveObject() + { + Assert.DoesNotThrow(() => + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate("", ""); + + client.CreateContainer("Shares"); + Assert.IsTrue(client.ContainerExists("Shares"),"Container Exists"); + + var filePath = @"e:\DeveloperGuide.pdf"; + FileInfo info=new FileInfo(filePath); + + using (var file = File.OpenRead(filePath)) + { + client.PutObject("Shares",info.Name, file,info.Length ); + } + + Assert.IsTrue(client.ObjectExists("Shares",info.Name),"File Created"); + + client.MoveObject("Shares",info.Name,"smoo.pdf"); + Assert.IsFalse(client.ObjectExists("Shares", info.Name),"Original File Deleted"); + Assert.IsTrue(client.ObjectExists("Shares", "smoo.pdf"), "Target File Created"); + + }); + + } + + [Test] + public void TestGetObjectMissing() + { + + } + + + [Test] + public void TestAuthenticateMissingArguments() + { + Assert.Catch(() => + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate("someUser",null); + }); + + Assert.Catch(() => + { + ICloudClient client = new CloudFilesClient(); + client.Authenticate(null,"someKey"); + }); + + } + } + + +} diff --git a/trunk/Pithos.Network.Test/Pithos.Network.Test.csproj b/trunk/Pithos.Network.Test/Pithos.Network.Test.csproj new file mode 100644 index 0000000..620960b --- /dev/null +++ b/trunk/Pithos.Network.Test/Pithos.Network.Test.csproj @@ -0,0 +1,67 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {E027200B-C26A-4877-BFD9-1A18CF5DF2F4} + Library + Properties + Pithos.Network.Test + Pithos.Network.Test + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4} + Pithos.Interfaces + + + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD} + Pithos.Network + + + + + \ No newline at end of file diff --git a/trunk/Pithos.Network.Test/Properties/AssemblyInfo.cs b/trunk/Pithos.Network.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..88a1171 --- /dev/null +++ b/trunk/Pithos.Network.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,42 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Microsoft.Pex.Framework.Instrumentation; +using Pithos.Network; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Pithos.Network.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Pithos.Network.Test")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("593df921-d80c-43fb-8df7-2b10a9c2b48d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] + + +// monitor types in Pithos.Network.dll +[assembly: PexInstrumentAssembly(typeof(CloudFilesClient))] diff --git a/trunk/Pithos.Network/CloudFilesClient.cs b/trunk/Pithos.Network/CloudFilesClient.cs new file mode 100644 index 0000000..d282d88 --- /dev/null +++ b/trunk/Pithos.Network/CloudFilesClient.cs @@ -0,0 +1,352 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics.Contracts; +using System.IO; +using System.Linq; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using Hammock; +using Hammock.Caching; +using Hammock.Retries; +using Hammock.Serialization; +using Hammock.Web; +using Newtonsoft.Json; +using Pithos.Interfaces; + +namespace Pithos.Network +{ + [Export(typeof(ICloudClient))] + public class CloudFilesClient:ICloudClient + { + string _authUrl = "https://auth.api.rackspacecloud.com/v1.0"; + private RestClient _client; + private readonly TimeSpan _timeout = TimeSpan.FromSeconds(10); + private readonly int _retries = 5; + public string ApiKey { get; set; } + public string UserName { get; set; } + public string StorageUrl { get; set; } + public string Token { get; set; } + + public void Authenticate(string userName,string apiKey) + { + if (String.IsNullOrWhiteSpace(userName)) + throw new ArgumentNullException("userName","The userName property can't be empty"); + if (String.IsNullOrWhiteSpace(apiKey)) + throw new ArgumentNullException("apiKey", "The apiKey property can't be empty"); + + + UserName = userName; + ApiKey = apiKey; + + RestClient authClient = new RestClient(); + var request = new RestRequest {Path = _authUrl}; + request.AddHeader("X-Auth-User",UserName); + request.AddHeader("X-Auth-Key",ApiKey); + + var response=authClient.Request(request); + + ThrowIfNotStatusOK(response, "Authentication failed"); + + var keys = response.Headers.AllKeys.AsQueryable(); + + var storageUrl=GetHeaderValue("X-Storage-Url", response, keys); + + if (String.IsNullOrWhiteSpace(storageUrl)) + throw new InvalidOperationException("Failed to obtain storage url"); + StorageUrl = storageUrl; + + + var token = GetHeaderValue("X-Auth-Token",response,keys); + if (String.IsNullOrWhiteSpace(token)) + throw new InvalidOperationException("Failed to obtain token url"); + Token = token; + + _client = new RestClient { Authority = StorageUrl, RetryPolicy = new RetryPolicy { RetryCount = _retries }, Timeout = _timeout }; + _client.RetryPolicy.RetryConditions.Add(new TimeoutRetryCondition()); + _client.AddHeader("X-Auth-Token", Token); + + + } + + public IList ListContainers() + { + var request = new RestRequest(); + request.AddParameter("format","json"); + var response = _client.Request(request); + + ThrowIfNotStatusOK(response, "List Containers failed"); + + if (response.StatusCode == HttpStatusCode.NoContent) + return new List(); + + var infos=JsonConvert.DeserializeObject>(response.Content); + + return infos; + } + + public IList ListObjects(string container) + { + if (String.IsNullOrWhiteSpace(container)) + throw new ArgumentNullException("container", "The container property can't be empty"); + + var request = new RestRequest{Path=container}; + request.AddParameter("format", "json"); + var response = _client.Request(request); + if (response.TimedOut) + return new List(); + + ThrowIfNotStatusOK(response, "List Objects failed"); + + if (response.StatusCode == HttpStatusCode.NoContent) + return new List(); + + + var infos = JsonConvert.DeserializeObject>(response.Content); + + return infos; + } + + public bool ContainerExists(string container) + { + if (String.IsNullOrWhiteSpace(container)) + throw new ArgumentNullException("container", "The container property can't be empty"); + + var request = new RestRequest {Path = container, Method = WebMethod.Head}; + var response = _client.Request(request); + + switch(response.StatusCode) + { + case HttpStatusCode.NoContent: + return true; + case HttpStatusCode.NotFound: + return false; + default: + throw new WebException(String.Format("ContainerExists failed with unexpected status code {0}",response.StatusCode)); + } + } + + public bool ObjectExists(string container,string objectName) + { + if (String.IsNullOrWhiteSpace(container)) + throw new ArgumentNullException("container", "The container property can't be empty"); + if (String.IsNullOrWhiteSpace(objectName)) + throw new ArgumentNullException("objectName", "The objectName property can't be empty"); + + + var request = new RestRequest { Path = container + "/" + objectName, Method = WebMethod.Head }; + var response = _client.Request(request); + + switch (response.StatusCode) + { + case HttpStatusCode.OK: + case HttpStatusCode.NoContent: + return true; + case HttpStatusCode.NotFound: + return false; + default: + throw new WebException(String.Format("ObjectExists failed with unexpected status code {0}", response.StatusCode)); + } + + } + + public ObjectInfo GetObjectInfo(string container, string objectName) + { + if (String.IsNullOrWhiteSpace(container)) + throw new ArgumentNullException("container", "The container property can't be empty"); + if (String.IsNullOrWhiteSpace(objectName)) + throw new ArgumentNullException("objectName", "The objectName property can't be empty"); + + + var request = new RestRequest { Path = container + "/" + objectName, Method = WebMethod.Head }; + var response = _client.Request(request); + + if (response.TimedOut) + return ObjectInfo.Empty; + + switch (response.StatusCode) + { + case HttpStatusCode.OK: + case HttpStatusCode.NoContent: + var keys = response.Headers.AllKeys.AsQueryable(); + return new ObjectInfo + { + Name=objectName, + Bytes = long.Parse(GetHeaderValue("Content-Length", response, keys)), + Hash = GetHeaderValue("ETag", response, keys), + Content_Type = GetHeaderValue("Content-Type", response, keys) + }; + case HttpStatusCode.NotFound: + return ObjectInfo.Empty; + default: + throw new WebException(String.Format("GetObjectInfo failed with unexpected status code {0}", response.StatusCode)); + } + } + + public ContainerInfo GetContainerInfo(string container) + { + if (String.IsNullOrWhiteSpace(container)) + throw new ArgumentNullException("container", "The container property can't be empty"); + + var request = new RestRequest {Path = container, Method = WebMethod.Head}; + var response = _client.Request(request); + + switch(response.StatusCode) + { + case HttpStatusCode.NoContent: + var keys = response.Headers.AllKeys.AsQueryable(); + var containerInfo = new ContainerInfo + { + Name = container, + Count =long.Parse(GetHeaderValue("X-Container-Object-Count", response, keys)), + Bytes =long.Parse(GetHeaderValue("X-Container-Bytes-Used", response, keys)) + }; + return containerInfo; + case HttpStatusCode.NotFound: + return ContainerInfo.Empty; + default: + throw new WebException(String.Format("ContainerExists failed with unexpected status code {0}",response.StatusCode)); + } + } + + public void CreateContainer(string container) + { + if (String.IsNullOrWhiteSpace(container)) + throw new ArgumentNullException("container", "The container property can't be empty"); + + var request = new RestRequest { Path = container, Method = WebMethod.Put }; + var response = _client.Request(request); + + + if (response.StatusCode!=HttpStatusCode.Created && response.StatusCode!=HttpStatusCode.Accepted ) + throw new WebException(String.Format("ContainerExists failed with unexpected status code {0}", response.StatusCode)); + } + + public Stream GetObject(string container, string objectName) + { + if (String.IsNullOrWhiteSpace(container)) + throw new ArgumentNullException("container", "The container property can't be empty"); + if (String.IsNullOrWhiteSpace(objectName)) + throw new ArgumentNullException("objectName", "The objectName property can't be empty"); + + var request = new RestRequest { Path = container + "/" + objectName, Method = WebMethod.Get }; + var response = _client.Request(request); + + if (response.StatusCode == HttpStatusCode.NotFound) + throw new FileNotFoundException(); + if (response.StatusCode == HttpStatusCode.OK) + { + return response.ContentStream; + } + else + throw new WebException(String.Format("GetObject failed with unexpected status code {0}", response.StatusCode)); + } + + public void PutObject(string container, string objectName, Stream file,long fileSize) + { + if (String.IsNullOrWhiteSpace(container)) + throw new ArgumentNullException("container", "The container property can't be empty"); + if (String.IsNullOrWhiteSpace(objectName)) + throw new ArgumentNullException("objectName", "The objectName property can't be empty"); + if (file==null) + throw new ArgumentNullException("file", "The file property can't be empty"); + + + string url = StorageUrl + "/" + container + "/" + objectName; + + WebRequest request = WebRequest.Create(url); + request.Headers["X-Auth-Token"]=Token; + request.Method = "PUT"; + //request.Headers.Add("Content-Length",fileSize.ToString()); + //request.Headers.Add("Content-Type","application/octet-stream"); + + + string hash = CalculateHash(file); + + request.Headers["ETag"] = hash; + using (var stream = request.GetRequestStream()) + { + file.Seek(0, SeekOrigin.Begin); + file.CopyTo(stream); + } + + + var response=request.GetResponse() as HttpWebResponse; + + if (response.StatusCode == HttpStatusCode.Created) + return; + if (response.StatusCode == HttpStatusCode.LengthRequired) + throw new InvalidOperationException(); + else + throw new WebException(String.Format("GetObject failed with unexpected status code {0}", response.StatusCode)); + } + + private static string CalculateHash(Stream file) + { + string hash; + using (var hasher = MD5.Create()) + { + var hashBuilder=new StringBuilder(); + foreach (byte b in hasher.ComputeHash(file)) + hashBuilder.Append(b.ToString("x2").ToLower()); + hash = hashBuilder.ToString(); + } + return hash; + } + + public void DeleteObject(string container, string objectName) + { + if (String.IsNullOrWhiteSpace(container)) + throw new ArgumentNullException("container", "The container property can't be empty"); + if (String.IsNullOrWhiteSpace(objectName)) + throw new ArgumentNullException("objectName", "The objectName property can't be empty"); + + var request = new RestRequest { Path = container + "/" + objectName, Method = WebMethod.Delete }; + var response = _client.Request(request); + + if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.NoContent) + return; + else + throw new WebException(String.Format("GetObject failed with unexpected status code {0}", response.StatusCode)); + + } + + public void MoveObject(string container, string oldObjectName, string newObjectName) + { + if (String.IsNullOrWhiteSpace(container)) + throw new ArgumentNullException("container", "The container property can't be empty"); + if (String.IsNullOrWhiteSpace(oldObjectName)) + throw new ArgumentNullException("oldObjectName", "The oldObjectName property can't be empty"); + if (String.IsNullOrWhiteSpace(newObjectName)) + throw new ArgumentNullException("newObjectName", "The newObjectName property can't be empty"); + + var request = new RestRequest { Path = container + "/" + newObjectName, Method = WebMethod.Put }; + request.AddHeader("X-Copy-From",String.Format("/{0}/{1}",container,oldObjectName)); + request.AddPostContent(new byte[]{}); + var response = _client.Request(request); + + if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.NoContent || response.StatusCode==HttpStatusCode.Created) + { + this.DeleteObject(container,oldObjectName); + } + else + throw new WebException(String.Format("MoveObject failed with unexpected status code {0}", response.StatusCode)); + } + + private string GetHeaderValue(string headerName, RestResponse response, IQueryable keys) + { + if (keys.Any(key => key == headerName)) + return response.Headers[headerName]; + else + throw new WebException(String.Format("The {0} header is missing",headerName)); + } + + private static void ThrowIfNotStatusOK(RestResponse response, string message) + { + int status = (int)response.StatusCode; + if (status < 200 || status >= 300) + throw new WebException(String.Format("{0} with code {1}",message, status)); + } + } +} diff --git a/trunk/Pithos.Network/Pithos.Network.csproj b/trunk/Pithos.Network/Pithos.Network.csproj new file mode 100644 index 0000000..55ca6d9 --- /dev/null +++ b/trunk/Pithos.Network/Pithos.Network.csproj @@ -0,0 +1,97 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD} + Library + Properties + Pithos.Network + Pithos.Network + v4.0 + 512 + 1 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + True + False + True + False + False + True + False + False + False + False + False + True + False + False + False + + + + + + + False + Full + Build + 0 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\packages\Hammock.1.2.3\lib\net40\Hammock.dll + + + + + + + + + + + + + + + + + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D} + Newtonsoft.Json + + + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4} + Pithos.Interfaces + + + + + + + + \ No newline at end of file diff --git a/trunk/Pithos.Network/Properties/AssemblyInfo.cs b/trunk/Pithos.Network/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..49eb5f4 --- /dev/null +++ b/trunk/Pithos.Network/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Pithos.Network")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Pithos.Network")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("495e23f2-f451-4d24-aa39-961f97144a90")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/trunk/Pithos.Network/TimeoutRetryCondition.cs b/trunk/Pithos.Network/TimeoutRetryCondition.cs new file mode 100644 index 0000000..193a6cb --- /dev/null +++ b/trunk/Pithos.Network/TimeoutRetryCondition.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using Hammock.Retries; + +namespace Pithos.Network +{ + class TimeoutRetryCondition:RetryResultCondition + { + public override Predicate RetryIf + { + get + { + return r => + (r.Exception != null && r.Exception is WebException + && (((WebException)r.Exception).Status == WebExceptionStatus.Timeout)); + } + } + } +} diff --git a/trunk/Pithos.Network/packages.config b/trunk/Pithos.Network/packages.config new file mode 100644 index 0000000..5d5eed2 --- /dev/null +++ b/trunk/Pithos.Network/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/trunk/Pithos.ShellExtensions.Test/FileContextMenuTest.cs b/trunk/Pithos.ShellExtensions.Test/FileContextMenuTest.cs new file mode 100644 index 0000000..fbf5990 --- /dev/null +++ b/trunk/Pithos.ShellExtensions.Test/FileContextMenuTest.cs @@ -0,0 +1,28 @@ +using System; +using System.ComponentModel.Composition.Hosting; +using System.Reflection; +using System.Runtime.InteropServices.ComTypes; +using NUnit.Framework; +using Pithos.ShellExtensions.Menus; +using System.Runtime.InteropServices; + +namespace Pithos.ShellExtensions.Test +{ + [TestFixture] + public class FileContextMenuTest + { + [SetUp] + public void Setup() + { + } + + [Test] + public void TestCreation() + { + + var ctxMenu = new FileContextMenu(); + ctxMenu.Context.Settings=new TestPithosSettings(); + + } + } +} diff --git a/trunk/Pithos.ShellExtensions.Test/FileContextTest.cs b/trunk/Pithos.ShellExtensions.Test/FileContextTest.cs new file mode 100644 index 0000000..48fa0c9 --- /dev/null +++ b/trunk/Pithos.ShellExtensions.Test/FileContextTest.cs @@ -0,0 +1,58 @@ +using NUnit.Framework; + +namespace Pithos.ShellExtensions.Test +{ + [TestFixture] + public class FileContextTest + { + [Test] + public void CheckIsManaged() + { + var ctx = new FileContext {CurrentFile = @"e:\Pithos\moo.txt"}; + ctx.Settings=new TestPithosSettings(); + + Assert.IsTrue(ctx.IsManaged); + Assert.IsFalse(ctx.IsFolder ); + + ctx = new FileContext { CurrentFile = @"e:\Pithos\" }; + ctx.Settings = new TestPithosSettings(); + Assert.IsTrue(ctx.IsManaged); + Assert.IsTrue(ctx.IsFolder); + + ctx = new FileContext { CurrentFile = @"e:\Pithos" }; + ctx.Settings = new TestPithosSettings(); + Assert.IsTrue(ctx.IsManaged); + Assert.IsTrue(ctx.IsFolder); + + ctx = new FileContext { CurrentFile = @"e:\pithos" }; + ctx.Settings = new TestPithosSettings(); + Assert.IsTrue(ctx.IsManaged); + Assert.IsTrue(ctx.IsFolder); + + ctx = new FileContext { CurrentFile = @"e:\Pithos.txt" }; + ctx.Settings = new TestPithosSettings(); + Assert.IsTrue(!ctx.IsManaged); + + ctx = new FileContext { CurrentFile = @"e:\Pithos\01New Folder" }; + ctx.Settings = new TestPithosSettings(); + Assert.IsTrue(ctx.IsManaged); + Assert.IsTrue(ctx.IsFolder); + } + + [Test] + public void CheckCurrentFile() + { + var ctx = new FileContext {CurrentFile = @"e:\Pithos\moo.txt"}; + ctx.Settings = new TestPithosSettings(); + Assert.AreEqual(@"e:\pithos", ctx.CurrentFolder); + Assert.IsTrue(ctx.IsManaged); + Assert.IsFalse(ctx.IsFolder); + + ctx = new FileContext { CurrentFile = @"e:\Pithos\01New Folder" }; + ctx.Settings = new TestPithosSettings(); + Assert.AreEqual(@"e:\pithos\01new folder", ctx.CurrentFolder); + Assert.IsTrue(ctx.IsManaged); + Assert.IsTrue(ctx.IsFolder); + } + } +} diff --git a/trunk/Pithos.ShellExtensions.Test/IconOverlayTest.cs b/trunk/Pithos.ShellExtensions.Test/IconOverlayTest.cs new file mode 100644 index 0000000..4093f19 --- /dev/null +++ b/trunk/Pithos.ShellExtensions.Test/IconOverlayTest.cs @@ -0,0 +1,77 @@ +using System; +using System.IO; +using NUnit.Framework; +using Pithos.ShellExtensions.Overlays; + +namespace Pithos.ShellExtensions.Test +{ + [TestFixture] + public class IconOverlayTest + { + public const int S_OK = 0x0000; + public const int S_FALSE = 0x0001; + + [SetUp] + public void Setup() + {/* + var ioc = IoC.Current; + var catalog = new AggregateCatalog(); + catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly())); + + ioc.Container = new CompositionContainer(catalog); +*/ + } + + [Test] + public void CreateOverlayTest() + { + var normalOverlay=new NormalIconOverlay(); + + Assert.IsFalse(String.IsNullOrWhiteSpace(normalOverlay.IconPath )); + Assert.IsFalse(String.IsNullOrWhiteSpace(normalOverlay.OverlayName)); + Assert.AreEqual("0PithosNormal", normalOverlay.OverlayName); + + var iconPath = @"C:\Program Files\Common Files\TortoiseOverlays\icons\XPStyle\"; + + Assert.AreEqual(Path.Combine(iconPath,"NormalIcon.ico"),normalOverlay.IconPath); + + var conflictOverlay = new ConflictIconOverlay(); + Assert.AreEqual(Path.Combine(iconPath, "ConflictIcon.ico"), conflictOverlay.IconPath); + + var modifiedOverlay = new ModifiedIconOverlay(); + Assert.AreEqual(Path.Combine(iconPath, "ModifiedIcon.ico"), modifiedOverlay.IconPath); + + var synchOverlay = new SynchIconOverlay(); + Assert.AreEqual(Path.Combine(iconPath, "SynchIcon.ico"), synchOverlay.IconPath); + } + + [Test] + public void TestMembership() + { + var overlay=new NormalIconOverlay(); + + var status = overlay.IsMemberOf(@"e:\pithos\0file.txt", 0); + Assert.AreEqual(status,S_OK); + + status = overlay.IsMemberOf(@"e:\Pithos\0file.txt", 0); + Assert.AreEqual(status, S_OK,"Failed checking Camel cased folder"); + + status = overlay.IsMemberOf(@"e:\Pithos\", 0); + Assert.AreEqual(status, S_OK,"Failed checking camel folder with slash"); + + status = overlay.IsMemberOf(@"e:\pithos\", 0); + Assert.AreEqual(status, S_OK,"Failed lower folder with slash"); + + status = overlay.IsMemberOf(@"e:\pithos", 0); + Assert.AreEqual(status, S_OK,"Failed lower folder no slash"); + + status = overlay.IsMemberOf(@"e:\0file.txt", 0); + Assert.AreEqual(status, S_FALSE,"Failed unrelated file"); + + status = overlay.IsMemberOf(@"e:\pithos\1file.txt", 0); + Assert.AreEqual(status, S_FALSE,"Failed different state file"); + + } + + } +} diff --git a/trunk/Pithos.ShellExtensions.Test/IoCTest.cs b/trunk/Pithos.ShellExtensions.Test/IoCTest.cs new file mode 100644 index 0000000..861c3fd --- /dev/null +++ b/trunk/Pithos.ShellExtensions.Test/IoCTest.cs @@ -0,0 +1,23 @@ +using System.Linq; +using NUnit.Framework; + +namespace Pithos.ShellExtensions.Test +{ + [TestFixture] + public class IoCTest + { + [Test] + public void TestIoCInit() + { + var ioc = IoC.Current; + /* var catalog = new AggregateCatalog(); + catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly())); + + ioc.Container = new CompositionContainer(catalog); +*/ + + + Assert.IsTrue(ioc.Container.Catalog.Parts.Any()); + } + } +} diff --git a/trunk/Pithos.ShellExtensions.Test/Pithos.ShellExtensions.Test.csproj b/trunk/Pithos.ShellExtensions.Test/Pithos.ShellExtensions.Test.csproj new file mode 100644 index 0000000..9626a0f --- /dev/null +++ b/trunk/Pithos.ShellExtensions.Test/Pithos.ShellExtensions.Test.csproj @@ -0,0 +1,72 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {2CFE2DF1-20AE-47E2-B1BB-36B974600BE1} + Library + Properties + Pithos.ShellExtensions.Test + Pithos.ShellExtensions.Test + v4.0 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + + + + + + + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4} + Pithos.Interfaces + + + {240B432F-1030-4623-BCC3-FF351D6C1B63} + Pithos.ShellExtensions + + + + + \ No newline at end of file diff --git a/trunk/Pithos.ShellExtensions.Test/Properties/AssemblyInfo.cs b/trunk/Pithos.ShellExtensions.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..108732d --- /dev/null +++ b/trunk/Pithos.ShellExtensions.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Pithos.ShellExtensions.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Pithos.ShellExtensions.Test")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("3c1311e4-b7aa-476f-9bbe-381c9c68c65d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/trunk/Pithos.ShellExtensions.Test/TestPithosSettings.cs b/trunk/Pithos.ShellExtensions.Test/TestPithosSettings.cs new file mode 100644 index 0000000..43da0e9 --- /dev/null +++ b/trunk/Pithos.ShellExtensions.Test/TestPithosSettings.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using System.Text; +using Pithos.Interfaces; + +namespace Pithos.ShellExtensions.Test +{ + [Export(typeof(IPithosSettings))] + class TestPithosSettings:IPithosSettings + { + public string PithosPath + { + get { return @"e:\pithos"; } + set { throw new NotImplementedException(); } + } + + public string IconsPath + { + get { return @"C:\Program Files\Common Files\TortoiseOverlays\icons\XPStyle\"; } + set { throw new NotImplementedException(); } + } + + public string UserName { get; set; } + public string ApiKey { get; set; } + + public void Save() + { + + } + + public void Reload() + { + + } + } +} diff --git a/trunk/Pithos.ShellExtensions.Test/TestStatusChecker.cs b/trunk/Pithos.ShellExtensions.Test/TestStatusChecker.cs new file mode 100644 index 0000000..f53a457 --- /dev/null +++ b/trunk/Pithos.ShellExtensions.Test/TestStatusChecker.cs @@ -0,0 +1,34 @@ +using System; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.IO; +using Pithos.Interfaces; + +namespace Pithos.ShellExtensions +{ + [Export(typeof(IStatusChecker))] + public class TestStatusChecker:IStatusChecker + { + [Import] + private IPithosSettings Settings; + + private readonly string[] _states = {"Normal", "Modified", "Conflict","Synch"}; + + public string GetFileStatus(string path) + { + Contract.Requires(!String.IsNullOrWhiteSpace(path)); + var pithosPath = Settings.PithosPath; + if (path.StartsWith(pithosPath,true,null)) + { + var fileName = Path.GetFileName(path); + if (String.IsNullOrWhiteSpace(fileName)) + return _states[0]; + + var status = Char.ConvertToUtf32(fileName, 0)%4; + return _states[status]; + } + return String.Empty; + } + } +} diff --git a/trunk/Pithos.ShellExtensions/FileContext.cs b/trunk/Pithos.ShellExtensions/FileContext.cs new file mode 100644 index 0000000..0450c65 --- /dev/null +++ b/trunk/Pithos.ShellExtensions/FileContext.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using Pithos.Interfaces; + +namespace Pithos.ShellExtensions +{ + [Export] + public class FileContext + { + [Import] + public IPithosSettings Settings { get; set; } + + [Import] + public IStatusChecker StatusChecker { get; set; } + + public string PithosPath { get { return Settings.PithosPath.ToLower(); } } + + + public bool IsManaged + { + get + { + Trace.Write(String.Format("Managed path is {0}\r\n Current Path is {1}", PithosPath, CurrentFile)); + return CurrentFolder.StartsWith(PithosPath, true, null); + } + } + + public bool IsFolder { get; set; } + private string _currentFolder; + public string CurrentFolder + { + get { return _currentFolder; } + set + { + _currentFolder = value.ToLower(); + IsFolder = true; + _currentFile = _currentFolder; + } + } + + private string _currentFile; + public string CurrentFile + { + get { return _currentFile; } + set + { + _currentFile = value.ToLower(); + Trace.Write(String.Format("File is {0}", _currentFile)); + if (Directory.Exists(_currentFile)) + { + _currentFolder = _currentFile; + IsFolder = true; + } + else + { + _currentFolder = Path.GetDirectoryName(_currentFile); + IsFolder = false; + } + + } + } + } + +} diff --git a/trunk/Pithos.ShellExtensions/IoC.cs b/trunk/Pithos.ShellExtensions/IoC.cs new file mode 100644 index 0000000..280555f --- /dev/null +++ b/trunk/Pithos.ShellExtensions/IoC.cs @@ -0,0 +1,44 @@ +using System; +using System.ComponentModel.Composition.Hosting; +using System.Diagnostics; +using System.Reflection; +using System.ComponentModel.Composition; + +namespace Pithos.ShellExtensions +{ + public class IoC + { + public CompositionContainer Container; + + static readonly Lazy Instance=new Lazy(); + + public static IoC Current + { + get { return Instance.Value; } + } + + public IoC() + { + var catalog = new AggregateCatalog(); + catalog.Catalogs.Add(new AssemblyCatalog(Assembly.GetExecutingAssembly())); + + Container=new CompositionContainer(catalog); + } + + + + public T Compose(T target) + { + try + { + Container.ComposeParts(target); + return target; + } + catch (Exception exc) + { + Trace.TraceError(exc.ToString()); + throw; + } + } + } +} diff --git a/trunk/Pithos.ShellExtensions/MarshalHelpers.cs b/trunk/Pithos.ShellExtensions/MarshalHelpers.cs new file mode 100644 index 0000000..93897c1 --- /dev/null +++ b/trunk/Pithos.ShellExtensions/MarshalHelpers.cs @@ -0,0 +1,30 @@ +using System; +using System.Diagnostics.Contracts; +using System.Runtime.InteropServices; +using System.Text; + +namespace Pithos.ShellExtensions +{ + public static class MarshalHelpers + { + + public static void CopyToBuffer(string input, IntPtr buffer, int bufferSize) + { + Contract.Requires(buffer != IntPtr.Zero); + Contract.Requires(bufferSize >=0 ); + + var bytes = Encoding.Unicode.GetBytes(input); + + if (bytes.Length + 2 >= bufferSize) + return; + + for (var i = 0; i < bytes.Length; i++) + { + Marshal.WriteByte(buffer, i, bytes[i]); + } + //write the null terminator "\0\0" + Marshal.WriteByte(buffer, bytes.Length, 0); + Marshal.WriteByte(buffer, bytes.Length + 1, 0); + } + } +} diff --git a/trunk/Pithos.ShellExtensions/Menus/DisplayFlags.cs b/trunk/Pithos.ShellExtensions/Menus/DisplayFlags.cs new file mode 100644 index 0000000..6702375 --- /dev/null +++ b/trunk/Pithos.ShellExtensions/Menus/DisplayFlags.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Pithos.ShellExtensions.Menus +{ + [Flags] + enum DisplayFlags + { + None, + Folder, + File, + All + } + +} diff --git a/trunk/Pithos.ShellExtensions/Menus/FileContextMenu.cs b/trunk/Pithos.ShellExtensions/Menus/FileContextMenu.cs new file mode 100644 index 0000000..e7bd4b8 --- /dev/null +++ b/trunk/Pithos.ShellExtensions/Menus/FileContextMenu.cs @@ -0,0 +1,506 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.Linq; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Text; + +namespace Pithos.ShellExtensions.Menus +{ + [ClassInterface(ClassInterfaceType.None)] + [Guid("B1F1405D-94A1-4692-B72F-FC8CAF8B8700"), ComVisible(true)] + public class FileContextMenu : IShellExtInit, IContextMenu + { + private const string MenuHandlername = "CSShellExtContextMenuHandler.FileContextMenuExt"; + + + private readonly Dictionary _items; + + + private FileContext _context =new FileContext(); + public FileContext Context + { + get { return _context; } + set { _context = value; } + } + + public FileContextMenu() + { + _items = new Dictionary{ + {"gotoPithos",new MenuItem{ + MenuText = "&Go to Pithos", + Verb = "gotoPithos", + VerbCanonicalName = "PITHOSGoTo", + VerbHelpText = "Go to Pithos", + MenuDisplayId = 0, + MenuCommand=OnVerbDisplayFileName, + DisplayFlags=DisplayFlags.All + }}, + {"prevVersions",new MenuItem{ + MenuText = "&Show Previous Versions", + Verb = "prevVersions", + VerbCanonicalName = "PITHOSPrevVersions", + VerbHelpText = "Go to Pithos and display previous versions", + MenuDisplayId = 1, + MenuCommand=OnVerbDisplayFileName, + DisplayFlags=DisplayFlags.File + }} + }; + + + } + + + void OnVerbDisplayFileName(IntPtr hWnd) + { + string message = String.Format("The selected file is {0}\r\n\r\nThe selected Path is {1}", + Context.CurrentFile, + Context.CurrentFolder); + + System.Windows.Forms.MessageBox.Show( + message, + "CSShellExtContextMenuHandler"); + NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_ASSOCCHANGED, HChangeNotifyFlags.SHCNF_IDLIST, + IntPtr.Zero, IntPtr.Zero); + } + + + #region Shell Extension Registration + + [ComRegisterFunction] + public static void Register(Type t) + { + try + { + ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, ".cs", + MenuHandlername); + ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, "Directory", + MenuHandlername); + ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, @"Directory\Background", + MenuHandlername); + ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, "*", + MenuHandlername); + + //ShellExtReg.MarkApproved(t.GUID, MenuHandlername); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); // Log the error + throw; // Re-throw the exception + } + } + + [ComUnregisterFunction] + public static void Unregister(Type t) + { + try + { + ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, ".cs", MenuHandlername); + ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, "Directory", MenuHandlername); + ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, @"Directory\Background", MenuHandlername); + ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, "*", MenuHandlername); + + //ShellExtReg.RemoveApproved(t.GUID, MenuHandlername); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); // Log the error + throw; // Re-throw the exception + } + } + + #endregion + + + #region IShellExtInit Members + + /// + /// Initialize the context menu handler. + /// + /// + /// A pointer to an ITEMIDLIST structure that uniquely identifies a folder. + /// + /// + /// A pointer to an IDataObject interface object that can be used to retrieve + /// the objects being acted upon. + /// + /// + /// The registry key for the file object or folder type. + /// + public void Initialize(IntPtr pidlFolder, IntPtr pDataObj, IntPtr hKeyProgID) + { + + if(pDataObj == IntPtr.Zero && pidlFolder == IntPtr.Zero) + { + throw new ArgumentException("pidlFolder and pDataObj shouldn't be null at the same time"); + } + + + Trace.Write("Initializing"); + + if (pDataObj != IntPtr.Zero) + { + Trace.Write("Got a data object"); + + FORMATETC fe = new FORMATETC(); + fe.cfFormat = (short)CLIPFORMAT.CF_HDROP; + fe.ptd = IntPtr.Zero; + fe.dwAspect = DVASPECT.DVASPECT_CONTENT; + fe.lindex = -1; + fe.tymed = TYMED.TYMED_HGLOBAL; + STGMEDIUM stm = new STGMEDIUM(); + + // The pDataObj pointer contains the objects being acted upon. In this + // example, we get an HDROP handle for enumerating the selected files + // and folders. + IDataObject dataObject = (IDataObject)Marshal.GetObjectForIUnknown(pDataObj); + dataObject.GetData(ref fe, out stm); + + try + { + // Get an HDROP handle. + IntPtr hDrop = stm.unionmember; + if (hDrop == IntPtr.Zero) + { + throw new ArgumentException(); + } + + // Determine how many files are involved in this operation. + uint nFiles = NativeMethods.DragQueryFile(hDrop, UInt32.MaxValue, null, 0); + + Trace.Write(String.Format("Got {0} files", nFiles)); + // This code sample displays the custom context menu item when only + // one file is selected. + if (nFiles == 1) + { + // Get the path of the file. + var fileName = new StringBuilder(260); + if (0 == NativeMethods.DragQueryFile(hDrop, 0, fileName, + fileName.Capacity)) + { + Marshal.ThrowExceptionForHR(WinError.E_FAIL); + } + Context.CurrentFile = fileName.ToString(); + } + /* else + { + Marshal.ThrowExceptionForHR(WinError.E_FAIL); + }*/ + + // [-or-] + + // Enumerate the selected files and folders. + //if (nFiles > 0) + //{ + // StringCollection selectedFiles = new StringCollection(); + // StringBuilder fileName = new StringBuilder(260); + // for (uint i = 0; i < nFiles; i++) + // { + // // Get the next file name. + // if (0 != NativeMethods.DragQueryFile(hDrop, i, fileName, + // fileName.Capacity)) + // { + // // Add the file name to the list. + // selectedFiles.Add(fileName.ToString()); + // } + // } + // + // // If we did not find any files we can work with, throw + // // exception. + // if (selectedFiles.Count == 0) + // { + // Marshal.ThrowExceptionForHR(WinError.E_FAIL); + // } + //} + //else + //{ + // Marshal.ThrowExceptionForHR(WinError.E_FAIL); + //} + } + finally + { + NativeMethods.ReleaseStgMedium(ref stm); + } + } + + if (pidlFolder != IntPtr.Zero) + { + Trace.Write("Got a folder"); + StringBuilder path = new StringBuilder(); + if (!NativeMethods.SHGetPathFromIDList(pidlFolder, path)) + { + int error = Marshal.GetHRForLastWin32Error(); + Marshal.ThrowExceptionForHR(error); + } + Context.CurrentFolder = path.ToString(); + Trace.Write(String.Format("Folder is {0}", Context.CurrentFolder)); + } + } + + #endregion + + + #region IContextMenu Members + + /// + /// Add commands to a shortcut menu. + /// + /// A handle to the shortcut menu. + /// + /// The zero-based position at which to insert the first new menu item. + /// + /// + /// The minimum value that the handler can specify for a menu item ID. + /// + /// + /// The maximum value that the handler can specify for a menu item ID. + /// + /// + /// Optional flags that specify how the shortcut menu can be changed. + /// + /// + /// If successful, returns an HRESULT value that has its severity value set + /// to SEVERITY_SUCCESS and its code value set to the offset of the largest + /// command identifier that was assigned, plus one. + /// + public int QueryContextMenu( + IntPtr hMenu, + uint iMenu, + uint idCmdFirst, + uint idCmdLast, + uint uFlags) + { + Trace.Write("Start qcm"); + // If uFlags include CMF_DEFAULTONLY then we should not do anything. + Trace.Write(String.Format("Flags {0}", uFlags)); + + if (((uint)CMF.CMF_DEFAULTONLY & uFlags) != 0) + { + Trace.Write("Default only flag, returning"); + return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0, 0); + } + + if (!Context.IsManaged) + { + Trace.Write("Not a PITHOS folder"); + return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0, 0); + } + + /* + if (!selectedFolder.ToLower().Contains("pithos")) + return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0, 0); + */ + + // Use either InsertMenu or InsertMenuItem to add menu items. + + uint largestID = 0; + + DisplayFlags itemType = (Context.IsFolder) ? DisplayFlags.Folder : DisplayFlags.File; + + Trace.Write(String.Format("Item Flags {0}", itemType)); + + foreach (var menuItem in _items.Values) + { + Trace.Write(String.Format("Menu Flags {0}", menuItem.DisplayFlags)); + if ((itemType & menuItem.DisplayFlags) != DisplayFlags.None) + { + Trace.Write("Adding Menu"); + + MENUITEMINFO mii = menuItem.CreateInfo(idCmdFirst); + if (!NativeMethods.InsertMenuItem(hMenu, iMenu, true, ref mii)) + { + return Marshal.GetHRForLastWin32Error(); + } + if (largestID < menuItem.MenuDisplayId) + largestID = menuItem.MenuDisplayId; + } + } + + Trace.Write("Adding Separator 1"); + // Add a separator. + MENUITEMINFO sep = new MENUITEMINFO(); + sep.cbSize = (uint)Marshal.SizeOf(sep); + sep.fMask = MIIM.MIIM_TYPE; + sep.fType = MFT.MFT_SEPARATOR; + if (!NativeMethods.InsertMenuItem(hMenu, iMenu, true, ref sep)) + { + Trace.TraceError("Error adding separator 1\r\n{0}", Marshal.GetLastWin32Error()); + return Marshal.GetHRForLastWin32Error(); + } + + + + + Trace.Write("Menus added"); + // Return an HRESULT value with the severity set to SEVERITY_SUCCESS. + // Set the code value to the offset of the largest command identifier + // that was assigned, plus one (1). + return WinError.MAKE_HRESULT(WinError.SEVERITY_SUCCESS, 0, + largestID + 1); + } + + /// + /// Carry out the command associated with a shortcut menu item. + /// + /// + /// A pointer to a CMINVOKECOMMANDINFO or CMINVOKECOMMANDINFOEX structure + /// containing information about the command. + /// + public void InvokeCommand(IntPtr pici) + { + bool isUnicode = false; + + // Determine which structure is being passed in, CMINVOKECOMMANDINFO or + // CMINVOKECOMMANDINFOEX based on the cbSize member of lpcmi. Although + // the lpcmi parameter is declared in Shlobj.h as a CMINVOKECOMMANDINFO + // structure, in practice it often points to a CMINVOKECOMMANDINFOEX + // structure. This struct is an extended version of CMINVOKECOMMANDINFO + // and has additional members that allow Unicode strings to be passed. + CMINVOKECOMMANDINFO ici = (CMINVOKECOMMANDINFO)Marshal.PtrToStructure( + pici, typeof(CMINVOKECOMMANDINFO)); + CMINVOKECOMMANDINFOEX iciex = new CMINVOKECOMMANDINFOEX(); + if (ici.cbSize == Marshal.SizeOf(typeof(CMINVOKECOMMANDINFOEX))) + { + if ((ici.fMask & CMIC.CMIC_MASK_UNICODE) != 0) + { + isUnicode = true; + iciex = (CMINVOKECOMMANDINFOEX)Marshal.PtrToStructure(pici, + typeof(CMINVOKECOMMANDINFOEX)); + } + } + + // Determines whether the command is identified by its offset or verb. + // There are two ways to identify commands: + // + // 1) The command's verb string + // 2) The command's identifier offset + // + // If the high-order word of lpcmi->lpVerb (for the ANSI case) or + // lpcmi->lpVerbW (for the Unicode case) is nonzero, lpVerb or lpVerbW + // holds a verb string. If the high-order word is zero, the command + // offset is in the low-order word of lpcmi->lpVerb. + + // For the ANSI case, if the high-order word is not zero, the command's + // verb string is in lpcmi->lpVerb. + if (!isUnicode && NativeMethods.HighWord(ici.verb.ToInt32()) != 0) + { + // Is the verb supported by this context menu extension? + string verbAnsi = Marshal.PtrToStringAnsi(ici.verb); + if (_items.ContainsKey(verbAnsi)) + { + _items[verbAnsi].MenuCommand(ici.hwnd); + } + else + { + // If the verb is not recognized by the context menu handler, it + // must return E_FAIL to allow it to be passed on to the other + // context menu handlers that might implement that verb. + Marshal.ThrowExceptionForHR(WinError.E_FAIL); + } + } + + // For the Unicode case, if the high-order word is not zero, the + // command's verb string is in lpcmi->lpVerbW. + else if (isUnicode && NativeMethods.HighWord(iciex.verbW.ToInt32()) != 0) + { + // Is the verb supported by this context menu extension? + string verbUTF = Marshal.PtrToStringUni(iciex.verbW); + if (_items.ContainsKey(verbUTF)) + { + _items[verbUTF].MenuCommand(ici.hwnd); + } + else + { + // If the verb is not recognized by the context menu handler, it + // must return E_FAIL to allow it to be passed on to the other + // context menu handlers that might implement that verb. + Marshal.ThrowExceptionForHR(WinError.E_FAIL); + } + } + + // If the command cannot be identified through the verb string, then + // check the identifier offset. + else + { + // Is the command identifier offset supported by this context menu + // extension? + int menuID = NativeMethods.LowWord(ici.verb.ToInt32()); + var menuItem = _items.FirstOrDefault(item => item.Value.MenuDisplayId == menuID).Value; + if (menuItem != null) + { + menuItem.MenuCommand(ici.hwnd); + } + else + { + // If the verb is not recognized by the context menu handler, it + // must return E_FAIL to allow it to be passed on to the other + // context menu handlers that might implement that verb. + Marshal.ThrowExceptionForHR(WinError.E_FAIL); + } + } + } + + /// + /// Get information about a shortcut menu command, including the help string + /// and the language-independent, or canonical, name for the command. + /// + /// Menu command identifier offset. + /// + /// Flags specifying the information to return. This parameter can have one + /// of the following values: GCS_HELPTEXTA, GCS_HELPTEXTW, GCS_VALIDATEA, + /// GCS_VALIDATEW, GCS_VERBA, GCS_VERBW. + /// + /// Reserved. Must be IntPtr.Zero + /// + /// The address of the buffer to receive the null-terminated string being + /// retrieved. + /// + /// + /// Size of the buffer, in characters, to receive the null-terminated string. + /// + public void GetCommandString( + UIntPtr idCmd, + uint uFlags, + IntPtr pReserved, + StringBuilder pszName, + uint cchMax) + { + uint menuID = idCmd.ToUInt32(); + var menuItem = _items.FirstOrDefault(item => item.Value.MenuDisplayId == menuID).Value; + if (menuItem != null) + { + switch ((GCS)uFlags) + { + case GCS.GCS_VERBW: + if (menuItem.VerbCanonicalName.Length > cchMax - 1) + { + Marshal.ThrowExceptionForHR(WinError.STRSAFE_E_INSUFFICIENT_BUFFER); + } + else + { + pszName.Clear(); + pszName.Append(menuItem.VerbCanonicalName); + } + break; + + case GCS.GCS_HELPTEXTW: + if (menuItem.VerbHelpText.Length > cchMax - 1) + { + Marshal.ThrowExceptionForHR(WinError.STRSAFE_E_INSUFFICIENT_BUFFER); + } + else + { + pszName.Clear(); + pszName.Append(menuItem.VerbHelpText); + } + break; + } + } + } + + #endregion + } +} diff --git a/trunk/Pithos.ShellExtensions/Menus/MenuItem.cs b/trunk/Pithos.ShellExtensions/Menus/MenuItem.cs new file mode 100644 index 0000000..f442de8 --- /dev/null +++ b/trunk/Pithos.ShellExtensions/Menus/MenuItem.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; + +namespace Pithos.ShellExtensions.Menus +{ + internal class MenuItem + { + public string MenuText { get; set; } + public string Verb { get; set; } + public string VerbCanonicalName { get; set; } + public string VerbHelpText { get; set; } + public uint MenuDisplayId { get; set; } + public Action MenuCommand { get; set; } + public DisplayFlags DisplayFlags { get; set; } + + public MENUITEMINFO CreateInfo(uint idCmdFirst) + { + var mii = new MENUITEMINFO(); + mii.cbSize = (uint)Marshal.SizeOf(mii); + mii.fMask = MIIM.MIIM_ID | MIIM.MIIM_TYPE | MIIM.MIIM_STATE; + mii.wID = idCmdFirst + MenuDisplayId; + mii.fType = MFT.MFT_STRING; + mii.dwTypeData = MenuText; + mii.fState = MFS.MFS_ENABLED; + return mii; + } + } +} diff --git a/trunk/Pithos.ShellExtensions/Overlays/ConflictIconOverlay.cs b/trunk/Pithos.ShellExtensions/Overlays/ConflictIconOverlay.cs new file mode 100644 index 0000000..89357ef --- /dev/null +++ b/trunk/Pithos.ShellExtensions/Overlays/ConflictIconOverlay.cs @@ -0,0 +1,36 @@ +using System; +using System.Runtime.InteropServices; + + +namespace Pithos.ShellExtensions.Overlays +{ + [ClassInterface(ClassInterfaceType.None), ComVisible(true)] + [Guid("3EFA16FC-C6B6-4673-BFEC-BD9518F1EFCE")] + public class ConflictIconOverlay: IconOverlayBase + { + + private static string _iconName = "Conflict"; + + public ConflictIconOverlay():base(_iconName) + { + + } + + #region Shell Extension Registration + [ComRegisterFunction()] + public static void Register(Type t) + { + RegisterOverlay(t,_iconName); + } + + [ComUnregisterFunction] + public static void Unregister(Type t) + { + UnregisterOverlay(t,_iconName); + } + + #endregion + + + } +} \ No newline at end of file diff --git a/trunk/Pithos.ShellExtensions/Overlays/IconOverlayBase.cs b/trunk/Pithos.ShellExtensions/Overlays/IconOverlayBase.cs new file mode 100644 index 0000000..56dd321 --- /dev/null +++ b/trunk/Pithos.ShellExtensions/Overlays/IconOverlayBase.cs @@ -0,0 +1,126 @@ +using System; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.IO; + +using System.ComponentModel.Composition; +using Pithos.Interfaces; + +namespace Pithos.ShellExtensions.Overlays +{ + + public class IconOverlayBase : IShellIconOverlayIdentifier + { + protected static string PithosPrefix = "0Pithos"; + + public string OverlayName { get; private set; } + public string IconPath { get; private set; } + + [Import(typeof (IStatusChecker))] + public IStatusChecker StatusChecker; + [Import(typeof (IPithosSettings))] + public IPithosSettings Settings; + + + + public IconOverlayBase(string iconName) + { + Trace.Write("Icon Overlay Instance created"); + IoC.Current.Compose(this); + + + string overlayName=PithosPrefix + iconName; + string iconFile = iconName + "Icon.ico"; + + + var iconsFolder = Settings.IconsPath; + var fullPath = Path.Combine(iconsFolder, iconFile); + + OverlayName = overlayName; + IconPath = fullPath; + + + } + + + public int IsMemberOf(string path, uint attributes) + { + if (String.IsNullOrWhiteSpace(path)) + throw new ArgumentNullException("Empty path"); + + Trace.Write(String.Format("ICON Status check for {0} - {1}",path,GetType().Name)); + + if (!path.StartsWith(Settings.PithosPath,true,null)) + return WinError.S_FALSE; + + var status=StatusChecker.GetFileOverlayStatus(path); + var isMember = (PithosPrefix + status == OverlayName); + Trace.Write(String.Format("Status check for {0} is {1}",path,isMember)); + return isMember ? WinError.S_OK : WinError.S_FALSE; + } + + public int GetOverlayInfo( + IntPtr iconFileBuffer, + int iconFileBufferSize, + out int iconIndex, + out uint flags) + { + Trace.Write("Looking for icons"); + + Trace.WriteLine(string.Format("ICON file {0}", IconPath)); + + int bytesCount = System.Text.Encoding.Unicode.GetByteCount(IconPath); + + Trace.WriteLine(string.Format(" GetOverlayInfo::{0}",bytesCount)); + + MarshalHelpers.CopyToBuffer(IconPath, iconFileBuffer, iconFileBufferSize); + + flags = (uint)(ISIOI.ISIOI_ICONFILE);// | ISIOI.ISIOI_ICONINDEX); + iconIndex = 0; + return WinError.S_OK; + } + + public int GetPriority(out int priority) + { + Trace.Write("Checking for priority"); + priority = 0; + return WinError.S_OK; + } + + + public static void RegisterOverlay(Type t,string iconName) + { + try + { + string overlayName = PithosPrefix + iconName; + ShellExtReg.RegisterIconOverlayIdentifier(t.GUID, overlayName); + + NativeMethods.SHChangeNotify(HChangeNotifyEventID.SHCNE_ASSOCCHANGED, HChangeNotifyFlags.SHCNF_IDLIST, + IntPtr.Zero, IntPtr.Zero); + Trace.Write("Registered icon handler"); + } + catch (Exception ex) + { + Trace.WriteLine(ex.Message); // Log the error + throw; // Re-throw the exception + } + } + + + public static void UnregisterOverlay(Type t,string iconName) + { + try + { + string overlayName = PithosPrefix + iconName; + ShellExtReg.UnregisterIconOverlayIdentifier(t.GUID, overlayName); + Trace.Write("UnRegistered icon handler"); + } + catch (Exception ex) + { + Trace.WriteLine(ex.Message); // Log the error + throw; // Re-throw the exception + } + } + } + +} \ No newline at end of file diff --git a/trunk/Pithos.ShellExtensions/Overlays/ModifiedIconOverlay.cs b/trunk/Pithos.ShellExtensions/Overlays/ModifiedIconOverlay.cs new file mode 100644 index 0000000..4646b82 --- /dev/null +++ b/trunk/Pithos.ShellExtensions/Overlays/ModifiedIconOverlay.cs @@ -0,0 +1,35 @@ +using System; +using System.Runtime.InteropServices; + +namespace Pithos.ShellExtensions.Overlays +{ + [ClassInterface(ClassInterfaceType.None), ComVisible(true)] + [Guid("3D05BCB0-733B-49CD-B340-9D79C17C73CC")] + public class ModifiedIconOverlay: IconOverlayBase + { + + private static string _iconName = "Modified"; + + public ModifiedIconOverlay():base(_iconName) + { + + } + + #region Shell Extension Registration + [ComRegisterFunction()] + public static void Register(Type t) + { + RegisterOverlay(t,_iconName); + } + + [ComUnregisterFunction] + public static void Unregister(Type t) + { + UnregisterOverlay(t,_iconName); + } + + #endregion + + + } +} \ No newline at end of file diff --git a/trunk/Pithos.ShellExtensions/Overlays/NormalIconOverlay.cs b/trunk/Pithos.ShellExtensions/Overlays/NormalIconOverlay.cs new file mode 100644 index 0000000..57bdf21 --- /dev/null +++ b/trunk/Pithos.ShellExtensions/Overlays/NormalIconOverlay.cs @@ -0,0 +1,35 @@ +using System; +using System.Runtime.InteropServices; + +namespace Pithos.ShellExtensions.Overlays +{ + [ClassInterface(ClassInterfaceType.None)] + [Guid("1941D8CA-2727-491B-BC03-9E8CA4FE972B"), ComVisible(true)] + public class NormalIconOverlay: IconOverlayBase + { + + private static string _iconName = "Normal"; + + public NormalIconOverlay():base(_iconName) + { + + } + + #region Shell Extension Registration + [ComRegisterFunction()] + public static void Register(Type t) + { + RegisterOverlay(t,_iconName); + } + + [ComUnregisterFunction] + public static void Unregister(Type t) + { + UnregisterOverlay(t,_iconName); + } + + #endregion + + + } +} diff --git a/trunk/Pithos.ShellExtensions/Overlays/SynchIconOverlay.cs b/trunk/Pithos.ShellExtensions/Overlays/SynchIconOverlay.cs new file mode 100644 index 0000000..04cea45 --- /dev/null +++ b/trunk/Pithos.ShellExtensions/Overlays/SynchIconOverlay.cs @@ -0,0 +1,35 @@ +using System; +using System.Runtime.InteropServices; + +namespace Pithos.ShellExtensions.Overlays +{ + [ClassInterface(ClassInterfaceType.None), ComVisible(true)] + [Guid("20FCF62D-E553-48FA-99B8-6C0A4E1C5D55")] + public class SynchIconOverlay: IconOverlayBase + { + + private static string _iconName = "Synch"; + + public SynchIconOverlay():base(_iconName) + { + + } + + #region Shell Extension Registration + [ComRegisterFunction()] + public static void Register(Type t) + { + RegisterOverlay(t,_iconName); + } + + [ComUnregisterFunction] + public static void Unregister(Type t) + { + UnregisterOverlay(t,_iconName); + } + + #endregion + + + } +} \ No newline at end of file diff --git a/trunk/Pithos.ShellExtensions/Pithos.ShellExtensions.csproj b/trunk/Pithos.ShellExtensions/Pithos.ShellExtensions.csproj new file mode 100644 index 0000000..09e9787 --- /dev/null +++ b/trunk/Pithos.ShellExtensions/Pithos.ShellExtensions.csproj @@ -0,0 +1,120 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {240B432F-1030-4623-BCC3-FF351D6C1B63} + Library + Properties + Pithos.ShellExtensions + Pithos.ShellExtensions + v4.0 + 512 + 1 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + True + False + True + False + False + True + False + False + False + False + False + True + False + False + True + + + + + + + False + Full + Build + 0 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + true + + + pithos.snk + + + + + + + + + + + + + + + + + + + + + + + + + + + Component + + + ProjectInstaller.cs + + + + + + + + + + + + + + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4} + Pithos.Interfaces + + + + + \ No newline at end of file diff --git a/trunk/Pithos.ShellExtensions/ProjectInstaller.cs b/trunk/Pithos.ShellExtensions/ProjectInstaller.cs new file mode 100644 index 0000000..e538286 --- /dev/null +++ b/trunk/Pithos.ShellExtensions/ProjectInstaller.cs @@ -0,0 +1,60 @@ +/********************************** Module Header **********************************\ +Module Name: ProjectInstaller.cs +Project: CSShellExtContextMenuHandler +Copyright (c) Microsoft Corporation. + +The installer class defines the custom actions in the setup. We use the custom +actions to register and unregister the COM-visible classes in the current managed +assembly. + +This source is subject to the Microsoft Public License. +See http://www.microsoft.com/opensource/licenses.mspx#Ms-PL. +All other rights reserved. + +THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE. +\***********************************************************************************/ + +#region Using directives + +using System.Collections; +using System.ComponentModel; +using System.Runtime.InteropServices; + +#endregion + + +namespace Pithos.ShellExtensions +{ + [RunInstaller(true), ComVisible(false)] + public partial class ProjectInstaller : System.Configuration.Install.Installer + { + public ProjectInstaller() + { + InitializeComponent(); + } + + public override void Install(IDictionary stateSaver) + { + base.Install(stateSaver); + + // Call RegistrationServices.RegisterAssembly to register the classes in + // the current managed assembly to enable creation from COM. + RegistrationServices regService = new RegistrationServices(); + regService.RegisterAssembly( + this.GetType().Assembly, + AssemblyRegistrationFlags.SetCodeBase); + } + + public override void Uninstall(IDictionary savedState) + { + base.Uninstall(savedState); + + // Call RegistrationServices.UnregisterAssembly to unregister the classes + // in the current managed assembly. + RegistrationServices regService = new RegistrationServices(); + regService.UnregisterAssembly(this.GetType().Assembly); + } + } +} \ No newline at end of file diff --git a/trunk/Pithos.ShellExtensions/ProjectInstaller.designer.cs b/trunk/Pithos.ShellExtensions/ProjectInstaller.designer.cs new file mode 100644 index 0000000..e7e4737 --- /dev/null +++ b/trunk/Pithos.ShellExtensions/ProjectInstaller.designer.cs @@ -0,0 +1,36 @@ +namespace Pithos.ShellExtensions +{ + partial class ProjectInstaller + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + components = new System.ComponentModel.Container(); + } + + #endregion + } +} \ No newline at end of file diff --git a/trunk/Pithos.ShellExtensions/Properties/AssemblyInfo.cs b/trunk/Pithos.ShellExtensions/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e78ae0d --- /dev/null +++ b/trunk/Pithos.ShellExtensions/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Pithos.ShellExtensions")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("Pithos.ShellExtensions")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2011")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("622f73d0-45af-4bad-8316-c6fa216cb1e9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/trunk/Pithos.ShellExtensions/ShellExtLib.cs b/trunk/Pithos.ShellExtensions/ShellExtLib.cs new file mode 100644 index 0000000..41e385b --- /dev/null +++ b/trunk/Pithos.ShellExtensions/ShellExtLib.cs @@ -0,0 +1,834 @@ +using System; +using System.Diagnostics; +using System.Text; +using Microsoft.Win32; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; + + +namespace Pithos.ShellExtensions +{ + #region Shell Interfaces + [Flags] + public enum ISIOI + { + ISIOI_ICONFILE = 1, + ISIOI_ICONINDEX = 2 + } + + [ComVisible(false)] + [ComImport] + [Guid("0C6C4200-C589-11D0-999A-00C04FD655E1")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + public interface IShellIconOverlayIdentifier + { + + [PreserveSig] + int IsMemberOf( + [MarshalAs(UnmanagedType.LPWStr)] string path, + + uint attributes); + + [PreserveSig] + int GetOverlayInfo( + IntPtr iconFileBuffer, + int iconFileBufferSize, + out int iconIndex, + out uint flags); + + [PreserveSig] + int GetPriority( + out int priority); + + } + + + + [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("000214e8-0000-0000-c000-000000000046")] + internal interface IShellExtInit + { + void Initialize( + IntPtr /*LPCITEMIDLIST*/ pidlFolder, + IntPtr /*LPDATAOBJECT*/ pDataObj, + IntPtr /*HKEY*/ hKeyProgID); + } + + + [ComImport(), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("000214e4-0000-0000-c000-000000000046")] + internal interface IContextMenu + { + [PreserveSig] + int QueryContextMenu( + IntPtr /*HMENU*/ hMenu, + uint iMenu, + uint idCmdFirst, + uint idCmdLast, + uint uFlags); + + void InvokeCommand(IntPtr pici); + + void GetCommandString( + UIntPtr idCmd, + uint uFlags, + IntPtr pReserved, + StringBuilder pszName, + uint cchMax); + } + + #endregion + + + #region Shell Registration + + internal class ShellExtReg + { + private const string _approvedKey = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved"; + + public static void RegisterIconOverlayIdentifier(Guid clsid, + string friendlyName) + { + if (clsid == Guid.Empty) + { + throw new ArgumentException("clsid must not be empty"); + } + if (string.IsNullOrEmpty(friendlyName)) + { + throw new ArgumentException("friendlyName must not be null or empty"); + } + + // Create the key HKCR\\shellex\ContextMenuHandlers\{}. + string keyName = string.Format(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\{0}", + friendlyName); + using (RegistryKey key = Registry.LocalMachine.CreateSubKey(keyName)) + { + // Set the default value of the key. + if (key != null)//&& !string.IsNullOrEmpty(clsid.ToString("B"))) + { + key.SetValue(null, clsid.ToString("B")); + } + } + } + + internal static void UnregisterIconOverlayIdentifier(Guid clsid, + string friendlyName) + { + if (clsid == null) + { + throw new ArgumentException("clsid must not be null"); + } + if (string.IsNullOrEmpty(friendlyName)) + { + throw new ArgumentException("friendlyName must not be null or empty"); + } + + + // Remove the key HKCR\\shellex\ContextMenuHandlers\{}. + string keyName = string.Format(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiers\{0}", + friendlyName); + Registry.ClassesRoot.DeleteSubKeyTree(keyName, false); + } + + + /// + /// Register the context menu handler. + /// + /// The CLSID of the component. + /// + /// The file type that the context menu handler is associated with. For + /// example, '*' means all file types; '.txt' means all .txt files. The + /// parameter must not be NULL or an empty string. + /// + /// The friendly name of the component. + public static void RegisterShellExtContextMenuHandler(Guid clsid, + string fileType, string friendlyName) + { + if (clsid == Guid.Empty) + { + throw new ArgumentException("clsid must not be empty"); + } + if (string.IsNullOrEmpty(fileType)) + { + throw new ArgumentException("fileType must not be null or empty"); + } + + // If fileType starts with '.', try to read the default value of the + // HKCR\ key which contains the ProgID to which the file type + // is linked. + if (fileType.StartsWith(".")) + { + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey(fileType)) + { + if (key != null) + { + // If the key exists and its default value is not empty, use + // the ProgID as the file type. + string defaultVal = key.GetValue(null) as string; + if (!string.IsNullOrEmpty(defaultVal)) + { + fileType = defaultVal; + } + } + } + } + + // Create the key HKCR\\shellex\ContextMenuHandlers\{}. + string keyName = string.Format(@"{0}\shellex\ContextMenuHandlers\{1}", + fileType, friendlyName); + using (RegistryKey key = Registry.ClassesRoot.CreateSubKey(keyName)) + { + // Set the default value of the key. + if (key != null)//&& !string.IsNullOrEmpty(clsid.ToString("B"))) + { + key.SetValue(null, clsid.ToString("B")); + } + } + } + + /// + /// Unregister the context menu handler. + /// + /// The CLSID of the component. + /// + /// The file type that the context menu handler is associated with. For + /// example, '*' means all file types; '.txt' means all .txt files. The + /// parameter must not be NULL or an empty string. + /// + public static void UnregisterShellExtContextMenuHandler(Guid clsid, + string fileType, string friendlyName) + { + if (clsid == null) + { + throw new ArgumentException("clsid must not be null"); + } + if (string.IsNullOrEmpty(fileType)) + { + throw new ArgumentException("fileType must not be null or empty"); + } + + // If fileType starts with '.', try to read the default value of the + // HKCR\ key which contains the ProgID to which the file type + // is linked. + if (fileType.StartsWith(".")) + { + using (RegistryKey key = Registry.ClassesRoot.OpenSubKey(fileType)) + { + if (key != null) + { + // If the key exists and its default value is not empty, use + // the ProgID as the file type. + string defaultVal = key.GetValue(null) as string; + if (!string.IsNullOrEmpty(defaultVal)) + { + fileType = defaultVal; + } + } + } + } + + // Remove the key HKCR\\shellex\ContextMenuHandlers\{}. + string keyName = string.Format(@"{0}\shellex\ContextMenuHandlers\{1}", + fileType, friendlyName); + Registry.ClassesRoot.DeleteSubKeyTree(keyName, false); + } + + public static void MarkApproved(Guid guid, string friendlyName) + { + Trace.Write(String.Format("Marking approved {0} {1}", guid, friendlyName)); + using (RegistryKey key = Registry.LocalMachine.OpenSubKey(_approvedKey)) + { + // Set the default value of the key. + if (key != null)//&& !string.IsNullOrEmpty(clsid.ToString("B"))) + { + key.SetValue(guid.ToString("B"), friendlyName, RegistryValueKind.String); + } + else + { + Trace.Write("Error - failed to open key " + _approvedKey); + } + } + } + + public static void RemoveApproved(Guid guid, string csshellextcontextmenuhandlerFilecontextmenuext) + { + using (RegistryKey key = Registry.LocalMachine.OpenSubKey(_approvedKey)) + { + // Set the default value of the key. + if (key != null)//&& !string.IsNullOrEmpty(clsid.ToString("B"))) + { + key.DeleteValue(guid.ToString("B"), false); + } + else + { + Trace.Write("Error - failed to open key " + _approvedKey); + } + } + } + } + + #endregion + + + #region Enums & Structs + + #region enum HChangeNotifyEventID + /// + /// Describes the event that has occurred. + /// Typically, only one event is specified at a time. + /// If more than one event is specified, the values contained + /// in the dwItem1 and dwItem2 + /// parameters must be the same, respectively, for all specified events. + /// This parameter can be one or more of the following values. + /// + /// + /// Windows NT/2000/XP: dwItem2 contains the index + /// in the system image list that has changed. + /// dwItem1 is not used and should be . + /// Windows 95/98: dwItem1 contains the index + /// in the system image list that has changed. + /// dwItem2 is not used and should be . + /// + [Flags] + enum HChangeNotifyEventID + { + /// + /// All events have occurred. + /// + SHCNE_ALLEVENTS = 0x7FFFFFFF, + + /// + /// A file type association has changed. + /// must be specified in the uFlags parameter. + /// dwItem1 and dwItem2 are not used and must be . + /// + SHCNE_ASSOCCHANGED = 0x08000000, + + /// + /// The attributes of an item or folder have changed. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the item or folder that has changed. + /// dwItem2 is not used and should be . + /// + SHCNE_ATTRIBUTES = 0x00000800, + + /// + /// A nonfolder item has been created. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the item that was created. + /// dwItem2 is not used and should be . + /// + SHCNE_CREATE = 0x00000002, + + /// + /// A nonfolder item has been deleted. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the item that was deleted. + /// dwItem2 is not used and should be . + /// + SHCNE_DELETE = 0x00000004, + + /// + /// A drive has been added. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the root of the drive that was added. + /// dwItem2 is not used and should be . + /// + SHCNE_DRIVEADD = 0x00000100, + + /// + /// A drive has been added and the Shell should create a new window for the drive. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the root of the drive that was added. + /// dwItem2 is not used and should be . + /// + SHCNE_DRIVEADDGUI = 0x00010000, + + /// + /// A drive has been removed. or + /// must be specified in uFlags. + /// dwItem1 contains the root of the drive that was removed. + /// dwItem2 is not used and should be . + /// + SHCNE_DRIVEREMOVED = 0x00000080, + + /// + /// Not currently used. + /// + SHCNE_EXTENDED_EVENT = 0x04000000, + + /// + /// The amount of free space on a drive has changed. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the root of the drive on which the free space changed. + /// dwItem2 is not used and should be . + /// + SHCNE_FREESPACE = 0x00040000, + + /// + /// Storage media has been inserted into a drive. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the root of the drive that contains the new media. + /// dwItem2 is not used and should be . + /// + SHCNE_MEDIAINSERTED = 0x00000020, + + /// + /// Storage media has been removed from a drive. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the root of the drive from which the media was removed. + /// dwItem2 is not used and should be . + /// + SHCNE_MEDIAREMOVED = 0x00000040, + + /// + /// A folder has been created. + /// or must be specified in uFlags. + /// dwItem1 contains the folder that was created. + /// dwItem2 is not used and should be . + /// + SHCNE_MKDIR = 0x00000008, + + /// + /// A folder on the local computer is being shared via the network. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the folder that is being shared. + /// dwItem2 is not used and should be . + /// + SHCNE_NETSHARE = 0x00000200, + + /// + /// A folder on the local computer is no longer being shared via the network. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the folder that is no longer being shared. + /// dwItem2 is not used and should be . + /// + SHCNE_NETUNSHARE = 0x00000400, + + /// + /// The name of a folder has changed. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the previous pointer to an item identifier list (PIDL) or name of the folder. + /// dwItem2 contains the new PIDL or name of the folder. + /// + SHCNE_RENAMEFOLDER = 0x00020000, + + /// + /// The name of a nonfolder item has changed. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the previous PIDL or name of the item. + /// dwItem2 contains the new PIDL or name of the item. + /// + SHCNE_RENAMEITEM = 0x00000001, + + /// + /// A folder has been removed. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the folder that was removed. + /// dwItem2 is not used and should be . + /// + SHCNE_RMDIR = 0x00000010, + + /// + /// The computer has disconnected from a server. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the server from which the computer was disconnected. + /// dwItem2 is not used and should be . + /// + SHCNE_SERVERDISCONNECT = 0x00004000, + + /// + /// The contents of an existing folder have changed, + /// but the folder still exists and has not been renamed. + /// or + /// must be specified in uFlags. + /// dwItem1 contains the folder that has changed. + /// dwItem2 is not used and should be . + /// If a folder has been created, deleted, or renamed, use SHCNE_MKDIR, SHCNE_RMDIR, or + /// SHCNE_RENAMEFOLDER, respectively, instead. + /// + SHCNE_UPDATEDIR = 0x00001000, + + /// + /// An image in the system image list has changed. + /// must be specified in uFlags. + /// + SHCNE_UPDATEIMAGE = 0x00008000, + + } + #endregion // enum HChangeNotifyEventID + + #region public enum HChangeNotifyFlags + /// + /// Flags that indicate the meaning of the dwItem1 and dwItem2 parameters. + /// The uFlags parameter must be one of the following values. + /// + [Flags] + public enum HChangeNotifyFlags + { + /// + /// The dwItem1 and dwItem2 parameters are DWORD values. + /// + SHCNF_DWORD = 0x0003, + /// + /// dwItem1 and dwItem2 are the addresses of ITEMIDLIST structures that + /// represent the item(s) affected by the change. + /// Each ITEMIDLIST must be relative to the desktop folder. + /// + SHCNF_IDLIST = 0x0000, + /// + /// dwItem1 and dwItem2 are the addresses of null-terminated strings of + /// maximum length MAX_PATH that contain the full path names + /// of the items affected by the change. + /// + SHCNF_PATHA = 0x0001, + /// + /// dwItem1 and dwItem2 are the addresses of null-terminated strings of + /// maximum length MAX_PATH that contain the full path names + /// of the items affected by the change. + /// + SHCNF_PATHW = 0x0005, + /// + /// dwItem1 and dwItem2 are the addresses of null-terminated strings that + /// represent the friendly names of the printer(s) affected by the change. + /// + SHCNF_PRINTERA = 0x0002, + /// + /// dwItem1 and dwItem2 are the addresses of null-terminated strings that + /// represent the friendly names of the printer(s) affected by the change. + /// + SHCNF_PRINTERW = 0x0006, + /// + /// The function should not return until the notification + /// has been delivered to all affected components. + /// As this flag modifies other data-type flags, it cannot by used by itself. + /// + SHCNF_FLUSH = 0x1000, + /// + /// The function should begin delivering notifications to all affected components + /// but should return as soon as the notification process has begun. + /// As this flag modifies other data-type flags, it cannot by used by itself. + /// + SHCNF_FLUSHNOWAIT = 0x2000 + } + #endregion // enum HChangeNotifyFlags + + internal enum GCS : uint + { + GCS_VERBA = 0x00000000, + GCS_HELPTEXTA = 0x00000001, + GCS_VALIDATEA = 0x00000002, + GCS_VERBW = 0x00000004, + GCS_HELPTEXTW = 0x00000005, + GCS_VALIDATEW = 0x00000006, + GCS_VERBICONW = 0x00000014, + GCS_UNICODE = 0x00000004 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct CMINVOKECOMMANDINFO + { + public uint cbSize; + public CMIC fMask; + public IntPtr hwnd; + public IntPtr verb; + [MarshalAs(UnmanagedType.LPStr)] + public string parameters; + [MarshalAs(UnmanagedType.LPStr)] + public string directory; + public int nShow; + public uint dwHotKey; + public IntPtr hIcon; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct CMINVOKECOMMANDINFOEX + { + public uint cbSize; + public CMIC fMask; + public IntPtr hwnd; + public IntPtr verb; + [MarshalAs(UnmanagedType.LPStr)] + public string parameters; + [MarshalAs(UnmanagedType.LPStr)] + public string directory; + public int nShow; + public uint dwHotKey; + public IntPtr hIcon; + [MarshalAs(UnmanagedType.LPStr)] + public string title; + public IntPtr verbW; + public string parametersW; + public string directoryW; + public string titleW; + POINT ptInvoke; + } + + [Flags] + internal enum CMIC : uint + { + CMIC_MASK_ICON = 0x00000010, + CMIC_MASK_HOTKEY = 0x00000020, + CMIC_MASK_NOASYNC = 0x00000100, + CMIC_MASK_FLAG_NO_UI = 0x00000400, + CMIC_MASK_UNICODE = 0x00004000, + CMIC_MASK_NO_CONSOLE = 0x00008000, + CMIC_MASK_ASYNCOK = 0x00100000, + CMIC_MASK_NOZONECHECKS = 0x00800000, + CMIC_MASK_FLAG_LOG_USAGE = 0x04000000, + CMIC_MASK_SHIFT_DOWN = 0x10000000, + CMIC_MASK_PTINVOKE = 0x20000000, + CMIC_MASK_CONTROL_DOWN = 0x40000000 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct POINT + { + public int X; + public int Y; + } + + internal enum CLIPFORMAT : uint + { + CF_TEXT = 1, + CF_BITMAP = 2, + CF_METAFILEPICT = 3, + CF_SYLK = 4, + CF_DIF = 5, + CF_TIFF = 6, + CF_OEMTEXT = 7, + CF_DIB = 8, + CF_PALETTE = 9, + CF_PENDATA = 10, + CF_RIFF = 11, + CF_WAVE = 12, + CF_UNICODETEXT = 13, + CF_ENHMETAFILE = 14, + CF_HDROP = 15, + CF_LOCALE = 16, + CF_MAX = 17, + + CF_OWNERDISPLAY = 0x0080, + CF_DSPTEXT = 0x0081, + CF_DSPBITMAP = 0x0082, + CF_DSPMETAFILEPICT = 0x0083, + CF_DSPENHMETAFILE = 0x008E, + + CF_PRIVATEFIRST = 0x0200, + CF_PRIVATELAST = 0x02FF, + + CF_GDIOBJFIRST = 0x0300, + CF_GDIOBJLAST = 0x03FF + } + + [Flags] + internal enum CMF : uint + { + CMF_NORMAL = 0x00000000, + CMF_DEFAULTONLY = 0x00000001, + CMF_VERBSONLY = 0x00000002, + CMF_EXPLORE = 0x00000004, + CMF_NOVERBS = 0x00000008, + CMF_CANRENAME = 0x00000010, + CMF_NODEFAULT = 0x00000020, + CMF_INCLUDESTATIC = 0x00000040, + CMF_ITEMMENU = 0x00000080, + CMF_EXTENDEDVERBS = 0x00000100, + CMF_DISABLEDVERBS = 0x00000200, + CMF_ASYNCVERBSTATE = 0x00000400, + CMF_OPTIMIZEFORINVOKE = 0x00000800, + CMF_SYNCCASCADEMENU = 0x00001000, + CMF_DONOTPICKDEFAULT = 0x00002000, + CMF_RESERVED = 0xFFFF0000 + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct MENUITEMINFO + { + public uint cbSize; + public MIIM fMask; + public MFT fType; + public MFS fState; + public uint wID; + public IntPtr hSubMenu; + public IntPtr hbmpChecked; + public IntPtr hbmpUnchecked; + public UIntPtr dwItemData; + public string dwTypeData; + public uint cch; + public IntPtr hbmpItem; + } + + [Flags] + internal enum MIIM : uint + { + MIIM_STATE = 0x00000001, + MIIM_ID = 0x00000002, + MIIM_SUBMENU = 0x00000004, + MIIM_CHECKMARKS = 0x00000008, + MIIM_TYPE = 0x00000010, + MIIM_DATA = 0x00000020, + MIIM_STRING = 0x00000040, + MIIM_BITMAP = 0x00000080, + MIIM_FTYPE = 0x00000100 + } + + internal enum MFT : uint + { + MFT_STRING = 0x00000000, + MFT_BITMAP = 0x00000004, + MFT_MENUBARBREAK = 0x00000020, + MFT_MENUBREAK = 0x00000040, + MFT_OWNERDRAW = 0x00000100, + MFT_RADIOCHECK = 0x00000200, + MFT_SEPARATOR = 0x00000800, + MFT_RIGHTORDER = 0x00002000, + MFT_RIGHTJUSTIFY = 0x00004000 + } + + internal enum MFS : uint + { + MFS_ENABLED = 0x00000000, + MFS_UNCHECKED = 0x00000000, + MFS_UNHILITE = 0x00000000, + MFS_GRAYED = 0x00000003, + MFS_DISABLED = 0x00000003, + MFS_CHECKED = 0x00000008, + MFS_HILITE = 0x00000080, + MFS_DEFAULT = 0x00001000 + } + + #endregion + + + internal class NativeMethods + { + /// + /// Retrieve the names of dropped files that result from a successful drag- + /// and-drop operation. + /// + /// + /// Identifier of the structure that contains the file names of the dropped + /// files. + /// + /// + /// Index of the file to query. If the value of this parameter is 0xFFFFFFFF, + /// DragQueryFile returns a count of the files dropped. + /// + /// + /// The address of a buffer that receives the file name of a dropped file + /// when the function returns. + /// + /// + /// The size, in characters, of the pszFile buffer. + /// + /// A non-zero value indicates a successful call. + [DllImport("shell32", CharSet = CharSet.Unicode)] + public static extern uint DragQueryFile( + IntPtr hDrop, + uint iFile, + StringBuilder pszFile, + int cch); + + /// + /// Free the specified storage medium. + /// + /// + /// Reference of the storage medium that is to be freed. + /// + [DllImport("ole32.dll", CharSet = CharSet.Unicode)] + public static extern void ReleaseStgMedium(ref STGMEDIUM pmedium); + + /// + /// Insert a new menu item at the specified position in a menu. + /// + /// + /// A handle to the menu in which the new menu item is inserted. + /// + /// + /// The identifier or position of the menu item before which to insert the + /// new item. The meaning of this parameter depends on the value of + /// fByPosition. + /// + /// + /// Controls the meaning of uItem. If this parameter is false, uItem is a + /// menu item identifier. Otherwise, it is a menu item position. + /// + /// + /// A reference of a MENUITEMINFO structure that contains information about + /// the new menu item. + /// + /// + /// If the function succeeds, the return value is true. + /// + [DllImport("user32", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool InsertMenuItem( + IntPtr hMenu, + uint uItem, + [MarshalAs(UnmanagedType.Bool)]bool fByPosition, + ref MENUITEMINFO mii); + + [DllImport("shell32", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool SHGetPathFromIDList( + IntPtr pidl, + StringBuilder pszpath); + + [DllImport("shell32.dll")] + public static extern void SHChangeNotify(HChangeNotifyEventID wEventId, + HChangeNotifyFlags uFlags, + IntPtr dwItem1, + IntPtr dwItem2); + + public static int HighWord(int number) + { + return ((number & 0x80000000) == 0x80000000) ? + (number >> 16) : ((number >> 16) & 0xffff); + } + + public static int LowWord(int number) + { + return number & 0xffff; + } + } + + internal static class WinError + { + public const int S_OK = 0x0000; + public const int S_FALSE = 0x0001; + public const int E_FAIL = -2147467259; + public const int E_INVALIDARG = -2147024809; + public const int E_OUTOFMEMORY = -2147024882; + public const int STRSAFE_E_INSUFFICIENT_BUFFER = -2147024774; + + public const uint SEVERITY_SUCCESS = 0; + public const uint SEVERITY_ERROR = 1; + + /// + /// Create an HRESULT value from component pieces. + /// + /// The severity to be used + /// The facility to be used + /// The error number + /// A HRESULT constructed from the above 3 values + public static int MAKE_HRESULT(uint sev, uint fac, uint code) + { + return (int)((sev << 31) | (fac << 16) | code); + } + } +} diff --git a/trunk/Pithos.ShellExtensions/TestPithosSettings.cs b/trunk/Pithos.ShellExtensions/TestPithosSettings.cs new file mode 100644 index 0000000..43da0e9 --- /dev/null +++ b/trunk/Pithos.ShellExtensions/TestPithosSettings.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using System.Text; +using Pithos.Interfaces; + +namespace Pithos.ShellExtensions.Test +{ + [Export(typeof(IPithosSettings))] + class TestPithosSettings:IPithosSettings + { + public string PithosPath + { + get { return @"e:\pithos"; } + set { throw new NotImplementedException(); } + } + + public string IconsPath + { + get { return @"C:\Program Files\Common Files\TortoiseOverlays\icons\XPStyle\"; } + set { throw new NotImplementedException(); } + } + + public string UserName { get; set; } + public string ApiKey { get; set; } + + public void Save() + { + + } + + public void Reload() + { + + } + } +} diff --git a/trunk/Pithos.ShellExtensions/TestStatusChecker.cs b/trunk/Pithos.ShellExtensions/TestStatusChecker.cs new file mode 100644 index 0000000..d77b07f --- /dev/null +++ b/trunk/Pithos.ShellExtensions/TestStatusChecker.cs @@ -0,0 +1,42 @@ +using System; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.Diagnostics.Contracts; +using System.IO; +using Pithos.Interfaces; +using Pithos.ShellExtensions.Test; + +namespace Pithos.ShellExtensions +{ + [Export(typeof(IStatusChecker))] + public class TestStatusChecker:IStatusChecker + { + + private IPithosSettings Settings=new TestPithosSettings(); + + private readonly string[] _states = {"Normal", "Modified", "Conflict","Synch"}; + + public FileOverlayStatus GetFileOverlayStatus(string path) + { + if (String.IsNullOrWhiteSpace(path)) + throw new ArgumentNullException("Empty path"); + + var pithosPath = Settings.PithosPath; + if (path.StartsWith(pithosPath,true,null)) + { + var fileName = Path.GetFileName(path); + if (String.IsNullOrWhiteSpace(fileName)) + return FileOverlayStatus.Normal; + + var status = Char.ConvertToUtf32(fileName, 0)%4; + return (FileOverlayStatus)status; + } + return FileOverlayStatus.NA; + } + + public PithosStatus GetPithosStatus() + { + return PithosStatus.InSynch; + } + } +} diff --git a/trunk/Pithos.ShellExtensions/app.config b/trunk/Pithos.ShellExtensions/app.config new file mode 100644 index 0000000..49cc43e --- /dev/null +++ b/trunk/Pithos.ShellExtensions/app.config @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/trunk/Pithos.ShellExtensions/pithos.snk b/trunk/Pithos.ShellExtensions/pithos.snk new file mode 100644 index 0000000..2069d32 Binary files /dev/null and b/trunk/Pithos.ShellExtensions/pithos.snk differ diff --git a/trunk/Pithos.sln b/trunk/Pithos.sln new file mode 100644 index 0000000..bc6d63d --- /dev/null +++ b/trunk/Pithos.sln @@ -0,0 +1,163 @@ + +Microsoft Visual Studio Solution File, Format Version 11.00 +# Visual Studio 2010 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pithos.ShellExtensions", "Pithos.ShellExtensions\Pithos.ShellExtensions.csproj", "{240B432F-1030-4623-BCC3-FF351D6C1B63}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pithos.ShellExtensions.Test", "Pithos.ShellExtensions.Test\Pithos.ShellExtensions.Test.csproj", "{2CFE2DF1-20AE-47E2-B1BB-36B974600BE1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pithos.Client", "Pithos.Client\Pithos.Client.csproj", "{5AC90E5E-60C6-4F53-9444-6088BD7BC929}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pithos.Interfaces", "Pithos.Interfaces\Pithos.Interfaces.csproj", "{7EEFF32F-CCF8-436A-9E0B-F40434C09AF4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common Files", "Common Files", "{E5971C66-D274-4818-B1A2-3F273994027D}" + ProjectSection(SolutionItems) = preProject + pithos.snk = pithos.snk + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pithos.Client.Test", "Pithos.Client.Test\Pithos.Client.Test.csproj", "{822F885B-83E8-4A9A-B02E-0FEAE444D960}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B5DD7C4D-D396-4C55-A8D5-DCFE865AA095}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ParallelExtensionsExtras", "Libraries\ParallelExtensionsExtras\ParallelExtensionsExtras.csproj", "{C45218F8-09E7-4F57-85BC-5D8D2AC736A3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pithos.Core", "Pithos.Core\Pithos.Core.csproj", "{142AF135-DF30-4563-B0AC-B604235AE874}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pithos.Network.Test", "Pithos.Network.Test\Pithos.Network.Test.csproj", "{E027200B-C26A-4877-BFD9-1A18CF5DF2F4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pithos.Network", "Pithos.Network\Pithos.Network.csproj", "{C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pithos.Core.Test", "Pithos.Core.Test\Pithos.Core.Test.csproj", "{F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Newtonsoft.Json", "Libraries\Json40r2\Source\Src\Newtonsoft.Json\Newtonsoft.Json.csproj", "{A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {240B432F-1030-4623-BCC3-FF351D6C1B63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {240B432F-1030-4623-BCC3-FF351D6C1B63}.Debug|Any CPU.Build.0 = Debug|Any CPU + {240B432F-1030-4623-BCC3-FF351D6C1B63}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {240B432F-1030-4623-BCC3-FF351D6C1B63}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {240B432F-1030-4623-BCC3-FF351D6C1B63}.Debug|x86.ActiveCfg = Debug|Any CPU + {240B432F-1030-4623-BCC3-FF351D6C1B63}.Release|Any CPU.ActiveCfg = Release|Any CPU + {240B432F-1030-4623-BCC3-FF351D6C1B63}.Release|Any CPU.Build.0 = Release|Any CPU + {240B432F-1030-4623-BCC3-FF351D6C1B63}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {240B432F-1030-4623-BCC3-FF351D6C1B63}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {240B432F-1030-4623-BCC3-FF351D6C1B63}.Release|x86.ActiveCfg = Release|Any CPU + {2CFE2DF1-20AE-47E2-B1BB-36B974600BE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2CFE2DF1-20AE-47E2-B1BB-36B974600BE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2CFE2DF1-20AE-47E2-B1BB-36B974600BE1}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {2CFE2DF1-20AE-47E2-B1BB-36B974600BE1}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {2CFE2DF1-20AE-47E2-B1BB-36B974600BE1}.Debug|x86.ActiveCfg = Debug|Any CPU + {2CFE2DF1-20AE-47E2-B1BB-36B974600BE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2CFE2DF1-20AE-47E2-B1BB-36B974600BE1}.Release|Any CPU.Build.0 = Release|Any CPU + {2CFE2DF1-20AE-47E2-B1BB-36B974600BE1}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {2CFE2DF1-20AE-47E2-B1BB-36B974600BE1}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {2CFE2DF1-20AE-47E2-B1BB-36B974600BE1}.Release|x86.ActiveCfg = Release|Any CPU + {5AC90E5E-60C6-4F53-9444-6088BD7BC929}.Debug|Any CPU.ActiveCfg = Debug|x86 + {5AC90E5E-60C6-4F53-9444-6088BD7BC929}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {5AC90E5E-60C6-4F53-9444-6088BD7BC929}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {5AC90E5E-60C6-4F53-9444-6088BD7BC929}.Debug|x86.ActiveCfg = Debug|x86 + {5AC90E5E-60C6-4F53-9444-6088BD7BC929}.Debug|x86.Build.0 = Debug|x86 + {5AC90E5E-60C6-4F53-9444-6088BD7BC929}.Release|Any CPU.ActiveCfg = Release|x86 + {5AC90E5E-60C6-4F53-9444-6088BD7BC929}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {5AC90E5E-60C6-4F53-9444-6088BD7BC929}.Release|Mixed Platforms.Build.0 = Release|x86 + {5AC90E5E-60C6-4F53-9444-6088BD7BC929}.Release|x86.ActiveCfg = Release|x86 + {5AC90E5E-60C6-4F53-9444-6088BD7BC929}.Release|x86.Build.0 = Release|x86 + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4}.Debug|x86.ActiveCfg = Debug|Any CPU + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4}.Release|Any CPU.Build.0 = Release|Any CPU + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {7EEFF32F-CCF8-436A-9E0B-F40434C09AF4}.Release|x86.ActiveCfg = Release|Any CPU + {822F885B-83E8-4A9A-B02E-0FEAE444D960}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {822F885B-83E8-4A9A-B02E-0FEAE444D960}.Debug|Any CPU.Build.0 = Debug|Any CPU + {822F885B-83E8-4A9A-B02E-0FEAE444D960}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {822F885B-83E8-4A9A-B02E-0FEAE444D960}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {822F885B-83E8-4A9A-B02E-0FEAE444D960}.Debug|x86.ActiveCfg = Debug|Any CPU + {822F885B-83E8-4A9A-B02E-0FEAE444D960}.Release|Any CPU.ActiveCfg = Release|Any CPU + {822F885B-83E8-4A9A-B02E-0FEAE444D960}.Release|Any CPU.Build.0 = Release|Any CPU + {822F885B-83E8-4A9A-B02E-0FEAE444D960}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {822F885B-83E8-4A9A-B02E-0FEAE444D960}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {822F885B-83E8-4A9A-B02E-0FEAE444D960}.Release|x86.ActiveCfg = Release|Any CPU + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Debug|x86.ActiveCfg = Debug|Any CPU + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Release|Any CPU.Build.0 = Release|Any CPU + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {C45218F8-09E7-4F57-85BC-5D8D2AC736A3}.Release|x86.ActiveCfg = Release|Any CPU + {142AF135-DF30-4563-B0AC-B604235AE874}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {142AF135-DF30-4563-B0AC-B604235AE874}.Debug|Any CPU.Build.0 = Debug|Any CPU + {142AF135-DF30-4563-B0AC-B604235AE874}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {142AF135-DF30-4563-B0AC-B604235AE874}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {142AF135-DF30-4563-B0AC-B604235AE874}.Debug|x86.ActiveCfg = Debug|Any CPU + {142AF135-DF30-4563-B0AC-B604235AE874}.Release|Any CPU.ActiveCfg = Release|Any CPU + {142AF135-DF30-4563-B0AC-B604235AE874}.Release|Any CPU.Build.0 = Release|Any CPU + {142AF135-DF30-4563-B0AC-B604235AE874}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {142AF135-DF30-4563-B0AC-B604235AE874}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {142AF135-DF30-4563-B0AC-B604235AE874}.Release|x86.ActiveCfg = Release|Any CPU + {E027200B-C26A-4877-BFD9-1A18CF5DF2F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E027200B-C26A-4877-BFD9-1A18CF5DF2F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E027200B-C26A-4877-BFD9-1A18CF5DF2F4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {E027200B-C26A-4877-BFD9-1A18CF5DF2F4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {E027200B-C26A-4877-BFD9-1A18CF5DF2F4}.Debug|x86.ActiveCfg = Debug|Any CPU + {E027200B-C26A-4877-BFD9-1A18CF5DF2F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E027200B-C26A-4877-BFD9-1A18CF5DF2F4}.Release|Any CPU.Build.0 = Release|Any CPU + {E027200B-C26A-4877-BFD9-1A18CF5DF2F4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {E027200B-C26A-4877-BFD9-1A18CF5DF2F4}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {E027200B-C26A-4877-BFD9-1A18CF5DF2F4}.Release|x86.ActiveCfg = Release|Any CPU + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD}.Debug|x86.ActiveCfg = Debug|Any CPU + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD}.Release|Any CPU.Build.0 = Release|Any CPU + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {C8E2BC8B-C7F1-4222-855C-4B04A57FFDFD}.Release|x86.ActiveCfg = Release|Any CPU + {F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA}.Debug|x86.ActiveCfg = Debug|Any CPU + {F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA}.Release|Any CPU.Build.0 = Release|Any CPU + {F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA}.Release|x86.ActiveCfg = Release|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Debug|x86.ActiveCfg = Debug|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Release|Any CPU.Build.0 = Release|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {A9AE40FF-1A21-414A-9FE7-3BE13644CC6D}.Release|x86.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {822F885B-83E8-4A9A-B02E-0FEAE444D960} = {B5DD7C4D-D396-4C55-A8D5-DCFE865AA095} + {2CFE2DF1-20AE-47E2-B1BB-36B974600BE1} = {B5DD7C4D-D396-4C55-A8D5-DCFE865AA095} + {E027200B-C26A-4877-BFD9-1A18CF5DF2F4} = {B5DD7C4D-D396-4C55-A8D5-DCFE865AA095} + {F9AF3E97-BCB7-46B7-8014-7FC858AEE9BA} = {B5DD7C4D-D396-4C55-A8D5-DCFE865AA095} + EndGlobalSection +EndGlobal diff --git a/trunk/Pithos.sln.docstates b/trunk/Pithos.sln.docstates new file mode 100644 index 0000000..40a06d0 Binary files /dev/null and b/trunk/Pithos.sln.docstates differ diff --git a/trunk/packages/Hammock.1.2.3/Hammock-Binaries.zip b/trunk/packages/Hammock.1.2.3/Hammock-Binaries.zip new file mode 100644 index 0000000..f9f6a7f Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/Hammock-Binaries.zip differ diff --git a/trunk/packages/Hammock.1.2.3/Hammock.1.2.3.nupkg b/trunk/packages/Hammock.1.2.3/Hammock.1.2.3.nupkg new file mode 100644 index 0000000..4f476a8 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/Hammock.1.2.3.nupkg differ diff --git a/trunk/packages/Hammock.1.2.3/LICENSE b/trunk/packages/Hammock.1.2.3/LICENSE new file mode 100644 index 0000000..b618fc9 --- /dev/null +++ b/trunk/packages/Hammock.1.2.3/LICENSE @@ -0,0 +1,23 @@ +Hammock (http://github.com/danielcrenna/hammock) +-------------------------------------- +Copyright (c) 2010 Daniel Crenna and Jason Diller + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and +to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Hammock for Silverlight and Windows Phone 7 +-------------------------------------- +This target optionally links to a port of SharpZipLib http://www.icsharpcode.net/opensource/sharpziplib as an independent +module that does not derive from, and is not based on, SharpZipLib. As such it is compliant with the special +exception of SharpZipLib's GNU General Public License and may be used in commercial, closed-source applications. +As the independent module, this is compliant with Hammock's MIT X11 Licence. + +Hammock Extras +-------------------------------------- +The Extras project in source code provides an example serializer that includes JSON.NET, +licensed under the MIT X11 License http://www.opensource.org/licenses/mit-license.php. \ No newline at end of file diff --git a/trunk/packages/Hammock.1.2.3/lib/net20/Hammock.dll b/trunk/packages/Hammock.1.2.3/lib/net20/Hammock.dll new file mode 100644 index 0000000..dfb690f Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/net20/Hammock.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/net20/LinqBridge.dll b/trunk/packages/Hammock.1.2.3/lib/net20/LinqBridge.dll new file mode 100644 index 0000000..bf038c2 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/net20/LinqBridge.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/net35-client/Hammock.ClientProfile.dll b/trunk/packages/Hammock.1.2.3/lib/net35-client/Hammock.ClientProfile.dll new file mode 100644 index 0000000..3f0a57c Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/net35-client/Hammock.ClientProfile.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/net35/Hammock.dll b/trunk/packages/Hammock.1.2.3/lib/net35/Hammock.dll new file mode 100644 index 0000000..63beb35 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/net35/Hammock.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/net40-client/Hammock.ClientProfile.dll b/trunk/packages/Hammock.1.2.3/lib/net40-client/Hammock.ClientProfile.dll new file mode 100644 index 0000000..5ad8033 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/net40-client/Hammock.ClientProfile.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/net40/Hammock.dll b/trunk/packages/Hammock.1.2.3/lib/net40/Hammock.dll new file mode 100644 index 0000000..41b6a79 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/net40/Hammock.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/net40/Hammock.dll.CodeAnalysisLog.xml b/trunk/packages/Hammock.1.2.3/lib/net40/Hammock.dll.CodeAnalysisLog.xml new file mode 100644 index 0000000..a5a31e0 --- /dev/null +++ b/trunk/packages/Hammock.1.2.3/lib/net40/Hammock.dll.CodeAnalysisLog.xml @@ -0,0 +1,700 @@ + + + + + + + + + + + + + + + + 'RestBase.RestBase()' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: RestBase..ctor() RestBase.Initialize():Void RestBase.set_Cookies(WebParameterCollection):Void + 'RestBase.RestBase()' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: RestBase..ctor() RestBase.Initialize():Void RestBase.set_Headers(NameValueCollection):Void + 'RestBase.RestBase()' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: RestBase..ctor() RestBase.Initialize():Void RestBase.set_Parameters(WebParameterCollection):Void + 'RestBase.RestBase()' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: RestBase..ctor() RestBase.Initialize():Void RestBase.set_PostParameters(ICollection<HttpPostParameter>):Void + + + + + + + + + + + Field 'RestClient._streamQuery' is a member of type 'RestClient', which is serializable, but is of type 'WebQuery', which is not serializable. Add the NonSerializedAttribute to 'RestClient._streamQuery'. + + + + + + + In method 'RestClient.BeginRequestWithTask(RestRequest, RestCallback, WebQuery, string, out WebQueryAsyncResult, object)', object '<>g__initLocal3b' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal3b' before all references to it are out of scope. + In method 'RestClient.BeginRequestWithTask(RestRequest, RestCallback, WebQuery, string, out WebQueryAsyncResult, object)', call System.IDisposable.Dispose on object 'task' before all references to it are out of scope. + + + + + + + In method 'RestClient.BeginRequestWithTask<T>(RestRequest, RestCallback<T>, WebQuery, string, out WebQueryAsyncResult, object)', object '<>g__initLocal43' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal43' before all references to it are out of scope. + In method 'RestClient.BeginRequestWithTask<T>(RestRequest, RestCallback<T>, WebQuery, string, out WebQueryAsyncResult, object)', call System.IDisposable.Dispose on object 'task' before all references to it are out of scope. + + + + + + + In method 'RestClient.BuildBaseResponse(WebQueryResult)', object 'response' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'response' before all references to it are out of scope. + + + + + + + In method 'RestClient.BuildBaseResponse<T>(WebQueryResult)', object 'response' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'response' before all references to it are out of scope. + + + + + + + In method 'RestClient.BuildRateLimitingTask(RestRequest, ITaskOptions, RestCallback, WebQuery, string, object)', call System.IDisposable.Dispose on object '<>g__initLocal49' before all references to it are out of scope. + + + + + + + In method 'RestClient.CompleteWithMockWebResponse(IAsyncResult, IAsyncResult, Triplet<RestRequest, RestCallback, object>)', object '<>g__initLocale' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocale' before all references to it are out of scope. + In method 'RestClient.CompleteWithMockWebResponse(IAsyncResult, IAsyncResult, Triplet<RestRequest, RestCallback, object>)', object 'm' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'm' before all references to it are out of scope. + + + Object 'stream' can be disposed more than once in method 'RestClient.CompleteWithMockWebResponse(IAsyncResult, IAsyncResult, Triplet<RestRequest, RestCallback, object>)'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 1007 + + + + + + + In method 'RestClient.CompleteWithMockWebResponse<T>(IAsyncResult, IAsyncResult, Triplet<RestRequest, RestCallback<T>, object>)', object '<>g__initLocald' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocald' before all references to it are out of scope. + In method 'RestClient.CompleteWithMockWebResponse<T>(IAsyncResult, IAsyncResult, Triplet<RestRequest, RestCallback<T>, object>)', object 'm' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'm' before all references to it are out of scope. + + + Object 'stream' can be disposed more than once in method 'RestClient.CompleteWithMockWebResponse<T>(IAsyncResult, IAsyncResult, Triplet<RestRequest, RestCallback<T>, object>)'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 943 + + + + + + + In method 'RestClient.GetQueryFor(RestBase, Uri)', object 'new BasicAuthWebQuery(info)' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'new BasicAuthWebQuery(info)' before all references to it are out of scope. + + + + + + + + + + + 'RestRequest.RestRequest()' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: RestRequest..ctor() RestRequest.Initialize():Void RestRequest.set_ExpectHeaders(WebHeaderCollection):Void + + + + + + + + + Provide an overridable implementation of Dispose(bool) on 'RestResponseBase' or mark the type as sealed. A call to Dispose(false) should only clean up native resources. A call to Dispose(true) should clean up both managed and native resources. + + + + + + + Modify 'RestResponseBase.Dispose()' so that it calls Dispose(true), then calls GC.SuppressFinalize on the current object instance ('this' or 'Me' in Visual Basic), and then returns. + + + + + + + In method 'RestResponseBase.ReplaceContentStreamWithMemoryStream()', object 'stream' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'stream' before all references to it are out of scope. + + + + + + + + + + + + + + + 'EntityAttribute.EntityAttribute()' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: EntityAttribute..ctor() EntityAttribute.set_ContentEncoding(Encoding):Void + + + + + + + + + + + + + + + In method 'OAuthTools.GetSignature(OAuthSignatureMethod, OAuthSignatureTreatment, string, string, string)', call System.IDisposable.Dispose on object 'crypto' before all references to it are out of scope. + + + + + + + + + + + 'OAuthWebQuery.OAuthWebQuery(OAuthWebQueryInfo)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWebQuery..ctor(OAuthWebQueryInfo) OAuthWebQuery.Initialize(OAuthWebQueryInfo):Void WebQuery.set_Method(WebMethod):Void + + + + + + + + + + + 'OAuthWorkflow.OAuthWorkflow(OAuthCredentials)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWorkflow..ctor(OAuthCredentials) OAuthWorkflow.InitializeFromCredentials(OAuthCredentials):Void OAuthWorkflow.set_CallbackUrl(String):Void + 'OAuthWorkflow.OAuthWorkflow(OAuthCredentials)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWorkflow..ctor(OAuthCredentials) OAuthWorkflow.InitializeFromCredentials(OAuthCredentials):Void OAuthWorkflow.set_ClientPassword(String):Void + 'OAuthWorkflow.OAuthWorkflow(OAuthCredentials)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWorkflow..ctor(OAuthCredentials) OAuthWorkflow.InitializeFromCredentials(OAuthCredentials):Void OAuthWorkflow.set_ClientUsername(String):Void + 'OAuthWorkflow.OAuthWorkflow(OAuthCredentials)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWorkflow..ctor(OAuthCredentials) OAuthWorkflow.InitializeFromCredentials(OAuthCredentials):Void OAuthWorkflow.set_ConsumerKey(String):Void + 'OAuthWorkflow.OAuthWorkflow(OAuthCredentials)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWorkflow..ctor(OAuthCredentials) OAuthWorkflow.InitializeFromCredentials(OAuthCredentials):Void OAuthWorkflow.set_ConsumerSecret(String):Void + 'OAuthWorkflow.OAuthWorkflow(OAuthCredentials)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWorkflow..ctor(OAuthCredentials) OAuthWorkflow.InitializeFromCredentials(OAuthCredentials):Void OAuthWorkflow.set_ParameterHandling(OAuthParameterHandling):Void + 'OAuthWorkflow.OAuthWorkflow(OAuthCredentials)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWorkflow..ctor(OAuthCredentials) OAuthWorkflow.InitializeFromCredentials(OAuthCredentials):Void OAuthWorkflow.set_SessionHandle(String):Void + 'OAuthWorkflow.OAuthWorkflow(OAuthCredentials)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWorkflow..ctor(OAuthCredentials) OAuthWorkflow.InitializeFromCredentials(OAuthCredentials):Void OAuthWorkflow.set_SignatureMethod(OAuthSignatureMethod):Void + 'OAuthWorkflow.OAuthWorkflow(OAuthCredentials)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWorkflow..ctor(OAuthCredentials) OAuthWorkflow.InitializeFromCredentials(OAuthCredentials):Void OAuthWorkflow.set_SignatureTreatment(OAuthSignatureTreatment):Void + 'OAuthWorkflow.OAuthWorkflow(OAuthCredentials)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWorkflow..ctor(OAuthCredentials) OAuthWorkflow.InitializeFromCredentials(OAuthCredentials):Void OAuthWorkflow.set_Token(String):Void + 'OAuthWorkflow.OAuthWorkflow(OAuthCredentials)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWorkflow..ctor(OAuthCredentials) OAuthWorkflow.InitializeFromCredentials(OAuthCredentials):Void OAuthWorkflow.set_TokenSecret(String):Void + 'OAuthWorkflow.OAuthWorkflow(OAuthCredentials)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWorkflow..ctor(OAuthCredentials) OAuthWorkflow.InitializeFromCredentials(OAuthCredentials):Void OAuthWorkflow.set_Verifier(String):Void + 'OAuthWorkflow.OAuthWorkflow(OAuthCredentials)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: OAuthWorkflow..ctor(OAuthCredentials) OAuthWorkflow.InitializeFromCredentials(OAuthCredentials):Void OAuthWorkflow.set_Version(String):Void + + + + + + + + + + + + + + + 'RetryPolicy.RetryPolicy()' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: RetryPolicy..ctor() RetryPolicy.Initialize():Void RetryPolicy.set_RetryConditions(ICollection<IRetryCondition>):Void + + + + + + + + + + + + + + + Object 'stream' can be disposed more than once in method 'HammockDataContractSerializer.Serialize(object, Type)'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 90 + + + + + + + + + + + Object 'stream' can be disposed more than once in method 'HammockXmlSerializer.Serialize(object, Type)'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 58 + + + + + + + + + Add [Serializable] to 'InvalidJsonException' as this type implements ISerializable. + + + + + + + + + + + Provide an overridable implementation of Dispose(bool) on 'TimedTask' or mark the type as sealed. A call to Dispose(false) should only clean up native resources. A call to Dispose(true) should clean up both managed and native resources. + + + + + + + Modify 'TimedTask.Dispose()' so that it calls Dispose(true), then calls GC.SuppressFinalize on the current object instance ('this' or 'Me' in Visual Basic), and then returns. + Ensure that 'TimedTask.Dispose()' is declared as public and sealed. + + + + + + + Declare the first parameter of 'Action<TimedTask, EventArgs>' as an object named 'sender'. + Declare the second parameter of 'Action<TimedTask, EventArgs>' as an EventArgs, or an instance of a type that extends EventArgs, named 'e'. + + + + + + + + + + + Modify 'TimedTask<T>.Dispose()' so that it calls Dispose(true), then calls GC.SuppressFinalize on the current object instance ('this' or 'Me' in Visual Basic), and then returns. + Ensure that 'TimedTask<T>.Dispose()' is declared as public and sealed. + + + + + + + + + + + + + + + Modify 'WebQuery.Dispose()' so that it calls Dispose(true), then calls GC.SuppressFinalize on the current object instance ('this' or 'Me' in Visual Basic), and then returns. + + + + + + + In method 'WebQuery.ExecuteGetOrDeleteAsync(ICache, string, string, DateTime, WebRequest, object)', object '<>g__initLocal4a' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal4a' before all references to it are out of scope. + In method 'WebQuery.ExecuteGetOrDeleteAsync(ICache, string, string, DateTime, WebRequest, object)', object '<>g__initLocal4e' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal4e' before all references to it are out of scope. + + + + + + + In method 'WebQuery.ExecuteGetOrDeleteAsync(ICache, string, string, WebRequest, object)', object '<>g__initLocal46' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal46' before all references to it are out of scope. + In method 'WebQuery.ExecuteGetOrDeleteAsync(ICache, string, string, WebRequest, object)', object '<>g__initLocal49' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal49' before all references to it are out of scope. + + + + + + + In method 'WebQuery.ExecuteGetOrDeleteAsync(ICache, string, string, TimeSpan, WebRequest, object)', object '<>g__initLocal4f' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal4f' before all references to it are out of scope. + In method 'WebQuery.ExecuteGetOrDeleteAsync(ICache, string, string, TimeSpan, WebRequest, object)', object '<>g__initLocal53' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal53' before all references to it are out of scope. + + + + + + + In method 'WebQuery.ExecuteGetOrDeleteAsync(GetDeleteHeadOptions, string, object)', object '<>g__initLocal45' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal45' before all references to it are out of scope. + + + + + + + Object 'stream' can be disposed more than once in method 'WebQuery.ExecutePostOrPut(PostOrPut, string, out WebException)'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 1135 + + + + + + + In method 'WebQuery.ExecutePostOrPutAsync(PostOrPut, string, IEnumerable<HttpPostParameter>, object)', object '<>g__initLocal67' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal67' before all references to it are out of scope. + + + + + + + In method 'WebQuery.ExecutePostOrPutAsync(PostOrPut, string, object)', object '<>g__initLocal64' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal64' before all references to it are out of scope. + + + + + + + In method 'WebQuery.ExecutePostOrPutAsync(PostOrPut, string, string, ICache, DateTime, object)', object '<>g__initLocal6e' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal6e' before all references to it are out of scope. + + + + + + + In method 'WebQuery.ExecutePostOrPutAsync(PostOrPut, string, string, ICache, object)', object '<>g__initLocal6a' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal6a' before all references to it are out of scope. + + + + + + + In method 'WebQuery.ExecutePostOrPutAsync(PostOrPut, string, string, ICache, TimeSpan, object)', object '<>g__initLocal72' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal72' before all references to it are out of scope. + + + + + + + In method 'WebQuery.ExecuteStreamGetAsync(string, TimeSpan, int)', object '<>g__initLocal73' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal73' before all references to it are out of scope. + + + + + + + In method 'WebQuery.ExecuteStreamPostAsync(string, TimeSpan, int)', object '<>g__initLocal78' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal78' before all references to it are out of scope. + + + + + + + In method 'WebQuery.PostAsyncRequestCallback(IAsyncResult)', call System.IDisposable.Dispose on object '<>g__initLocal5d' before all references to it are out of scope. + + + + + + + In method 'WebQuery.PostAsyncRequestCallbackMultiPart(IAsyncResult)', call System.IDisposable.Dispose on object '<>g__initLocal62' before all references to it are out of scope. + + + + + + + In method 'WebQuery.PostAsyncResponseCallback(IAsyncResult)', call System.IDisposable.Dispose on object 'new MemoryStream()' before all references to it are out of scope. + + + + + + + Object 'stream' can be disposed more than once in method 'WebQuery.PostAsyncStreamRequestCallback(IAsyncResult)'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 642 + + + + + + + In method 'WebQuery.ProcessBuffer(string)', call System.IDisposable.Dispose on object 'new MemoryStream(messageBytes)' before all references to it are out of scope. + + + + + + + In method 'WebQuery.TimedOutCallback(object, bool)', call System.IDisposable.Dispose on object '<>g__initLocal55' before all references to it are out of scope. + + + + + + + In method 'WebQuery.WriteMultiPartImpl(bool, IEnumerable<HttpPostParameter>, string, Encoding, Stream)', call System.IDisposable.Dispose on object 'new FileStream(parameter.FilePath, FileMode.Open, FileAccess.Read)' before all references to it are out of scope. + + + Object 'new FileStream(parameter.FilePath, FileMode.Open, FileAccess.Read)' can be disposed more than once in method 'WebQuery.WriteMultiPartImpl(bool, IEnumerable<HttpPostParameter>, string, Encoding, Stream)'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 1346 + Object 'parameter.FileStream' can be disposed more than once in method 'WebQuery.WriteMultiPartImpl(bool, IEnumerable<HttpPostParameter>, string, Encoding, Stream)'. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 1346 + + + + + + + Declare the first parameter of 'WebQuery.NewStreamMessage' as an object named 'sender'. + Declare the second parameter of 'WebQuery.NewStreamMessage' as an EventArgs, or an instance of a type that extends EventArgs, named 'e'. + + + + + + + + + Provide an overridable implementation of Dispose(bool) on 'WebQueryAsyncResult' or mark the type as sealed. A call to Dispose(false) should only clean up native resources. A call to Dispose(true) should clean up both managed and native resources. + + + + + + + 'WebQueryAsyncResult' contains field 'WebQueryAsyncResult._block' that is of IDisposable type: 'AutoResetEvent'. Change the Dispose method on 'WebQueryAsyncResult' to call Dispose or Close on this field. + + + Modify 'WebQueryAsyncResult.Dispose()' so that it calls Dispose(true), then calls GC.SuppressFinalize on the current object instance ('this' or 'Me' in Visual Basic), and then returns. + + + + + + + + + + + + + + + 'MockHttpWebRequest.MockHttpWebRequest(Uri)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: MockHttpWebRequest..ctor(Uri) MockHttpWebRequest.Initialize():Void WebRequest.set_Headers(WebHeaderCollection):Void + 'MockHttpWebRequest.MockHttpWebRequest(Uri)' contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: MockHttpWebRequest..ctor(Uri) MockHttpWebRequest.Initialize():Void WebRequest.set_Headers(WebHeaderCollection):Void MockHttpWebRequest.set_ExpectHeaders(WebHeaderCollection):Void + + + + + + + In method 'MockHttpWebRequest.BeginGetResponse(AsyncCallback, object)', call System.IDisposable.Dispose on object '<>g__initLocal1' before all references to it are out of scope. + + + + + + + In method 'MockHttpWebRequest.CreateResponse()', object '<>g__initLocal0' is not disposed along all exception paths. Call System.IDisposable.Dispose on object '<>g__initLocal0' before all references to it are out of scope. + + + + + + + + + + + In method 'MockHttpWebResponse.GetResponseStream()', object 'stream' is not disposed along all exception paths. Call System.IDisposable.Dispose on object 'stream' before all references to it are out of scope. + + + + + + + + + + + + + 'WebHeaderCollection' is marked ComVisible(true) but has the following ComVisible(false) types in its object hierarchy: 'NameValueCollection', 'NameObjectCollectionBase' + + + + + + + + + + + + + In method 'HttpUtility.UrlDecode(byte[], int, int, Encoding)', call System.IDisposable.Dispose on object 'acc' before all references to it are out of scope. + + + + + + + In method 'HttpUtility.UrlDecode(string, Encoding)', call System.IDisposable.Dispose on object 'bytes' before all references to it are out of scope. + + + + + + + In method 'HttpUtility.UrlDecodeToBytes(byte[], int, int)', call System.IDisposable.Dispose on object 'result' before all references to it are out of scope. + + + + + + + In method 'HttpUtility.UrlEncodeToBytes(byte[], int, int)', call System.IDisposable.Dispose on object 'result' before all references to it are out of scope. + + + + + + + In method 'HttpUtility.UrlEncodeUnicodeToBytes(string)', call System.IDisposable.Dispose on object 'result' before all references to it are out of scope. + + + + + + + In method 'HttpUtility.UrlPathEncode(string)', call System.IDisposable.Dispose on object 'result' before all references to it are out of scope. + + + + + + + + + + + + + + + COM visible type base types should be COM visible + COM visible types should have an object hierarchy that is uniformly COM visible. + {0} is marked ComVisible(true) but has the following ComVisible(false) types in its object hierarchy: {1} + + http://msdn.microsoft.com/library/ms182202(VS.100).aspx + [none] + Error + + + + Declare event handlers correctly + By convention, .NET events have two parameters that specify the event sender and event data. Event handler signatures should follow this form: void MyEventHandler(object sender, EventArgs e). The 'sender' parameter is always of type System.Object, even if it is possible to employ a more specific type. The 'e' parameter is always of type System.EventArgs. Events that do not provide event data should use the System.EventHandler delegate type. Event handlers return void so that they can send each event to multiple target methods. Any value returned by a target would be lost after the first call. + Declare the first parameter of {0} as an object named 'sender'. + Declare the second parameter of {0} as an EventArgs, or an instance of a type that extends EventArgs, named 'e'. + + http://msdn.microsoft.com/library/ms182133(VS.100).aspx + [none] + Error + + + + Disposable fields should be disposed + If a type that implements IDisposable owns fields that also implement IDisposable, the encapsulating type's Dispose() implementation should call Dispose() on each disposable field. + {0} contains field {1} that is of IDisposable type: {2}. Change the Dispose method on {0} to call Dispose or Close on this field. + + http://msdn.microsoft.com/library/ms182328(VS.100).aspx + [none] + Warning + + + + Dispose objects before losing scope + If a disposable object is not explicitly disposed before all references to it are out of scope, the object will be disposed at some indeterminate time when the garbage collector runs the finalizer of the object. Because an exceptional event might occur that will prevent the finalizer of the object from running, the object should be explicitly disposed instead. + In method {0}, object {1} is not disposed along all exception paths. Call System.IDisposable.Dispose on object {1} before all references to it are out of scope. + In method {0}, call System.IDisposable.Dispose on object {1} before all references to it are out of scope. + RuleOwner + http://msdn.microsoft.com/library/ms182289(VS.100).aspx + + Warning + + + + Do not dispose objects multiple times + A correctly implemented Dispose method can be called multiple times without throwing an exception. However, this is not guaranteed and to avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object. + Object {0} can be disposed more than once in method {1}. To avoid generating a System.ObjectDisposedException you should not call Dispose more than one time on an object.: Lines: 1007 + RuleOwner + http://msdn.microsoft.com/library/ms182334(VS.100).aspx + + Warning + + + + Do not call overridable methods in constructors + Virtual methods defined on the class should not be called from constructors. If a derived class has overridden the method, the derived class version will be called (before the derived class constructor is called). + {0} contains a call chain that results in a call to a virtual method defined by the class. Review the following call stack for unintended consequences: {1} + + http://msdn.microsoft.com/library/ms182331(VS.100).aspx + [none] + CriticalWarning + + + + Implement IDisposable correctly + All IDisposable types should implement the Dispose pattern correctly. + Modify {0} so that it calls Dispose(true), then calls GC.SuppressFinalize on the current object instance ('this' or 'Me' in Visual Basic), and then returns. + Ensure that {0} is declared as public and sealed. + Provide an overridable implementation of Dispose(bool) on {0} or mark the type as sealed. A call to Dispose(false) should only clean up native resources. A call to Dispose(true) should clean up both managed and native resources. + + http://msdn.microsoft.com/library/ms244737(VS.100).aspx + [none] + Error + + + + Mark all non-serializable fields + All fields that cannot be serialized directly should have the NonSerializedAttribute. Types that have the SerializableAttribute should not have fields of types that do not have the SerializableAttribute unless the fields are marked with the NonSerializedAttribute. + Field {0} is a member of type {1}, which is serializable, but is of type {2}, which is not serializable. Add the NonSerializedAttribute to {0}. + + http://msdn.microsoft.com/library/ms182349(VS.100).aspx + [none] + Error + + + + Mark ISerializable types with SerializableAttribute + The System.Runtime.Serialization.ISerializable interface allows the type to customize its serialization, while the Serializable attribute enables the runtime to recognize the type as being serializable. + Add [Serializable] to {0} as this type implements ISerializable. + + http://msdn.microsoft.com/library/ms182350(VS.100).aspx + [none] + Warning + + + + + Category + Certainty + Collapse All + Check Id + Error + error(s) + Expand All + Help + Line + message(s) + [Location not stored in Pdb] + Project + Resolution + Rule + Rule File + Rule Description + Source + Status + Target + Warning + warning(s) + Code Analysis Report + + diff --git a/trunk/packages/Hammock.1.2.3/lib/net40/Hammock.dll.lastcodeanalysissucceeded b/trunk/packages/Hammock.1.2.3/lib/net40/Hammock.dll.lastcodeanalysissucceeded new file mode 100644 index 0000000..e69de29 diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/Hammock.Silverlight.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/Hammock.Silverlight.dll new file mode 100644 index 0000000..b0e2303 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/Hammock.Silverlight.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/ICSharpCode.SharpZipLib.Silverlight.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/ICSharpCode.SharpZipLib.Silverlight.dll new file mode 100644 index 0000000..538aea8 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/ICSharpCode.SharpZipLib.Silverlight.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/System.Runtime.Serialization.Json.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/System.Runtime.Serialization.Json.dll new file mode 100644 index 0000000..46b4cfb Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/System.Runtime.Serialization.Json.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/System.Runtime.Serialization.Json.xml b/trunk/packages/Hammock.1.2.3/lib/sl3/System.Runtime.Serialization.Json.xml new file mode 100644 index 0000000..8ebcdbd --- /dev/null +++ b/trunk/packages/Hammock.1.2.3/lib/sl3/System.Runtime.Serialization.Json.xml @@ -0,0 +1,65 @@ + + + + System.Runtime.Serialization.Json + + + + Produces instances of that can read data encoded with JavaScript Object Notation (JSON) from a stream or buffer and map it to an XML Infoset, and produces instances of that can map an XML Infoset to JSON and write JSON-encoded data to a stream. + + + Creates an that can map a buffer encoded with JavaScript Object Notation (JSON), of a specified size and offset, to an XML Infoset. + An that can read JSON. + The input buffer array from which to read. + Starting position from which to read in . + Number of bytes that can be read from . + The must be set to in Silverlight version 3 applications. + + is null. + + is negative or exceeds the length. + + is negative or exceeds the length minus the . + + + Creates an that can map a specified buffer encoded with JavaScript Object Notation (JSON) to an XML Infoset. + An that can process JavaScript Object Notation (JSON) data from the buffer specified. + The input buffer array from which to read. + The must be set to in Silverlight version 3 applications used to prevent Denial of Service (DoS) attacks when reading untrusted data. + + is null. + + + Creates an that can map a specified stream encoded with JavaScript Object Notation (JSON) to an XML Infoset. + An that can read JSON. + The input from which to read. + The must be set to in Silverlight version 3 applications. + + is null. + + + Creates an that writes data encoded with JSON to a stream using a UTF-8 character encoding. + An that writes data encoded with JSON to the stream from an XML Infoset. + The output for the JSON writer. + + is null. + + + Creates an that writes data encoded with JSON to a stream with a specified character encoding. + An that writes data encoded with JSON to the stream from an XML Infoset. + The output for the JSON writer. + The that specifies the character encoding used by the writer. The default encoding is UTF-8. + + or is null. + + + Creates an that writes data encoded with JSON to a stream with a specified character encoding and that specifies whether the output stream is closed by the writer when it is done. + An that writes data encoded with JSON to the stream from an XML Infoset. + The output for the JSON writer. + The that specifies the character encoding used by the writer. The default encoding is UTF-8. + If true, the output stream is closed by the writer when done; otherwise, false. The default value is true. + + or is null. + + + \ No newline at end of file diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/System.Xml.Linq.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/System.Xml.Linq.dll new file mode 100644 index 0000000..35611d8 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/System.Xml.Linq.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/System.Xml.Linq.xml b/trunk/packages/Hammock.1.2.3/lib/sl3/System.Xml.Linq.xml new file mode 100644 index 0000000..b003380 --- /dev/null +++ b/trunk/packages/Hammock.1.2.3/lib/sl3/System.Xml.Linq.xml @@ -0,0 +1,1632 @@ + + + + System.Xml.Linq + + + + Contains the LINQ to XML extension methods. + + + Returns a collection of elements that contains the ancestors of every node in the source collection. + An of that contains the ancestors of every node in the source collection. + An of that contains the source collection. + The type of the objects in , constrained to . + + is null. + + + Returns a filtered collection of elements that contains the ancestors of every node in the source collection. Only elements that have a matching are included in the collection. + An of that contains the ancestors of every node in the source collection. Only elements that have a matching are included in the collection. + An of that contains the source collection. + The to match. + The type of the objects in , constrained to . + + is null. + + + Returns a collection of elements that contains every element in the source collection, and the ancestors of every element in the source collection. + An of that contains every element in the source collection, and the ancestors of every element in the source collection. + An of that contains the source collection. + + is null. + + + Returns a filtered collection of elements that contains every element in the source collection, and the ancestors of every element in the source collection. Only elements that have a matching are included in the collection. + An of that contains every element in the source collection, and the ancestors of every element in the source collection. Only elements that have a matching are included in the collection. + An of that contains the source collection. + The to match. + + is null. + + + Returns a collection of the attributes of every element in the source collection. + An of that contains the attributes of every element in the source collection. + An of that contains the source collection. + + is null. + + + Returns a filtered collection of the attributes of every element in the source collection. Only elements that have a matching are included in the collection. + An of that contains a filtered collection of the attributes of every element in the source collection. Only elements that have a matching are included in the collection. + An of that contains the source collection. + The to match. + + is null. + + + Returns a collection of the descendant nodes of every document and element in the source collection. + An of of the descendant nodes of every document and element in the source collection. + An of that contains the source collection. + The type of the objects in , constrained to . + + is null. + + + Returns a collection of nodes that contains every element in the source collection, and the descendant nodes of every element in the source collection. + An of that contains every element in the source collection, and the descendant nodes of every element in the source collection. + An of that contains the source collection. + + is null. + + + Returns a collection of elements that contains the descendant elements of every element and document in the source collection. + An of that contains the descendant elements of every element and document in the source collection. + An of that contains the source collection. + The type of the objects in , constrained to . + + is null. + + + Returns a filtered collection of elements that contains the descendant elements of every element and document in the source collection. Only elements that have a matching are included in the collection. + An of that contains the descendant elements of every element and document in the source collection. Only elements that have a matching are included in the collection. + An of that contains the source collection. + The to match. + The type of the objects in , constrained to . + + is null. + + + Returns a collection of elements that contains every element in the source collection, and the descendent elements of every element in the source collection. + An of that contains every element in the source collection, and the descendent elements of every element in the source collection. + An of that contains the source collection. + + is null. + + + Returns a filtered collection of elements that contains every element in the source collection, and the descendents of every element in the source collection. Only elements that have a matching are included in the collection. + An of that contains every element in the source collection, and the descendents of every element in the source collection. Only elements that have a matching are included in the collection. + An of that contains the source collection. + The to match. + + is null. + + + Returns a collection of the child elements of every element and document in the source collection. + An of of the child elements of every element or document in the source collection. + An of that contains the source collection. + The type of the objects in , constrained to . + + is null. + + + Returns a filtered collection of the child elements of every element and document in the source collection. Only elements that have a matching are included in the collection. + An of of the child elements of every element and document in the source collection. Only elements that have a matching are included in the collection. + An of that contains the source collection. + The to match. + The type of the objects in , constrained to . + + is null. + + + Returns a collection of nodes that contains all nodes in the source collection, sorted in document order. + An of that contains all nodes in the source collection, sorted in document order. + An of that contains the source collection. + The type of the objects in , constrained to . + + + Returns a collection of the child nodes of every document and element in the source collection. + An of of the child nodes of every document and element in the source collection. + An of that contains the source collection. + The type of the objects in , constrained to . + + is null. + + + Removes every attribute in the source collection from its parent element. + An of that contains the source collection. + + is null. + + + Removes every node in the source collection from its parent node. + An of that contains the source collection. + The type of the objects in , constrained to . + + is null. + + + Specifies load options when parsing XML. + + + Does not preserve insignificant white space or load base URI and line information. + + + Preserves insignificant white space while parsing. + + + Requests the base URI information from the , and makes it available via the property. + + + Requests the line information from the and makes it available via properties on . + + + Specifies whether to omit duplicate namespaces when loading an with an . + + + No reader options specified. + + + Omit duplicate namespaces when loading the . + + + Specifies serialization options. + + + Formats (indent) the XML while serializing. + + + Preserves all insignificant white space while serializing. + + + Removes duplicate namespace declarations. For the duplicate namespace declarations to be removed, both the prefix and the namespace have to match. + + + Represents an XML attribute. + + + Initializes a new instance of the class from another object. + An object to copy from. + The parameter is null. + + + Initializes a new instance of the class from the specified name and value. + The of the attribute. + An containing the value of the attribute. + The or parameter is null. + + + Gets an empty collection of attributes. + An of containing an empty collection. + + + Determines if this attribute is a namespace declaration. + true if this attribute is a namespace declaration; otherwise false. + + + Gets the expanded name of this attribute. + An containing the name of this attribute. + + + Gets the next attribute of the parent element. + An containing the next attribute of the parent element. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The attribute does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The attribute does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to an . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The attribute does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The attribute does not contain a valid value. + + + Cast the value of this to an . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Gets the previous attribute of the parent element. + An containing the previous attribute of the parent element. + + + Removes this attribute from its parent element. + The parent element is null. + + + Sets the value of this attribute. + The value to assign to this attribute. + The parameter is null. + The is an . + + + Converts the current object to a string representation. + A containing the XML text representation of an attribute and its value. + + + Gets or sets the value of this attribute. + A containing the value of this attribute. + When setting, the is null. + + + Represents a text node that contains CDATA. + + + Initializes a new instance of the class. + A string that contains the value of the node. + + + Initializes a new instance of the class. + The node to copy from. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Writes this CDATA object to an . + An into which this method will write. + + is null. + + + Represents an XML comment. + + + Initializes a new instance of the class with the specified string content. + A string that contains the contents of the new object. + The parameter is null. + + + Initializes a new instance of the class from an existing comment node. + The node to copy from. + The parameter is null. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Gets or sets the string value of this comment. + A that contains the string value of this comment. + The is null. + + + Write this comment to an . + An into which this method will write. + + is null. + + + Represents a node that can contain other nodes. + + + Adds the specified content as children of this . + A content object containing simple content or a collection of content objects to be added. + + + Adds the specified content as children of this . + A parameter list of content objects. + + + Adds the specified content as the first children of this document or element. + A content object containing simple content or a collection of content objects to be added. + + + Adds the specified content as the first children of this document or element. + A parameter list of content objects. + The parent is null. + + + Creates an that can be used to add nodes to the . + An that is ready to have content written to it. + + + Returns a collection of the descendant nodes for this document or element, in document order. + An of containing the descendant nodes of the , in document order. + + + Returns a collection of the descendant elements for this document or element, in document order. + An of containing the descendant elements of the . + + + Returns a filtered collection of the descendant elements for this document or element, in document order. Only elements that have a matching are included in the collection. + An of containing the descendant elements of the that match the specified . + The to match. + + + Gets the first (in document order) child element with the specified . + A that matches the specified , or null. + The to match. + + + Returns a collection of the child elements of this element or document, in document order. + An of containing the child elements of this , in document order. + + + Returns a filtered collection of the child elements of this element or document, in document order. Only elements that have a matching are included in the collection. + An of containing the children of the that have a matching , in document order. + The to match. + + + Get the first child node of this node. + An containing the first child node of the . + + + Get the last child node of this node. + An containing the last child node of the . + + + Returns a collection of the child nodes of this element or document, in document order. + An of containing the contents of this , in document order. + + + Removes the child nodes from this document or element. + + + Replaces the children nodes of this document or element with the specified content. + A content object containing simple content or a collection of content objects that replace the children nodes. + + + Replaces the children nodes of this document or element with the specified content. + A parameter list of content objects. + + + Represents an XML declaration. + + + Initializes a new instance of the class with the specified version, encoding, and standalone status. + The version of the XML, usually "1.0". + The encoding for the XML document. + A string containing "yes" or "no" that specifies whether the XML is standalone or requires external entities to be resolved. + + + Initializes a new instance of the class from another object. + The used to initialize this object. + + is null. + + + Gets or sets the encoding for this document. + A containing the code page name for this document. + + + Gets or sets the standalone property for this document. + A containing the standalone property for this document. + + + Provides the declaration as a formatted string. + A that contains the formatted XML string. + + + Gets or sets the version property for this document. + A containing the version property for this document. + + + Represents an XML document. + + + Initializes a new instance of the class. + + + Initializes a new instance of the class with the specified content. + A parameter list of content objects to add to this document. + + + Initializes a new instance of the class with the specified and content. + An for the document. + The content of the document. + + + Initializes a new instance of the class from an existing object. + The object that will be copied. + + + Gets or sets the XML declaration for this document. + An that contains the XML declaration for this document. + + + Gets the Document Type Definition (DTD) for this document. + A that contains the DTD for this document. + + + Creates a new instance using the specified stream. + An object used to read the data contained in the stream. + The stream containing the XML data. + + + Creates a new instance using the specified stream, optionally preserving white space, setting the base URI, and retaining line information. + An object used to read the data contained in the stream. + The stream containing the XML data. + A that specifies whether to load base URI and line information. + + + Creates a new from a . + An that contains the contents of the specified . + A that contains the content for the . + + + Creates a new from a , optionally preserving white space, setting the base URI, and retaining line information. + An that contains the XML that was read from the specified . + A that contains the content for the . + A that specifies white space behavior, and whether to load base URI and line information. + + + Creates a new from a file located in the application's XAP package. + An that contains the contents of the specified file. + A URI string that references the file to be loaded into a new . This file is located in the application's XAP package. If you want to download a file from some other location, follow the steps described in How to: Load an XML File from an Arbitrary URI Location with LINQ to XML. + + + Creates a new from a file located in the application's XAP package, optionally preserving white space, setting the base URI, and retaining line information. + An that contains the contents of the specified file. + A URI string that references the file to be loaded into a new . This file is located in the application's XAP package. If you want to download a file from some other location, follow the steps described in How to: Load an XML File from an Arbitrary URI Location with LINQ to XML. + A that specifies how white space is handled and whether to load base URI and line information. + + + Creates a new from an . + An that contains the contents of the specified . + A that contains the content for the . + + + Creates a new from an , optionally setting the base URI, and retaining line information. + An that contains the XML that was read from the specified . + A that will be read for the content of the . + A that specifies whether to load base URI and line information. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Creates a new from a string. + An populated from the string that contains XML. + A string that contains XML. + + + Creates a new from a string, optionally preserving white space, setting the base URI, and retaining line information. + An populated from the string that contains XML. + A string that contains XML. + A that specifies white space behavior, and whether to load base URI and line information. + + + Gets the root element of the XML Tree for this document. + The root of the XML tree. + + + Outputs this to the specified . + The stream to output this to. + + + Outputs this to the specified , optionally specifying formatting behavior. + The stream to output this to. + A that specifies formatting behavior. + + + Serialize this to a . + A that the will be written to. + + + Serialize this to a , optionally disabling formatting. + The to output the XML to. + A that specifies formatting behavior. + + + Serialize this to an . + A that the will be written to. + + + Write this document to an . + An into which this method will write. + + + Represents an XML Document Type Definition (DTD). + + + Initializes an instance of the class. + A that contains the qualified name of the DTD, which is the same as the qualified name of the root element of the XML document. + A that contains the public identifier of an external public DTD. + A that contains the system identifier of an external private DTD. + A that contains the internal subset for an internal DTD. + + + Initializes an instance of the class from another object. + An object to copy from. + + + Gets or sets the internal subset for this Document Type Definition (DTD). + A that contains the internal subset for this Document Type Definition (DTD). + + + Gets or sets the name for this Document Type Definition (DTD). + A that contains the name for this Document Type Definition (DTD). + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Gets or sets the public identifier for this Document Type Definition (DTD). + A that contains the public identifier for this Document Type Definition (DTD). + + + Gets or sets the system identifier for this Document Type Definition (DTD). + A that contains the system identifier for this Document Type Definition (DTD). + + + Write this to an . + An into which this method will write. + + + Represents an XML element. + + + Initializes a new instance of the class from another object. + An object to copy from. + + + Initializes a new instance of the class with the specified name. + An that contains the name of the element. + + + Initializes a new instance of the class with the specified name and content. + An that contains the element name. + The contents of the element. + + + Initializes a new instance of the class with the specified name and content. + An that contains the element name. + The initial content of the element. + + + Initializes a new instance of the class from an object. + An that contains unevaluated queries that will be iterated for the contents of this . + + + Returns a collection of elements that contain this element, and the ancestors of this element. + An of of elements that contain this element, and the ancestors of this element. + + + Returns a filtered collection of elements that contain this element, and the ancestors of this element. Only elements that have a matching are included in the collection. + An of that contain this element, and the ancestors of this element. Only elements that have a matching are included in the collection. + The to match. + + + Returns the of this that has the specified . + An that has the specified ; null if there is no attribute with the specified name. + The of the to get. + + + Returns a collection of attributes of this element. + An of of attributes of this element. + + + Returns a filtered collection of attributes of this element. Only elements that have a matching are included in the collection. + An of that contains the attributes of this element. Only elements that have a matching are included in the collection. + The to match. + + + Returns a collection of nodes that contain this element, and all descendant nodes of this element, in document order. + An of that contain this element, and all descendant nodes of this element, in document order. + + + Returns a collection of elements that contain this element, and all descendant elements of this element, in document order. + An of of elements that contain this element, and all descendant elements of this element, in document order. + + + Returns a filtered collection of elements that contain this element, and all descendant elements of this element, in document order. Only elements that have a matching are included in the collection. + An of that contain this element, and all descendant elements of this element, in document order. Only elements that have a matching are included in the collection. + The to match. + + + Gets an empty collection of elements. + An of that contains an empty collection. + + + Gets the first attribute of this element. + An that contains the first attribute of this element. + + + Gets the default of this . + An that contains the default namespace of this . + + + Gets the namespace associated with a particular prefix for this . + An for the namespace associated with the prefix for this . + A string that contains the namespace prefix to look up. + + + Gets the prefix associated with a namespace for this . + A that contains the namespace prefix. + An to look up. + + + Gets a value indicating whether this element as at least one attribute. + true if this element has at least one attribute; otherwise false. + + + Gets a value indicating whether this element has at least one child element. + true if this element has at least one child element; otherwise false. + + + Gets a value indicating whether this element contains no content. + true if this element contains no content; otherwise false. + + + Gets the last attribute of this element. + An that contains the last attribute of this element. + + + Creates a new instance using the specified stream. + An object used to read the data contained in the stream. + The stream containing the XML data. + + + Creates a new instance using the specified stream, optionally preserving white space, setting the base URI, and retaining line information. + An object used to read the data contained in the stream. + The stream containing the XML data. + A that specifies whether to load base URI and line information. + + + Loads an from a . + An that contains the XML that was read from the specified . + A that will be read for the content. + + + Loads an from a , optionally preserving white space and retaining line information. + An that contains the XML that was read from the specified . + A that will be read for the content. + A that specifies white space behavior, and whether to load base URI and line information. + + + Loads an from a file located in the applications' XAP package. + An that contains the contents of the specified file. + A URI string that references the file to be loaded into a new . This file is located in the application's XAP package. If you want to download a file from some other location, follow the steps described in How to: Load an XML File from an Arbitrary URI Location with LINQ to XML. + + + Loads an from a file located in the application's XAP package, optionally preserving white space, setting the base URI, and retaining line information. + An that contains the contents of the specified file. + A URI string that references the file to be loaded into a new . This file is located in the application's XAP package. If you want to download a file from some other location, follow the steps described in How to: Load an XML File from an Arbitrary URI Location with LINQ to XML. + A that specifies white space behavior, and whether to load base URI and line information. + + + Loads an from an . + An that contains the XML that was read from the specified . + A that will be read for the content of the . + + + Loads an from an , optionally preserving white space, setting the base URI, and retaining line information. + An that contains the XML that was read from the specified . + A that will be read for the content of the . + A that specifies white space behavior, and whether to load base URI and line information. + + + Gets the name of this element. + An that contains the name of this element. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to an of . + The element does not contain a valid value. + + + Cast the value of this to an . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to an . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Load an from a string that contains XML. + An populated from the string that contains XML. + A that contains XML. + + + Load an from a string that contains XML, optionally preserving white space and retaining line information. + An populated from the string that contains XML. + A that contains XML. + A that specifies white space behavior, and whether to load base URI and line information. + + + Removes nodes and attributes from this . + + + Removes the attributes of this . + + + Replaces the child nodes and the attributes of this element with the specified content. + The content that will replace the child nodes and attributes of this element. + + + Replaces the child nodes and the attributes of this element with the specified content. + A parameter list of content objects. + + + Replaces the attributes of this element with the specified content. + The content that will replace the attributes of this element. + + + Replaces the attributes of this element with the specified content. + A parameter list of content objects. + + + Outputs this to the specified . + The stream to output this to. + + + Outputs this to the specified , optionally specifying formatting behavior. + The stream to output this to. + A that specifies formatting behavior. + + + Serialize this element to a . + A that the will be written to. + + + Serialize this element to a , optionally disabling formatting. + The to output the XML to. + A that specifies formatting behavior. + + + Serialize this element to an . + A that the will be written to. + + + Sets the value of an attribute, adds an attribute, or removes an attribute. + An that contains the name of the attribute to change. + The value to assign to the attribute. The attribute is removed if the value is null. Otherwise, the value is converted to its string representation and assigned to the property of the attribute. + The is an instance of . + + + Sets the value of a child element, adds a child element, or removes a child element. + An that contains the name of the child element to change. + The value to assign to the child element. The child element is removed if the value is null. Otherwise, the value is converted to its string representation and assigned to the property of the child element. + The is an instance of . + + + Sets the value of this element. + The value to assign to this element. The value is converted to its string representation and assigned to the property. + The is null. + The is an . + + + Gets an XML schema definition that describes the XML representation of this object. + An that describes the XML representation of the object that is produced by the method and consumed by the method. + + + Generates an object from its XML representation. + The from which the object is deserialized. + + + Converts an object into its XML representation. + The to which this object is serialized. + + + Gets the concatenated text contents of this element. + A that contains all of the text content of this element. If there are multiple text nodes, they will be concatenated. + + + Write this element to an . + An into which this method will write. + + + Represents a name of an XML element or attribute. + + + Determines whether the specified is equal to this . + true if the specified is equal to the current ; otherwise false. + The to compare to the current . + + + Gets an object from an expanded name. + An object constructed from the expanded name. + A that contains an expanded XML name in the format {namespace}localname. + + + Gets an object from a local name and a namespace. + An object created from the specified local name and namespace. + A local (unqualified) name. + An XML namespace. + + + Gets a hash code for this . + An that contains the hash code for the . + + + Gets the local (unqualified) part of the name. + A that contains the local (unqualified) part of the name. + + + Gets the namespace part of the fully qualified name. + An that contains the namespace part of the name. + + + Returns the URI of the for this . + The URI of the for this . + + + Returns a value indicating whether two instances of are equal. + true if and are equal; otherwise false. + The first to compare. + The second to compare. + + + Converts a string formatted as an expanded XML name (that is,{namespace}localname) to an object. + An object constructed from the expanded name. + A string that contains an expanded XML name in the format {namespace}localname. + + + Returns a value indicating whether two instances of are not equal. + true if and are not equal; otherwise false. + The first to compare. + The second to compare. + + + Indicates whether the current is equal to the specified . + true if this is equal to the specified , otherwise false. + The to compare with this . + + + Returns the expanded XML name in the format {namespace}localname. + A that contains the expanded XML name in the format {namespace}localname. + + + Represents an XML namespace. This class cannot be inherited. + + + Determines whether the specified is equal to the current . + A that indicates whether the specified is equal to the current . + The to compare to the current . + + + Gets an for the specified Uniform Resource Identifier (URI). + An created from the specified URI. + A that contains a namespace URI. + + + Gets a hash code for this . + An that contains the hash code for the . + + + Returns an object created from this and the specified local name. + An created from this and the specified local name. + A that contains a local name. + + + Gets the Uniform Resource Identifier (URI) of this namespace. + A that contains the URI of the namespace. + + + Gets the object that corresponds to no namespace. + The that corresponds to no namespace. + + + Combines an object with a local name to create an . + The new constructed from the namespace and local name. + An that contains the namespace. + A that contains the local name. + + + Returns a value indicating whether two instances of are equal. + A that indicates whether and are equal. + The first to compare. + The second to compare. + + + Converts a string containing a Uniform Resource Identifier (URI) to an . + An constructed from the URI string. + A that contains the namespace URI. + + + Returns a value indicating whether two instances of are not equal. + A that indicates whether and are not equal. + The first to compare. + The second to compare. + + + Returns the URI of this . + The URI of this . + + + Gets the object that corresponds to the XML URI (http://www.w3.org/XML/1998/namespace). + The that corresponds to the XML URI (http://www.w3.org/XML/1998/namespace). + + + Gets the object that corresponds to the xmlns URI (http://www.w3.org/2000/xmlns/). + The that corresponds to the xmlns URI (http://www.w3.org/2000/xmlns/). + + + Represents the abstract concept of a node (one of: element, comment, document type, processing instruction, or text node) in the XML tree. + + + Adds the specified content immediately after this node. + A content object that contains simple content or a collection of content objects to be added after this node. + The parent is null. + + + Adds the specified content immediately after this node. + A parameter list of content objects. + The parent is null. + + + Adds the specified content immediately before this node. + A content object that contains simple content or a collection of content objects to be added before this node. + The parent is null. + + + Adds the specified content immediately before this node. + A parameter list of content objects. + The parent is null. + + + Returns a collection of the ancestor elements of this node. + An of of the ancestor elements of this node. + + + Returns a filtered collection of the ancestor elements of this node. Only elements that have a matching are included in the collection. + An of of the ancestor elements of this node. Only elements that have a matching are included in the collection.The nodes in the returned collection are in reverse document order.This method uses deferred execution. + The to match. + + + Compares two nodes to determine their relative XML document order. + An int containing 0 if the nodes are equal; -1 if is before ; 1 if is after . + First to compare. + Second to compare. + The two nodes do not share a common ancestor. + + + Creates an for this node. + An that can be used to read this node and its descendants. + + + + Compares the values of two nodes, including the values of all descendant nodes. + true if the nodes are equal; otherwise false. + The first to compare. + The second to compare. + + + Gets a comparer that can compare the relative position of two nodes. + A that can compare the relative position of two nodes. + + + Returns a collection of the sibling elements after this node, in document order. + An of of the sibling elements after this node, in document order. + + + Returns a filtered collection of the sibling elements after this node, in document order. Only elements that have a matching are included in the collection. + An of of the sibling elements after this node, in document order. Only elements that have a matching are included in the collection. + The to match. + + + Returns a collection of the sibling elements before this node, in document order. + An of of the sibling elements before this node, in document order. + + + Returns a filtered collection of the sibling elements before this node, in document order. Only elements that have a matching are included in the collection. + An of of the sibling elements before this node, in document order. Only elements that have a matching are included in the collection. + The to match. + + + Gets a comparer that can compare two nodes for value equality. + A that can compare two nodes for value equality. + + + Determines if the current node appears after a specified node in terms of document order. + true if this node appears after the specified node; otherwise false. + The to compare for document order. + + + Determines if the current node appears before a specified node in terms of document order. + true if this node appears before the specified node; otherwise false. + The to compare for document order. + + + Gets the next sibling node of this node. + The that contains the next sibling node. + + + Returns a collection of the sibling nodes after this node, in document order. + An of of the sibling nodes after this node, in document order. + + + Returns a collection of the sibling nodes before this node, in document order. + An of of the sibling nodes before this node, in document order. + + + Gets the previous sibling node of this node. + The that contains the previous sibling node. + + + Creates an from an . + An that contains the node and its descendant nodes that were read from the reader. The runtime type of the node is determined by the node type () of the first node encountered in the reader. + An positioned at the node to read into this . + The is not positioned on a recognized node type. + The underlying throws an exception. + + + Removes this node from its parent. + The parent is null. + + + Replaces this node with the specified content. + Content that replaces this node. + + + Replaces this node with the specified content. + A parameter list of the new content. + + + Returns the indented XML for this node. + A containing the indented XML. + + + Returns the XML for this node, optionally disabling formatting. + A containing the XML. + A that specifies formatting behavior. + + + Writes this node to an . + An into which this method will write. + + + Contains functionality to compare nodes for their document order. This class cannot be inherited. + + + Initializes a new instance of the class. + + + Compares two nodes to determine their relative document order. + An that contains 0 if the nodes are equal; -1 if is before ; 1 if is after . + The first to compare. + The second to compare. + The two nodes do not share a common ancestor. + + + Compares nodes to determine whether they are equal. This class cannot be inherited. + + + Initializes a new instance of the class. + + + Compares the values of two nodes. + A indicating if the nodes are equal. + The first to compare. + The second to compare. + + + Returns a hash code based on an . + A that contains a value-based hash code for the node. + The to hash. + + + Represents a node or an attribute in an XML tree. + + + Adds an object to the annotation list of this . + An that contains the annotation to add. + + + Get the first annotation object of the specified type from this . + The first annotation object that matches the specified type, or null if no annotation is of the specified type. + The type of the annotation to retrieve. + + + Gets the first annotation object of the specified type from this . + The that contains the first annotation object that matches the specified type, or null if no annotation is of the specified type. + The of the annotation to retrieve. + + + Gets a collection of annotations of the specified type for this . + An that contains the annotations for this . + The type of the annotations to retrieve. + + + Gets a collection of annotations of the specified type for this . + An of that contains the annotations that match the specified type for this . + The of the annotations to retrieve. + + + Gets the base URI for this . + A that contains the base URI for this . + + + Raised when this or any of its descendants have changed. + + + Raised when this or any of its descendants are about to change. + + + Gets the for this . + The for this . + + + Gets the node type for this . + The node type for this . + + + Gets the parent of this . + The parent of this . + + + Removes the annotations of the specified type from this . + The type of annotations to remove. + + + Removes the annotations of the specified type from this . + The of annotations to remove. + + + Gets a value indicating whether or not this has line information. + true if the has line information, otherwise false. + + + Gets the line number that the underlying reported for this . + An that contains the line number reported by the for this . + + + Gets the line position that the underlying reported for this . + An that contains the line position reported by the for this . + + + Specifies the event type when an event is raised for an . + + + An has been or will be added to an . + + + An has been or will be removed from an . + + + An has been or will be renamed. + + + The value of an has been or will be changed. In addition, a change in the serialization of an empty element (either from an empty tag to start/end tag pair or vice versa) raises this event. + + + Provides data for the and events. + + + Initializes a new instance of the class. + An that contains the event arguments for LINQ to XML events. + + + Event argument for an change event. + + + Event argument for a change event. + + + Gets the type of change. + An that contains the type of change. + + + Event argument for a change event. + + + Event argument for a change event. + + + Represents an XML processing instruction. + + + Initializes a new instance of the class. + A containing the target application for this . + The string data for this . + The or parameter is null. + The does not follow the constraints of an XML name. + + + Initializes a new instance of the class. + The node to copy from. + + + Gets or sets the string value of this processing instruction. + A that contains the string value of this processing instruction. + The string is null. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Gets or sets a string containing the target application for this processing instruction. + A containing the target application for this processing instruction. + The string is null. + The does not follow the constraints of an XML name. + + + Writes this processing instruction to an . + The to write this processing instruction to. + + + Represents elements in an XML tree that supports deferred streaming output. + + + Initializes a new instance of the class from the specified . + An that contains the name of the element. + + + Initializes a new instance of the class with the specified name and content. + An that contains the element name. + The contents of the element. + + + Initializes a new instance of the class with the specified name and content. + An that contains the element name. + The contents of the element. + + + Adds the specified content as children to this . + Content to be added to the streaming element. + + + Adds the specified content as children to this . + Content to be added to the streaming element. + + + Gets or sets the name of this streaming element. + An that contains the name of this streaming element. + + + Outputs this to the specified . + The stream to output this to. + + + Outputs this to the specified , optionally specifying formatting behavior. + The stream to output this to. + A that specifies formatting behavior. + + + Serialize this streaming element to a . + A that the will be written to. + + + Serialize this streaming element to a , optionally disabling formatting. + The to output the XML to. + A that specifies formatting behavior. + + + Serialize this streaming element to an . + A that the will be written to. + + + Returns the formatted (indented) XML for this streaming element. + A containing the indented XML. + + + Returns the XML for this streaming element, optionally disabling formatting. + A containing the XML. + A that specifies formatting behavior. + + + Writes this streaming element to an . + An into which this method will write. + + + Represents a text node. + + + Initializes a new instance of the class. + The that contains the value of the node. + + + Initializes a new instance of the class from another object. + The node to copy from. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Gets or sets the value of this node. + A that contains the value of this node. + + + Writes this node to an . + An into which this method will write. + + + \ No newline at end of file diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/de/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/de/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..0dbc0ab Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/de/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/de/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/de/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..a88de0e Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/de/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/es/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/es/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..d68a035 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/es/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/es/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/es/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..2bf0bc9 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/es/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/fr/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/fr/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..4710166 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/fr/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/fr/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/fr/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..5990df6 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/fr/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/it/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/it/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..f854209 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/it/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/it/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/it/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..e3610cf Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/it/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/ja/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/ja/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..eaa69c0 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/ja/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/ja/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/ja/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..2b03196 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/ja/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/ko/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/ko/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..1b6377e Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/ko/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/ko/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/ko/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..364886b Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/ko/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/zh-Hans/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/zh-Hans/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..e9cfcaa Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/zh-Hans/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/zh-Hans/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/zh-Hans/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..4c73ad6 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/zh-Hans/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/zh-Hant/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/zh-Hant/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..8d2eb89 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/zh-Hant/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl3/zh-Hant/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl3/zh-Hant/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..8c91e9d Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl3/zh-Hant/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4-wp/Hammock.WindowsPhone.dll b/trunk/packages/Hammock.1.2.3/lib/sl4-wp/Hammock.WindowsPhone.dll new file mode 100644 index 0000000..a5df487 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4-wp/Hammock.WindowsPhone.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4-wp/ICSharpCode.SharpZipLib.WindowsPhone.dll b/trunk/packages/Hammock.1.2.3/lib/sl4-wp/ICSharpCode.SharpZipLib.WindowsPhone.dll new file mode 100644 index 0000000..98b6900 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4-wp/ICSharpCode.SharpZipLib.WindowsPhone.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/Hammock.Silverlight.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/Hammock.Silverlight.dll new file mode 100644 index 0000000..9a56e56 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/Hammock.Silverlight.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/ICSharpCode.SharpZipLib.Silverlight.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/ICSharpCode.SharpZipLib.Silverlight.dll new file mode 100644 index 0000000..9bbd479 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/ICSharpCode.SharpZipLib.Silverlight.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/Microsoft.CSharp.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/Microsoft.CSharp.dll new file mode 100644 index 0000000..8cb4b2f Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/Microsoft.CSharp.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/Microsoft.CSharp.xml b/trunk/packages/Hammock.1.2.3/lib/sl4/Microsoft.CSharp.xml new file mode 100644 index 0000000..8f9dc37 --- /dev/null +++ b/trunk/packages/Hammock.1.2.3/lib/sl4/Microsoft.CSharp.xml @@ -0,0 +1,190 @@ + + + + Microsoft.CSharp + + + + Contains factory methods to create dynamic call site binders for CSharp. + + + Initializes a new CSharp binary operation binder. + Returns a new CSharp binary operation binder. + The flags with which to initialize the binder. + The binary operation kind. + The object that indicates where this operation is used. + The sequence of instances for the arguments to this operation. + + + Initializes a new CSharp convert binder. + Returns a new CSharp convert binder. + The flags with which to initialize the binder. + The type to convert to. + The object that indicates where this operation is used. + + + Initializes a new CSharp get index binder. + Returns a new CSharp get index binder. + The flags with which to initialize the binder. + The that indicates where this operation is used. + The sequence of instances for the arguments to this operation. + + + Initializes a new CSharp get member binder. + Returns a new CSharp get member binder. + The flags with which to initialize the binder. + The name of the member to get. + The that indicates where this operation is used. + The sequence of instances for the arguments to this operation. + + + Initializes a new CSharp invoke binder. + Returns a new CSharp invoke binder. + The flags with which to initialize the binder. + The that indicates where this operation is used. + The sequence of instances for the arguments to this operation. + + + Initializes a new CSharp invoke constructor binder. + Returns a new CSharp invoke constructor binder. + The flags with which to initialize the binder. + The that indicates where this operation is used. + The sequence of instances for the arguments to this operation. + + + Initializes a new CSharp invoke member binder. + Returns a new CSharp invoke member binder. + The flags with which to initialize the binder. + The name of the member to invoke. + The list of type arguments specified for this invoke. + The that indicates where this operation is used. + The sequence of instances for the arguments to this operation. + + + Initializes a new CSharp is event binder. + Returns a new CSharp is event binder. + The flags with which to initialize the binder. + The name of the event to look for. + The that indicates where this operation is used. + + + Initializes a new CSharp set index binder. + Returns a new CSharp set index binder. + The flags with which to initialize the binder. + The that indicates where this operation is used. + The sequence of instances for the arguments to this operation. + + + Initializes a new CSharp set member binder. + Returns a new CSharp set member binder. + The flags with which to initialize the binder. + The name of the member to set. + The that indicates where this operation is used. + The sequence of instances for the arguments to this operation. + + + Initializes a new CSharp unary operation binder. + Returns a new CSharp unary operation binder. + The flags with which to initialize the binder. + The unary operation kind. + The object that indicates where this operation is used. + The sequence of instances for the arguments to this operation. + + + Represents information about C# dynamic operations that are specific to particular arguments at a call site. Instances of this class are generated by the C# compiler. + + + Initializes a new instance of the class. + A new instance of the class. + The flags for the argument. + The name of the argument, if named; otherwise null. + + + Represents information about C# dynamic operations that are specific to particular arguments at a call site. Instances of this class are generated by the C# compiler. + + + No additional information to represent. + + + The argument's compile-time type should be considered during binding. + + + The argument is a constant. + + + The argument is a named argument. + + + The argument is passed to a ref parameter. + + + The argument is passed to an out parameter. + + + The argument is a indicating an actual type name used in source. Used only for target objects in static calls. + + + Represents information about C# dynamic operations that are not specific to particular arguments at a call site. Instances of this class are generated by the C# compiler. + + + There is no additional information required for this binder. + + + The evaluation of this binder happens in a checked context. + + + The binder represents an invoke on a simple name. + + + The binder represents an invoke on a specialname. + + + The binder represents a logical AND or logical OR that is part of a conditional logical operator evaluation. + + + The binder represents an explicit conversion. + + + The binder represents an implicit conversion for use in an array creation expression. + + + The result of any bind is going to be indexed get a set index or get index binder. + + + The value in this set index or set member comes a compound assignment operator. + + + The binder is used in a position that does not require a result, and can therefore bind to a void returning method. + + + Represents an error that occurs when a dynamic bind in the C# runtime binder is processed. + + + Initializes a new instance of the class. + + + Initializes a new instance of the class that has a specified error message. + The message that describes the exception. The caller of this constructor is required to ensure that this string has been localized for the current system culture.The error message that explains the reason for the exception. + + + Initializes a new instance of the class that has a specified error message and a reference to the inner exception that is the cause of this exception. + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or a null reference if no inner exception is specified. + + + Represents an error that occurs when a dynamic bind in the C# runtime binder is processed. + + + Initializes a new instance of the class with a system-supplied message that describes the error. + + + Initializes a new instance of the class with a specified message that describes the error. + The message that describes the exception. The caller of this constructor is required to ensure that this string has been localized for the current system culture. + + + Initializes a new instance of the class that has a specified error message and a reference to the inner exception that is the cause of this exception. + The error message that explains the reason for the exception. + The exception that is the cause of the current exception, or a null reference if no inner exception is specified. + + + \ No newline at end of file diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/System.Json.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/System.Json.dll new file mode 100644 index 0000000..dc9eb76 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/System.Json.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/System.Json.xml b/trunk/packages/Hammock.1.2.3/lib/sl4/System.Json.xml new file mode 100644 index 0000000..8a736f4 --- /dev/null +++ b/trunk/packages/Hammock.1.2.3/lib/sl4/System.Json.xml @@ -0,0 +1,632 @@ + + + + System.Json + + + + A is an ordered sequence of zero or more objects. + + + 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. + + + 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. + + + Adds a object to the end of the array. + The object to add. + + + Adds the elements from a collection of type to the . + Collection of types to add. + + + Adds the elements from an array of type to the . + The array of type to be added to the . + + is null. + + + Removes all JSON CLR types from the . + + + Checks whether a specified JSON CLR type is in the . + true if is found in the ; otherwise, false. + The to check for in the array. + + + Copies the contents of the current JSON CLR array instance into a specified destination array beginning at the specified index. + The destination 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. + + + Returns the number of elements in the array. + The number of objects in the . + + + + Searches for a specified object and returns the zero-based index of its first occurrence within the . + The zero-based index of the first occurrence of within the , if found; otherwise, –1. + The object to look up. + + + 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. + + is less than zero or larger than the size of the array. + + + Gets a value that indicates whether the is read-only. + true if the is read-only; otherwise, false. + + + Gets or sets the JSON value at a specified index. + The at the specified index. + The zero-based index of the element to get or set. + + is not a valid index for the array. + The property is set and the is of a type that is not assignable to the array. + + + Gets the JSON type of the . + Returns . + + + Removes the first occurrence of the specified JSON value from the array. + true if is successfully removed; otherwise, false. This method also returns false if was not found in the . + The to remove from the . + + + Remove the JSON value at a specified index of . + The zero-based index at which to remove the . + + is less than zero or is equal or larger than the size of the array. + + + Serializes a JSON CLR array type into a stream of text-based JSON. + The stream to which the text-based JSON is written. + + is null. + + + Returns an enumerator that iterates through the objects in the array. + Returns an object that iterates through the elements in the . + + + Returns an enumerator that iterates through the objects in the array. + Returns an object that iterates through the elements in the . + + + + A is an unordered collection of zero or more key/value pairs. + + + Creates an instance of the class initialized with a collection of key/value pairs. + The collection of used to initialize the key/value pairs + + is null. + + + Creates an instance of the class initialized with a collection of key/value pairs. + The object used to initialize the key/value pairs. + + + Creates an instance of the class initialized with a . + The used to initialize the object. + + + Adds a key/value pair to the JSON CLR object. + The to add. + + + Adds a key/value pair to the JSON CLR object type. + The key for the element added. + The for the element added. + + is null. + + + Adds a specified collection of key/value pairs to the current instance of the . + The collection of key/value pairs to add. + + is null. + + + Adds a specified array of key/value pairs to the current instance of . + Collection of key/value pairs. + + + Removes all key/value pairs from the JSON CLR object. + + + Checks whether a key/value pair with a specified key exists in the JSON CLR object type. + true if the JSON CLR object contains the ; otherwise, false. + + The key to check for. + + is null. + + + Copies the contents of the JSON CLR object into a specified key/value destination array beginning at a specified index. + The destination array of type to which the elements of the are copied + The zero-based index at which to begin the insertion of the contents from the JSON CLR object type. + + + Returns the number of key/value pairs in the . + The number of key/value pairs in the JSON CLR object. + + + Returns an enumerator over the key/value pairs contained in the JSON CLR object type. + + . + + + Gets or sets an indexer used to look up a key/value pair based on a specified key. + The that contains the key/value pair looked up. + The key of the pair to look up. + + + Gets the JSON type of the . + Returns . + + + Returns a collection that contains the keys in the . + An that contains the keys from the JSON CLR object. + + + Removes the key/value pair with a specified key from the JSON CLR object type. + true if the element is successfully found and removed; otherwise, false. This method returns false if is not found in the JSON CLR object. + The key of the item to remove. + + is null. + + + Serializes a JSON CLR object into text-based JSON. + The stream to which the text-based JSON is written. + + is null. + + + Checks whether the contains a specified key/value pair. + true if the is contained in the instance of the ; otherwise, false. + The key/value pair to check for. + + is null. + + + Gets a value that indicates whether this JSON CLR object is read-only. + true if it is read-only; otherwise, false. + + + Removes the first occurrence of a specified key/value pair from the . + true if was successfully removed from the ; otherwise, false. This method also returns false if is not found in the . + The key/value pair to remove. + + is null. + + + Returns an enumerator that iterates through the key/value pairs in the . + +an object that iterates through the key/value pairs in the . + + + + Attempts to get the value that corresponds to the specified key. + true if the instance of the contains an element with the specified ; otherwise, false. + The key of the value to retrieve. + The primitive or structured object that has the specified. This parameter is passed uninitialized. + + is null. + + + Returns a collection that contains the values in the . + An that contains the set of from the JSON CLR object. + + + Represents a JavaScript Object Notation (JSON) primitive type in the common language runtime (CLR). + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + is null. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + + Initializes a new instance of a type with a type. + The object that initializes the new instance. + + is null. + + + Gets the that is associated with this object. + Each object is associated with a that is determined by the type of common language runtime (CLR) object used to initiate it. + + + + Serializes the object into text-based JSON. + The stream to which the text-based JSON is written. + + is null. + + + An enumeration that specifies primitive and structured JavaScript Object Notation (JSON) common language runtime (CLR) types. + + + Specifies the JSON string CLR type. + + + Specifies the JSON number CLR type. + + + Specifies the JSON object CLR type that consists of an unordered collection of key/value pairs, where the key is of type and the value is of type , which can, in turn, be either a primitive or a structured JSON type. + + + Specifies the JSON array CLR type that consists of an ordered collection of types, which can, in turn, be either primitive or structured JSON types. + + + Specifies the JSON Boolean CLR type. + + + This is the abstract base class for JavaScript Object Notation (JSON) common language runtime (CLR) types. + + + Throws an . + +Returns . + + The key to check. + + is null. + + + This method is not supported and throws an exception when called. + +Returns . + + + + This indexer is not supported for this base class and throws an exception. + +A . + + The zero-based index of the element to get or set. + + + This indexer is not supported for this base class and throws an exception. + +Returns . + + The key of the element to get or set. + + + When implemented in a derived class, indicates the JSON CLR type represented by the derived type. + +Returns . + + + + Deserializes text-based JSON from a stream into a JSON CLR type. + +Returns a class derived from that contains the deserialized text-based JSON. + A that contains text-based JSON content. + + is null. + + + Deserializes text-based JSON from a text reader into a JSON CLR type. + +Returns a class derived from that contains the deserialized text-based JSON. + A over text-based JSON content. + + is null. + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to , or there is not exactly one character in the parameter. + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified or null if is null. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and the object does not contain two fields that are called “DateTime” and “OffsetMinutes”. + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified, or null if is null. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from an instance of type to a object. + The initialized with the value specified. + The instance of used to initialize the object. + The of is not a and so cannot be cast to . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified, or null if is null. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified. + The instance used to initialize the . + + + Enables implicit casts from type to a . + The initialized with the specified, or null if is null. + The instance used to initialize the . + + + Deserializes text-based JSON into a JSON CLR type. + The object that represents the parsed text-based JSON as a CLR type. + The text-based JSON to be parsed into a JSON CLR type. + The length of is zero. + + is null. + + + When implemented in a derived class, serializes the CLR type into text-based JSON using a stream. + Stream to which to write text-based JSON. + + + Serializes the CLR type into text-based JSON using a text writer. + The used to write text-based JSON. + + is null. + + + This method is not supported for this base class and throws an exception. + +An . + + + + Saves (serializes) this JSON CLR type into text-based JSON. + +Returns , which contains text-based JSON. + + + \ No newline at end of file diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/System.Runtime.Serialization.Json.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/System.Runtime.Serialization.Json.dll new file mode 100644 index 0000000..cebb67e Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/System.Runtime.Serialization.Json.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/System.Runtime.Serialization.Json.xml b/trunk/packages/Hammock.1.2.3/lib/sl4/System.Runtime.Serialization.Json.xml new file mode 100644 index 0000000..f57db9e --- /dev/null +++ b/trunk/packages/Hammock.1.2.3/lib/sl4/System.Runtime.Serialization.Json.xml @@ -0,0 +1,65 @@ + + + + System.Runtime.Serialization.Json + + + + Produces instances of that can read data encoded with JavaScript Object Notation (JSON) from a stream or buffer and map it to an XML Infoset, and produces instances of that can map an XML Infoset to JSON and write JSON-encoded data to a stream. + + + Creates an that can map a buffer encoded with JavaScript Object Notation (JSON), of a specified size and offset, to an XML Infoset. + An that can read JSON. + The input buffer array from which to read. + Starting position from which to read in . + Number of bytes that can be read from . + The must be set to in Silverlight version 4 Release Candidate applications. + + is null. + + is negative or exceeds the length. + + is negative or exceeds the length minus the . + + + Creates an that can map a specified buffer encoded with JavaScript Object Notation (JSON) to an XML Infoset. + An that can process JavaScript Object Notation (JSON) data from the buffer specified. + The input buffer array from which to read. + The must be set to in Silverlight version 4 Release Candidate applications used to prevent Denial of Service (DoS) attacks when reading untrusted data. + + is null. + + + Creates an that can map a specified stream encoded with JavaScript Object Notation (JSON) to an XML Infoset. + An that can read JSON. + The input from which to read. + The must be set to in Silverlight version 4 Release Candidate applications. + + is null. + + + Creates an that writes data encoded with JSON to a stream using a UTF-8 character encoding. + An that writes data encoded with JSON to the stream from an XML Infoset. + The output for the JSON writer. + + is null. + + + Creates an that writes data encoded with JSON to a stream with a specified character encoding. + An that writes data encoded with JSON to the stream from an XML Infoset. + The output for the JSON writer. + The that specifies the character encoding used by the writer. The default encoding is UTF-8. + + or is null. + + + Creates an that writes data encoded with JSON to a stream with a specified character encoding and that specifies whether the output stream is closed by the writer when it is done. + An that writes data encoded with JSON to the stream from an XML Infoset. + The output for the JSON writer. + The that specifies the character encoding used by the writer. The default encoding is UTF-8. + If true, the output stream is closed by the writer when done; otherwise, false. The default value is true. + + or is null. + + + \ No newline at end of file diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/System.Xml.Linq.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/System.Xml.Linq.dll new file mode 100644 index 0000000..a31bfef Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/System.Xml.Linq.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/System.Xml.Linq.xml b/trunk/packages/Hammock.1.2.3/lib/sl4/System.Xml.Linq.xml new file mode 100644 index 0000000..873e381 --- /dev/null +++ b/trunk/packages/Hammock.1.2.3/lib/sl4/System.Xml.Linq.xml @@ -0,0 +1,1655 @@ + + + + System.Xml.Linq + + + + Contains the LINQ to XML extension methods. + + + Returns a collection of elements that contains the ancestors of every node in the source collection. + An of that contains the ancestors of every node in the source collection. + An of that contains the source collection. + The type of the objects in , constrained to . + + is null. + + + Returns a filtered collection of elements that contains the ancestors of every node in the source collection. Only elements that have a matching are included in the collection. + An of that contains the ancestors of every node in the source collection. Only elements that have a matching are included in the collection. + An of that contains the source collection. + The to match. + The type of the objects in , constrained to . + + is null. + + + Returns a collection of elements that contains every element in the source collection, and the ancestors of every element in the source collection. + An of that contains every element in the source collection, and the ancestors of every element in the source collection. + An of that contains the source collection. + + is null. + + + Returns a filtered collection of elements that contains every element in the source collection, and the ancestors of every element in the source collection. Only elements that have a matching are included in the collection. + An of that contains every element in the source collection, and the ancestors of every element in the source collection. Only elements that have a matching are included in the collection. + An of that contains the source collection. + The to match. + + is null. + + + Returns a collection of the attributes of every element in the source collection. + An of that contains the attributes of every element in the source collection. + An of that contains the source collection. + + is null. + + + Returns a filtered collection of the attributes of every element in the source collection. Only elements that have a matching are included in the collection. + An of that contains a filtered collection of the attributes of every element in the source collection. Only elements that have a matching are included in the collection. + An of that contains the source collection. + The to match. + + is null. + + + Returns a collection of the descendant nodes of every document and element in the source collection. + An of of the descendant nodes of every document and element in the source collection. + An of that contains the source collection. + The type of the objects in , constrained to . + + is null. + + + Returns a collection of nodes that contains every element in the source collection, and the descendant nodes of every element in the source collection. + An of that contains every element in the source collection, and the descendant nodes of every element in the source collection. + An of that contains the source collection. + + is null. + + + Returns a collection of elements that contains the descendant elements of every element and document in the source collection. + An of that contains the descendant elements of every element and document in the source collection. + An of that contains the source collection. + The type of the objects in , constrained to . + + is null. + + + Returns a filtered collection of elements that contains the descendant elements of every element and document in the source collection. Only elements that have a matching are included in the collection. + An of that contains the descendant elements of every element and document in the source collection. Only elements that have a matching are included in the collection. + An of that contains the source collection. + The to match. + The type of the objects in , constrained to . + + is null. + + + Returns a collection of elements that contains every element in the source collection, and the descendent elements of every element in the source collection. + An of that contains every element in the source collection, and the descendent elements of every element in the source collection. + An of that contains the source collection. + + is null. + + + Returns a filtered collection of elements that contains every element in the source collection, and the descendents of every element in the source collection. Only elements that have a matching are included in the collection. + An of that contains every element in the source collection, and the descendents of every element in the source collection. Only elements that have a matching are included in the collection. + An of that contains the source collection. + The to match. + + is null. + + + Returns a collection of the child elements of every element and document in the source collection. + An of of the child elements of every element or document in the source collection. + An of that contains the source collection. + The type of the objects in , constrained to . + + is null. + + + Returns a filtered collection of the child elements of every element and document in the source collection. Only elements that have a matching are included in the collection. + An of of the child elements of every element and document in the source collection. Only elements that have a matching are included in the collection. + An of that contains the source collection. + The to match. + The type of the objects in , constrained to . + + is null. + + + Returns a collection of nodes that contains all nodes in the source collection, sorted in document order. + An of that contains all nodes in the source collection, sorted in document order. + An of that contains the source collection. + The type of the objects in , constrained to . + + + Returns a collection of the child nodes of every document and element in the source collection. + An of of the child nodes of every document and element in the source collection. + An of that contains the source collection. + The type of the objects in , constrained to . + + is null. + + + Removes every attribute in the source collection from its parent element. + An of that contains the source collection. + + is null. + + + Removes every node in the source collection from its parent node. + An of that contains the source collection. + The type of the objects in , constrained to . + + is null. + + + Specifies load options when parsing XML. + + + Does not preserve insignificant white space or load base URI and line information. + + + Preserves insignificant white space while parsing. + + + Requests the base URI information from the , and makes it available via the property. + + + Requests the line information from the and makes it available via properties on . + + + Specifies whether to omit duplicate namespaces when loading an with an . + + + No reader options specified. + + + Omit duplicate namespaces when loading the . + + + Specifies serialization options. + + + Formats (indent) the XML while serializing. + + + Preserves all insignificant white space while serializing. + + + Removes duplicate namespace declarations. For the duplicate namespace declarations to be removed, both the prefix and the namespace have to match. + + + Represents an XML attribute. + + + Initializes a new instance of the class from another object. + An object to copy from. + The parameter is null. + + + Initializes a new instance of the class from the specified name and value. + The of the attribute. + An containing the value of the attribute. + The or parameter is null. + + + Gets an empty collection of attributes. + An of containing an empty collection. + + + Determines if this attribute is a namespace declaration. + true if this attribute is a namespace declaration; otherwise false. + + + Gets the expanded name of this attribute. + An containing the name of this attribute. + + + Gets the next attribute of the parent element. + An containing the next attribute of the parent element. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The attribute does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The attribute does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to an . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The attribute does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The attribute does not contain a valid value. + + + Cast the value of this to an . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to a of . + The attribute does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The attribute does not contain a valid value. + The parameter is null. + + + Gets the previous attribute of the parent element. + An containing the previous attribute of the parent element. + + + Removes this attribute from its parent element. + The parent element is null. + + + Sets the value of this attribute. + The value to assign to this attribute. + The parameter is null. + The is an . + + + Converts the current object to a string representation. + A containing the XML text representation of an attribute and its value. + + + Gets or sets the value of this attribute. + A containing the value of this attribute. + When setting, the is null. + + + Represents a text node that contains CDATA. + + + Initializes a new instance of the class. + A string that contains the value of the node. + + + Initializes a new instance of the class. + The node to copy from. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Writes this CDATA object to an . + An into which this method will write. + + is null. + + + Represents an XML comment. + + + Initializes a new instance of the class with the specified string content. + A string that contains the contents of the new object. + The parameter is null. + + + Initializes a new instance of the class from an existing comment node. + The node to copy from. + The parameter is null. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Gets or sets the string value of this comment. + A that contains the string value of this comment. + The is null. + + + Write this comment to an . + An into which this method will write. + + is null. + + + Represents a node that can contain other nodes. + + + Adds the specified content as children of this . + A content object containing simple content or a collection of content objects to be added. + + + Adds the specified content as children of this . + A parameter list of content objects. + + + Adds the specified content as the first children of this document or element. + A content object containing simple content or a collection of content objects to be added. + + + Adds the specified content as the first children of this document or element. + A parameter list of content objects. + The parent is null. + + + Creates an that can be used to add nodes to the . + An that is ready to have content written to it. + + + Returns a collection of the descendant nodes for this document or element, in document order. + An of containing the descendant nodes of the , in document order. + + + Returns a collection of the descendant elements for this document or element, in document order. + An of containing the descendant elements of the . + + + Returns a filtered collection of the descendant elements for this document or element, in document order. Only elements that have a matching are included in the collection. + An of containing the descendant elements of the that match the specified . + The to match. + + + Gets the first (in document order) child element with the specified . + A that matches the specified , or null. + The to match. + + + Returns a collection of the child elements of this element or document, in document order. + An of containing the child elements of this , in document order. + + + Returns a filtered collection of the child elements of this element or document, in document order. Only elements that have a matching are included in the collection. + An of containing the children of the that have a matching , in document order. + The to match. + + + Get the first child node of this node. + An containing the first child node of the . + + + Get the last child node of this node. + An containing the last child node of the . + + + Returns a collection of the child nodes of this element or document, in document order. + An of containing the contents of this , in document order. + + + Removes the child nodes from this document or element. + + + Replaces the children nodes of this document or element with the specified content. + A content object containing simple content or a collection of content objects that replace the children nodes. + + + Replaces the children nodes of this document or element with the specified content. + A parameter list of content objects. + + + Represents an XML declaration. + + + Initializes a new instance of the class with the specified version, encoding, and standalone status. + The version of the XML, usually "1.0". + The encoding for the XML document. + A string containing "yes" or "no" that specifies whether the XML is standalone or requires external entities to be resolved. + + + Initializes a new instance of the class from another object. + The used to initialize this object. + + is null. + + + Gets or sets the encoding for this document. + A containing the code page name for this document. + + + Gets or sets the standalone property for this document. + A containing the standalone property for this document. + + + Provides the declaration as a formatted string. + A that contains the formatted XML string. + + + Gets or sets the version property for this document. + A containing the version property for this document. + + + Represents an XML document. + + + Initializes a new instance of the class. + + + Initializes a new instance of the class with the specified content. + A parameter list of content objects to add to this document. + + + Initializes a new instance of the class with the specified and content. + An for the document. + The content of the document. + + + Initializes a new instance of the class from an existing object. + The object that will be copied. + + + Gets or sets the XML declaration for this document. + An that contains the XML declaration for this document. + + + Gets the Document Type Definition (DTD) for this document. + A that contains the DTD for this document. + + + Creates a new instance using the specified stream. + An object used to read the data contained in the stream. + The stream containing the XML data. + + + Creates a new instance using the specified stream, optionally preserving white space, setting the base URI, and retaining line information. + An object used to read the data contained in the stream. + The stream containing the XML data. + A that specifies whether to load base URI and line information. + + + Creates a new from a . + An that contains the contents of the specified . + A that contains the content for the . + + + Creates a new from a , optionally preserving white space, setting the base URI, and retaining line information. + An that contains the XML that was read from the specified . + A that contains the content for the . + A that specifies white space behavior, and whether to load base URI and line information. + + + Creates a new from a file located in the application's XAP package. + An that contains the contents of the specified file. + A URI string that references the file to be loaded into a new . This file is located in the application's XAP package. If you want to download a file from some other location, follow the steps described in How to: Load an XML File from an Arbitrary URI Location with LINQ to XML. + + + Creates a new from a file located in the application's XAP package, optionally preserving white space, setting the base URI, and retaining line information. + An that contains the contents of the specified file. + A URI string that references the file to be loaded into a new . This file is located in the application's XAP package. If you want to download a file from some other location, follow the steps described in How to: Load an XML File from an Arbitrary URI Location with LINQ to XML. + A that specifies how white space is handled and whether to load base URI and line information. + + + Creates a new from an . + An that contains the contents of the specified . + A that contains the content for the . + + + Creates a new from an , optionally setting the base URI, and retaining line information. + An that contains the XML that was read from the specified . + A that will be read for the content of the . + A that specifies whether to load base URI and line information. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Creates a new from a string. + An populated from the string that contains XML. + A string that contains XML. + + + Creates a new from a string, optionally preserving white space, setting the base URI, and retaining line information. + An populated from the string that contains XML. + A string that contains XML. + A that specifies white space behavior, and whether to load base URI and line information. + + + Gets the root element of the XML Tree for this document. + The root of the XML tree. + + + Outputs this to the specified . + The stream to output this to. + + + Outputs this to the specified , optionally specifying formatting behavior. + The stream to output this to. + A that specifies formatting behavior. + + + Serialize this to a . + A that the will be written to. + + + Serialize this to a , optionally disabling formatting. + The to output the XML to. + A that specifies formatting behavior. + + + Serialize this to an . + A that the will be written to. + + + Write this document to an . + An into which this method will write. + + + Represents an XML Document Type Definition (DTD). + + + Initializes an instance of the class. + A that contains the qualified name of the DTD, which is the same as the qualified name of the root element of the XML document. + A that contains the public identifier of an external public DTD. + A that contains the system identifier of an external private DTD. + A that contains the internal subset for an internal DTD. + + + Initializes an instance of the class from another object. + An object to copy from. + + + Gets or sets the internal subset for this Document Type Definition (DTD). + A that contains the internal subset for this Document Type Definition (DTD). + + + Gets or sets the name for this Document Type Definition (DTD). + A that contains the name for this Document Type Definition (DTD). + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Gets or sets the public identifier for this Document Type Definition (DTD). + A that contains the public identifier for this Document Type Definition (DTD). + + + Gets or sets the system identifier for this Document Type Definition (DTD). + A that contains the system identifier for this Document Type Definition (DTD). + + + Write this to an . + An into which this method will write. + + + Represents an XML element. + + + Initializes a new instance of the class from another object. + An object to copy from. + + + Initializes a new instance of the class with the specified name. + An that contains the name of the element. + + + Initializes a new instance of the class with the specified name and content. + An that contains the element name. + The contents of the element. + + + Initializes a new instance of the class with the specified name and content. + An that contains the element name. + The initial content of the element. + + + Initializes a new instance of the class from an object. + An that contains unevaluated queries that will be iterated for the contents of this . + + + Returns a collection of elements that contain this element, and the ancestors of this element. + An of of elements that contain this element, and the ancestors of this element. + + + Returns a filtered collection of elements that contain this element, and the ancestors of this element. Only elements that have a matching are included in the collection. + An of that contain this element, and the ancestors of this element. Only elements that have a matching are included in the collection. + The to match. + + + Returns the of this that has the specified . + An that has the specified ; null if there is no attribute with the specified name. + The of the to get. + + + Returns a collection of attributes of this element. + An of of attributes of this element. + + + Returns a filtered collection of attributes of this element. Only elements that have a matching are included in the collection. + An of that contains the attributes of this element. Only elements that have a matching are included in the collection. + The to match. + + + Returns a collection of nodes that contain this element, and all descendant nodes of this element, in document order. + An of that contain this element, and all descendant nodes of this element, in document order. + + + Returns a collection of elements that contain this element, and all descendant elements of this element, in document order. + An of of elements that contain this element, and all descendant elements of this element, in document order. + + + Returns a filtered collection of elements that contain this element, and all descendant elements of this element, in document order. Only elements that have a matching are included in the collection. + An of that contain this element, and all descendant elements of this element, in document order. Only elements that have a matching are included in the collection. + The to match. + + + Gets an empty collection of elements. + An of that contains an empty collection. + + + Gets the first attribute of this element. + An that contains the first attribute of this element. + + + Gets the default of this . + An that contains the default namespace of this . + + + Gets the namespace associated with a particular prefix for this . + An for the namespace associated with the prefix for this . + A string that contains the namespace prefix to look up. + + + Gets the prefix associated with a namespace for this . + A that contains the namespace prefix. + An to look up. + + + Gets a value indicating whether this element as at least one attribute. + true if this element has at least one attribute; otherwise false. + + + Gets a value indicating whether this element has at least one child element. + true if this element has at least one child element; otherwise false. + + + Gets a value indicating whether this element contains no content. + true if this element contains no content; otherwise false. + + + Gets the last attribute of this element. + An that contains the last attribute of this element. + + + Creates a new instance using the specified stream. + An object used to read the data contained in the stream. + The stream containing the XML data. + + + Creates a new instance using the specified stream, optionally preserving white space, setting the base URI, and retaining line information. + An object used to read the data contained in the stream. + The stream containing the XML data. + A that specifies whether to load base URI and line information. + + + Loads an from a . + An that contains the XML that was read from the specified . + A that will be read for the content. + + + Loads an from a , optionally preserving white space and retaining line information. + An that contains the XML that was read from the specified . + A that will be read for the content. + A that specifies white space behavior, and whether to load base URI and line information. + + + Loads an from a file located in the applications' XAP package. + An that contains the contents of the specified file. + A URI string that references the file to be loaded into a new . This file is located in the application's XAP package. If you want to download a file from some other location, follow the steps described in How to: Load an XML File from an Arbitrary URI Location with LINQ to XML. + + + Loads an from a file located in the application's XAP package, optionally preserving white space, setting the base URI, and retaining line information. + An that contains the contents of the specified file. + A URI string that references the file to be loaded into a new . This file is located in the application's XAP package. If you want to download a file from some other location, follow the steps described in How to: Load an XML File from an Arbitrary URI Location with LINQ to XML. + A that specifies white space behavior, and whether to load base URI and line information. + + + Loads an from an . + An that contains the XML that was read from the specified . + A that will be read for the content of the . + + + Loads an from an , optionally preserving white space, setting the base URI, and retaining line information. + An that contains the XML that was read from the specified . + A that will be read for the content of the . + A that specifies white space behavior, and whether to load base URI and line information. + + + Gets the name of this element. + An that contains the name of this element. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to an of . + The element does not contain a valid value. + + + Cast the value of this to an . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to a of . + A of that contains the content of this . + The to cast to of . + The element does not contain a valid value. + + + Cast the value of this to an . + A that contains the content of this . + The to cast to . + The element does not contain a valid value. + The parameter is null. + + + Load an from a string that contains XML. + An populated from the string that contains XML. + A that contains XML. + + + Load an from a string that contains XML, optionally preserving white space and retaining line information. + An populated from the string that contains XML. + A that contains XML. + A that specifies white space behavior, and whether to load base URI and line information. + + + Removes nodes and attributes from this . + + + Removes the attributes of this . + + + Replaces the child nodes and the attributes of this element with the specified content. + The content that will replace the child nodes and attributes of this element. + + + Replaces the child nodes and the attributes of this element with the specified content. + A parameter list of content objects. + + + Replaces the attributes of this element with the specified content. + The content that will replace the attributes of this element. + + + Replaces the attributes of this element with the specified content. + A parameter list of content objects. + + + Outputs this to the specified . + The stream to output this to. + + + Outputs this to the specified , optionally specifying formatting behavior. + The stream to output this to. + A that specifies formatting behavior. + + + Serialize this element to a . + A that the will be written to. + + + Serialize this element to a , optionally disabling formatting. + The to output the XML to. + A that specifies formatting behavior. + + + Serialize this element to an . + A that the will be written to. + + + Sets the value of an attribute, adds an attribute, or removes an attribute. + An that contains the name of the attribute to change. + The value to assign to the attribute. The attribute is removed if the value is null. Otherwise, the value is converted to its string representation and assigned to the property of the attribute. + The is an instance of . + + + Sets the value of a child element, adds a child element, or removes a child element. + An that contains the name of the child element to change. + The value to assign to the child element. The child element is removed if the value is null. Otherwise, the value is converted to its string representation and assigned to the property of the child element. + The is an instance of . + + + Sets the value of this element. + The value to assign to this element. The value is converted to its string representation and assigned to the property. + The is null. + The is an . + + + Gets an XML schema definition that describes the XML representation of this object. + An that describes the XML representation of the object that is produced by the method and consumed by the method. + + + Generates an object from its XML representation. + The from which the object is deserialized. + + + Converts an object into its XML representation. + The to which this object is serialized. + + + Gets the concatenated text contents of this element. + A that contains all of the text content of this element. If there are multiple text nodes, they will be concatenated. + + + Write this element to an . + An into which this method will write. + + + Represents a name of an XML element or attribute. + + + Determines whether the specified is equal to this . + true if the specified is equal to the current ; otherwise false. + The to compare to the current . + + + Gets an object from an expanded name. + An object constructed from the expanded name. + A that contains an expanded XML name in the format {namespace}localname. + + + Gets an object from a local name and a namespace. + An object created from the specified local name and namespace. + A local (unqualified) name. + An XML namespace. + + + Gets a hash code for this . + An that contains the hash code for the . + + + Gets the local (unqualified) part of the name. + A that contains the local (unqualified) part of the name. + + + Gets the namespace part of the fully qualified name. + An that contains the namespace part of the name. + + + Returns the URI of the for this . + The URI of the for this . + + + Returns a value indicating whether two instances of are equal. + true if and are equal; otherwise false. + The first to compare. + The second to compare. + + + Converts a string formatted as an expanded XML name (that is,{namespace}localname) to an object. + An object constructed from the expanded name. + A string that contains an expanded XML name in the format {namespace}localname. + + + Returns a value indicating whether two instances of are not equal. + true if and are not equal; otherwise false. + The first to compare. + The second to compare. + + + Indicates whether the current is equal to the specified . + true if this is equal to the specified , otherwise false. + The to compare with this . + + + Returns the expanded XML name in the format {namespace}localname. + A that contains the expanded XML name in the format {namespace}localname. + + + Represents an XML namespace. This class cannot be inherited. + + + Determines whether the specified is equal to the current . + A that indicates whether the specified is equal to the current . + The to compare to the current . + + + Gets an for the specified Uniform Resource Identifier (URI). + An created from the specified URI. + A that contains a namespace URI. + + + Gets a hash code for this . + An that contains the hash code for the . + + + Returns an object created from this and the specified local name. + An created from this and the specified local name. + A that contains a local name. + + + Gets the Uniform Resource Identifier (URI) of this namespace. + A that contains the URI of the namespace. + + + Gets the object that corresponds to no namespace. + The that corresponds to no namespace. + + + Combines an object with a local name to create an . + The new constructed from the namespace and local name. + An that contains the namespace. + A that contains the local name. + + + Returns a value indicating whether two instances of are equal. + A that indicates whether and are equal. + The first to compare. + The second to compare. + + + Converts a string containing a Uniform Resource Identifier (URI) to an . + An constructed from the URI string. + A that contains the namespace URI. + + + Returns a value indicating whether two instances of are not equal. + A that indicates whether and are not equal. + The first to compare. + The second to compare. + + + Returns the URI of this . + The URI of this . + + + Gets the object that corresponds to the XML URI (http://www.w3.org/XML/1998/namespace). + The that corresponds to the XML URI (http://www.w3.org/XML/1998/namespace). + + + Gets the object that corresponds to the xmlns URI (http://www.w3.org/2000/xmlns/). + The that corresponds to the xmlns URI (http://www.w3.org/2000/xmlns/). + + + Represents the abstract concept of a node (one of: element, comment, document type, processing instruction, or text node) in the XML tree. + + + Adds the specified content immediately after this node. + A content object that contains simple content or a collection of content objects to be added after this node. + The parent is null. + + + Adds the specified content immediately after this node. + A parameter list of content objects. + The parent is null. + + + Adds the specified content immediately before this node. + A content object that contains simple content or a collection of content objects to be added before this node. + The parent is null. + + + Adds the specified content immediately before this node. + A parameter list of content objects. + The parent is null. + + + Returns a collection of the ancestor elements of this node. + An of of the ancestor elements of this node. + + + Returns a filtered collection of the ancestor elements of this node. Only elements that have a matching are included in the collection. + An of of the ancestor elements of this node. Only elements that have a matching are included in the collection.The nodes in the returned collection are in reverse document order.This method uses deferred execution. + The to match. + + + Compares two nodes to determine their relative XML document order. + An int containing 0 if the nodes are equal; -1 if is before ; 1 if is after . + First to compare. + Second to compare. + The two nodes do not share a common ancestor. + + + Creates an for this node. + An that can be used to read this node and its descendants. + + + Creates an for this node. + An that can be used to read this node and its descendants. + Specifies whether to omit duplicate namespaces. + + + Compares the values of two nodes, including the values of all descendant nodes. + true if the nodes are equal; otherwise false. + The first to compare. + The second to compare. + + + Gets a comparer that can compare the relative position of two nodes. + A that can compare the relative position of two nodes. + + + Returns a collection of the sibling elements after this node, in document order. + An of of the sibling elements after this node, in document order. + + + Returns a filtered collection of the sibling elements after this node, in document order. Only elements that have a matching are included in the collection. + An of of the sibling elements after this node, in document order. Only elements that have a matching are included in the collection. + The to match. + + + Returns a collection of the sibling elements before this node, in document order. + An of of the sibling elements before this node, in document order. + + + Returns a filtered collection of the sibling elements before this node, in document order. Only elements that have a matching are included in the collection. + An of of the sibling elements before this node, in document order. Only elements that have a matching are included in the collection. + The to match. + + + Gets a comparer that can compare two nodes for value equality. + A that can compare two nodes for value equality. + + + Determines if the current node appears after a specified node in terms of document order. + true if this node appears after the specified node; otherwise false. + The to compare for document order. + + + Determines if the current node appears before a specified node in terms of document order. + true if this node appears before the specified node; otherwise false. + The to compare for document order. + + + Gets the next sibling node of this node. + The that contains the next sibling node. + + + Returns a collection of the sibling nodes after this node, in document order. + An of of the sibling nodes after this node, in document order. + + + Returns a collection of the sibling nodes before this node, in document order. + An of of the sibling nodes before this node, in document order. + + + Gets the previous sibling node of this node. + The that contains the previous sibling node. + + + Creates an from an . + An that contains the node and its descendant nodes that were read from the reader. The runtime type of the node is determined by the node type () of the first node encountered in the reader. + An positioned at the node to read into this . + The is not positioned on a recognized node type. + The underlying throws an exception. + + + Removes this node from its parent. + The parent is null. + + + Replaces this node with the specified content. + Content that replaces this node. + + + Replaces this node with the specified content. + A parameter list of the new content. + + + Returns the indented XML for this node. + A containing the indented XML. + + + Returns the XML for this node, optionally disabling formatting. + A containing the XML. + A that specifies formatting behavior. + + + Writes this node to an . + An into which this method will write. + + + Contains functionality to compare nodes for their document order. This class cannot be inherited. + + + Initializes a new instance of the class. + + + Compares two nodes to determine their relative document order. + An that contains 0 if the nodes are equal; -1 if is before ; 1 if is after . + The first to compare. + The second to compare. + The two nodes do not share a common ancestor. + + + Compares nodes to determine whether they are equal. This class cannot be inherited. + + + Initializes a new instance of the class. + + + Compares the values of two nodes. + A indicating if the nodes are equal. + The first to compare. + The second to compare. + + + Returns a hash code based on an . + A that contains a value-based hash code for the node. + The to hash. + + + Represents a node or an attribute in an XML tree. + + + Adds an object to the annotation list of this . + An that contains the annotation to add. + + + Get the first annotation object of the specified type from this . + The first annotation object that matches the specified type, or null if no annotation is of the specified type. + The type of the annotation to retrieve. + + + Gets the first annotation object of the specified type from this . + The that contains the first annotation object that matches the specified type, or null if no annotation is of the specified type. + The of the annotation to retrieve. + + + Gets a collection of annotations of the specified type for this . + An that contains the annotations for this . + The type of the annotations to retrieve. + + + Gets a collection of annotations of the specified type for this . + An of that contains the annotations that match the specified type for this . + The of the annotations to retrieve. + + + Gets the base URI for this . + A that contains the base URI for this . + + + Raised when this or any of its descendants have changed. + + + Raised when this or any of its descendants are about to change. + + + Gets the for this . + The for this . + + + Gets the node type for this . + The node type for this . + + + Gets the parent of this . + The parent of this . + + + Removes the annotations of the specified type from this . + The type of annotations to remove. + + + Removes the annotations of the specified type from this . + The of annotations to remove. + + + Gets a value indicating whether or not this has line information. + true if the has line information, otherwise false. + + + Gets the line number that the underlying reported for this . + An that contains the line number reported by the for this . + + + Gets the line position that the underlying reported for this . + An that contains the line position reported by the for this . + + + Specifies the event type when an event is raised for an . + + + An has been or will be added to an . + + + An has been or will be removed from an . + + + An has been or will be renamed. + + + The value of an has been or will be changed. In addition, a change in the serialization of an empty element (either from an empty tag to start/end tag pair or vice versa) raises this event. + + + Provides data for the and events. + + + Initializes a new instance of the class. + An that contains the event arguments for LINQ to XML events. + + + Event argument for an change event. + + + Event argument for a change event. + + + Gets the type of change. + An that contains the type of change. + + + Event argument for a change event. + + + Event argument for a change event. + + + Represents an XML processing instruction. + + + Initializes a new instance of the class. + A containing the target application for this . + The string data for this . + The or parameter is null. + The does not follow the constraints of an XML name. + + + Initializes a new instance of the class. + The node to copy from. + + + Gets or sets the string value of this processing instruction. + A that contains the string value of this processing instruction. + The string is null. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Gets or sets a string containing the target application for this processing instruction. + A containing the target application for this processing instruction. + The string is null. + The does not follow the constraints of an XML name. + + + Writes this processing instruction to an . + The to write this processing instruction to. + + + Represents elements in an XML tree that supports deferred streaming output. + + + Initializes a new instance of the class from the specified . + An that contains the name of the element. + + + Initializes a new instance of the class with the specified name and content. + An that contains the element name. + The contents of the element. + + + Initializes a new instance of the class with the specified name and content. + An that contains the element name. + The contents of the element. + + + Adds the specified content as children to this . + Content to be added to the streaming element. + + + Adds the specified content as children to this . + Content to be added to the streaming element. + + + Gets or sets the name of this streaming element. + An that contains the name of this streaming element. + + + Outputs this to the specified . + The stream to output this to. + + + Outputs this to the specified , optionally specifying formatting behavior. + The stream to output this to. + A that specifies formatting behavior. + + + Serialize this streaming element to a . + A that the will be written to. + + + Serialize this streaming element to a , optionally disabling formatting. + The to output the XML to. + A that specifies formatting behavior. + + + Serialize this streaming element to an . + A that the will be written to. + + + Returns the formatted (indented) XML for this streaming element. + A containing the indented XML. + + + Returns the XML for this streaming element, optionally disabling formatting. + A containing the XML. + A that specifies formatting behavior. + + + Writes this streaming element to an . + An into which this method will write. + + + Represents a text node. + + + Initializes a new instance of the class. + The that contains the value of the node. + + + Initializes a new instance of the class from another object. + The node to copy from. + + + Gets the node type for this node. + The node type. For objects, this value is . + + + Gets or sets the value of this node. + A that contains the value of this node. + + + Writes this node to an . + An into which this method will write. + + + Compares two nodes to determine their relative document order. + An that contains 0 if the nodes are equal; -1 if is before ; 1 if is after . + The first to compare. + The second to compare. + The two nodes do not share a common ancestor. + The two nodes are not derived from . + + + Compares the values of two nodes. + true if the nodes are equal; otherwise false. + The first to compare. + The second to compare. + + + Returns a hash code based on the value of a node. + A that contains a value-based hash code for the node. + The node to hash. + + + \ No newline at end of file diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/de/Microsoft.CSharp.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/de/Microsoft.CSharp.resources.dll new file mode 100644 index 0000000..578348f Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/de/Microsoft.CSharp.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/de/System.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/de/System.Json.resources.dll new file mode 100644 index 0000000..f47219d Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/de/System.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/de/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/de/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..628d002 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/de/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/de/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/de/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..8f968ed Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/de/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/es/Microsoft.CSharp.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/es/Microsoft.CSharp.resources.dll new file mode 100644 index 0000000..640f83d Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/es/Microsoft.CSharp.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/es/System.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/es/System.Json.resources.dll new file mode 100644 index 0000000..3704372 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/es/System.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/es/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/es/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..a699db2 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/es/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/es/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/es/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..08aac27 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/es/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/fr/Microsoft.CSharp.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/fr/Microsoft.CSharp.resources.dll new file mode 100644 index 0000000..a9de5c4 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/fr/Microsoft.CSharp.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/fr/System.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/fr/System.Json.resources.dll new file mode 100644 index 0000000..18d5676 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/fr/System.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/fr/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/fr/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..f30cf5c Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/fr/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/fr/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/fr/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..24d4207 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/fr/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/it/Microsoft.CSharp.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/it/Microsoft.CSharp.resources.dll new file mode 100644 index 0000000..b605d22 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/it/Microsoft.CSharp.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/it/System.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/it/System.Json.resources.dll new file mode 100644 index 0000000..e2a0427 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/it/System.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/it/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/it/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..bd173f5 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/it/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/it/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/it/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..94c1c16 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/it/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/ja/Microsoft.CSharp.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/ja/Microsoft.CSharp.resources.dll new file mode 100644 index 0000000..835edff Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/ja/Microsoft.CSharp.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/ja/System.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/ja/System.Json.resources.dll new file mode 100644 index 0000000..72c9c6f Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/ja/System.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/ja/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/ja/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..614e67a Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/ja/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/ja/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/ja/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..d9ff5ce Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/ja/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/ko/Microsoft.CSharp.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/ko/Microsoft.CSharp.resources.dll new file mode 100644 index 0000000..d49d7b5 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/ko/Microsoft.CSharp.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/ko/System.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/ko/System.Json.resources.dll new file mode 100644 index 0000000..bbee67a Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/ko/System.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/ko/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/ko/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..e7fa814 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/ko/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/ko/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/ko/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..ae2e5ff Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/ko/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/ru/Microsoft.CSharp.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/ru/Microsoft.CSharp.resources.dll new file mode 100644 index 0000000..eabd804 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/ru/Microsoft.CSharp.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/ru/System.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/ru/System.Json.resources.dll new file mode 100644 index 0000000..055117f Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/ru/System.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/ru/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/ru/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..5105120 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/ru/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/ru/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/ru/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..35fa767 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/ru/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hans/Microsoft.CSharp.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hans/Microsoft.CSharp.resources.dll new file mode 100644 index 0000000..8663d91 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hans/Microsoft.CSharp.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hans/System.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hans/System.Json.resources.dll new file mode 100644 index 0000000..724ea6f Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hans/System.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hans/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hans/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..0d38886 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hans/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hans/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hans/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..f43418a Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hans/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hant/Microsoft.CSharp.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hant/Microsoft.CSharp.resources.dll new file mode 100644 index 0000000..e935e3a Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hant/Microsoft.CSharp.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hant/System.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hant/System.Json.resources.dll new file mode 100644 index 0000000..7055428 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hant/System.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hant/System.Runtime.Serialization.Json.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hant/System.Runtime.Serialization.Json.resources.dll new file mode 100644 index 0000000..550bfd3 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hant/System.Runtime.Serialization.Json.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hant/System.Xml.Linq.resources.dll b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hant/System.Xml.Linq.resources.dll new file mode 100644 index 0000000..233d5c3 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/lib/sl4/zh-Hant/System.Xml.Linq.resources.dll differ diff --git a/trunk/packages/Hammock.1.2.3/mono/Hammock.Mono.dll b/trunk/packages/Hammock.1.2.3/mono/Hammock.Mono.dll new file mode 100644 index 0000000..73d38f0 Binary files /dev/null and b/trunk/packages/Hammock.1.2.3/mono/Hammock.Mono.dll differ diff --git a/trunk/packages/Massive.1.0/Massive.1.0.nupkg b/trunk/packages/Massive.1.0/Massive.1.0.nupkg new file mode 100644 index 0000000..a19b5a1 Binary files /dev/null and b/trunk/packages/Massive.1.0/Massive.1.0.nupkg differ diff --git a/trunk/packages/Massive.1.0/content/App_Code/LICENSE.txt b/trunk/packages/Massive.1.0/content/App_Code/LICENSE.txt new file mode 100644 index 0000000..f90aa76 --- /dev/null +++ b/trunk/packages/Massive.1.0/content/App_Code/LICENSE.txt @@ -0,0 +1,11 @@ +New BSD License +http://www.opensource.org/licenses/bsd-license.php +Copyright (c) 2009, Rob Conery (robconery@gmail.com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +Neither the name of the SubSonic nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/trunk/packages/Massive.1.0/content/App_Code/Massive.cs b/trunk/packages/Massive.1.0/content/App_Code/Massive.cs new file mode 100644 index 0000000..af02d0c --- /dev/null +++ b/trunk/packages/Massive.1.0/content/App_Code/Massive.cs @@ -0,0 +1,400 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Configuration; +using System.Data; +using System.Data.Common; +using System.Dynamic; +using System.Linq; +using System.Text; + +namespace Massive { + public static class ObjectExtensions { + /// + /// Extension method for adding in a bunch of parameters + /// + public static void AddParams(this DbCommand cmd, object[] args) { + foreach (var item in args) { + AddParam(cmd, item); + } + } + /// + /// Extension for adding single parameter + /// + public static void AddParam(this DbCommand cmd, object item) { + var p = cmd.CreateParameter(); + p.ParameterName = string.Format("@{0}", cmd.Parameters.Count); + //fix for NULLs as parameter values + if (item == null) { + p.Value = DBNull.Value; + } else { + //fix for Guids + if (item.GetType() == typeof(Guid)) { + p.Value = item.ToString(); + p.DbType = DbType.String; + } else { + p.Value = item; + } + } + cmd.Parameters.Add(p); + } + /// + /// Turns an IDataReader to a Dynamic list of things + /// + public static List ToExpandoList(this IDataReader rdr) { + var result = new List(); + //work with the Expando as a Dictionary + while (rdr.Read()) { + dynamic e = new ExpandoObject(); + var d = e as IDictionary; + for (int i = 0; i < rdr.FieldCount; i++) + d.Add(rdr.GetName(i), rdr[i]); + result.Add(e); + } + return result; + } + /// + /// Turns the object into an ExpandoObject + /// + /// + /// + public static dynamic ToExpando(this object o) { + var result = new ExpandoObject(); + var d = result as IDictionary; //work with the Expando as a Dictionary + if (o.GetType() == typeof(ExpandoObject)) return o; //shouldn't have to... but just in case + //special for form submissions + if (o.GetType() == typeof(NameValueCollection)) { + var nv = (NameValueCollection)o; + nv.Cast().Select(key => new KeyValuePair(key, nv[key])).ToList().ForEach(i => d.Add(i)); + } else { + //assume it's a regular lovely object + var props = o.GetType().GetProperties(); + foreach (var item in props) { + d.Add(item.Name, item.GetValue(o, null)); + } + } + return result; + } + /// + /// Turns the object into a Dictionary + /// + /// + /// + public static IDictionary ToDictionary(this object thingy) { + return (IDictionary)thingy.ToExpando(); + } + } + /// + /// A class that wraps your database table in Dynamic Funtime + /// + public abstract class DynamicModel : DynamicObject { + DbProviderFactory _factory; + string _connectionStringName; + string _connectionString; + + public IList Query(string sql, params object[] args) { + var result = new List(); + using (var conn = OpenConnection()) { + using (var cmd = CreateCommand(sql, args)) { + cmd.Connection = conn; + using (var rdr = cmd.ExecuteReader(CommandBehavior.CloseConnection)) { + result = rdr.ToExpandoList(); + } + } + } + return result; + } + /// + /// Creates a DBCommand that you can use for loving your database. + /// + DbCommand CreateCommand(string sql, params object[] args) { + DbCommand result = null; + result = _factory.CreateCommand(); + result.CommandText = sql; + if (args.Length > 0) + result.AddParams(args); + return result; + } + DbConnection GetConnection() { + var connection = _factory.CreateConnection(); + connection.ConnectionString = _connectionString; + return connection; + } + DbConnection OpenConnection() { + var conn = GetConnection(); + conn.Open(); + return conn; + } + /// + /// Creates a slick, groovy little wrapper for your action + /// + /// + public DynamicModel(string connectionStringName) { + //can be overridden by property setting + TableName = this.GetType().Name; + _connectionStringName = connectionStringName; + + var providerName = "System.Data.SqlClient"; + if (ConfigurationManager.ConnectionStrings[_connectionStringName] != null) { + providerName = ConfigurationManager.ConnectionStrings[_connectionStringName].ProviderName ?? "System.Data.SqlClient"; + } else { + throw new InvalidOperationException("Can't find a connection string with the name '" + _connectionStringName + "'"); + } + _factory = DbProviderFactories.GetFactory(providerName); + _connectionString = ConfigurationManager.ConnectionStrings[_connectionStringName].ConnectionString; + } + string _primaryKeyField; + /// + /// Conventionally returns a PK field. The default is "ID" if you don't set one + /// + public string PrimaryKeyField { + get { return string.IsNullOrEmpty(_primaryKeyField) ? /*a bit of convention here*/ "ID" : /*oh well - did our best*/ _primaryKeyField; } + set { _primaryKeyField = value; } + } + /// + /// Conventionally introspects the object passed in for a field that + /// looks like a PK. If you've named your PrimaryKeyField, this becomes easy + /// + public bool HasPrimaryKey(object o) { + var result = o.ToDictionary().ContainsKey(PrimaryKeyField); + return result; + } + /// + /// If the object passed in has a property with the same name as your PrimaryKeyField + /// it is returned here. + /// + public object GetPrimaryKey(object o) { + var d = o.ToDictionary(); + object result = null; + d.TryGetValue(PrimaryKeyField, out result); + return result; + } + /// + /// The name of the Database table we're working with. This defaults to + /// the class name - set this value if it's different + /// + public string TableName { get; set; } + /// + /// Adds a record to the database. You can pass in an Anonymous object, an ExpandoObject, + /// A regular old POCO, or a NameValueColletion from a Request.Form or Request.QueryString + /// + public dynamic Insert(object o) { + dynamic result = 0; + if (BeforeInsert(o)) { + using (var conn = OpenConnection()) { + using (var cmd = CreateInsertCommand(o)) { + cmd.Connection = conn; + result = cmd.ExecuteScalar(); + } + AfterInsert(o); + } + } + return result; + } + + /// + /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with + /// + public DbCommand CreateInsertCommand(object o) { + DbCommand result = null; + //turn this into an expando - we'll need that for the validators + var expando = o.ToExpando(); + var settings = (IDictionary)expando; + var sbKeys = new StringBuilder(); + var sbVals = new StringBuilder(); + var stub = "INSERT INTO {0} ({1}) \r\n VALUES ({2}); \r\nSELECT SCOPE_IDENTITY()"; + result = CreateCommand(stub); + + int counter = 0; + foreach (var item in settings) { + sbKeys.AppendFormat("{0},", item.Key); + sbVals.AppendFormat("@{0},", counter.ToString()); + result.AddParam(item.Value); + counter++; + } + if (counter > 0) { + //strip off trailing commas + var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 1); + var vals = sbVals.ToString().Substring(0, sbVals.Length - 1); + var sql = string.Format(stub, TableName, keys, vals); + result.CommandText = sql; + } else throw new InvalidOperationException("Can't parse this object to the database - there are no properties set"); + return result; + } + + /// + /// Creates a command for use with transactions - internal stuff mostly, but here for you to play with + /// + public DbCommand CreateUpdateCommand(object o, object key) { + var expando = o.ToExpando(); + var settings = (IDictionary)expando; + var sbKeys = new StringBuilder(); + var stub = "UPDATE {0} SET {1} WHERE {2} = @{3}"; + var args = new List(); + var result = CreateCommand(stub); + int counter = 0; + foreach (var item in settings) { + var val = item.Value; + if (!item.Key.Equals(PrimaryKeyField, StringComparison.CurrentCultureIgnoreCase) && item.Value != null) { + result.AddParam(val); + sbKeys.AppendFormat("{0} = @{1}, \r\n", item.Key, counter.ToString()); + counter++; + } + } + if (counter > 0) { + //add the key + result.AddParam(key); + //strip the last commas + var keys = sbKeys.ToString().Substring(0, sbKeys.Length - 4); + result.CommandText = string.Format(stub, TableName, keys, PrimaryKeyField, counter); + } else throw new InvalidOperationException("No parsable object was sent in - could not divine any name/value pairs"); + return result; + } + /// + /// Updates a record in the database. You can pass in an Anonymous object, an ExpandoObject, + /// A regular old POCO, or a NameValueCollection from a Request.Form or Request.QueryString + /// + public int Update(object o, object key) { + //turn this into an expando - we'll need that for the validators + int result = 0; + if (BeforeUpdate(o)) { + using (var conn = OpenConnection()) { + using (var cmd = CreateUpdateCommand(o, key)) { + result = cmd.ExecuteNonQuery(); + AfterUpdate(o); + } + } + } + return result; + } + /// + /// Updates a bunch of records in the database within a transaction. You can pass Anonymous objects, ExpandoObjects, + /// Regular old POCOs - these all have to have a PK set + /// + public int InsertMany(IEnumerable things) { + int result = 0; + using (var conn = OpenConnection()) { + using (var tx = conn.BeginTransaction()) { + foreach (var item in things) { + if (BeforeInsert(item)) { + using (var cmd = CreateInsertCommand(item)) { + cmd.Connection = conn; + cmd.Transaction = tx; + cmd.ExecuteNonQuery(); + } + AfterInsert(item); + } + result++; + } + tx.Commit(); + } + } + return result; + } + /// + /// Updates a bunch of records in the database within a transaction. You can pass Anonymous objects, ExpandoObjects, + /// Regular old POCOs - these all have to have a PK set + /// + public int UpdateMany(IEnumerable things) { + int result = 0; + using (var conn = OpenConnection()) { + using (var tx = conn.BeginTransaction()) { + foreach (var item in things) { + var pk = GetPrimaryKey(item); + if (pk == null) + throw new InvalidOperationException("Please be sure to set a value for the primary key"); + if (BeforeUpdate(item)) { + using (var cmd = CreateUpdateCommand(item, pk)) { + cmd.Connection = conn; + cmd.Transaction = tx; + cmd.ExecuteNonQuery(); + } + AfterUpdate(item); + } + result++; + } + tx.Commit(); + } + } + return result; + } + /// + /// If you're feeling lazy, or are just unsure about whether to use Update or Insert you can use + /// this method. It will look for a PrimaryKeyField with a set value to determine if this should + /// be an Insert or Save. You can pass in an Anonymous object, an ExpandoObject, + /// A regular old POCO, or a NameValueColletion from a Request.Form or Request.QueryString + /// + public dynamic Save(object o) { + dynamic result = 0; + if (BeforeSave(o)) { + var expando = o.ToExpando(); + //decide insert or update + result = HasPrimaryKey(expando) ? Update(expando, GetPrimaryKey(o)) : Insert(expando); + AfterSave(o); + } + return result; + } + /// + /// Removes a record from the database + /// + public int Delete(object key) { + //execute + var sql = string.Format("DELETE FROM {0} WHERE {1} = @0", TableName, PrimaryKeyField); + var result = 0; + using (var conn = OpenConnection()) { + using (var cmd = CreateCommand(sql, key)) { + cmd.Connection = conn; + result = cmd.ExecuteNonQuery(); + } + } + return result; + } + /// + /// Removes one or more records from the DB according to the passed-in WHERE + /// + public dynamic Delete(string where, params object[] args) { + //execute + var sql = string.Format("DELETE FROM {0} ", TableName); + sql += where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase) ? where : "WHERE " + where; + var result = 0; + using (var conn = OpenConnection()) { + using (var cmd = CreateCommand(sql, args)) { + cmd.Connection = conn; + result = cmd.ExecuteNonQuery(); + } + } + return result; + } + /// + /// Returns all records complying with the passed-in WHERE clause and arguments, + /// ordered as specified, limited (TOP) by limit. + /// + public IEnumerable All(string where = "", string orderBy = "", int limit = 0, params object[] args) { + string sql = limit > 0 ? "SELECT TOP " + limit + " * FROM {0} " : "SELECT * FROM {0} "; + if (!string.IsNullOrEmpty(where)) + sql += where.Trim().StartsWith("where", StringComparison.CurrentCultureIgnoreCase) ? where : "WHERE " + where; + if (!String.IsNullOrEmpty(orderBy)) + sql += orderBy.Trim().StartsWith("order by", StringComparison.CurrentCultureIgnoreCase) ? orderBy : " ORDER BY " + orderBy; + return Query(string.Format(sql, TableName), args); + } + /// + /// Returns a single row from the database + /// + /// ExpandoObject + public dynamic Single(object key) { + var sql = string.Format("SELECT * FROM {0} WHERE {1} = @0", TableName, PrimaryKeyField); + return Query(sql, key).FirstOrDefault(); + } + #region hooks + //hooks for save routines + public virtual bool BeforeInsert(object o) { return true; } + public virtual bool BeforeUpdate(object o) { return true; } + public virtual bool BeforeSave(object o) { return true; } + public virtual bool BeforeDelete(object key) { return true; } + public virtual void AfterInsert(object o) { } + public virtual void AfterUpdate(object o) { } + public virtual void AfterSave(object o) { } + public virtual void AfterDelete(object key) { } + #endregion + } +} \ No newline at end of file diff --git a/trunk/packages/repositories.config b/trunk/packages/repositories.config new file mode 100644 index 0000000..46c3084 --- /dev/null +++ b/trunk/packages/repositories.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/trunk/pithos.snk b/trunk/pithos.snk new file mode 100644 index 0000000..2069d32 Binary files /dev/null and b/trunk/pithos.snk differ