/ABB.SrcML.Data-Monolithic/SrcMLData.cs
C# | 410 lines | 268 code | 46 blank | 96 comment | 31 complexity | a45490075a9a686f2f0f6f89c55f3425 MD5 | raw file
- /******************************************************************************
- * Copyright (c) 2011 ABB Group
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Vinay Augustine (ABB Group) - initial API, implementation, & documentation
- * Patrick Francis (ABB Group) - Additional implementation
- *****************************************************************************/
- using System.Linq;
- using System.Xml.Linq;
- using System.Collections.Generic;
- using System.Data.Linq;
- using System.Diagnostics;
- using Microsoft.Samples.EntityDataReader;
- using System.Data;
- using System.Data.SqlClient;
- using System;
- using System.Globalization;
- using System.IO;
- namespace ABB.SrcML.Data
- {
- /// <summary>
- /// Data context object for accessing a SrcMLData database
- /// </summary>
- partial class SrcMLDataContext
- {
- /// <summary>String format to use for database connection strings where the name must be specified.</summary>
- public const string ConnectionStringFormat = @"data source=.\SQLEXPRESS;Integrated Security=SSPI;Database={0}";
- /// <summary>String format to use for database connection strings where the name must be specified.</summary>
- public const string ConnectionStringWithUserInstanceFormat = @"data source=.\SQLEXPRESS;Trusted_Connection=Yes;User Instance=true;AttachDbFilename={0}";
-
- /// <summary>event handler for progress events.</summary>
- public event EventHandler<ProgressEventArgs> RaiseProgressEvent;
- /// <summary>
- /// Raises the progress event to indicate that progress has been made generating srcML data
- /// </summary>
- /// <param name="e">arguments for this event.</param>
- protected virtual void OnRaiseProgressEvent(ProgressEventArgs e)
- {
- EventHandler<ProgressEventArgs> handler = RaiseProgressEvent;
- if (handler != null)
- {
- handler(this, e);
- }
- }
- /// <summary>
- /// Creates the database connection. If the database does not exist it will be created.
- /// </summary>
- /// <param name="dbName">Name of the db.</param>
- /// <returns></returns>
- public static SrcMLDataContext CreateDatabaseConnection(string dbName)
- {
- return CreateDatabaseConnection(dbName, false);
- }
- /// <summary>
- /// Creates the database connection. if the database does not exist it will be created.
- /// </summary>
- /// <param name="dbName">Name of the db.</param>
- /// <param name="dropIfExists">if set to <c>true</c> [drop if exists].</param>
- /// <returns></returns>
- public static SrcMLDataContext CreateDatabaseConnection(string dbName, bool dropIfExists)
- {
- if (dropIfExists)
- {
- DropDatabase(dbName);
- }
- var connectionString = String.Format(CultureInfo.InvariantCulture, ConnectionStringFormat, dbName);
- CreateDatabaseIfMissing(connectionString);
- return new SrcMLDataContext(connectionString);
- }
- /// <summary>
- /// Drops the database.
- /// </summary>
- /// <param name="dbName">Name of the db.</param>
- public static void DropDatabase(string dbName)
- {
- DropDatabase(dbName, false);
- }
- /// <summary>
- /// Creates a user instance database connection with the given DB file path. if the database does not exist it will be created.
- /// The DB name will be automatically generated by SQL Server Express.
- /// </summary>
- /// <param name="dbFileName">Path to the DB file.</param>
- /// <returns></returns>
- public static SrcMLDataContext CreateUserInstanceDatabaseConnection(string dbFileName)
- {
- var connectionString = String.Format(CultureInfo.InvariantCulture, ConnectionStringWithUserInstanceFormat, dbFileName);
- CreateDatabaseIfMissing(connectionString);
- return new SrcMLDataContext(connectionString);
- }
- /// <summary>
- /// Drops the database in the user-instance SQLServerExpress.
- /// </summary>
- /// <param name="dbName">Name of the db.</param>
- public static void DropUserInstanceDatabase(string dbName)
- {
- DropDatabase(dbName, true);
- }
- /// <summary>
- /// Makes the name of the db.
- /// </summary>
- /// <param name="xmlFileName">Name of the XML file.</param>
- /// <returns>a valid DB name</returns>
- // TODO add more input validation
- public static string MakeDBName(string xmlFileName)
- {
- return Path.GetFileNameWithoutExtension(xmlFileName).Replace('.', '_').Replace("-", String.Empty);
- }
- /// <summary>
- /// Checks if a database with the given name exists.
- /// </summary>
- /// <param name="dbName">Name of the db.</param>
- /// <returns>true if the database exists; false otherwise</returns>
- public static bool DatabaseExists(string dbName)
- {
- bool result = false;
- using (var db = new SrcMLDataContext(String.Format(CultureInfo.InvariantCulture, ConnectionStringFormat, dbName)))
- {
- result = db.DatabaseExists();
- }
- return result;
- }
-
- private static void DropDatabase(string dbName, bool useUserInstance)
- {
- var connectionString = String.Format(CultureInfo.InvariantCulture, ConnectionStringFormat, dbName);
- if (useUserInstance)
- connectionString += ";User Instance=true";
- using (var db = new SrcMLDataContext(connectionString))
- {
- if (db.DatabaseExists())
- db.DeleteDatabase();
- }
- }
- private static void CreateDatabaseIfMissing(string connectionString)
- {
- using (var db = new SrcMLDataContext(connectionString))
- {
- if (!db.DatabaseExists())
- {
- db.CreateDatabase();
- }
- }
- }
- private static void Load(Archive archive, XElement fileUnit, string connectionString)
- {
- using (var db = new SrcMLDataContext(connectionString))
- {
- db.Definitions.InsertAllOnSubmit(archive.GetDefinitionsFromFileUnit(fileUnit));
- db.SubmitChanges();
- }
- }
- private Dictionary<string, int> MakeDefinitionMap(int archiveId)
- {
- var map = new Dictionary<string, int>();
- var pairs = from definition in this.Definitions
- where definition.ArchiveId == archiveId
- select new KeyValuePair<string, int>(definition.XPath, definition.Id);
- foreach (var kvp in pairs)
- {
- map[kvp.Key] = kvp.Value;
- }
- return map;
- }
- /// <summary>
- /// Loads the srcml archive specified by <paramref name="fileName"/> into the database
- /// </summary>
- /// <param name="fileName">Name of the file.</param>
- public void Load(string fileName)
- {
- // create a new archive
- var archive = new Archive(new SrcMLFile(fileName));
- this.Archives.InsertOnSubmit(archive);
- this.SubmitChanges();
-
- Debug.WriteLine("Added archive {0} for {1}", archive.Path, archive.Document.ProjectDirectory);
- OnRaiseProgressEvent(new ProgressEventArgs(archive.Path,
- String.Format(CultureInfo.CurrentCulture, "Added archive for {0}", archive.Document.ProjectDirectory)));
-
- // get definitions from the archive on disk and insert them into the database
- foreach (var unit in archive.Document.FileUnits)
- {
- Load(archive, unit, this.Connection.ConnectionString);
- }
- OnRaiseProgressEvent(new ProgressEventArgs(archive.Path, "Finished loading definitions"));
-
- UpdateFunctionsWithDefaults(archive.Id);
- OnRaiseProgressEvent(new ProgressEventArgs(archive.Path, "Finished updating functions with default values."));
-
- CreateScopeRelations(archive);
- OnRaiseProgressEvent(new ProgressEventArgs(archive.Path, "Finished creating variable-scope relations."));
-
- CreateCallGraph(archive);
- OnRaiseProgressEvent(new ProgressEventArgs(archive.Path, "Finished creating call graph."));
- }
- /// <summary>
- /// Given the SrcML Name element for a use of a variable, this method returns the corresponding ABB.SrcML.Data.VariableDeclaration.
- /// </summary>
- /// <param name="nameElement">The SrcML Name element to define.</param>
- public VariableDeclaration GetDeclarationForVariableName(XElement nameElement)
- {
- if(nameElement == null) { throw new ArgumentNullException("nameElement"); }
- if(nameElement.Name != SRC.Name)
- {
- throw new ArgumentException(string.Format("Expected a name element, recieved {0}", nameElement.Name), "nameElement");
- }
- VariableDeclaration result = null;
- var childNames = nameElement.Elements(SRC.Name).ToList();
- if(childNames.Count > 0)
- {
- //name element has nested name elements
- //for example: std::foo, or MyClass::foo, or argv[0]
- //just grab the rightmost nested name element
- nameElement = childNames.Last();
- //TODO: make this search properly within class definitions if necessary, and handle other cases
- //TODO: names with indexers after them will not match a declaration properly. This needs to be fixed in the database.
- }
- var name = nameElement.Value;
- var parent = (from ancestor in nameElement.Ancestors()
- where ContainerNames.All.Contains(ancestor.Name)
- select ancestor).FirstOrDefault();
- var parentXPath = parent.GetXPath(false);
- //search for a local declaration for the variable
- var declarations = from scope in this.ValidScopes
- where scope.XPath == parentXPath
- let declaration = (VariableDeclaration)scope.Definition
- where declaration.DeclarationName == name
- select declaration;
- if(declarations != null)
- {
- result = declarations.FirstOrDefault();
- if(result == null)
- {
- //no local declarations found, search for a global declaration
- var globalDeclarations = from declaration in this.Definitions.OfType<VariableDeclaration>()
- where declaration.IsGlobal ?? false
- where declaration.DeclarationName == name
- select declaration;
- result = globalDeclarations.FirstOrDefault();
- }
- }
- return result;
- }
- /// <summary>
- /// Returns the MethodDefinition associated with a given method call srcML element.
- /// </summary>
- /// <param name="callElement">The Call element to find the method definition for.</param>
- public MethodDefinition GetDefinitionForMethodCall(XElement callElement)
- {
- if(callElement == null) { throw new ArgumentNullException("callElement"); }
- if(callElement.Name != SRC.Call) { throw new ArgumentException("Passed element not a Call.", "callElement"); }
- var callees = from call in this.MethodCalls
- where call.XPath == callElement.GetXPath(false)
- select call.CalleeDefinition;
- MethodDefinition methodDef = null;
- if(callees != null)
- {
- //found the definition for the method call
- methodDef = callees.OfType<MethodDefinition>().FirstOrDefault();
- }
- return methodDef;
- }
- private static Tuple<string,string,int> MakeKey(string className, string methodName, Nullable<int> numberOfParameters)
- {
- return Tuple.Create(className, methodName, numberOfParameters ?? 0);
- }
-
- private Dictionary<Tuple<string,string,int>, MethodDefinition> MakeFunctionMap(int archiveId)
- {
- var map = new Dictionary<Tuple<string,string,int>, MethodDefinition>();
- var methods = from method in this.Definitions.OfType<MethodDefinition>()
- where method.ArchiveId == archiveId
- select new KeyValuePair<Tuple<string, string, int>, MethodDefinition>(
- MakeKey(method.MethodClassName, method.MethodName, method.NumberOfMethodParameters),
- method);
- foreach (var method in methods)
- map[method.Key] = method.Value;
- return map;
- }
- private void UpdateFunctionsWithDefaults(int archiveId)
- {
- var functionMap = MakeFunctionMap(archiveId);
- var methodDeclarations = from decl in this.Definitions.OfType<MethodDeclaration>()
- where decl.ArchiveId == archiveId
- select new {
- Key = MakeKey(decl.DeclarationClassName, decl.DeclarationName, decl.DeclarationNumberOfParameters),
- Count = decl.DeclarationNumberOfParametersWithDefaults
- };
- int numDecls = 0, numMatches = 0;
- foreach (var declaration in methodDeclarations)
- {
- numDecls++;
- MethodDefinition method;
- if (functionMap.TryGetValue(declaration.Key, out method))
- {
- numMatches++;
- method.NumberOfMethodParametersWithDefaults = declaration.Count;
- }
- }
- // Debug.WriteLine("{0} method declarations and {1} matches", numDecls, numMatches);
- this.SubmitChanges();
- }
- private void CreateScopeRelations(Archive archive)
- {
- // create a definition map that maps XPath values to their DB IDs
- var definitionMap = MakeDefinitionMap(archive.Id);
- // create new scope/definition relations. This uses the definitionMap to get database IDs
- // for each of the XPath queries
- var validScopes = from unit in archive.Document.FileUnits
- from tuple in ScopeDefinition.GetElements(unit, this)
- select new ValidScope()
- {
- XPath = tuple.Item1,
- DefinitionId = definitionMap[tuple.Item2]
- };
- // Use SqlBulkCopy to upload the ScopeDefinitionRelations to the database
- // This is necessary as the sheer volume of them (1.1 million for Notepad++) can take a long time using LINQ2SQL
- UploadTable<ValidScope>(this.Connection.ConnectionString, validScopes);
- }
- private void CreateCallGraph(Archive archive)
- {
- var methodCalls = CallGraph.BuildGraph(this.Connection.ConnectionString, archive.Path);
- UploadTable<MethodCall>(this.Connection.ConnectionString, methodCalls);
- }
- [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "batchSize")]
- internal void UploadTable<TABLE>(string connectionString, IEnumerable<TABLE> data, int batchSize = 1000, int bulkCopyTimeout = 600)
- {
- using (var uploader = new SqlBulkCopy(connectionString))
- {
- var tableMapping = Mapping.GetTable(typeof(TABLE));
- uploader.DestinationTableName = string.Format(CultureInfo.InvariantCulture, "dbo.{0}", tableMapping.TableName);
- uploader.BatchSize = batchSize;
- uploader.BulkCopyTimeout = bulkCopyTimeout;
- uploader.WriteToServer(data.AsDataReader());
- }
- }
- internal static IEnumerable<TypeDefinition> GetDefinitionForType(SrcMLDataContext db, int archiveId, string typeName)
- {
- var q = CompiledQuery.Compile<SrcMLDataContext, int, string, IQueryable<TypeDefinition>>((SrcMLDataContext sd, int _archiveId, string _typeName) =>
- from t in sd.Definitions.OfType<TypeDefinition>()
- where t.ArchiveId == _archiveId
- where t.TypeName == _typeName
- select t);
- return q(db, archiveId, typeName);
- }
- }
- /// <summary>
- /// Enum for distinguishing between the different definition types
- /// </summary>
- public enum DefinitionType : int
- {
- ///<summary>indicates that this is a <see cref="UnknownDefinition"/></summary>
- Unknown = 0,
- ///<summary>indicates that this is a <see cref="ScopeDefinition"/></summary>
- Scope = 1,
- ///<summary>indicates that this is a <see cref="Declaration"/></summary>
- Declaration = 2,
- ///<summary>indicates that this is a <see cref="TypeDefinition"/></summary>
- ScopeType = 3,
- ///<summary>indicates that this is a <see cref="MethodDefinition"/></summary>
- ScopeMethod = 4,
- ///<summary>indicates that this is a <see cref="VariableDeclaration"/></summary>
- DeclarationVariable = 5,
- ///<summary>indicates that this is a <see cref="MethodDeclaration"/></summary>
- DeclarationMethod = 6,
- ///<summary>indicates that this is a <see cref="TypeDeclaration"/></summary>
- DeclarationType = 7
- }
- }