// Returns a set of namespaces referenced by the given namespace. // A namespace is referenced if any type in the given namespace has a property which // references a type in another namespace. public static List <IRNamespace> ResolveNSReferences(IRNamespace ns) { HashSet <IRNamespace> references = new HashSet <IRNamespace>(); // Only classes make references. foreach (var irClass in ns.Classes) { // Loop each property and record the referenced namespace. foreach (var prop in irClass.Properties) { if (prop.Type == PropertyTypeKind.TYPE_REF) { // Go up in the parent chain to find the containing namespace! var parent = prop.ReferencedType.Parent; // A non-set parent could wreak havoc here.. while (!(parent is IRNamespace)) { parent = parent.Parent; } // Parent should be a namespace instance by now references.Add((parent as IRNamespace)); } } } // Remove reference to our own file. references.Remove(ns); return(references.ToList()); }
// The list of types start and end with the same type! private void ResolveCircularNS(List <IRNamespace> circle) { if (circle.Count() > 3) { throw new Exception("This method is not created for solving big circular dependancies!"); } // Suppose there are ONLY DIRECT CIRCULAR DEPENDANCIES. // This means one type points to another which points to the first! var nsOne = circle[0]; var nsTwo = circle[1]; // Decide which namespace is not allowed to point to the other. var cmpNS = CompareNSHeights(nsOne, nsTwo); // Namespaces indicating where types will be moved towards. // By default, nsOne is the target; nsTwo the source. IRNamespace source = nsTwo; IRNamespace target = nsOne; if (cmpNS == 1) { // The compare function says otherwise, so swap source/target. source = nsOne; target = nsTwo; } // Select all types that need to be moved from source. var typesToMove = GetAllReferencingClasses(source, target); // Move these types to target. foreach (var type in typesToMove) { MoveType(type, source, target); } }
private void RenameNamespace(IRNamespace ns) { Program.Log.Info("Renaming namespace `{0}`", ns.OriginalName); // Use the original name to generate a hash // and append it to the namespace. var nsHash = GetMD5Hash(ns.OriginalName); // Generate affix var affix = string.Format("_{0}", nsHash.Substring(0, 3)); // Append affix to both shortname and fullname. ns.ShortName = ns.ShortName + affix; ns.FullName = ns.FullName + affix; // Update all the types of this namespace. foreach (var irClass in ns.Classes) { irClass.UpdateTypeName(ns); } foreach (var irEnum in ns.Enums) { irEnum.UpdateTypeName(ns); } }
// Returns the namespace object for the given object. public static IRNamespace GetNamespaceForType(IRTypeNode type, out List <IRProgramNode> parentChain) { // Keep track of the path towards the parent namespace. parentChain = new List <IRProgramNode>(); IRNamespace result = null; // Recursively call all parents until namespace is reached var p = type.Parent; while (p != null) { // Add p as parent. parentChain.Add(p); if (p is IRNamespace) { result = p as IRNamespace; break; } p = p.Parent; } // Reverse chain so that it starts with the namespace. // The last element in the list is the direct parent of the type. parentChain.Reverse(); return(result); }
private void ResolveCircularTypes(List <IRTypeNode> circle) { // All unique types within the given list are moved together into a seperate // namespace, because that's the only way to solve these kind of circular dependancies. // This list will NEVER contain enums! IEnumerable <IRClass> distincttypes = circle.Distinct().Cast <IRClass>(); // TODO: Better naming algorithm?! (Longest commong substring) // The longest substring between the fullnames of each type's namespace. var allTypeNamespaceNames = circle.Select(type => _TypeNSMapper[type].FullName).ToList(); string newNSName = DependancyUtil.LongestmatchingSubstring(allTypeNamespaceNames).Trim('.'); // Also construct a shortname from the new namespace fullname string newNSShortName = newNSName.Substring(newNSName.LastIndexOf('.') + 1); // NOTE; A suffix is appended here in order to not increase the degree count // for the subject namespace (vertex). // Pulling types into a seperate namespace helps flattening the graph into a spanning, // while the alternative would increase the length of circular namespace chains. newNSName += EXTRACTED_NS_SUFFIX; newNSShortName += EXTRACTED_NS_SUFFIX; // There still might already be a namespace with that name, so a test is necessary anyway! IRNamespace newNS = _program.GetCreateNamespace(newNSName); // And copy over all types to this namespace. foreach (IRClass irClass in distincttypes) { IRNamespace oldNS = _TypeNSMapper[irClass]; MoveType(irClass, oldNS, newNS); } // No need to add the namespace to the program anymore! }
public static List <IRNamespace> FindNSParents(IRProgram program, IRNamespace subject) { // Return all namespaces whos fullname are found at the beginnen of the subject // namespace. var subjName = subject.FullName; var parents = program.Namespaces.Where(p => subjName.StartsWith(p.FullName)).ToList(); // Remove subject, because fullname matches always with itself. parents.Remove(subject); return(parents); }
// Write out all set options onto the given textstream. protected void WriteFileOptions(IRNamespace ns, string fileName, TextWriter w) { // Loop each option and write to the TextWriter. foreach (var kv in _fileOptions) { var optValue = kv.Value(ns, fileName); var option = string.Format("option {0} = \"{1}\";", kv.Key, optValue); w.WriteLine(option); } }
// Function almost identical to TypeTopologicalVisit(..) private void NSTopologicalVisit(IRNamespace node, List <IRNamespace> topologicalOrder, Dictionary <IRNamespace, NODE_STATE> nodeState, List <IRNamespace> visitedNodes) { // Each type is represented as a NODE. // NODE STATES: // - ALIVE: Node needs to be processed. // - UNDEAD: Currently processing this node. // - DEAD: This node has been succesfully visited. NODE_STATE state = nodeState[node]; if (state == NODE_STATE.DEAD) { return; } visitedNodes.Add(node); if (state == NODE_STATE.UNDEAD) { // Create a list of all nodes within our circle. var circleEntryIDx = visitedNodes.IndexOf(node); // Do not switch into 0-based indexing, because we want a list beginning and ending // with the same node! var circleLength = visitedNodes.Count() - circleEntryIDx; var circle = visitedNodes.GetRange(circleEntryIDx, circleLength); throw new CircularException <IRNamespace>(circle); } // Node is ALIVE and will be processed. nodeState[node] = NODE_STATE.UNDEAD; // Find all dependancies of the current namespace. var dependancies = _NSDependancies[node]; // Visit each namespace dependancy. foreach (var dep in dependancies) { // Send a copy of visited nodes to not litter the list of circular dependant nodes // when we reach an undead node. NSTopologicalVisit(dep, topologicalOrder, nodeState, visitedNodes.ToList()); } // Node is recursively visited. nodeState[node] = NODE_STATE.DEAD; topologicalOrder.Add(node); }
// Solves name collisions between types in one namespace. private void SolveCollisionsWithin(IRNamespace ns, List <string> allShortNames) { // Find all types which collide. var collisions = allShortNames.GroupBy(x => x) .Where(group => group.Count() > 1) .Select(group => group.Key); foreach (var collisionName in collisions) { // Find all types matching the collision name. // NO case mismatch! var classesEnumeration = ns.Classes.Where(c => c.ShortName.Equals(collisionName)); var enumEnumeration = ns.Enums.Where(e => e.ShortName.Equals(collisionName)); // Throw them together in one list. List <IRTypeNode> collidedTypes = new List <IRTypeNode>(); collidedTypes.AddRange(classesEnumeration); collidedTypes.AddRange(enumEnumeration); // Rename collided types. RenameTypes(collidedTypes); } }
// Converts a namespace to a package string. // The package string is based on the location of the actual file. // The package string equals the (relative) path to the folder containing files that // share the same package as parent. // eg: pkg_parent.pkg_child => <rel path>/pkg_parent/pkg_child/some_file.proto public static string ResolvePackageName(IRNamespace ns) { return(ns.FullName.ToLower()); }
// Converts data from internal caches into the IR Program structure. public override IRProgram GetRoot() { // Create a list of all types to process. List <TypeDefinition> classesToProcess = new List <TypeDefinition>(_classCache.Keys); List <TypeDefinition> enumsToProcess = new List <TypeDefinition>(_enumCache.Keys); // Holds a set of all known namespaces. Dictionary <string, IRNamespace> nsSet = new Dictionary <string, IRNamespace>(); // Until all types are processed, we repeat the same operation. while (classesToProcess.Count() > 0) { // Take the first item from the list. var currentType = classesToProcess.ElementAt(0); classesToProcess.RemoveAt(0); // Find namespace list. var nsName = GetNamespaceName(currentType); IRNamespace ns; nsSet.TryGetValue(nsName, out ns); if (ns == null) { // Construct a shortname of the last namespace part of the full name. var lastDotIdx = nsName.LastIndexOf('.'); var nsShortname = (lastDotIdx != -1) ? nsName.Substring(lastDotIdx + 1) : nsName; ns = new IRNamespace(nsName, nsShortname) { Classes = new List <IRClass>(), Enums = new List <IREnum>(), }; nsSet[nsName] = ns; } // Get the matching IR object. var irClass = _classCache[currentType]; if (irClass.IsPrivate != true && irClass.Parent == null) { irClass.Parent = ns; } // Save it into the namespace. ns.Classes.Add(irClass); } // Do basically the same for enums. while (enumsToProcess.Count() > 0) { // Take the first item from the list. var currentType = enumsToProcess.ElementAt(0); enumsToProcess.RemoveAt(0); // Find namespace list. var nsName = GetNamespaceName(currentType); IRNamespace ns; nsSet.TryGetValue(nsName, out ns); if (ns == null) { // Construct a shortname of the last namespace part of the full name. var lastDotIdx = nsName.LastIndexOf('.'); var nsShortname = (lastDotIdx != -1) ? nsName.Substring(lastDotIdx + 1) : nsName; ns = new IRNamespace(nsName, nsShortname) { Classes = new List <IRClass>(), Enums = new List <IREnum>(), }; nsSet[nsName] = ns; } // Get the matching IR object. var irEnum = _enumCache[currentType]; if (irEnum.IsPrivate != true && irEnum.Parent == null) { irEnum.Parent = ns; } // Save it into the namespace. ns.Enums.Add(irEnum); } // Generate IR root of all namespaces. _root = new IRProgram() { Namespaces = nsSet.Values.ToList(), }; // Return the program root. return(_root); }