/// <summary> /// During the generation process of hbm.xml files from the various JSON metadata, it /// is possiable that the names of properties may be duplicated and\or the name of /// properties match their class name. /// This function is intended to handle all such circumstances and correct them as needed /// appending a '00', '01' ,'02', etc. to the ends of duplicate names. /// </summary> /// <param name="hbmXmlOutputPath"> /// This file must already exist on disk and, upon duplicates being found, is overwritten with the corrected contents. /// </param> /// <param name="storedProcRules"> /// Set this flag to true if the <see cref="hbmXmlOutputPath"/> /// is working a file with contains any 'return-property' nodes (typically present on /// sql-query nodes). /// </param> /// <remarks> /// This function will, in theory, work for any hbm.xml file which is of the xmlns <see cref="Globals.HBM_XML_NS"/> /// but was designed only for hbm.xml files generated herein. /// </remarks> public static void CorrectHbmXmlDuplicateNames(string hbmXmlOutputPath, bool storedProcRules = false) { if (!File.Exists(hbmXmlOutputPath)) return; var overwriteFlag = false; var hbmXml = new XmlDocument(); hbmXml.Load(hbmXmlOutputPath); var nsMgr = new XmlNamespaceManager(hbmXml.NameTable); nsMgr.AddNamespace("nhib", Globals.HBM_XML_NS); var hbmClassNode = hbmXml.SelectSingleNode("//nhib:class", nsMgr); if (hbmClassNode == null || hbmClassNode.Attributes == null) throw new InvalidHbmNameException( string.Format( "Xml file at '{0}' either does not have a node named 'class' or the 'class' node has no attributes.", hbmXmlOutputPath)); var hbmClassName = hbmClassNode.Attributes["name"].Value; var hbmTypeName = new Util.NfTypeName(hbmClassName); hbmClassName = hbmTypeName.ClassName; var propertyNames = new List<string>(); var keyPropertyNames = new List<string>(); var keyMtoPropertyNames = new List<string>(); var returnPropNames = new List<string>(); var propertyNodes = hbmXml.SelectNodes("//nhib:property", nsMgr); var manyToOneNodes = hbmXml.SelectNodes("//nhib:many-to-one", nsMgr); var bagNodes = hbmXml.SelectNodes("//nhib:bag", nsMgr); var keyPropertyNodes = hbmXml.SelectNodes("//nhib:key-property", nsMgr); var keyMtoPropertyNodes = hbmXml.SelectNodes("//nhib:key-many-to-one", nsMgr); var returnPropNodes = hbmXml.SelectNodes("//nhib:return-property", nsMgr); propertyNames.Add("Id"); if (propertyNodes != null) propertyNames.AddRange(from XmlNode propNode in propertyNodes where propNode.Attributes != null && propNode.Attributes["name"] != null select propNode.Attributes["name"].Value); if (manyToOneNodes != null) propertyNames.AddRange(from XmlNode propNode in manyToOneNodes where propNode.Attributes != null && propNode.Attributes["name"] != null select propNode.Attributes["name"].Value); if (bagNodes != null) propertyNames.AddRange(from XmlNode propNode in bagNodes where propNode.Attributes != null && propNode.Attributes["name"] != null select propNode.Attributes["name"].Value); if (keyPropertyNodes != null) keyPropertyNames.AddRange(from XmlNode propNode in keyPropertyNodes where propNode.Attributes != null && propNode.Attributes["name"] != null select propNode.Attributes["name"].Value); if (keyMtoPropertyNodes != null) keyMtoPropertyNames.AddRange(from XmlNode propNode in keyMtoPropertyNodes where propNode.Attributes != null && propNode.Attributes["name"] != null select propNode.Attributes["name"].Value); if (returnPropNodes != null) returnPropNames.AddRange(from XmlNode propNode in returnPropNodes where propNode.Attributes != null && propNode.Attributes["name"] != null select propNode.Attributes["name"].Value); //acid test for duplicate names var dupPropOverwriteFlag = storedProcRules ? AcidTestDuplicateNames(hbmXmlOutputPath, propertyNames, hbmXml, nsMgr, "property") : AcidTestDuplicateNames(hbmXmlOutputPath, propertyNames, hbmXml, nsMgr); var dupKeyPropOverwriteFlag = AcidTestDuplicateNames(hbmXmlOutputPath, keyPropertyNames, hbmXml, nsMgr, "key-property"); var dubMtoKeyPropOverwriteFlag = AcidTestDuplicateNames(hbmXmlOutputPath, keyMtoPropertyNames, hbmXml, nsMgr, "key-many-to-one"); var dupReturnPropFlag = storedProcRules && AcidTestDuplicateNames(hbmXmlOutputPath, returnPropNames, hbmXml, nsMgr, "return-property"); overwriteFlag = dupKeyPropOverwriteFlag || dupPropOverwriteFlag || dubMtoKeyPropOverwriteFlag || dupReturnPropFlag; if (propertyNames.Contains(hbmClassName)) { var pnNodes = hbmXml.SelectNodes(string.Format("//nhib:*[@name='{0}']", hbmClassName), nsMgr); if (pnNodes == null) throw new ItsDeadJim( string.Format( "Property names which match the class name '{0}' are supposed to be present but an XPath selector returned null", hbmClassName)); for (var i = 0; i < pnNodes.Count; i++) { overwriteFlag = true; if (pnNodes[i] == null || pnNodes[i].Attributes == null || pnNodes[i].Attributes["name"] == null) continue; pnNodes[i].Attributes["name"].Value = string.Format("{0}{1:00}", hbmClassName, i); } Console.WriteLine("{0:yyyy-MM-dd HH:mm:ss.ffff}'{1}' duplicate property [{2}]", DateTime.Now, hbmXmlOutputPath, hbmClassName); } if (!overwriteFlag) return; using (var myXmlWriter = new XmlTextWriter(hbmXmlOutputPath, System.Text.Encoding.UTF8) { Formatting = Formatting.Indented }) { hbmXml.WriteContentTo(myXmlWriter); myXmlWriter.Flush(); } }
public static string BagPropertyName(string fullAssemblyQualTypeName) { var pluralBagName = new Util.NfTypeName(fullAssemblyQualTypeName); return Util.Etymological.En.ToPlural(pluralBagName.ClassName, true); }
/// <summary> /// Produces a single hbm.xml for the given <see cref="tbl"/> /// </summary> /// <param name="outputNamespace"></param> /// <param name="tbl"></param> /// <returns>The path the generated hbm.xml file</returns> public static string GetSingleHbmXml(string outputNamespace, string tbl) { if(HbmKeys == null || HbmOneToMany == null || HbmBags == null) throw new RahRowRagee("Assign the static variables HbmKeys, HbmOneToMany and HbmBags" + " then try again (These values are calculated from Sorting)."); if (Settings.DoNotReference.Contains(tbl)) return null; var hbmPk = HbmKeys.Data; var hbmFk = HbmOneToMany.Data; var hbmBags = HbmBags.Data; //possiable duplicate names handled within this var className = Compose.ClassName(tbl, outputNamespace); //not having the naming pattern is exceptional Compose.ValidSplit(tbl, 2); var tableName = Util.Etc.ExtractLastWholeWord(tbl, null); var schemaName = tbl.Replace(string.Format(".{0}", tableName), string.Empty); var xe = XeFactory.HibernateMappingNode(); var classXe = XeFactory.ClassNode(className, tableName, schemaName); //----PK var pkId = hbmPk.ContainsKey(tbl) ? hbmPk[tbl].Id : null; var hasNoPkAtAll = !hbmPk.ContainsKey(tbl); if (pkId == null && !hasNoPkAtAll) { var compClassName = Compose.CompKeyClassName(tbl, outputNamespace); const string compPropertyName = Globals.HbmXmlNames.ID; var compKeyXe = XeFactory.CompositeIdNode(compPropertyName, compClassName); //key-many-to-one var hbmKeyManyToOne = hbmPk[tbl].KeyManyToOne ?? new Dictionary<string, List<ColumnMetadata>>(); if (hbmKeyManyToOne.Count > 0) { foreach (var keyname in hbmKeyManyToOne.Keys.Where(k => !Settings.DoNotReference.Contains(k))) { //need to iterate here on a per 'constraint_name', the json data-struct misses this //eg a table, 4 columns; 2 are comp to another table's Pk and the other 2 are also a comp the SAME table's PK var keyManytoOneColumns = new List<ColumnMetadata>(); if (!HbmKeys.GetKeyManyToOneColumns(tbl, ref keyManytoOneColumns)) continue; foreach (var distinctColumn in keyManytoOneColumns.Distinct(new ConstraintNameComparer())) { var dKmtoCName = distinctColumn.constraint_name; var keyMtoFullyQualTypeName = Compose.ClassName(keyname, outputNamespace); var keyMtoNfTypeNameObj = new Util.NfTypeName(keyMtoFullyQualTypeName); var keyMtoSimpleName = keyMtoNfTypeNameObj.ClassName; var keyMtoXe = XeFactory.KeyManyToOneNode(keyMtoSimpleName, keyMtoFullyQualTypeName); var dKmtoColumnData = hbmKeyManyToOne[keyname].Where( x => string.Equals(x.constraint_name, dKmtoCName, Sorting.C)).ToList(); if (dKmtoColumnData.Count <= 0) continue; foreach (var kmtoColumn in dKmtoColumnData) { kmtoColumn.CopyFrom(Sorting.GetFromAllColumnMetadata(kmtoColumn)); var fullColumnName = kmtoColumn.column_name; //not having the naming pattern is exceptional Compose.ValidSplit(fullColumnName, 3); var keyMtoColumnXe = XeFactory.ColumnNode(Util.Etc.ExtractLastWholeWord(fullColumnName, null), kmtoColumn.ToJsonString()); keyMtoXe.Add(keyMtoColumnXe); } compKeyXe.Add(keyMtoXe); }//end foreach distinct constraintname } }//end key-many-to-one var hbmKeyProperty = hbmPk[tbl].KeyProperty ?? new Dictionary<string, List<ColumnMetadata>>(); //these would have been added as key-many-to-one, but thier underlying table has been excluded. foreach (var reduced in hbmKeyManyToOne.Where(k => Settings.DoNotReference.Contains(k.Key))) { foreach (var redux in reduced.Value) redux.CopyFrom(Sorting.GetFromAllColumnMetadata(redux));//insure they got everything for being a value type hbmKeyProperty.Add(reduced.Key, reduced.Value); } if (hbmKeyProperty.Count > 0) { //Dictionary<string, List<ColumnMetadata>> foreach (var keyName in hbmKeyProperty.Keys) { //SPECIAL NOTE: the original PS code seemed to imply that a key-property is always just one column... var keyPropJsonData = hbmKeyProperty[keyName].FirstOrDefault(); if (keyPropJsonData == null) continue; keyPropJsonData.CopyFrom(Sorting.GetFromAllColumnMetadata(keyPropJsonData)); var keyPropPropertyName = Compose.PropertyName(keyPropJsonData.column_name); var fullcolumnName = keyPropJsonData.column_name; Compose.ValidSplit(fullcolumnName, 3); var keyPropDataType = Util.Lexicon.Mssql2HbmTypes[(keyPropJsonData.data_type)]; var keyPropColumn = Util.Etc.ExtractLastWholeWord(fullcolumnName, null); var keyPropLen = keyPropJsonData.string_length ?? Globals.MSSQL_MAX_VARCHAR; if (string.Equals(keyPropDataType, Globals.HbmXmlNames.ANSI_STRING)) { if (keyPropLen <= 0) keyPropLen = Globals.MSSQL_MAX_VARCHAR; var keyPropXe = XeFactory.KeyPropertyNode(keyPropPropertyName, keyPropColumn, keyPropDataType, keyPropLen.ToString(CultureInfo.InvariantCulture), keyPropJsonData.ToJsonString()); compKeyXe.Add(keyPropXe); } else { var keyPropXe = XeFactory.KeyPropertyNode(keyPropPropertyName, keyPropColumn, keyPropDataType, keyPropJsonData.ToJsonString()); compKeyXe.Add(keyPropXe); } } } classXe.Add(compKeyXe); } else if (pkId != null) { GetSimpleId(pkId, classXe); }//end PK //having no pk, add the contrived comp-key to the class if (hasNoPkAtAll) { GetAllColumnsAsCompositeKey(tbl, classXe, outputNamespace); } else//simple properties { foreach (var columnName in Sorting.DbContainers.FlatData.Data.Where(x => string.Equals(x.table_name, tbl, Sorting.C))) { var fullColumnName = columnName.column_name; Compose.ValidSplit(fullColumnName, 3); var simplePropXe = GetSimplePropertyHbmXml(columnName, Globals.HbmXmlNames.PROPERTY); classXe.Add(simplePropXe); } } //----fks which are not part of pks if (hbmFk.ContainsKey(tbl)) { var tblFks = hbmFk[tbl].ManyToOne; foreach (var fkName in tblFks.Keys) { //these would be FK ref to another type but its underlying table is excluded so now its just a bunch of value types if (Settings.DoNotReference.Contains(fkName)) { foreach (var reducedFk in tblFks[fkName]) { var reducedFkSimpProp = GetSimplePropertyHbmXml(reducedFk, Globals.HbmXmlNames.PROPERTY); classXe.Add(reducedFkSimpProp); } continue;//these need representation but not as class types } var manytoOneColumns = new List<ColumnMetadata>(); if (!HbmOneToMany.GetManyToOneColumns(tbl, ref manytoOneColumns)) continue; var fkColumnsByDistinctConstraintName = manytoOneColumns.Select(x => x.constraint_name).Distinct().ToList(); foreach (var distinctConstraintName in fkColumnsByDistinctConstraintName) { var dMtoColumnData = tblFks[fkName].Where( x => string.Equals(x.constraint_name, distinctConstraintName, Sorting.C)).ToList(); if (dMtoColumnData.Count <= 0) continue; var fkColumnXes = new List<XElement>(); var fkColumnNames = new List<string>(); foreach (var x in dMtoColumnData) { x.CopyFrom(Sorting.GetFromAllColumnMetadata(x)); var fullColumnName = x.column_name; Compose.ValidSplit(fullColumnName, 3); var cn = Util.Etc.ExtractLastWholeWord(fullColumnName, null); //need to store these temp, since we are also drafting thier parent's name fkColumnXes.Add(XeFactory.ColumnNode(cn,x.ToJsonString())); fkColumnNames.Add(cn); } var fkPropertyType = new Util.NfTypeName(Compose.ClassName(fkName, outputNamespace)); var fkPropertyName = Compose.ManyToOnePropertyName(Compose.ClassName(fkName, outputNamespace), fkColumnNames.ToArray()); var manyToOneXe = XeFactory.ManyToOneNode(fkPropertyName, fkPropertyType.AssemblyQualifiedName); foreach (var fkXe in fkColumnXes) manyToOneXe.Add(fkXe); classXe.Add(manyToOneXe); } } }//----end Fk //----hbm bags var hbmBagNames = new List<string>(); //check for duplicates if (!Settings.DoNotReference.Contains(tbl) && hbmBags.ContainsKey(tbl) && !hasNoPkAtAll) { var distinctBagConstraintNames = hbmBags[tbl].Select(x => x.constraint_name).Distinct().ToList(); foreach (var distinctBagConstraintName in distinctBagConstraintNames) { var hbmBagPropertyName = Compose.PropertyName(distinctBagConstraintName); var hbmBagXe = XeFactory.BagNode(hbmBagPropertyName, Globals.HbmXmlNames.ALL_DELETE_ORPHAN, bool.TrueString.ToLower(), bool.TrueString.ToLower(), Globals.REPRESENT_512); var bagColumns = hbmBags[tbl].Where(x => string.Equals(x.constraint_name, distinctBagConstraintName, Sorting.C)) .Select(x => x.column_name) .ToList(); string hbmOneToMany; if (bagColumns.Count > 1) { var hbmBagFirstKey = hbmBags[tbl].First( x => string.Equals(x.constraint_name, distinctBagConstraintName, Sorting.C)); if (Settings.DoNotReference.Contains(hbmBagFirstKey.table_name)) continue; hbmOneToMany = Compose.ClassName((hbmBagFirstKey.table_name), outputNamespace); var hbmBagFkKeyXe = XeFactory.KeyNodeClassName(className); foreach ( var columnData in hbmBags[tbl].Where( x => string.Equals(x.constraint_name, distinctBagConstraintName, Sorting.C)).ToList()) { columnData.CopyFrom(Sorting.GetFromAllColumnMetadata(columnData)); var fullColumnName = columnData.column_name; Compose.ValidSplit(fullColumnName, 3); var hbmBagKeyColumn = Util.Etc.ExtractLastWholeWord(fullColumnName, null); var hbmBagKeyXe = XeFactory.ColumnNode(hbmBagKeyColumn,columnData.ToJsonString()); hbmBagFkKeyXe.Add(hbmBagKeyXe); } hbmBagXe.Add(hbmBagFkKeyXe); } else { var hbmBagFirstKey = hbmBags[tbl].First( x => string.Equals(x.constraint_name, distinctBagConstraintName, Sorting.C)); if (Settings.DoNotReference.Contains(hbmBagFirstKey.table_name)) continue; hbmBagFirstKey.CopyFrom(Sorting.GetFromAllColumnMetadata(hbmBagFirstKey)); var fullColumnName = hbmBagFirstKey.column_name; hbmOneToMany = Compose.ClassName((hbmBagFirstKey.table_name), outputNamespace); Compose.ValidSplit(fullColumnName, 3); var hbmBagKeyColumn = Util.Etc.ExtractLastWholeWord(fullColumnName, null); var hbmBagKeyXe = XeFactory.KeyNodeColumnName(hbmBagKeyColumn, hbmBagFirstKey.ToJsonString()); hbmBagXe.Add(hbmBagKeyXe); } var hbmOneToManyXe = XeFactory.OneToManyNode(hbmOneToMany); hbmBagXe.Add(hbmOneToManyXe); //attempt to make the name plural var newBagName = Compose.BagPropertyName(hbmOneToMany); hbmBagXe.FirstAttribute.SetValue(newBagName); classXe.Add(hbmBagXe); hbmBagNames.Add(hbmBagPropertyName); } } xe.Add(classXe); var hbmXmlOutputPath = Path.Combine(Settings.HbmDirectory, string.Format("{0}.hbm.xml", Util.Etc.CapitalizeFirstLetterOfWholeWords(tbl, '.'))); var xmlContent = xe.ToString() .Replace("<hibernate-mapping>", "<hibernate-mapping xmlns=\"urn:nhibernate-mapping-2.2\">"); File.WriteAllText(hbmXmlOutputPath, xmlContent); //perform rename of any properties which match classname or are duplicated therein CorrectHbmXmlDuplicateNames(hbmXmlOutputPath); return hbmXmlOutputPath; }