private void MapSubclasses(System.Collections.ArrayList subclasses, System.Collections.Specialized.StringCollection extendedClassesNames, System.Collections.ArrayList mappedClassesNames, System.Xml.XmlTextWriter writer) { System.Collections.ArrayList mappedSubclassesNames = new System.Collections.ArrayList(); // Map each subclass after the class it extends while (subclasses.Count > 0) { for (int i = subclasses.Count - 1; i >= 0; i--) { System.Type type = subclasses[i] as System.Type; string extendedClassName = extendedClassesNames[i]; if (extendedClassName == null) { throw new MappingException("You must specify the Extends attribute of the Subclass: " + type.FullName); } if (!mappedClassesNames.Contains(extendedClassName) && !mappedSubclassesNames.Contains(extendedClassName)) { bool extendedClassFoundButNotMappedYet = false; // Make sure that the extended class is mapped (in this assembly) foreach (System.Type subclass in subclasses) { if (HbmWriterHelper.GetNameWithAssembly(subclass) == extendedClassName) { if (subclass == type) { throw new MappingException("The Subclass " + type.FullName + " extends itself."); } else { extendedClassFoundButNotMappedYet = true; break; } } } if (extendedClassFoundButNotMappedYet) { continue; // Map this one later } // Else unknown extended class: // Assume it is mapped somewhere else and map this subclass } if (IsSubclass(type, typeof(SubclassAttribute))) { HbmWriter.WriteSubclass(writer, type); } else if (IsSubclass(type, typeof(JoinedSubclassAttribute))) { HbmWriter.WriteJoinedSubclass(writer, type); } else if (IsSubclass(type, typeof(UnionSubclassAttribute))) { HbmWriter.WriteUnionSubclass(writer, type); } // Note: Do not add to mappedClassesNames because it is for x-subclasses (and a x-subclasses shouldn't extend another x-subclasses) mappedSubclassesNames.Add(HbmWriterHelper.GetNameWithAssembly(type)); subclasses.RemoveAt(i); extendedClassesNames.RemoveAt(i); } } }
/// <summary> Writes the mapping of all mapped classes of the specified assembly in the specified stream. </summary> /// <param name="stream">Where the xml is written.</param> /// <param name="assembly">Assembly used to extract user-defined types containing a valid attribute (can be [Class] or [xSubclass]).</param> public virtual void Serialize(System.IO.Stream stream, System.Reflection.Assembly assembly) { if (stream == null) { throw new System.ArgumentNullException("stream"); } if (assembly == null) { throw new System.ArgumentNullException("assembly"); } System.Xml.XmlTextWriter writer = new System.Xml.XmlTextWriter(stream, System.Text.Encoding.UTF8); writer.Formatting = System.Xml.Formatting.Indented; writer.WriteStartDocument(); if (WriteDateComment) { writer.WriteComment(string.Format("Generated from NHibernate.Mapping.Attributes on {0}.", System.DateTime.Now.ToString("u"))); } WriteHibernateMapping(writer, null); // Write imports (classes decorated with the [ImportAttribute]) foreach (System.Type type in assembly.GetTypes()) { object[] imports = type.GetCustomAttributes(typeof(ImportAttribute), false); foreach (ImportAttribute import in imports) { writer.WriteStartElement("import"); if (import.Class != null && import.Class != string.Empty) { writer.WriteAttributeString("class", import.Class); } else // Assume that it is the current type that must be imported { writer.WriteAttributeString("class", HbmWriterHelper.GetNameWithAssembly(type)); } if (import.Rename != null && import.Rename != string.Empty) { writer.WriteAttributeString("rename", import.Rename); } writer.WriteEndElement(); } } // Write classes and x-subclasses (classes must come first if inherited by "external" subclasses) int classCount = 0; System.Collections.ArrayList mappedClassesNames = new System.Collections.ArrayList(); foreach (System.Type type in assembly.GetTypes()) { if (!IsClass(type)) { continue; } HbmWriter.WriteClass(writer, type); mappedClassesNames.Add(HbmWriterHelper.GetNameWithAssembly(type)); classCount++; } System.Collections.ArrayList subclasses = new System.Collections.ArrayList(); System.Collections.Specialized.StringCollection extendedClassesNames = new System.Collections.Specialized.StringCollection(); foreach (System.Type type in assembly.GetTypes()) { if (!IsSubclass(type)) { continue; } bool map = true; System.Type t = type; while ((t = t.DeclaringType) != null) { if (IsClass(t) || AreSameSubclass(type, t)) // If a base class is also mapped... (Note: A x-subclass can only contain x-subclasses of the same family) { map = false; // This class's mapping is already included in the mapping of the base class break; } } if (map) { subclasses.Add(type); if (IsSubclass(type, typeof(SubclassAttribute))) { extendedClassesNames.Add((type.GetCustomAttributes(typeof(SubclassAttribute), false)[0] as SubclassAttribute).Extends); } else if (IsSubclass(type, typeof(JoinedSubclassAttribute))) { extendedClassesNames.Add((type.GetCustomAttributes(typeof(JoinedSubclassAttribute), false)[0] as JoinedSubclassAttribute).Extends); } else if (IsSubclass(type, typeof(UnionSubclassAttribute))) { extendedClassesNames.Add((type.GetCustomAttributes(typeof(UnionSubclassAttribute), false)[0] as UnionSubclassAttribute).Extends); } } } classCount += subclasses.Count; MapSubclasses(subclasses, extendedClassesNames, mappedClassesNames, writer); writer.WriteEndElement(); // </hibernate-mapping> writer.WriteEndDocument(); writer.Flush(); if (classCount == 0) { throw new MappingException("The following assembly contains no mapped classes: " + assembly.FullName); } if (!Validate) { return; } // Validate the generated XML stream try { writer.BaseStream.Position = 0; System.Xml.XmlTextReader tr = new System.Xml.XmlTextReader(writer.BaseStream); var reader = CreateReader(tr); _stop = false; while (reader.Read() && !_stop) // Read to validate (stop at the first error) { ; } } catch (System.Exception ex) { Error.Append(ex.ToString()).Append(System.Environment.NewLine + System.Environment.NewLine); } }