DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
ExternalRoutineInfo.cs
Go to the documentation of this file.
1 //
2 // Copyright 2010-2014 Deveel
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 
16 using System;
17 using System.Collections.Generic;
18 using System.Linq;
19 using System.Reflection;
20 using System.Text;
21 
22 using Deveel.Data.Types;
23 
24 namespace Deveel.Data.Routines {
25  public sealed class ExternalRoutineInfo {
26  public ExternalRoutineInfo(Type type, Type[] argTypes)
27  : this(type, null, argTypes) {
28  }
29 
30  public ExternalRoutineInfo(Type type)
31  : this(type, String.Empty) {
32  }
33 
34  public ExternalRoutineInfo(Type type, string methodName)
35  : this(type, methodName, Type.EmptyTypes) {
36  }
37 
38  public ExternalRoutineInfo(Type type, string methodName, Type[] argTypes)
39  : this(type.FullName, methodName, ToTypeNames(argTypes)) {
40  }
41 
42  public ExternalRoutineInfo(string typeString, string methodName)
43  : this(typeString, methodName, new string[0]) {
44  }
45 
46  public ExternalRoutineInfo(string typeString, string methodName, string[] argNames) {
47  this.TypeName = typeString;
48  this.MethodName = methodName;
49  this.Arguments = argNames;
50  }
51 
52  public string TypeName { get; private set; }
53 
54  public string MethodName { get; private set; }
55 
56  public bool HasMethodName {
57  get { return !String.IsNullOrEmpty(MethodName); }
58  }
59 
60  public string[] Arguments { get; private set; }
61 
62  private static string[] ToTypeNames(Type[] argTypes) {
63  if (argTypes == null || argTypes.Length == 0)
64  return new string[0];
65 
66  var argNames = new string[argTypes.Length];
67  for (var i = 0; i < argTypes.Length; i++) {
68  argNames[i] = argTypes[i].FullName;
69  }
70 
71  return argNames;
72  }
73 
79  private static Type ResolveToType(string typeString) {
80  // Trim the string
81  typeString = typeString.Trim();
82 
83  // Is this an array? Count the number of array dimensions.
84  int dimensions = -1;
85  int lastIndex = typeString.Length;
86  while (lastIndex > 0) {
87  ++dimensions;
88  lastIndex = typeString.LastIndexOf("[]", lastIndex, StringComparison.InvariantCulture) - 1;
89  }
90 
91  // Remove the array part
92  int arrayEnd = typeString.Length - (dimensions * 2);
93  String typePart = typeString.Substring(0, arrayEnd);
94  // Check there's no array parts in the class part
95  if (typePart.IndexOf("[]", StringComparison.InvariantCulture) != -1)
96  throw new Exception("Type specification incorrectly formatted: " + typeString);
97 
98  // Convert the specification to a .NET Type. For example,
99  // String is converted to typeof(System.String), etc.
100  Type cl;
101  // Is there a '.' in the class specification?
102  if (typePart.IndexOf('.') != -1) {
103  // Must be a specification such as 'System.Uri' or 'System.Collection.IList'.
104  try {
105  cl = Type.GetType(typePart);
106  } catch (TypeLoadException) {
107  throw new Exception("Type not found: " + typePart);
108  }
109  }
110 
111  // Try for a primitive types
112  else if (typePart.Equals("boolean") ||
113  typePart.Equals("bool")) {
114  cl = typeof(bool);
115  } else if (typePart.Equals("byte")) {
116  cl = typeof(byte);
117  } else if (typePart.Equals("short")) {
118  cl = typeof(short);
119  } else if (typePart.Equals("char")) {
120  cl = typeof(char);
121  } else if (typePart.Equals("int")) {
122  cl = typeof(int);
123  } else if (typePart.Equals("long")) {
124  cl = typeof(long);
125  } else if (typePart.Equals("float")) {
126  cl = typeof(float);
127  } else if (typePart.Equals("double")) {
128  cl = typeof(double);
129  } else {
130  // Not a primitive type so try resolving against System.* or some
131  // key classes in Deveel.Data.*
132  if (typePart.Equals("IProcedureConnection")) {
133  cl = typeof(IProcedureConnection);
134  } else {
135  try {
136  cl = Type.GetType("System." + typePart);
137  } catch (TypeLoadException) {
138  // No luck so give up,
139  throw new Exception("Type not found: " + typePart);
140  }
141  }
142  }
143 
144  // Finally make into a dimension if necessary
145  if (dimensions > 0) {
146  // This is a little untidy way of doing this. Perhaps a better approach
147  // would be to make an array encoded string.
148  cl = Array.CreateInstance(cl, new int[dimensions]).GetType();
149  }
150 
151  return cl;
152  }
153 
154 
155  public MethodInfo ResolveMethod(TType[] paramTypes) {
156  // The name of the class
157  String typeName;
158  // The name of the invokation method in the class.
159  String methodName;
160  // The object specification that must be matched. If any entry is 'null'
161  // then the argument parameter is discovered.
162  Type[] argTypes;
163  bool firstProcedureConnectionIgnore;
164 
165  if (!HasMethodName) {
166  // This means the typeString only specifies a class name, so we use
167  // 'Invoke' as the static method to call, and discover the arguments.
168  typeName = TypeName;
169  methodName = "Invoke";
170  // All null which means we discover the arg types dynamically
171  argTypes = new Type[paramTypes.Length];
172  // ignore IProcedureConnection is first argument
173  firstProcedureConnectionIgnore = true;
174  } else {
175  // This means we specify a class and method name and argument
176  // specification.
177  typeName = TypeName;
178  methodName = MethodName;
179  argTypes = new Type[Arguments.Length];
180 
181  for (int i = 0; i < Arguments.Length; ++i) {
182  String typeSpec = Arguments[i];
183  argTypes[i] = ResolveToType(typeSpec);
184  }
185 
186  firstProcedureConnectionIgnore = false;
187  }
188 
189  Type routineType = Type.GetType(typeName, false, true);
190  if (routineType == null)
191  throw new Exception("Procedure class not found: " + typeName);
192 
193  // Get all the methods in this class
194  MethodInfo[] methods = routineType.GetMethods(BindingFlags.Public | BindingFlags.Static);
195  MethodInfo invokeMethod = null;
196  // Search for the invoke method
197  foreach (MethodInfo method in methods) {
198  if (method.Name.Equals(methodName)) {
199  bool paramsMatch;
200 
201  // Get the parameters for this method
202  ParameterInfo[] methodArgs = method.GetParameters();
203 
204  // If no methods, and object_specification has no args then this is a
205  // match.
206  if (methodArgs.Length == 0 && argTypes.Length == 0) {
207  paramsMatch = true;
208  } else {
209  int searchStart = 0;
210  // Is the first arugments a IProcedureConnection implementation?
211  if (firstProcedureConnectionIgnore &&
212  typeof(IProcedureConnection).IsAssignableFrom(methodArgs[0].ParameterType)) {
213  searchStart = 1;
214  }
215 
216  // Do the number of arguments match
217  if (argTypes.Length == methodArgs.Length - searchStart) {
218  // Do they match the specification?
219  bool matchSpec = true;
220  for (int n = 0; n < argTypes.Length && matchSpec; ++n) {
221  Type argType = argTypes[n];
222  if (argType != null &&
223  argType != methodArgs[n + searchStart].ParameterType) {
224  matchSpec = false;
225  }
226  }
227  paramsMatch = matchSpec;
228  } else {
229  paramsMatch = false;
230  }
231  }
232 
233  if (paramsMatch) {
234  if (invokeMethod != null)
235  throw new Exception("Ambiguous public static " + methodName + " methods in stored procedure class '" + typeName + "'");
236 
237  invokeMethod = method;
238  }
239  }
240  }
241 
242  // Return the invoke method we found
243  return invokeMethod;
244  }
245 
246  public static ExternalRoutineInfo Parse(string s) {
247  // Look for the first parenthese
248  int parentheseDelim = s.IndexOf("(", StringComparison.InvariantCulture);
249 
250  if (parentheseDelim != -1) {
251  // This represents type/method
252  string typeMethod = s.Substring(0, parentheseDelim);
253  // This will be deliminated by a '.'
254  int methodDelim = typeMethod.LastIndexOf(".", StringComparison.InvariantCulture);
255  if (methodDelim == -1)
256  throw new FormatException("Incorrectly formatted method string: " + s);
257 
258  string typeString = typeMethod.Substring(0, methodDelim);
259  string methodString = typeMethod.Substring(methodDelim + 1);
260 
261  // Next parse the argument list
262  int endParentheseDelim = s.LastIndexOf(")", StringComparison.InvariantCulture);
263  if (endParentheseDelim == -1)
264  throw new FormatException("Incorrectly formatted method string: " + s);
265 
266  string argListStr = s.Substring(parentheseDelim + 1, endParentheseDelim - (parentheseDelim + 1));
267 
268  // Now parse the list of arguments
269  string[] args = argListStr.Split(new[]{','}, StringSplitOptions.RemoveEmptyEntries);
270 
271  return new ExternalRoutineInfo(typeString, methodString, args);
272  }
273 
274  // No parenthese so we assume this is a class
275  return new ExternalRoutineInfo(s, null);
276  }
277 
278  public static string FormatString(Type type) {
279  return FormatString(type, Type.EmptyTypes);
280  }
281 
282  public static string FormatString(Type type, Type[] argTypes) {
283  return FormatString(type, null, argTypes);
284  }
285 
286  public static string FormatString(Type type, string methodName) {
287  return FormatString(type, methodName, Type.EmptyTypes);
288  }
289 
290  public static string FormatString(Type type, string methodName, Type[] argTypes) {
291  var routineInfo = new ExternalRoutineInfo(type, methodName, argTypes);
292  return routineInfo.ToString();
293  }
294 
295  public override string ToString() {
296  var sb = new StringBuilder(TypeName);
297  if (HasMethodName) {
298  sb.Append('.');
299  sb.Append(MethodName);
300  }
301 
302  sb.Append('(');
303  if (Arguments != null && Arguments.Length > 0) {
304  for (int i = 0; i < Arguments.Length; i++) {
305  sb.Append(Arguments[i]);
306 
307  if (i < Arguments.Length - 1)
308  sb.Append(", ");
309  }
310  }
311 
312  sb.Append(')');
313  return sb.ToString();
314  }
315  }
316 }
ExternalRoutineInfo(Type type, Type[] argTypes)
static string FormatString(Type type, Type[] argTypes)
A long string in the system.
ExternalRoutineInfo(Type type, string methodName)
static string FormatString(Type type, string methodName)
MethodInfo ResolveMethod(TType[] paramTypes)
static ExternalRoutineInfo Parse(string s)
static string FormatString(Type type, string methodName, Type[] argTypes)
ExternalRoutineInfo(string typeString, string methodName, string[] argNames)
ExternalRoutineInfo(Type type, string methodName, Type[] argTypes)
static Type ResolveToType(string typeString)
Resolves a type specification string to a Type.
static string[] ToTypeNames(Type[] argTypes)
ExternalRoutineInfo(string typeString, string methodName)