Json.NET
Code Coverage Statistics for Source File

Newtonsoft.Json\Linq\JPath.cs

Symbol Coverage: 100.00% (81 of 81)

Branch Coverage: 100.00% (48 of 48)

Cyclomatic Complexity Avg: 5.17 Max:11

Code Lines: 79


L V Source
1
using System;
2
using System.Collections.Generic;
3
using System.Globalization;
4
using Newtonsoft.Json.Utilities;
5

  
6
namespace Newtonsoft.Json.Linq
7
{
8
  internal class JPath
9
  {
10
    private readonly string _expression;
11
    public List<object> Parts { get; private set; }
12

  
13
    private int _currentIndex;
14

  
15
 33
    public JPath(string expression)
16
    {
17
 33
      ValidationUtils.ArgumentNotNull(expression, "expression");
18
 33
      _expression = expression;
19
 33
      Parts = new List<object>();
20

  
21
 33
      ParseMain();
22
 28
    }
23

  
24
    private void ParseMain()
25
    {
26
 33
      int currentPartStartIndex = _currentIndex;
27
 33
      bool followingIndexer = false;
28

  
29
 313
      while (_currentIndex < _expression.Length)
30
      {
31
 285
        char currentChar = _expression[_currentIndex];
32

  
33
 285
        switch (currentChar)
34
        {
35
          case '[':
36
          case '(':
37
 31
            if (_currentIndex > currentPartStartIndex)
38
            {
39
 16
              string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
40
 16
              Parts.Add(member);
41
            }
42

  
43
 31
            ParseIndexer(currentChar);
44
 28
            currentPartStartIndex = _currentIndex + 1;
45
 28
            followingIndexer = true;
46
 28
            break;
47
          case ']':
48
          case ')':
49
 1
            throw new Exception("Unexpected character while parsing path: " + currentChar);
50
          case '.':
51
 27
            if (_currentIndex > currentPartStartIndex)
52
            {
53
 6
              string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
54
 6
              Parts.Add(member);
55
            }
56
 27
            currentPartStartIndex = _currentIndex + 1;
57
 27
            followingIndexer = false;
58
 27
            break;
59
          default:
60
 226
            if (followingIndexer)
61
 1
              throw new Exception("Unexpected character following indexer: " + currentChar);
62
 225
            break;
63
        }
64

  
65
 280
        _currentIndex++;
66
      }
67

  
68
 28
      if (_currentIndex - 1 > currentPartStartIndex)
69
      {
70
 18
        string member = _expression.Substring(currentPartStartIndex, _currentIndex - currentPartStartIndex);
71
 18
        Parts.Add(member);
72
      }
73
 28
    }
74

  
75
    private void ParseIndexer(char indexerOpenChar)
76
    {
77
 31
      _currentIndex++;
78

  
79
 31
      char indexerCloseChar = (indexerOpenChar == '[') ? ']' : ')';
80
 31
      int indexerStart = _currentIndex;
81
 31
      int indexerLength = 0;
82
 31
      bool indexerClosed = false;
83

  
84
 83
      while (_currentIndex < _expression.Length)
85
      {
86
 82
        char currentCharacter = _expression[_currentIndex];
87
 82
        if (char.IsDigit(currentCharacter))
88
        {
89
 52
          indexerLength++;
90
        }
91
 30
        else if (currentCharacter == indexerCloseChar)
92
        {
93
 29
          indexerClosed = true;
94
 29
          break;
95
        }
96
        else
97
        {
98
 1
          throw new Exception("Unexpected character while parsing path indexer: " + currentCharacter);
99
        }
100

  
101
 52
        _currentIndex++;
102
      }
103

  
104
 30
      if (!indexerClosed)
105
 1
        throw new Exception("Path ended with open indexer. Expected " + indexerCloseChar);
106

  
107
 29
      if (indexerLength == 0)
108
 1
        throw new Exception("Empty path indexer.");
109

  
110
 28
      string indexer = _expression.Substring(indexerStart, indexerLength);
111
 28
      Parts.Add(Convert.ToInt32(indexer, CultureInfo.InvariantCulture));
112
 28
    }
113

  
114
    internal JToken Evaluate(JToken root, bool errorWhenNoMatch)
115
    {
116
 21
      JToken current = root;
117

  
118
 21
      foreach (object part in Parts)
119
      {
120
 40
        string propertyName = part as string;
121
 40
        if (propertyName != null)
122
        {
123
 23
          JObject o = current as JObject;
124
 23
          if (o != null)
125
          {
126
 21
            current = o[propertyName];
127

  
128
 21
            if (current == null && errorWhenNoMatch)
129
 1
              throw new Exception("Property '{0}' does not exist on JObject.".FormatWith(CultureInfo.InvariantCulture, propertyName));
130
          }
131
          else
132
          {
133
 2
            if (errorWhenNoMatch)
134
 1
              throw new Exception("Property '{0}' not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, propertyName, current.GetType().Name));
135

  
136
 1
            return null;
137
          }
138
        }
139
        else
140
        {
141
 17
          int index = (int) part;
142

  
143
 17
          JArray a = current as JArray;
144

  
145
 17
          if (a != null)
146
          {
147
 13
            if (a.Count <= index)
148
            {
149
 3
              if (errorWhenNoMatch)
150
 1
                throw new IndexOutOfRangeException("Index {0} outside the bounds of JArray.".FormatWith(CultureInfo.InvariantCulture, index));
151
              
152
 2
              return null;
153
            }
154

  
155
 10
            current = a[index];
156
          }
157
          else
158
          {
159
 4
            if (errorWhenNoMatch)
160
 2
              throw new Exception("Index {0} not valid on {1}.".FormatWith(CultureInfo.InvariantCulture, index, current.GetType().Name));
161

  
162
 2
            return null;
163
          }
164
        }
165
      }
166

  
167
 11
      return current;
168
 16
    }
169
  }
170
}