|
|
|
Title:
|
SAP Parser
|
Language:
|
C#
|
|
Description:
|
A sequential access parser (api) for parsing a types graph
|
Views:
|
340
|
|
Author:
|
Stephen Smith
|
Date Added:
|
6/27/2012
|
Copy
|
Code
|
|
1/// <summary>
2/// A sequential access parser (api) for parsing a types graph
3/// </summary>
4/// <remarks>
5/// Throws an <see cref="InvalidOperationException"/> if a cyclic
6/// dependency is found.
7/// </remarks>
8public class SapTypeParser
9 {
10 protected static Type[] DefaultNotCustomClasses =
11 {
12 typeof (Type),
13 typeof (Uri)
14 };
15
16 private readonly List<Type> _isMapped = new List<Type>(3);
17 private readonly Type[] _notCustomClasses;
18 private char _pathSeperator;
19
20 public enum Continuation
21 {
22 Continue,
23 Stop,
24 Skip
25 };
26
27 public delegate Continuation ParseTypeEventHandler(Type type, string path);
28 public delegate Continuation ParsePropertyEventHandler(PropertyInfo property, string path);
29
30 public event ParseTypeEventHandler OnType;
31 public event ParseTypeEventHandler OnTypeEnd;
32 public event ParsePropertyEventHandler ScalarProperty;
33 public event ParsePropertyEventHandler ClassProperty;
34 public event ParsePropertyEventHandler ClassPropertyEnd;
35 public event ParsePropertyEventHandler EnumerableProperty;
36 public event ParsePropertyEventHandler EnumerablePropertyEnd;
37 public event ParsePropertyEventHandler OnProperty;
38
39 public SapTypeParser(Type type, params Type[] notCustomClasses)
40 {
41 Type = type;
42 _notCustomClasses = notCustomClasses.Length == 0 ? DefaultNotCustomClasses : notCustomClasses;
43 }
44
45 protected Type Type { get; private set; }
46
47 public void Parse(Func<PropertyInfo, bool> predicate = null, char pathSeperator = '.')
48 {
49 _pathSeperator = pathSeperator;
50 predicate = predicate ?? (p => true);
51 Parse(Type, string.Empty, predicate);
52 }
53
54 protected virtual Continuation Parse(Type type, string path, Func<PropertyInfo, bool> predicate)
55 {
56 if (_isMapped.Contains(type))
57 throw new InvalidOperationException(
58 string.Format(
59 "A cyclic dependency has been identified for type {0} ({1})! We cannot parse cyclic dependant graphs.",
60 type.Name, path));
61
62 var result = DoOnType(type, ref path, predicate);
63
64 if (result != Continuation.Stop)
65 result = DoOnTypeEnd(type, ref path);
66
67 return result;
68 }
69
70 protected virtual Continuation DoOnType(Type type, ref string path, Func<PropertyInfo, bool> predicate)
71 {
72 if (OnType != null)
73 {
74 var continuation = OnType(type, path);
75 if (!Continue(ref continuation)) return continuation;
76 }
77 var properties = type.GetProperties().Where(predicate);
78 return ParseProperties(properties, ref path, predicate);
79 }
80
81 protected virtual Continuation DoOnTypeEnd(Type type, ref string path)
82 {
83 var result = Continuation.Continue;
84
85 if (OnTypeEnd != null)
86 {
87 result = OnTypeEnd(type, path);
88 }
89 return result;
90 }
91
92 private Continuation ParseProperties(IEnumerable<PropertyInfo> properties, ref string path,
93 Func<PropertyInfo, bool> predicate)
94 {
95 var result = Continuation.Continue;
96
97 foreach (var property in properties)
98 {
99 DoOnProperty(property, ref path);
100
101 if (result == Continuation.Continue)
102 {
103 if (IsEnumerable(property))
104 {
105 DoEnumerableProperty(property, ref path, predicate);
106 if (result == Continuation.Continue)
107 DoEnumerablePropertyEnd(property, ref path);
108 }
109 else if (IsCustomClass(property))
110 {
111 result = DoClassProperty(property, ref path, predicate);
112 if (result == Continuation.Continue)
113 result = DoClassPropertyEnd(property, ref path);
114 }
115 else
116 {
117 DoScalarProperty(property, ref path);
118 }
119 }
120 if (result == Continuation.Stop) return result;
121 }
122 return Continuation.Continue;
123 }
124
125 protected virtual Continuation DoOnProperty(PropertyInfo property, ref string path)
126 {
127 var result = Continuation.Continue;
128 if (OnProperty != null)
129 result = OnProperty(property, path);
130 return result;
131 }
132
133 protected virtual Continuation DoEnumerableProperty(PropertyInfo property, ref string path,
134 Func<PropertyInfo, bool> predicate)
135 {
136 var result = Continuation.Continue;
137
138 if (EnumerableProperty != null)
139 {
140 result = EnumerableProperty(property, path);
141 }
142
143 if (result == Continuation.Continue)
144 {
145 var elementType = GetElementType(property);
146 if (IsCustomClass(elementType))
147 {
148 result = Parse(elementType, Append(path, property.Name), predicate);
149 }
150 }
151
152 return result;
153 }
154
155 protected virtual Continuation DoEnumerablePropertyEnd(PropertyInfo property, ref string path)
156 {
157 var result = Continuation.Continue;
158 if (EnumerablePropertyEnd != null)
159 result = EnumerablePropertyEnd(property, path);
160 return result;
161 }
162
163 protected virtual Continuation DoClassProperty(PropertyInfo property, ref string path,
164 Func<PropertyInfo, bool> predicate)
165 {
166 var result = Continuation.Continue;
167
168 if (ClassProperty != null)
169 {
170 result = ClassProperty(property, path);
171 }
172
173 if (result == Continuation.Continue)
174 {
175 result = Parse(property.PropertyType, Append(path, property.Name), predicate);
176 }
177
178 return result;
179 }
180
181 protected virtual Continuation DoClassPropertyEnd(PropertyInfo property, ref string path)
182 {
183 return ClassPropertyEnd != null ? ClassPropertyEnd(property, path) : Continuation.Continue;
184 }
185
186 protected virtual Continuation DoScalarProperty(PropertyInfo property, ref string path)
187 {
188 return ScalarProperty != null ? ScalarProperty(property, path) : Continuation.Continue;
189 }
190
191 private static bool Continue(ref Continuation continuation)
192 {
193 switch (continuation)
194 {
195 case Continuation.Skip:
196 continuation = Continuation.Continue;
197 return false;
198 case Continuation.Stop:
199 return false;
200 case Continuation.Continue:
201 return true;
202 default:
203 throw new NotImplementedException("Continuation value is not supported! Internal error.");
204 }
205 }
206
207 private string Append(string path, string name)
208 {
209 if (string.IsNullOrEmpty(path)) return name;
210 path = path.TrimEnd(_pathSeperator);
211 return string.Format("{0}{1}{2}", path, _pathSeperator, name);
212 }
213
214 private bool IsCustomClass(PropertyInfo property)
215 {
216 var type = property.PropertyType;
217 return (Type.GetTypeCode(type) == TypeCode.Object && !_notCustomClasses.Contains(type));
218 }
219
220 private static bool IsCustomClass(Type type)
221 {
222 return (Type.GetTypeCode(type) == TypeCode.Object);
223 }
224
225 private static bool IsEnumerable(PropertyInfo property)
226 {
227 var type = property.PropertyType;
228 return typeof (IEnumerable).IsAssignableFrom(type) && Type.GetTypeCode(type) != TypeCode.String;
229 }
230
231 private static Type GetElementType(PropertyInfo property)
232 {
233 var type = property.PropertyType;
234
235 if (type.IsArray)
236 {
237 return type.GetElementType();
238 }
239
240 return type.GetInterfaces()
241 .Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEnumerable<>))
242 .Select(i => i.GetGenericArguments()[0])
243 .FirstOrDefault()
244 ??
245 typeof(object); // default to object, for non-generic collections!
246 }
247 }
248
|
|
Usage
|
Use the various events to handle the parsed token.
Code is easy to read so usage can easily be understood
|
|
|