Welcome to mirror list, hosted at ThFree Co, Russian Federation.

Span.cs « Objects « Data « System « System.Data.Entity « referencesource « class « mcs - github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: c74e95d9534de4a9f4ecbcaf5b1a51c0f3b9f2b6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
//---------------------------------------------------------------------
// <copyright file="Span.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner       [....]
// @backupOwner [....]
//---------------------------------------------------------------------

namespace System.Data.Objects
{
    using System.Collections.Generic;
    using System.Data.Common.Internal;
    using System.Diagnostics;
    using System.Text;

    /// <summary>
    /// A collection of paths to determine which entities are spanned into a query.
    /// </summary>
    internal sealed class Span
    {
        private List<SpanPath> _spanList;
        private string _cacheKey;

        internal Span()
        {
            _spanList = new List<SpanPath>();
        }

        /// <summary>
        /// The list of paths that should be spanned into the query
        /// </summary>
        internal List<SpanPath> SpanList
        {
            get { return _spanList; }
        }

        /// <summary>
        /// Checks whether relationship span needs to be performed. Currently this is only when the query is
        /// not using MergeOption.NoTracking.
        /// </summary>
        /// <param name="mergeOption"></param>
        /// <returns>True if the query needs a relationship span rewrite</returns>
        internal static bool RequiresRelationshipSpan(MergeOption mergeOption)
        {
            return (mergeOption != MergeOption.NoTracking);
        }

        /// <summary>
        /// Includes the specified span path in the specified span instance and returns the updated span instance.
        /// If <paramref name="spanToIncludeIn"/> is null, a new span instance is constructed and returned that contains
        /// the specified include path.
        /// </summary>
        /// <param name="spanToIncludeIn">The span instance to which the include path should be added. May be null</param>
        /// <param name="pathToInclude">The include path to add</param>
        /// <returns>A non-null span instance that contains the specified include path in addition to any paths ut already contained</returns>
        internal static Span IncludeIn(Span spanToIncludeIn, string pathToInclude)
        {
            if (null == spanToIncludeIn)
            {
                spanToIncludeIn = new Span();
            }

            spanToIncludeIn.Include(pathToInclude);
            return spanToIncludeIn;
        }

        /// <summary>
        /// Returns a span instance that is the union of the two specified span instances.
        /// If <paramref name="span1"/> and <paramref name="span2"/> are both <c>null</c>,
        /// then <c>null</c> is returned.
        /// If <paramref name="span1"/> or <paramref name="span2"/> is null, but the remaining argument is non-null,
        /// then the non-null argument is returned.
        /// If neither <paramref name="span1"/> nor <paramref name="span2"/> are null, a new span instance is returned
        /// that contains the merged span paths from both.
        /// </summary>
        /// <param name="span1">The first span instance from which to include span paths; may be <c>null</c></param>
        /// <param name="span2">The second span instance from which to include span paths; may be <c>null</c></param>
        /// <returns>A span instance representing the union of the two arguments; may be <c>null</c> if both arguments are null</returns>
        internal static Span CopyUnion(Span span1, Span span2)
        {
            if (null == span1)
            {
                return span2;
            }

            if (null == span2)
            {
                return span1;
            }

            Span retSpan = span1.Clone();
            foreach (SpanPath path in span2.SpanList)
            {
                retSpan.AddSpanPath(path);
            }

            return retSpan;
        }

        internal string GetCacheKey()
        {
            if (null == _cacheKey)
            {
                if (_spanList.Count > 0)
                {
                    // If there is only a single Include path with a single property,
                    // then simply use the property name as the cache key rather than
                    // creating any new strings.
                    if (_spanList.Count == 1 &&
                       _spanList[0].Navigations.Count == 1)
                    {
                        _cacheKey = _spanList[0].Navigations[0];
                    }
                    else
                    {
                        StringBuilder keyBuilder = new StringBuilder();
                        for (int pathIdx = 0; pathIdx < _spanList.Count; pathIdx++)
                        {
                            if (pathIdx > 0)
                            {
                                keyBuilder.Append(";");
                            }

                            SpanPath thisPath = _spanList[pathIdx];
                            keyBuilder.Append(thisPath.Navigations[0]);
                            for (int propIdx = 1; propIdx < thisPath.Navigations.Count; propIdx++)
                            {
                                keyBuilder.Append(".");
                                keyBuilder.Append(thisPath.Navigations[propIdx]);
                            }
                        }

                        _cacheKey = keyBuilder.ToString();
                    }
                }
            }

            return _cacheKey;
        }

        /// <summary>
        /// Adds a path to span into the query.
        /// </summary>
        /// <param name="path">The path to span</param>
        public void Include(string path)
        {
            EntityUtil.CheckStringArgument(path, "path");
            if (path.Trim().Length == 0)
            {
                throw new ArgumentException(System.Data.Entity.Strings.ObjectQuery_Span_WhiteSpacePath, "path");
            }
            
            SpanPath spanPath = new SpanPath(ParsePath(path));
            AddSpanPath(spanPath);
            _cacheKey = null;
        }

        /// <summary>
        /// Creates a new Span with the same SpanPaths as this Span
        /// </summary>
        /// <returns></returns>
        internal Span Clone()
        {
            Span newSpan = new Span();
            newSpan.SpanList.AddRange(_spanList);
            newSpan._cacheKey = this._cacheKey;

            return newSpan;
        }

        /// <summary>
        /// Adds the path if it does not already exist
        /// </summary>
        /// <param name="spanPath"></param>
        internal void AddSpanPath(SpanPath spanPath)
        {
            if (ValidateSpanPath(spanPath))
            {
                RemoveExistingSubPaths(spanPath);
                _spanList.Add(spanPath);
            }
        }

        /// <summary>
        /// Returns true if the path can be added
        /// </summary>
        /// <param name="spanPath"></param>
        private bool ValidateSpanPath(SpanPath spanPath)
        {

            // Check for dupliacte entries
            for (int i = 0; i < _spanList.Count; i++)
            { 
                // make sure spanPath is not a sub-path of anything already in the list
                if (spanPath.IsSubPath(_spanList[i]))
                {
                    return false;
                }
            }
            return true;
        }

        private void RemoveExistingSubPaths(SpanPath spanPath)
        {
            List<SpanPath> toDelete = new List<SpanPath>();
            for (int i = 0; i < _spanList.Count; i++)
            {
                // make sure spanPath is not a sub-path of anything already in the list
                if (_spanList[i].IsSubPath(spanPath))
                {
                    toDelete.Add(_spanList[i]);
                }
            }

            foreach (SpanPath path in toDelete)
            {
                _spanList.Remove(path);
            }
        }

        /// <summary>
        /// Storage for a span path
        /// Currently this includes the list of navigation properties
        /// </summary>
        internal class SpanPath
        {
            public readonly List<string> Navigations;

            public SpanPath(List<string> navigations)
            {
                Navigations = navigations;
            }

            public bool IsSubPath(SpanPath rhs)
            {
                // this is a subpath of rhs if it has fewer paths, and all the path element values are equal
                if (Navigations.Count > rhs.Navigations.Count)
                {
                    return false;
                }

                for (int i = 0; i < Navigations.Count; i++)
                {
                    if (!Navigations[i].Equals(rhs.Navigations[i], StringComparison.OrdinalIgnoreCase))
                    {
                        return false;
                    }
                }

                return true;
            }
        }

        private static List<string> ParsePath(string path)
        {
            List<string> navigations = MultipartIdentifier.ParseMultipartIdentifier(path, "[", "]", '.');

            for (int i = navigations.Count - 1; i >= 0; i--)
            {
                if (navigations[i] == null)
                {
                    navigations.RemoveAt(i);
                }
                else if (navigations[i].Length == 0)
                {
                    throw EntityUtil.SpanPathSyntaxError();
                }
            }

            Debug.Assert(navigations.Count > 0, "Empty path found");
            return navigations;
        }
    
    }
}