PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/ABB.SrcML.Data-Monolithic/SrcMLData.cs

https://github.com/nkcsgexi/SrcML.NET
C# | 410 lines | 268 code | 46 blank | 96 comment | 31 complexity | a45490075a9a686f2f0f6f89c55f3425 MD5 | raw file
  1. /******************************************************************************
  2. * Copyright (c) 2011 ABB Group
  3. * All rights reserved. This program and the accompanying materials
  4. * are made available under the terms of the Eclipse Public License v1.0
  5. * which accompanies this distribution, and is available at
  6. * http://www.eclipse.org/legal/epl-v10.html
  7. *
  8. * Contributors:
  9. * Vinay Augustine (ABB Group) - initial API, implementation, & documentation
  10. * Patrick Francis (ABB Group) - Additional implementation
  11. *****************************************************************************/
  12. using System.Linq;
  13. using System.Xml.Linq;
  14. using System.Collections.Generic;
  15. using System.Data.Linq;
  16. using System.Diagnostics;
  17. using Microsoft.Samples.EntityDataReader;
  18. using System.Data;
  19. using System.Data.SqlClient;
  20. using System;
  21. using System.Globalization;
  22. using System.IO;
  23. namespace ABB.SrcML.Data
  24. {
  25. /// <summary>
  26. /// Data context object for accessing a SrcMLData database
  27. /// </summary>
  28. partial class SrcMLDataContext
  29. {
  30. /// <summary>String format to use for database connection strings where the name must be specified.</summary>
  31. public const string ConnectionStringFormat = @"data source=.\SQLEXPRESS;Integrated Security=SSPI;Database={0}";
  32. /// <summary>String format to use for database connection strings where the name must be specified.</summary>
  33. public const string ConnectionStringWithUserInstanceFormat = @"data source=.\SQLEXPRESS;Trusted_Connection=Yes;User Instance=true;AttachDbFilename={0}";
  34. /// <summary>event handler for progress events.</summary>
  35. public event EventHandler<ProgressEventArgs> RaiseProgressEvent;
  36. /// <summary>
  37. /// Raises the progress event to indicate that progress has been made generating srcML data
  38. /// </summary>
  39. /// <param name="e">arguments for this event.</param>
  40. protected virtual void OnRaiseProgressEvent(ProgressEventArgs e)
  41. {
  42. EventHandler<ProgressEventArgs> handler = RaiseProgressEvent;
  43. if (handler != null)
  44. {
  45. handler(this, e);
  46. }
  47. }
  48. /// <summary>
  49. /// Creates the database connection. If the database does not exist it will be created.
  50. /// </summary>
  51. /// <param name="dbName">Name of the db.</param>
  52. /// <returns></returns>
  53. public static SrcMLDataContext CreateDatabaseConnection(string dbName)
  54. {
  55. return CreateDatabaseConnection(dbName, false);
  56. }
  57. /// <summary>
  58. /// Creates the database connection. if the database does not exist it will be created.
  59. /// </summary>
  60. /// <param name="dbName">Name of the db.</param>
  61. /// <param name="dropIfExists">if set to <c>true</c> [drop if exists].</param>
  62. /// <returns></returns>
  63. public static SrcMLDataContext CreateDatabaseConnection(string dbName, bool dropIfExists)
  64. {
  65. if (dropIfExists)
  66. {
  67. DropDatabase(dbName);
  68. }
  69. var connectionString = String.Format(CultureInfo.InvariantCulture, ConnectionStringFormat, dbName);
  70. CreateDatabaseIfMissing(connectionString);
  71. return new SrcMLDataContext(connectionString);
  72. }
  73. /// <summary>
  74. /// Drops the database.
  75. /// </summary>
  76. /// <param name="dbName">Name of the db.</param>
  77. public static void DropDatabase(string dbName)
  78. {
  79. DropDatabase(dbName, false);
  80. }
  81. /// <summary>
  82. /// Creates a user instance database connection with the given DB file path. if the database does not exist it will be created.
  83. /// The DB name will be automatically generated by SQL Server Express.
  84. /// </summary>
  85. /// <param name="dbFileName">Path to the DB file.</param>
  86. /// <returns></returns>
  87. public static SrcMLDataContext CreateUserInstanceDatabaseConnection(string dbFileName)
  88. {
  89. var connectionString = String.Format(CultureInfo.InvariantCulture, ConnectionStringWithUserInstanceFormat, dbFileName);
  90. CreateDatabaseIfMissing(connectionString);
  91. return new SrcMLDataContext(connectionString);
  92. }
  93. /// <summary>
  94. /// Drops the database in the user-instance SQLServerExpress.
  95. /// </summary>
  96. /// <param name="dbName">Name of the db.</param>
  97. public static void DropUserInstanceDatabase(string dbName)
  98. {
  99. DropDatabase(dbName, true);
  100. }
  101. /// <summary>
  102. /// Makes the name of the db.
  103. /// </summary>
  104. /// <param name="xmlFileName">Name of the XML file.</param>
  105. /// <returns>a valid DB name</returns>
  106. // TODO add more input validation
  107. public static string MakeDBName(string xmlFileName)
  108. {
  109. return Path.GetFileNameWithoutExtension(xmlFileName).Replace('.', '_').Replace("-", String.Empty);
  110. }
  111. /// <summary>
  112. /// Checks if a database with the given name exists.
  113. /// </summary>
  114. /// <param name="dbName">Name of the db.</param>
  115. /// <returns>true if the database exists; false otherwise</returns>
  116. public static bool DatabaseExists(string dbName)
  117. {
  118. bool result = false;
  119. using (var db = new SrcMLDataContext(String.Format(CultureInfo.InvariantCulture, ConnectionStringFormat, dbName)))
  120. {
  121. result = db.DatabaseExists();
  122. }
  123. return result;
  124. }
  125. private static void DropDatabase(string dbName, bool useUserInstance)
  126. {
  127. var connectionString = String.Format(CultureInfo.InvariantCulture, ConnectionStringFormat, dbName);
  128. if (useUserInstance)
  129. connectionString += ";User Instance=true";
  130. using (var db = new SrcMLDataContext(connectionString))
  131. {
  132. if (db.DatabaseExists())
  133. db.DeleteDatabase();
  134. }
  135. }
  136. private static void CreateDatabaseIfMissing(string connectionString)
  137. {
  138. using (var db = new SrcMLDataContext(connectionString))
  139. {
  140. if (!db.DatabaseExists())
  141. {
  142. db.CreateDatabase();
  143. }
  144. }
  145. }
  146. private static void Load(Archive archive, XElement fileUnit, string connectionString)
  147. {
  148. using (var db = new SrcMLDataContext(connectionString))
  149. {
  150. db.Definitions.InsertAllOnSubmit(archive.GetDefinitionsFromFileUnit(fileUnit));
  151. db.SubmitChanges();
  152. }
  153. }
  154. private Dictionary<string, int> MakeDefinitionMap(int archiveId)
  155. {
  156. var map = new Dictionary<string, int>();
  157. var pairs = from definition in this.Definitions
  158. where definition.ArchiveId == archiveId
  159. select new KeyValuePair<string, int>(definition.XPath, definition.Id);
  160. foreach (var kvp in pairs)
  161. {
  162. map[kvp.Key] = kvp.Value;
  163. }
  164. return map;
  165. }
  166. /// <summary>
  167. /// Loads the srcml archive specified by <paramref name="fileName"/> into the database
  168. /// </summary>
  169. /// <param name="fileName">Name of the file.</param>
  170. public void Load(string fileName)
  171. {
  172. // create a new archive
  173. var archive = new Archive(new SrcMLFile(fileName));
  174. this.Archives.InsertOnSubmit(archive);
  175. this.SubmitChanges();
  176. Debug.WriteLine("Added archive {0} for {1}", archive.Path, archive.Document.ProjectDirectory);
  177. OnRaiseProgressEvent(new ProgressEventArgs(archive.Path,
  178. String.Format(CultureInfo.CurrentCulture, "Added archive for {0}", archive.Document.ProjectDirectory)));
  179. // get definitions from the archive on disk and insert them into the database
  180. foreach (var unit in archive.Document.FileUnits)
  181. {
  182. Load(archive, unit, this.Connection.ConnectionString);
  183. }
  184. OnRaiseProgressEvent(new ProgressEventArgs(archive.Path, "Finished loading definitions"));
  185. UpdateFunctionsWithDefaults(archive.Id);
  186. OnRaiseProgressEvent(new ProgressEventArgs(archive.Path, "Finished updating functions with default values."));
  187. CreateScopeRelations(archive);
  188. OnRaiseProgressEvent(new ProgressEventArgs(archive.Path, "Finished creating variable-scope relations."));
  189. CreateCallGraph(archive);
  190. OnRaiseProgressEvent(new ProgressEventArgs(archive.Path, "Finished creating call graph."));
  191. }
  192. /// <summary>
  193. /// Given the SrcML Name element for a use of a variable, this method returns the corresponding ABB.SrcML.Data.VariableDeclaration.
  194. /// </summary>
  195. /// <param name="nameElement">The SrcML Name element to define.</param>
  196. public VariableDeclaration GetDeclarationForVariableName(XElement nameElement)
  197. {
  198. if(nameElement == null) { throw new ArgumentNullException("nameElement"); }
  199. if(nameElement.Name != SRC.Name)
  200. {
  201. throw new ArgumentException(string.Format("Expected a name element, recieved {0}", nameElement.Name), "nameElement");
  202. }
  203. VariableDeclaration result = null;
  204. var childNames = nameElement.Elements(SRC.Name).ToList();
  205. if(childNames.Count > 0)
  206. {
  207. //name element has nested name elements
  208. //for example: std::foo, or MyClass::foo, or argv[0]
  209. //just grab the rightmost nested name element
  210. nameElement = childNames.Last();
  211. //TODO: make this search properly within class definitions if necessary, and handle other cases
  212. //TODO: names with indexers after them will not match a declaration properly. This needs to be fixed in the database.
  213. }
  214. var name = nameElement.Value;
  215. var parent = (from ancestor in nameElement.Ancestors()
  216. where ContainerNames.All.Contains(ancestor.Name)
  217. select ancestor).FirstOrDefault();
  218. var parentXPath = parent.GetXPath(false);
  219. //search for a local declaration for the variable
  220. var declarations = from scope in this.ValidScopes
  221. where scope.XPath == parentXPath
  222. let declaration = (VariableDeclaration)scope.Definition
  223. where declaration.DeclarationName == name
  224. select declaration;
  225. if(declarations != null)
  226. {
  227. result = declarations.FirstOrDefault();
  228. if(result == null)
  229. {
  230. //no local declarations found, search for a global declaration
  231. var globalDeclarations = from declaration in this.Definitions.OfType<VariableDeclaration>()
  232. where declaration.IsGlobal ?? false
  233. where declaration.DeclarationName == name
  234. select declaration;
  235. result = globalDeclarations.FirstOrDefault();
  236. }
  237. }
  238. return result;
  239. }
  240. /// <summary>
  241. /// Returns the MethodDefinition associated with a given method call srcML element.
  242. /// </summary>
  243. /// <param name="callElement">The Call element to find the method definition for.</param>
  244. public MethodDefinition GetDefinitionForMethodCall(XElement callElement)
  245. {
  246. if(callElement == null) { throw new ArgumentNullException("callElement"); }
  247. if(callElement.Name != SRC.Call) { throw new ArgumentException("Passed element not a Call.", "callElement"); }
  248. var callees = from call in this.MethodCalls
  249. where call.XPath == callElement.GetXPath(false)
  250. select call.CalleeDefinition;
  251. MethodDefinition methodDef = null;
  252. if(callees != null)
  253. {
  254. //found the definition for the method call
  255. methodDef = callees.OfType<MethodDefinition>().FirstOrDefault();
  256. }
  257. return methodDef;
  258. }
  259. private static Tuple<string,string,int> MakeKey(string className, string methodName, Nullable<int> numberOfParameters)
  260. {
  261. return Tuple.Create(className, methodName, numberOfParameters ?? 0);
  262. }
  263. private Dictionary<Tuple<string,string,int>, MethodDefinition> MakeFunctionMap(int archiveId)
  264. {
  265. var map = new Dictionary<Tuple<string,string,int>, MethodDefinition>();
  266. var methods = from method in this.Definitions.OfType<MethodDefinition>()
  267. where method.ArchiveId == archiveId
  268. select new KeyValuePair<Tuple<string, string, int>, MethodDefinition>(
  269. MakeKey(method.MethodClassName, method.MethodName, method.NumberOfMethodParameters),
  270. method);
  271. foreach (var method in methods)
  272. map[method.Key] = method.Value;
  273. return map;
  274. }
  275. private void UpdateFunctionsWithDefaults(int archiveId)
  276. {
  277. var functionMap = MakeFunctionMap(archiveId);
  278. var methodDeclarations = from decl in this.Definitions.OfType<MethodDeclaration>()
  279. where decl.ArchiveId == archiveId
  280. select new {
  281. Key = MakeKey(decl.DeclarationClassName, decl.DeclarationName, decl.DeclarationNumberOfParameters),
  282. Count = decl.DeclarationNumberOfParametersWithDefaults
  283. };
  284. int numDecls = 0, numMatches = 0;
  285. foreach (var declaration in methodDeclarations)
  286. {
  287. numDecls++;
  288. MethodDefinition method;
  289. if (functionMap.TryGetValue(declaration.Key, out method))
  290. {
  291. numMatches++;
  292. method.NumberOfMethodParametersWithDefaults = declaration.Count;
  293. }
  294. }
  295. // Debug.WriteLine("{0} method declarations and {1} matches", numDecls, numMatches);
  296. this.SubmitChanges();
  297. }
  298. private void CreateScopeRelations(Archive archive)
  299. {
  300. // create a definition map that maps XPath values to their DB IDs
  301. var definitionMap = MakeDefinitionMap(archive.Id);
  302. // create new scope/definition relations. This uses the definitionMap to get database IDs
  303. // for each of the XPath queries
  304. var validScopes = from unit in archive.Document.FileUnits
  305. from tuple in ScopeDefinition.GetElements(unit, this)
  306. select new ValidScope()
  307. {
  308. XPath = tuple.Item1,
  309. DefinitionId = definitionMap[tuple.Item2]
  310. };
  311. // Use SqlBulkCopy to upload the ScopeDefinitionRelations to the database
  312. // This is necessary as the sheer volume of them (1.1 million for Notepad++) can take a long time using LINQ2SQL
  313. UploadTable<ValidScope>(this.Connection.ConnectionString, validScopes);
  314. }
  315. private void CreateCallGraph(Archive archive)
  316. {
  317. var methodCalls = CallGraph.BuildGraph(this.Connection.ConnectionString, archive.Path);
  318. UploadTable<MethodCall>(this.Connection.ConnectionString, methodCalls);
  319. }
  320. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "batchSize")]
  321. internal void UploadTable<TABLE>(string connectionString, IEnumerable<TABLE> data, int batchSize = 1000, int bulkCopyTimeout = 600)
  322. {
  323. using (var uploader = new SqlBulkCopy(connectionString))
  324. {
  325. var tableMapping = Mapping.GetTable(typeof(TABLE));
  326. uploader.DestinationTableName = string.Format(CultureInfo.InvariantCulture, "dbo.{0}", tableMapping.TableName);
  327. uploader.BatchSize = batchSize;
  328. uploader.BulkCopyTimeout = bulkCopyTimeout;
  329. uploader.WriteToServer(data.AsDataReader());
  330. }
  331. }
  332. internal static IEnumerable<TypeDefinition> GetDefinitionForType(SrcMLDataContext db, int archiveId, string typeName)
  333. {
  334. var q = CompiledQuery.Compile<SrcMLDataContext, int, string, IQueryable<TypeDefinition>>((SrcMLDataContext sd, int _archiveId, string _typeName) =>
  335. from t in sd.Definitions.OfType<TypeDefinition>()
  336. where t.ArchiveId == _archiveId
  337. where t.TypeName == _typeName
  338. select t);
  339. return q(db, archiveId, typeName);
  340. }
  341. }
  342. /// <summary>
  343. /// Enum for distinguishing between the different definition types
  344. /// </summary>
  345. public enum DefinitionType : int
  346. {
  347. ///<summary>indicates that this is a <see cref="UnknownDefinition"/></summary>
  348. Unknown = 0,
  349. ///<summary>indicates that this is a <see cref="ScopeDefinition"/></summary>
  350. Scope = 1,
  351. ///<summary>indicates that this is a <see cref="Declaration"/></summary>
  352. Declaration = 2,
  353. ///<summary>indicates that this is a <see cref="TypeDefinition"/></summary>
  354. ScopeType = 3,
  355. ///<summary>indicates that this is a <see cref="MethodDefinition"/></summary>
  356. ScopeMethod = 4,
  357. ///<summary>indicates that this is a <see cref="VariableDeclaration"/></summary>
  358. DeclarationVariable = 5,
  359. ///<summary>indicates that this is a <see cref="MethodDeclaration"/></summary>
  360. DeclarationMethod = 6,
  361. ///<summary>indicates that this is a <see cref="TypeDeclaration"/></summary>
  362. DeclarationType = 7
  363. }
  364. }