/********************************************************************************************* * Copyright 2021 - Volian Enterprises, Inc. All rights reserved. * Volian Enterprises - Proprietary Information - DO NOT COPY OR DISTRIBUTE * ------------------------------------------------------------------------------ * $Workfile: RODBInterface.cs $ $Revision: 48 $ * $Author: Kathy $ $Date: 9/06/05 11:25a $ * * $History: RODBInterface.cs $ * * ***************** Version 48 ***************** * User: Kathy Date: 9/06/05 Time: 11:25a * Updated in $/LibSource/RODBInterface * B2005-035 * * ***************** Version 47 ***************** * User: Jsj Date: 5/03/05 Time: 11:45a * Updated in $/LibSource/RODBInterface * * ***************** Version 46 ***************** * User: Kathy Date: 7/15/04 Time: 11:10a * Updated in $/LibSource/RODBInterface * Fix B2004-016 * * ***************** Version 45 ***************** * User: Jsj Date: 5/19/04 Time: 11:11a * Updated in $/LibSource/RODBInterface * fixed typo in warning message * * ***************** Version 44 ***************** * User: Jsj Date: 5/13/04 Time: 2:19p * Updated in $/LibSource/RODBInterface * RO FST will skip records that do not have a valid parent. * * ***************** Version 43 ***************** * User: Jsj Date: 5/11/04 Time: 9:28a * Updated in $/LibSource/RODBInterface * We were getting multiple tree entries after creating the FST file. * * ***************** Version 42 ***************** * User: Jsj Date: 4/08/04 Time: 9:42a * Updated in $/LibSource/RODBInterface * Optimized and added new read functions to speed up RO.FST file * creation. * * ***************** Version 41 ***************** * User: Jsj Date: 3/19/04 Time: 9:40a * Updated in $/LibSource/RODBInterface * bug fix, was using parent node's group definition instead of the * current node's group definition * B2004-008 * * ***************** Version 40 ***************** * User: Jsj Date: 1/12/04 Time: 12:08p * Updated in $/LibSource/RODBInterface * changed SQL Server catalog name from"RO" to "VEPROMS RO" * * ***************** Version 39 ***************** * User: Jsj Date: 1/07/04 Time: 2:17p * Updated in $/LibSource/RODBInterface * allow use of SQL Server * * ***************** Version 38 ***************** * User: Kathy Date: 5/30/03 Time: 12:47p * Updated in $/LibSource/RODBInterface * B2003-040: flag inserted groups have no children for xml * * ***************** Version 37 ***************** * User: Kathy Date: 5/21/03 Time: 12:44p * Updated in $/LibSource/RODBInterface * B2003-041: field name changes fixes. * * ***************** Version 36 ***************** * User: Kathy Date: 5/07/03 Time: 1:57p * Updated in $/LibSource/RODBInterface * B2003-033 fix * * ***************** Version 35 ***************** * User: Jsj Date: 4/14/03 Time: 2:59p * Updated in $/LibSource/RODBInterface * changes to help speed up RO FST file creation * * ***************** Version 34 ***************** * User: Kathy Date: 4/04/03 Time: 9:39a * Updated in $/LibSource/RODBInterface * B2003-030: flag parent's HasChild attribute for new ros * * ***************** Version 33 ***************** * User: Kathy Date: 3/13/03 Time: 1:17p * Updated in $/LibSource/RODBInterface * master different than local fieldname, use local * * ***************** Version 32 ***************** * User: Kathy Date: 1/27/03 Time: 2:12p * Updated in $/LibSource/RODBInterface * test oledb * * ***************** Version 31 ***************** * User: Kathy Date: 12/17/02 Time: 2:32p * Updated in $/LibSource/RODBInterface * new top group with apostrophe failed * * ***************** Version 30 ***************** * User: Kathy Date: 12/17/02 Time: 11:05a * Updated in $/LibSource/RODBInterface * fixed bug on apostrophe in field def (schema string) * * ***************** Version 29 ***************** * User: Kathy Date: 12/10/02 Time: 2:24p * Updated in $/LibSource/RODBInterface * fieldname special chars & various bug fixes * * ***************** Version 28 ***************** * User: Kathy Date: 12/06/02 Time: 11:57a * Updated in $/LibSource/RODBInterface * mods for SQL Server * * ***************** Version 27 ***************** * User: Kathy Date: 12/02/02 Time: 8:28a * Updated in $/LibSource/RODBInterface * fieldname replace chars * * ***************** Version 26 ***************** * User: Kathy Date: 12/02/02 Time: 6:11a * Updated in $/LibSource/RODBInterface * Fix some bugs and add status bar on long ops * * ***************** Version 25 ***************** * User: Jsj Date: 11/27/02 Time: 12:46p * Updated in $/LibSource/RODBInterface * modifications for RO.FST file creation * * ***************** Version 24 ***************** * User: Kathy Date: 11/26/02 Time: 12:56p * Updated in $/LibSource/RODBInterface * fixed single quote crash in accpageid * * ***************** Version 23 ***************** * User: Kathy Date: 11/11/02 Time: 7:15a * Updated in $/LibSource/RODBInterface * Added RODB_ReadRO to read an RO given a recid/table name * * ***************** Version 22 ***************** * User: Kathy Date: 10/24/02 Time: 11:16a * Updated in $/LibSource/RODBInterface * delete hi-level group (table) * * ***************** Version 21 ***************** * User: Kathy Date: 10/15/02 Time: 2:16p * Updated in $/LibSource/RODBInterface * minor bug fixes & new group (database, i.e. table level) * * ***************** Version 20 ***************** * User: Kathy Date: 10/10/02 Time: 1:04p * Updated in $/LibSource/RODBInterface * xmltree=tree control * * ***************** Version 19 ***************** * User: Kathy Date: 10/10/02 Time: 10:56a * Updated in $/LibSource/RODBInterface * console.writeline->Messagebox * * ***************** Version 18 ***************** * User: Kathy Date: 10/10/02 Time: 10:00a * Updated in $/LibSource/RODBInterface * delete bug fix & accessory page id support * * ***************** Version 17 ***************** * User: Kathy Date: 10/07/02 Time: 11:25a * Updated in $/LibSource/RODBInterface * image file saving of xml using xmltextwriter was missing sometext * * ***************** Version 16 ***************** * User: Kathy Date: 10/02/02 Time: 1:41p * Updated in $/LibSource/RODBInterface * tie tree to insert/delete changes (round 1) * * ***************** Version 15 ***************** * User: Jsj Date: 10/01/02 Time: 4:45p * Updated in $/LibSource/RODBInterface * hooks for passed in database path * * ***************** Version 14 ***************** * User: Kathy Date: 9/27/02 Time: 1:22p * Updated in $/LibSource/RODBInterface * remove using vlnxml * * ***************** Version 13 ***************** * User: Kathy Date: 9/27/02 Time: 1:18p * Updated in $/LibSource/RODBInterface * fix digit as first char in fieldname & recurse through tree adjusting * for modified field names * * ***************** Version 12 ***************** * User: Jsj Date: 9/26/02 Time: 11:13a * Updated in $/LibSource/RODBInterface * added VLNXML reference * * ***************** Version 11 ***************** * User: Kathy Date: 9/25/02 Time: 9:55a * Updated in $/LibSource/RODBInterface * generate xml strings for writing to database by using xmltextwriter * * ***************** Version 10 ***************** * User: Kathy Date: 9/19/02 Time: 10:02a * Updated in $/LibSource/RODBInterface * minor changes for schema and other bug fixes * * ***************** Version 9 ***************** * User: Kathy Date: 9/11/02 Time: 1:14p * Updated in $/LibSource/RODBInterface * vlnxml * * ***************** Version 8 ***************** * User: Kathy Date: 9/10/02 Time: 12:54p * Updated in $/LibSource/RODBInterface * menu titles * * ***************** Version 7 ***************** * User: Kathy Date: 9/06/02 Time: 11:43a * Updated in $/LibSource/RODBInterface * datetimestamp * * ***************** Version 6 ***************** * User: Kathy Date: 8/30/02 Time: 11:56a * Updated in $/LibSource/RODBInterface * new/update ro field records. * * ***************** Version 5 ***************** * User: Kathy Date: 8/30/02 Time: 9:43a * Updated in $/LibSource/RODBInterface * compile * * ***************** Version 4 ***************** * User: Jsj Date: 8/28/02 Time: 3:40p * Updated in $/LibSource/RODBInterface * added a create table function * * ***************** Version 3 ***************** * User: Jsj Date: 8/28/02 Time: 3:03p * Updated in $/LibSource/RODBInterface * read connection strings from a text file * * ***************** Version 2 ***************** * User: Kathy Date: 8/28/02 Time: 10:52a * Updated in $/LibSource/RODBInterface * development *********************************************************************************************/ using System; using System.Collections; using System.IO; using System.Data; using System.Xml; using System.Xml.XPath; using System.Xml.Schema; using System.Text; using System.Windows.Forms; using DBEncapsulation; using ROFields; using VlnStatus; using System.Collections.Specialized; using Org.Mentalis.Files; using System.Data.SqlClient; using System.Collections.Generic; namespace RODBInterface { /// /// Summary description for Class1 /// public enum RecordType : uint { Master = 0, SubDatabase = 1, Schema = 2, Group = 3, GroupSchema = 4, RRO = 5, SchemaStart = 6, SchemaEnd = 7, ConvertedToSql = 8 } /// /// The following class handles generation of sql strings to get/put requested parts of /// XML RO Tree. And performs the database access using the VLN_DB class. /// public abstract partial class RODB { #region Properties public DBEncapsulation.DBEncapsulate DBE; public VlnXmlDocument ROXml; public string schemastart; public string schemaend; public string lastTable = ""; public HybridDictionary dicFldTypes; public string RODirectoryPath; public int dbProviderType = 0; public string strDatabaseConnectionCommand; public string dbServerPath = ""; public string dbServerUserName = ""; public string dbServerPassword = ""; public enum DB_PROVIDER { ACCESS, SQL_SERVER, ORACLE }; private string _MyDBID; public string MyDBID { get { return _MyDBID; } set { _MyDBID = value; } } private string _MyRecID; public string MyRecID { get { return _MyRecID; } set { _MyRecID = value; } } private bool _OnlyConnectOnce=false; public bool OnlyConnectOnce { get { return _OnlyConnectOnce; } set { _OnlyConnectOnce = value; } } // C2021-026 used to access in the list of P/C Children private static string[] _PCChildList = null; public static string[] PCChildList { get { return _PCChildList; } set { _PCChildList = value; } } #endregion #region abstracts // need these for each method that must be defined for each database type public abstract string RODB_GetNextGroupTable(); public abstract string RODB_GetNextRecId(string table); public abstract bool RODB_GetRootGroups(VlnXmlElement root); public abstract bool RODB_DeleteGroup(XmlNode group, string tbname, string toprecid); public abstract bool RODB_DeleteRO(XmlNode group); public abstract bool RODB_WriteGroup(XmlNode group, VlnXmlElement master); public abstract bool RODB_InsertGroup(VlnXmlElement group); public abstract string RODB_AddNewTable(string TblName, string newgrpname); public abstract bool RODB_CopyFieldDefs(string fromtb, string totb, int type); public abstract bool RODB_GetGroupAndSubgroups(VlnXmlElement node, StringBuilder sb); public abstract bool RODB_GetChildData(VlnXmlElement node, bool CheckChildCount); public abstract bool IsDuplicateAccPageID(VlnXmlElement ro, string newacc); public abstract VlnXmlElement RODB_ReadRO(string tbl, string recid); public abstract bool RODB_WriteRO(VlnXmlElement ro, bool movedRO = false); public abstract bool RODB_InsertRO(VlnXmlElement ro); public abstract ushort RODB_GetFieldType(VlnXmlElement elem, string TableName, string Fld); public abstract ArrayList RODB_GetFields(VlnXmlElement elem, uint rtype, bool refresh = false); public abstract string RODB_GetSchemaPiece(string Recid, string table); public abstract bool RODB_NewSchemaPiece(string recid, string parentid, string table, string schpiece, uint rtype); public abstract bool RODB_WriteSchemaPiece(string Recid, string table, string schpiece); public abstract bool RODB_ProcessRROFieldChange(VlnXmlElement child, string oldname, string newname, uint editlevel, VlnStatusMessage StatMsgWindow, bool combofield); public abstract bool RODB_UpdateFieldRecord(ROField myrof, VlnXmlElement myelem, string strschema, string oldname, string newname, uint editlevel, bool combofield); public abstract XmlSchema RODB_GetGroupSchema(VlnXmlElement elem); public abstract XmlSchema RODB_GetSchema(VlnXmlElement elem); public abstract int RODB_GetNumberOfROValueRecords(string tablename); public abstract int RODB_GetNumberOfGroupRecords(string tablename); public abstract string RODB_GetDBNameForAbout(); public abstract string RODB_GetDBServerForAbout(); public abstract string RODB_HasBeenConverted(); public abstract bool RODB_WriteSqlConnectToAccess(string newConectStr); #endregion public RODB() { } #region GeneralDB public void GetDbServerInfo(string ROdir) { string strServerProvider; string ROiniPath = ROdir + "\\ROAPP.INI"; // Get the database Provider name - default to Access IniReader in1 = new IniReader(ROiniPath); strServerProvider = in1.ReadString("Database Server", "Driver", "ACCESS"); strServerProvider = strServerProvider.ToUpper(); if (strServerProvider.Equals("SQL SERVER")) dbProviderType = (int)DB_PROVIDER.SQL_SERVER; else if (strServerProvider.Equals("ORACLE")) dbProviderType = (int)DB_PROVIDER.ORACLE; else // default to ACCESS dbProviderType = (int)DB_PROVIDER.ACCESS; // Get the database server path dbServerPath = in1.ReadString("Database Server", "Path", ROdir); dbServerUserName = in1.ReadString("Database Server", "UserID", ""); dbServerPassword = in1.ReadString("Database Server", "Password", ""); if (dbProviderType.Equals((int)DB_PROVIDER.ACCESS)) { if (dbServerUserName.Equals("")) dbServerUserName = "Admin"; // default user name for Access } //else if (dbServerUserName.Equals("") || (dbServerUserName.Length > 0 && dbServerPassword.Equals(""))) //{ // userpass pwdlg = new userpass(dbServerUserName); // // If using SQL Server or Oracle // // prompt for a user name and password. // pwdlg.Text = strServerProvider + pwdlg.Text; // if (pwdlg.ShowDialog() == DialogResult.OK) // { // dbServerUserName = pwdlg.uname; // dbServerPassword = pwdlg.pword; // } // else // { // dbServerUserName = ""; // dbServerPassword = ""; // } //} } public void BuildConnectionString(string ropath, string DataConnectionPath) { GetDbServerInfo(ropath); if (!dbProviderType.Equals((int)DB_PROVIDER.SQL_SERVER)) { strDatabaseConnectionCommand = "Provider=Microsoft.Jet.OLEDB.4.0;Password=\"\";User ID=Admin;Data Source=" + DataConnectionPath + "\\ROMaster.mdb;Mode=Share Deny None;Extended Properties=\"\";Jet OLEDB:System database=\"\";Jet OLEDB:Registry Path=\"\";Jet OLEDB:Database Password=\"\";Jet OLEDB:Engine Type=5;Jet OLEDB:Database Locking Mode=1;Jet OLEDB:Global Partial Bulk Ops=2;Jet OLEDB:Global Bulk Transactions=1;Jet OLEDB:New Database Password=\"\";Jet OLEDB:Create System Database=False;Jet OLEDB:Encrypt Database=False;Jet OLEDB:Don't Copy Locale on Compact=False;Jet OLEDB:Compact Without Replica Repair=False;Jet OLEDB:SFP=False"; } } #endregion #region XML public VlnXmlDocument RODB_GetRoot() { // Create an xmldoc and initialize it with a root node ROXml = new VlnXmlDocument(); XmlNode elem = ROXml.CreateNode(XmlNodeType.Element, "RO_Root", ""); ROXml.AppendChild(elem); return ROXml; } public string ParseEleName(string info) { string elemname; // need to find element type too. int issingle = info.IndexOf("Single"); int idx1 = info.IndexOf("element name="); if (idx1 > 0) { elemname = info.Substring(idx1 + 14, info.Length - idx1 - 14); idx1 = elemname.IndexOf("\""); elemname = CvtFldToUserFld(elemname.Substring(0, idx1)); // if we have a choice element, remove the last character (it was added to the // field names in order to allow for unique fields names for the choice items. if (info.IndexOf("xsd:choice") > 0) { string trun = elemname.Remove((elemname.Length) - 1, 1); return trun; } return elemname; } return null; } // Given an element to start at, get the fields in use. Pass in the element you're // on, the available list for this group & a string representing 'FieldsInUse' or // 'GroupFieldsInUse'. public ArrayList RODB_GetFieldsInUse(VlnXmlElement elem, ArrayList avail, string strFieldsInUse, ref string origFieldsInUse, bool RemoveFromAvailList) { VlnXmlElement startelem = elem; // if this element doesn't have any fields in use, walk up the tree until // we find them. string FieldsInUse = ""; if (startelem.HasAttribute(strFieldsInUse) == false) { VlnXmlElement parent; parent = (VlnXmlElement)startelem.ParentNode; while (parent != null && FieldsInUse == "") { FieldsInUse = parent.GetAttribute(strFieldsInUse); if (parent.Name != "RO_Root") parent = (VlnXmlElement)parent.ParentNode; else parent = null; } } else FieldsInUse = startelem.GetAttribute(strFieldsInUse); origFieldsInUse = FieldsInUse; // make a copy to compare to later. ArrayList inuse = new ArrayList(); // if there are no fields in use (may be inserting a top group) // return an empty list. if (FieldsInUse == "") return inuse; // Go through the FieldsInUse list and move elements from the avail list // to the inuse list int strpos = 0; string recid; recid = FieldsInUse.Substring(strpos, 8); MyRecID = recid; while (recid != null) { // find it in avail list. Remove it from there & add to inuse list for (int i = 0; i < avail.Count; i++) { ROField rof = (ROField)avail[i]; string lrecid = rof.GetRecID; if (lrecid == recid) { // remove it from avail list & add it to inuse list ROField inuserof = new ROField(rof.GetFieldname, rof.GetRecID, rof.GetMasterRecID, rof.GetFieldType); inuse.Add((object)inuserof); if (RemoveFromAvailList) avail.RemoveAt(i); break; } } strpos = strpos + 9; if (strpos > FieldsInUse.Length) recid = null; else { recid = FieldsInUse.Substring(strpos, 8); } } return inuse; } //C2021-026 adding ability to have field values based on Parent/Child applicability // This reads the database and returns a list of the field where applicability was enabled public ArrayList RODB_GetApplicabilityEnabledFields(VlnXmlElement elem, ArrayList fieldsInUse, ref string origApplcFields, bool pcApplicabilityEnabled) { ArrayList newApplicList = new ArrayList(); if (!pcApplicabilityEnabled) { origApplcFields = null; return newApplicList; } VlnXmlElement startelem = elem; string atribApplicFields = "ApplicabilityEnabled"; //xml attribute containing list of field recids // if this element doesn't have any fields in use, walk up the tree until // we find them. string applicFields = ""; if (startelem.HasAttribute(atribApplicFields) == false) { VlnXmlElement parent; parent = (VlnXmlElement)startelem.ParentNode; while (parent != null && applicFields == "") { applicFields = parent.GetAttribute(atribApplicFields); if (parent.Name != "RO_Root") parent = (VlnXmlElement)parent.ParentNode; else parent = null; } } else applicFields = startelem.GetAttribute(atribApplicFields); origApplcFields = applicFields; // make a copy to compare to later. // if there are no fields with applicability (may be inserting a top group) // return an empty list. if (applicFields == "") return newApplicList; // Go through the FieldsInUse list and copy elements to newApplicList int strpos = 0; string recid; recid = applicFields.Substring(strpos, 8); MyRecID = recid; while (recid != null) { // find it in avail list and add to newApplicList for (int i = 0; i < fieldsInUse.Count; i++) { ROField rof = (ROField)fieldsInUse[i]; string lrecid = rof.GetRecID; if (lrecid == recid) { // add to newApplilcList if it is not already there list ROField inuserof = new ROField(rof.GetFieldname, rof.GetRecID, rof.GetMasterRecID, rof.GetFieldType); bool addit = true; foreach (ROField arof in newApplicList) { if (arof.GetRecID == inuserof.GetRecID) { addit = false; //this field is already in the list break; } } if (addit) newApplicList.Add((object)inuserof); break; } } strpos = strpos + 9; if (strpos > applicFields.Length) recid = null; else { recid = applicFields.Substring(strpos, 8); } } return newApplicList; } public string CvtUserFldToFld(string fldname) { if (fldname.Length < 2) return fldname; // a digit cannot start an xml fieldname, prepend a "__" to it. string tmp0; if (char.IsDigit(fldname, 0)) tmp0 = "__" + fldname; else tmp0 = fldname; // an xml fieldname cannot have a space, change it to a "__" string tmpstr = tmp0.Replace(" ", "__"); int len = tmpstr.Length; int cnt = 0; // this is also our sequence that tells us the follow 3 digits is the ascii number (base 10) // of the character we replaced. string OKpunch = "-._"; string outstr = ""; int decval; while (cnt < len) { char tmpchr = tmpstr[cnt]; if (!char.IsLetterOrDigit(tmpchr) && (OKpunch.IndexOf(tmpchr) == -1)) { decval = tmpchr; outstr += OKpunch + decval.ToString("D3"); } else { outstr += tmpchr.ToString(); } cnt++; } return outstr; } public string CvtFldToUserFld(string fldname) { string tmpstr0; if (fldname.Length < 2) return fldname; // an xml element name cannot begin with a digit. we had prepended a "__" if (fldname.Substring(0, 2) == "__" && char.IsDigit(fldname, 2)) tmpstr0 = fldname.Substring(2, fldname.Length - 2); else tmpstr0 = fldname; // an xml element name cannot have a space, we converted to a "__" string tmpstr = tmpstr0.Replace("__", " "); int len = tmpstr.Length; int cur = 0; // this is also our sequence that tells us the follow 3 digits is the ascii number (base 10) // of the character we replaced. string OKpunch = "-._"; string outstr = ""; int decval, indx; if (tmpstr.Length < 6) indx = -1; else indx = tmpstr.IndexOf(OKpunch, cur); string asc_spchar; while (indx >= 0) { outstr += tmpstr.Substring(cur, indx - cur); asc_spchar = tmpstr.Substring(indx + 3, 3); decval = System.Convert.ToInt16(asc_spchar, 10); outstr += System.Convert.ToChar(decval).ToString(); cur = indx + 6; if (cur + 6 > len) indx = -1; else indx = tmpstr.IndexOf(OKpunch, cur); } if (cur < len) outstr += tmpstr.Substring(cur, len - cur); return outstr; } // Process the given field type. // return the type value used in the RO.FST file public ushort GetFSTreturnType(ushort fldtype, string fldname, VlnXmlElement elem) { ushort rtnval = 0; /* * Here are the possible field types: * public enum FieldTypes: uint { Nil=0, SingleTxt=1, VariableTxt=2, FrmtSingleTxt=4, XYPlot=8, Table=10, Image=20, MultiTxt=40, Combination=128, MultiFld=100 } */ switch ((uint)fldtype) { case 1: // Fixed length text case 2: // Variable length text case 4: // formatted text case 40: // Multi line text rtnval = 1; break; case 10: // Table rtnval = 2; break; case 8: // X/Y Plot rtnval = 4; break; case 32: // image (intergrated graphics) - this is the HEX representation rtnval = 8; break; } return rtnval; } public string GenerateXmlString(XmlNode node, bool isgroup) // KBR not sure - see end of method { string retstr; XmlTextWriter xmlTextWriter; StringWriter strWriter = new StringWriter(); xmlTextWriter = new XmlTextWriter(strWriter); VlnXmlElement enode; XmlNode kid; kid = node.FirstChild; enode = (VlnXmlElement)node; if (isgroup == true) { xmlTextWriter.WriteStartElement("vlnGroup"); if (enode.HasAttribute("RetVal")) { xmlTextWriter.WriteStartAttribute("RetVal", null); xmlTextWriter.WriteString(enode.GetAttribute("RetVal")); xmlTextWriter.WriteEndAttribute(); } if (enode.HasAttribute("MenuItem")) { xmlTextWriter.WriteStartAttribute("MenuItem", null); xmlTextWriter.WriteString(enode.GetAttribute("MenuItem")); xmlTextWriter.WriteEndAttribute(); } if (enode.HasAttribute("FieldsInUse")) { xmlTextWriter.WriteStartAttribute("FieldsInUse", null); xmlTextWriter.WriteString(enode.GetAttribute("FieldsInUse")); xmlTextWriter.WriteEndAttribute(); } if (enode.HasAttribute("ApplicabilityEnabled")) //C2021-026 save field applicability { xmlTextWriter.WriteStartAttribute("ApplicabilityEnabled", null); xmlTextWriter.WriteString(enode.GetAttribute("ApplicabilityEnabled")); xmlTextWriter.WriteEndAttribute(); } if (enode.HasAttribute("GroupMenuItem")) { xmlTextWriter.WriteStartAttribute("GroupMenuItem", null); xmlTextWriter.WriteString(enode.GetAttribute("GroupMenuItem")); xmlTextWriter.WriteEndAttribute(); } if (enode.HasAttribute("GroupFieldsInUse")) { xmlTextWriter.WriteStartAttribute("GroupFieldsInUse", null); xmlTextWriter.WriteString(enode.GetAttribute("GroupFieldsInUse")); xmlTextWriter.WriteEndAttribute(); } if (enode.HasAttribute("AccPageID")) { xmlTextWriter.WriteStartAttribute("AccPageID", null); xmlTextWriter.WriteString(enode.GetAttribute("AccPageID")); xmlTextWriter.WriteEndAttribute(); } if (enode.HasAttribute("AccPageIDPrefix")) { xmlTextWriter.WriteStartAttribute("AccPageIDPrefix", null); xmlTextWriter.WriteString(enode.GetAttribute("AccPageIDPrefix")); xmlTextWriter.WriteEndAttribute(); } xmlTextWriter.WriteStartAttribute("MenuTitle", null); xmlTextWriter.WriteString(enode.GetAttribute("MenuTitle")); xmlTextWriter.WriteEndAttribute(); while (kid != null) { if ((kid is XmlText) || (kid.ChildNodes.Count == 1 && kid.Name != "vlnGroup")) kid.WriteTo(xmlTextWriter); kid = kid.NextSibling; } } else { xmlTextWriter.WriteStartElement(node.Name); xmlTextWriter.WriteStartAttribute("MenuTitle", null); xmlTextWriter.WriteString(enode.GetAttribute("MenuTitle")); xmlTextWriter.WriteEndAttribute(); while (kid != null) { kid.WriteTo(xmlTextWriter); kid = kid.NextSibling; } } xmlTextWriter.WriteEndElement(); // vlnGroup or top of RO. xmlTextWriter.Flush(); xmlTextWriter.Close(); retstr = strWriter.ToString(); // replace the single quote because ODBC fails if it's in a string because // the single quote represents the beg/end of string when sending string to // ODBC. retstr = retstr.Replace("\'", "'"); // KBR - not sure if this method should be in RODB class or db specific class return (retstr); } // Create a new field record in master and reference in the current table. // pass in current rodb object, the xml element creating and editlevel // is a recordType of either Group (group field definitions) or rro for // the ro definition. the string apd represents an append string which is // appended to the fieldname for combo types, otherwise it's null public bool RODB_NewFieldRecord(ROField myrof, VlnXmlElement myelem, string strschema, uint editlevel, string apd) { bool success; // get a new record id for this and save it in the master table. string mrecid = RODB_GetNextRecId("ROMaster"); // get recid in ROMaster for group, so that we can set the parent field. // walk up the tree until we find a 'MasterRecID' attribute. VlnXmlElement parent; parent = myelem; while (parent != null) { if (parent.HasAttribute("MasterRecID")) break; parent = (VlnXmlElement)parent.ParentNode; } success = RODB_NewSchemaPiece(mrecid, parent.GetAttribute("MasterRecID"), "ROMaster", strschema, editlevel); if (success != true) return false; // also reference it from the local table. string lrecid = RODB_GetNextRecId(myelem.GetAttribute("Table")); myrof.SetRecID(lrecid); myrof.SetMasterRecID(mrecid); StringBuilder strbld = new StringBuilder(); strbld.Append(mrecid); strbld.Append(" "); string strtype = myrof.GetFieldType.ToString(); strbld.Append(strtype.PadLeft(3, '0')); strbld.Append(" "); strbld.Append(myrof.MakeFieldName(myrof.GetFieldname)); if (apd != null) strbld.Append(apd); // Need ParentID field, get by looking up xml tree to find parentID attribute = "00000000", then // use this recid. parent = myelem; while (parent.GetAttribute("ParentID") != "00000000") { parent = (VlnXmlElement)parent.ParentNode; } success = RODB_NewSchemaPiece(lrecid, parent.GetAttribute("RecID"), myelem.GetAttribute("Table"), strbld.ToString(), editlevel); return success; } // C2021-026 rename any P/C Child fields public string DoReplaceParentChildField(string infstr, string oldName, string newName) { string rtnInfoStr = infstr; if (PCChildList == null) return rtnInfoStr; int pcChildIdx = 0; // children indexing starts at one so initialize with zero foreach (string chld in PCChildList) { pcChildIdx++; string csufx = string.Format("_PCCHILD{0}", pcChildIdx); // Create child field name string oldFldNameChild = oldName.Insert(oldName.Length - 1, CvtUserFldToFld(csufx)); string newFldNameChild = newName.Insert(newName.Length - 1, CvtUserFldToFld(csufx)); rtnInfoStr = rtnInfoStr.Replace(oldFldNameChild, newFldNameChild); } return rtnInfoStr; } public string DoCmbFieldReplace(string repstring, string oname, string nname, string letter) { string cmboname, cmbnname; cmboname = oname.Insert(oname.Length - 1, letter); cmbnname = nname.Insert(nname.Length - 1, letter); string rtnstr = repstring.Replace(cmboname, cmbnname); rtnstr = DoReplaceParentChildField(rtnstr, cmboname, cmbnname); // C2021-026 create child fields in combo group return rtnstr; } // RODB_UpdateNamesInROs: goes through subtree (top node is 'fld') and if this // updated field is used (either in [Group]FieldsInUse or was used in parent, then // 1) if group node, check for attributes using "" and if found, update // to " // 2) if group edit, modify this node's xml data with the new fieldname, // otherwise, edit 'ROs' under this to replace with // Input: VlnXmlElement fld: start processing at this node. // string myrecid: local recid to be used for check to see if used (in FieldsInUse // or GroupFieldsInUse. // string oldname, newname - old and new field names // string myrecid - local record storing field name // uint editlevel - if editting groups, do check for for item 1 above in // Group attributes, otherwise do in // bool parentused - set to false from caller, this is recursive and will // be set appropriately for recursive calls. public bool RODB_UpdateFieldNames(VlnXmlElement fld, string myrecid, string oldname, string newname, uint editlevel, bool parentused, VlnStatusMessage StatMsgWindow, bool combofield) { bool success; bool useAtLevel = false; XmlNode node; VlnXmlElement child; string fldinuse; if (editlevel == (uint)RecordType.Schema) fldinuse = "FieldsInUse"; else fldinuse = "GroupFieldsInUse"; // Do the current level before walking through any children groups. if (fld.Name == "vlnGroup") { if (fld.HasAttribute(fldinuse)) { // if recid not in here, don't process for this node. string fiu = fld.GetAttribute(fldinuse); if (fiu.IndexOf(myrecid) >= 0) useAtLevel = true; } if (useAtLevel == true || parentused == true) useAtLevel = true; if (useAtLevel == true) { string haskids, kidsloaded; haskids = fld.GetAttribute("HasChild"); kidsloaded = fld.GetAttribute("ChildLoaded"); if (haskids == "True" && kidsloaded == "False") { success = RODB_GetChildData(fld, true); if (success == false) return false; fld.SetAttribute("ChildLoaded", "True"); } parentused = true; // for kids of this group. success = RODB_ProcessRROFieldChange(fld, oldname, newname, editlevel, StatMsgWindow, combofield); if (success == false) return false; } } node = (XmlNode)fld.FirstChild; while (node != null) { if (node is VlnXmlElement) { useAtLevel = false; child = (VlnXmlElement)node; // If this is a group menu edit, process group nodes, by recursive // call and then modify data on group record. // For both, when hitting a group node, check that it either doesn't // have 'FieldsInUse' or 'GroupFieldsInUse' or that if it does, // this record id is in the list -> only process these, otherwise // the field is not used. if (child.Name == "vlnGroup") { // see if inuse for this one, either defined here or from parent. if (child.HasAttribute(fldinuse)) { // if recid not in here, don't process for this node. Still // need to check children because they may redefine fields in use. string fiu = child.GetAttribute(fldinuse); if (fiu.IndexOf(myrecid) >= 0) useAtLevel = true; } if (useAtLevel == false && parentused == true) useAtLevel = true; // update this record if it doesn't have it's own , then do kids // of it. success = RODB_UpdateFieldNames(child, myrecid, oldname, newname, editlevel, useAtLevel, StatMsgWindow, combofield); if (success == false) return false; } } node = node.NextSibling; } return true; } // check if the name entered is one used for setting up // the new graphics or setpoint databases. public bool RODB_CheckForStandardName(string oname) { string oldname = CvtFldToUserFld(oname); if (oldname.CompareTo("Name") == 0) return true; if (oldname.CompareTo("Image ID") == 0) return true; if (oldname.CompareTo("Image") == 0) return true; if (oldname.CompareTo("Setpoint ID") == 0) return true; if (oldname.CompareTo("Setpoint Value") == 0) return true; if (oldname.CompareTo("Associated System/Component") == 0) return true; if (oldname.CompareTo("Applicability") == 0) return true; if (oldname.CompareTo("Revision") == 0) return true; if (oldname.CompareTo("Short Description") == 0) return true; if (oldname.CompareTo("Description") == 0) return true; if (oldname.CompareTo("Key Assumptions") == 0) return true; if (oldname.CompareTo("Basis") == 0) return true; if (oldname.CompareTo("References") == 0) return true; if (oldname.CompareTo("Group") == 0) return true; if (oldname.CompareTo("Parameter") == 0) return true; return false; } #endregion } /// /// The following class handles generation of sql strings to get/put requested parts of /// XML RO Tree. And performs the database access using the VLN_DB class. This class is /// to be used for MS Access /// public partial class AccessRODB : RODB { public DBEncapsulation.OLEDB_DBEncap DBE_OLEDB; public AccessRODB(string rODirectoyPath) { RODirectoryPath = rODirectoyPath; BuildConnectionString(rODirectoyPath, rODirectoyPath); DBE_OLEDB = new OLEDB_DBEncap(); DBE = DBE_OLEDB; try { DBE.Connection(strDatabaseConnectionCommand); } catch (Exception e) { MessageBox.Show(e.Message, "Setup of OLEDB/ODBC"); } } public override string RODB_GetDBNameForAbout() { return ("Database: RoMaster"); } public override string RODB_GetDBServerForAbout() { return ("Microsoft Access"); } public override string RODB_HasBeenConverted() { try { DBE.OpenConnection(); } catch (Exception e) { MessageBox.Show(e.Message, "Could not open database connection"); // MessageBox.Show(e.StackTrace,"Debug"); return null; } string constring = null; string strHasBeen = "SELECT Info FROM ROMaster where RecType=" + (uint)RecordType.ConvertedToSql; try { DBE.Command(strHasBeen); DBE.Reader(); if (DBE.Read()) { constring = DBE.GetString(0); DBE.ReaderClose(); DBE.CommandDispose(); } } catch (Exception ex) { } DBE.CloseConnection(); return constring; } public override bool RODB_WriteSqlConnectToAccess(string newConectStr) { //try //{ // DBE.OpenConnection(); //} //catch (Exception e) //{ // MessageBox.Show(e.Message, "Could not open database connection"); // // MessageBox.Show(e.StackTrace,"Debug"); // return false; //} string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now); string strUpdate = "UPDATE ROMaster SET Info = '" + newConectStr + "'"; // note that '8' for rectype flags database converted: strUpdate = strUpdate + ", ModDateTime = '" + dt + "' WHERE RecType=" + (uint)RecordType.ConvertedToSql; try { DBE.Command(strUpdate); DBE.Reader(); DBE.ReaderClose(); DBE.CommandDispose(); } catch (Exception ex) { return false; } return true; } // get the next table name & update it to the next possible. public override string RODB_GetNextGroupTable() { // read record type of master for this table. This is where the highest recid is stored. // Update the recid by 1, write it out and return the new recid. string strGetNxt = "SELECT Info FROM ROMaster where RecID = '00000001' and RecType=" + (uint)RecordType.Master; string rettable = null; try { DBE.Command(strGetNxt); DBE.Reader(); if (DBE.Read()) { rettable = DBE.GetString(0); string nxttable = rettable.Substring(2, 6); int ltbl = System.Convert.ToInt32(nxttable, 10); ltbl++; nxttable = ltbl.ToString("d6"); // this format "d6" should pad left with zeros string newstr = "RO" + nxttable; DBE.ReaderClose(); DBE.CommandDispose(); string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now); string strUpdate = "UPDATE ROMaster SET Info = '" + newstr + "'"; strUpdate = strUpdate + ", ModDateTime = '" + dt + "' WHERE RecID = '00000001' and RecType=" + (uint)RecordType.Master; DBE.Command(strUpdate); DBE.Reader(); DBE.ReaderClose(); DBE.CommandDispose(); return rettable; } } catch (Exception e) { MessageBox.Show(e.Message, "Getting new record id"); } return null; } // get the next recid for this table & update it. public override string RODB_GetNextRecId(string table) { // read record type of master for this table. This is where the highest recid is stored. // Update the recid by 1, write it out and return the new recid. string strGetNxt = "SELECT Info FROM " + table + " where RecID = '00000000' and RecType=" + (uint)RecordType.Master; try { DBE.Command(strGetNxt); DBE.Reader(); if (DBE.Read()) { string RecID = DBE.GetString(0); int lrecid = System.Convert.ToInt32(RecID, 16); lrecid++; RecID = lrecid.ToString("x"); // need an 8 char string so pad left with zero string padstr = RecID.PadLeft(8, '0'); DBE.ReaderClose(); DBE.CommandDispose(); string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now); string strUpdate = "UPDATE " + table + " SET Info = '" + padstr + "'"; strUpdate = strUpdate + ", ModDateTime = '" + dt + "' WHERE RecID = '00000000' and RecType=" + (uint)RecordType.Master; DBE.Command(strUpdate); DBE.Reader(); DBE.ReaderClose(); DBE.CommandDispose(); return padstr; } } catch (Exception e) { MessageBox.Show(e.Message, "Getting new record id"); } return null; } // pass in root node. This will get Groups from Root node. This is a special case because // the list of top level groups is off of the ROMaster database. public override bool RODB_GetRootGroups(VlnXmlElement root) { ShowCount.GetRootGroups++; string table; string group; string title; // Get menu fields to display try { DBE.OpenConnection(); } catch (Exception e) { MessageBox.Show(e.Message, "Could not open database connection"); // MessageBox.Show(e.StackTrace,"Debug"); return false; } string strGetGroups = "SELECT RecID,Info FROM ROMaster where RecType = " + (uint)RecordType.SubDatabase; strGetGroups += " ORDER BY RecID ASC"; DBE.Command(strGetGroups); DBE.Reader(); SortedList mySL = new SortedList(); while (DBE.Read()) { // Parse info string, it has group name & table name. Store in a sorted // list, sorting by Group Name. string mrecid = DBE.GetString(0); group = DBE.GetString(1); mySL.Add(mrecid, group); } DBE.CommandDispose(); DBE.ReaderClose(); // Now, for each group, i.e. table, make xml elements for the top level groups by // reading in the Xml Element for the group from each table. for (int i = 0; i < mySL.Count; i++) { group = mySL.GetByIndex(i).ToString(); int grnamindx = group.IndexOf("\t"); if (grnamindx == -1) {// no tab, default to RO000001 table = "RO000001"; title = group; } else { StringBuilder tablesb = new StringBuilder(70); tablesb.Append(group); tablesb.Remove(0, grnamindx + 1); table = tablesb.ToString(); title = group.Substring(0, grnamindx); } strGetGroups = "SELECT RecID,Info FROM " + table + " where RecType = " + (uint)RecordType.Group; strGetGroups = strGetGroups + " and ParentID = '00000000'"; strGetGroups += " ORDER BY RecID ASC"; try { DBE.Command(strGetGroups); DBE.Reader(); if (DBE.Read()) { string RecID = DBE.GetString(0); string Info = DBE.GetString(1); XmlTextReader rdr = new XmlTextReader(Info, XmlNodeType.Element, null); XmlNode nd = ROXml.ReadNode(rdr); VlnXmlElement elem = (VlnXmlElement)nd; elem.MyROID = RecID; root.AppendChild(elem); elem.SetAttribute("RecID", RecID); elem.SetAttribute("ParentID", "00000000"); elem.SetAttribute("Table", table); elem.SetAttribute("MasterRecID", mySL.GetKey(i).ToString()); //elem.SetAttribute("MenuTitle", elem.InnerText); } } catch (Exception e) { MessageBox.Show(e.Message, "Error getting group data"); // try to continue on with the next group - may be one missing table. } DBE.CommandDispose(); DBE.ReaderClose(); } // get child count for each group. XmlNode node = root.FirstChild; while (node != null) { if (node is VlnXmlElement) { VlnXmlElement child = (VlnXmlElement)node; //get child count for each table listed in innertext. string strGetSubCount = "SELECT COUNT (RecID) as cnt FROM " + child.GetAttribute("Table") + " WHERE "; strGetSubCount = strGetSubCount + "ParentID='" + child.GetAttribute("RecID") + "'"; try { DBE.Command(strGetSubCount); DBE.Reader(); } catch (Exception e) { MessageBox.Show(e.Message, "Error reading RO Data"); return false; } if (DBE.Read()) { int cnt = DBE.GetInt32(0); if (cnt > 0) child.SetAttribute("HasChild", "True"); else child.SetAttribute("HasChild", "False"); child.SetAttribute("ChildLoaded", "False"); } DBE.CommandDispose(); DBE.ReaderClose(); } node = node.NextSibling; } // for the beginning and end schema element, go to the master & read it from there. Store these to be used // by all of the databases. string strGetSchemaPiece; schemastart = null; strGetSchemaPiece = "SELECT Info From ROMaster where (ParentID='00000000' and RecType = " + (uint)RecordType.SchemaStart + ")"; DBE.Command(strGetSchemaPiece); DBE.Reader(); if (DBE.Read()) schemastart = DBE.GetString(0); else { DBE.CommandDispose(); DBE.ReaderClose(); return false; } DBE.CommandDispose(); DBE.ReaderClose(); schemaend = null; strGetSchemaPiece = "SELECT Info From ROMaster where (ParentID='00000000' and RecType = " + (uint)RecordType.SchemaEnd + ")"; DBE.Command(strGetSchemaPiece); DBE.Reader(); if (DBE.Read()) schemaend = DBE.GetString(0); else { DBE.CommandDispose(); DBE.ReaderClose(); return false; } DBE.CommandDispose(); DBE.ReaderClose(); return true; } public override bool RODB_DeleteGroup(XmlNode group, string tbname, string toprecid) { VlnXmlElement egroup = null; string strDel; strDel = "DELETE FROM ROMaster WHERE "; // delete table entry in ROMaster if (group != null) { egroup = (VlnXmlElement)group; strDel = strDel + "RecID='" + egroup.GetAttribute("MasterRecID") + "'"; } else strDel = strDel + "RecID='" + toprecid + "'"; try { DBE.Command(strDel); DBE.Reader(); DBE.Read(); } catch (Exception e) { MessageBox.Show(e.Message, "Error When deleting SubGroup ROs."); DBE.CommandDispose(); DBE.ReaderClose(); return false; } DBE.CommandDispose(); DBE.ReaderClose(); // now remove the table. if (egroup != null) strDel = "DROP TABLE " + egroup.GetAttribute("Table"); else strDel = "DROP TABLE " + tbname; try { DBE.Command(strDel); DBE.Reader(); DBE.Read(); } catch (Exception e) { MessageBox.Show(e.Message, "Error When deleting SubGroup ROs."); DBE.CommandDispose(); DBE.ReaderClose(); return false; } DBE.CommandDispose(); DBE.ReaderClose(); return true; } // Delete from database either the group or RO from input node. public override bool RODB_DeleteRO(XmlNode group) { // delete from database and then delete from tree. delete group and all // subgroups & ros beneath it. delete subgroups by calling this. // note that if just an ro is passed in, it will only delete it. XmlNode node = group.FirstChild; VlnXmlElement child; VlnXmlElement egroup = (VlnXmlElement)group; string strDelSub; if (group.Name == "vlnGroup") { while (node != null) { if (node is VlnXmlElement) { child = (VlnXmlElement)node; // if this is a group, call this again. if (child.Name == "vlnGroup") { bool success = RODB_DeleteRO(node); if (success == false) { MessageBox.Show("error in RODB_DeleteGroup"); return false; } } } node = node.NextSibling; } // delete all db records that have the ParentID = to this group RecID. // (All subgroups should be processed from above recursion. strDelSub = "DELETE FROM " + egroup.GetAttribute("Table") + " WHERE "; strDelSub = strDelSub + "ParentID='" + egroup.GetAttribute("RecID") + "'"; try { DBE.Command(strDelSub); DBE.Reader(); DBE.Read(); } catch (Exception e) { MessageBox.Show(e.Message, "Error When deleting SubGroup ROs."); DBE.CommandDispose(); DBE.ReaderClose(); return false; } DBE.CommandDispose(); DBE.ReaderClose(); } // now delete this ro or group, i.e. the one passed in. strDelSub = "DELETE FROM " + egroup.GetAttribute("Table") + " WHERE "; strDelSub = strDelSub + "RecID='" + egroup.GetAttribute("RecID") + "'"; try { DBE.Command(strDelSub); DBE.Reader(); DBE.Read(); } catch (Exception e) { MessageBox.Show(e.Message, "Error When deleting this RO."); DBE.CommandDispose(); DBE.ReaderClose(); return false; } DBE.CommandDispose(); DBE.ReaderClose(); return true; } //public string GenerateXmlString(XmlNode node, bool isgroup) // KBR not sure - see end of method //{ // string retstr; // XmlTextWriter xmlTextWriter; // StringWriter strWriter = new StringWriter(); // xmlTextWriter = new XmlTextWriter(strWriter); // VlnXmlElement enode; // XmlNode kid; // kid = node.FirstChild; // enode = (VlnXmlElement)node; // if (isgroup == true) // { // xmlTextWriter.WriteStartElement("vlnGroup"); // if (enode.HasAttribute("RetVal")) // { // xmlTextWriter.WriteStartAttribute("RetVal", null); // xmlTextWriter.WriteString(enode.GetAttribute("RetVal")); // xmlTextWriter.WriteEndAttribute(); // } // if (enode.HasAttribute("MenuItem")) // { // xmlTextWriter.WriteStartAttribute("MenuItem", null); // xmlTextWriter.WriteString(enode.GetAttribute("MenuItem")); // xmlTextWriter.WriteEndAttribute(); // } // if (enode.HasAttribute("FieldsInUse")) // { // xmlTextWriter.WriteStartAttribute("FieldsInUse", null); // xmlTextWriter.WriteString(enode.GetAttribute("FieldsInUse")); // xmlTextWriter.WriteEndAttribute(); // } // if (enode.HasAttribute("GroupMenuItem")) // { // xmlTextWriter.WriteStartAttribute("GroupMenuItem", null); // xmlTextWriter.WriteString(enode.GetAttribute("GroupMenuItem")); // xmlTextWriter.WriteEndAttribute(); // } // if (enode.HasAttribute("GroupFieldsInUse")) // { // xmlTextWriter.WriteStartAttribute("GroupFieldsInUse", null); // xmlTextWriter.WriteString(enode.GetAttribute("GroupFieldsInUse")); // xmlTextWriter.WriteEndAttribute(); // } // if (enode.HasAttribute("AccPageID")) // { // xmlTextWriter.WriteStartAttribute("AccPageID", null); // xmlTextWriter.WriteString(enode.GetAttribute("AccPageID")); // xmlTextWriter.WriteEndAttribute(); // } // if (enode.HasAttribute("AccPageIDPrefix")) // { // xmlTextWriter.WriteStartAttribute("AccPageIDPrefix", null); // xmlTextWriter.WriteString(enode.GetAttribute("AccPageIDPrefix")); // xmlTextWriter.WriteEndAttribute(); // } // xmlTextWriter.WriteStartAttribute("MenuTitle", null); // xmlTextWriter.WriteString(enode.GetAttribute("MenuTitle")); // xmlTextWriter.WriteEndAttribute(); // while (kid != null) // { // if ((kid is XmlText) || (kid.ChildNodes.Count == 1 && kid.Name != "vlnGroup")) // kid.WriteTo(xmlTextWriter); // kid = kid.NextSibling; // } // } // else // { // xmlTextWriter.WriteStartElement(node.Name); // xmlTextWriter.WriteStartAttribute("MenuTitle", null); // xmlTextWriter.WriteString(enode.GetAttribute("MenuTitle")); // xmlTextWriter.WriteEndAttribute(); // while (kid != null) // { // kid.WriteTo(xmlTextWriter); // kid = kid.NextSibling; // } // } // xmlTextWriter.WriteEndElement(); // vlnGroup or top of RO. // xmlTextWriter.Flush(); // xmlTextWriter.Close(); // retstr = strWriter.ToString(); // // replace the single quote because ODBC fails if it's in a string because // // the single quote represents the beg/end of string when sending string to // // ODBC. // retstr = retstr.Replace("\'", "'"); // KBR - not sure if this method should be in RODB class or db specific class // return (retstr); //} public override bool RODB_WriteGroup(XmlNode group, VlnXmlElement master) { bool retval = true; string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now); string xmlstr = GenerateXmlString(group, true); string strUpdate = "UPDATE " + master.GetAttribute("Table") + " SET Info = '" + xmlstr + "'"; strUpdate = strUpdate + ", ModDateTime = '" + dt + "' WHERE RecID='" + master.GetAttribute("RecID") + "'"; try { DBE.Command(strUpdate); DBE.Reader(); } catch (Exception e) { MessageBox.Show(e.Message, "Error on write group"); retval = false; } DBE.ReaderClose(); DBE.CommandDispose(); return retval; } public override bool RODB_InsertGroup(VlnXmlElement group) { bool retval = true; string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now); string xmlstr = GenerateXmlString(group, true); string strInsert = "INSERT INTO " + group.GetAttribute("Table") + "(RecID, RecType, ParentID, ModDateTime, Info) "; strInsert = strInsert + " VALUES ('" + group.GetAttribute("RecID") + "'," + (uint)RecordType.Group + ",'"; strInsert = strInsert + group.GetAttribute("ParentID") + "','" + dt + "','" + xmlstr + "');"; try { DBE.Command(strInsert); DBE.Reader(); } catch (Exception e) { MessageBox.Show(e.Message, "Error on insert group"); retval = false; } DBE.ReaderClose(); DBE.CommandDispose(); return retval; } // add a new database table, add a record to master to point to it. return // that record number. public override string RODB_AddNewTable(string TblName, string newgrpname) { bool retval = false; // create the table. this is done by doing a select into the new table // from the RoMaster because using the create table command didn't have // a datatype that was compatible between sql & ms-access (for the memo // field 'Info'). string strMkTable = "SELECT * INTO " + TblName; strMkTable = strMkTable + " FROM ROMaster WHERE RecID = '00000000';"; try { DBE.Command(strMkTable); DBE.Reader(); retval = true; } catch (Exception e) { MessageBox.Show(e.Message, "Error creating table."); } DBE.ReaderClose(); DBE.CommandDispose(); // return if error on table create. if (retval == false) return null; // remove the first record. It was inserted just to have something to copy to // create the table. (see previous comment) string strDel = "DELETE FROM " + TblName + " WHERE RecID = '00000000'"; try { DBE.Command(strDel); DBE.Reader(); DBE.Read(); } catch (Exception e) { MessageBox.Show(e.Message, "Error When deleting SubGroup ROs."); DBE.CommandDispose(); DBE.ReaderClose(); return null; } DBE.CommandDispose(); DBE.ReaderClose(); // now insert the first record string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now); string strInsert = "INSERT INTO " + TblName + "( RecID, RecType, ParentID, ModDateTime, Info ) "; strInsert = strInsert + " VALUES ('00000000', 0,'00000000','" + dt + "','00000001');"; retval = false; try { DBE.Command(strInsert); DBE.Reader(); retval = true; } catch (Exception e) { MessageBox.Show(e.Message, "Error creating table."); } DBE.ReaderClose(); DBE.CommandDispose(); // return if error on insert of master record of new table, otherwise add // this table reference to the master. if (retval == false) return null; string recid = RODB_GetNextRecId("ROMaster"); string info = CvtUserFldToFld(newgrpname) + "\t" + TblName; strInsert = "INSERT INTO ROMaster ( RecID, RecType, ParentID, ModDateTime, Info ) "; strInsert = strInsert + " VALUES ('" + recid + "', 1,'00000001','" + dt + "','" + info + "');"; retval = false; try { DBE.Command(strInsert); DBE.Reader(); retval = true; } catch (Exception e) { MessageBox.Show(e.Message, "Error writing new table to master."); } DBE.ReaderClose(); DBE.CommandDispose(); if (retval == false) return null; else return recid; } // Copy over field definitions from the ROMaster to the input table. public override bool RODB_CopyFieldDefs(string fromtb, string totb, int type) { ArrayList retlist = new ArrayList(); string strGetFields; string Info; string RecID; string name; uint ftype; ROField rof; bool success; // select all of the field definition records in this table. strGetFields = "SELECT RecID, Info from ROMaster where RecType = 2"; DBE.Command(strGetFields); DBE.Reader(); while (DBE.Read()) { RecID = DBE.GetString(0); Info = DBE.GetString(1); // it's defined in the local table if the first character is "<" which starts // the schema definition string. name = ParseEleName(Info); if (name != null) { // what type of schema element? rof = new ROField(name, RecID, null, 0); ftype = rof.ParseFieldType(Info); rof.SetFieldType(ftype); retlist.Add((object)rof); } } DBE.ReaderClose(); DBE.CommandDispose(); // using this list, add new records for field definition references to the new // table. for (int i = 0; i < retlist.Count; i++) { rof = (ROField)retlist[i]; if (rof.GetFieldname != null) { string mrecid = rof.GetRecID; string lrecid = RODB_GetNextRecId(totb); StringBuilder strbld = new StringBuilder(); strbld.Append(mrecid); strbld.Append(" "); string strtype = rof.GetFieldType.ToString(); strbld.Append(strtype.PadLeft(3, '0')); strbld.Append(" "); if (strtype == "128") // append an 'a' for combo type strbld.Append(rof.MakeFieldName(rof.GetFieldname) + "a"); else strbld.Append(rof.MakeFieldName(rof.GetFieldname)); // add new field. success = RODB_NewSchemaPiece(lrecid, "00000002", totb, strbld.ToString(), 2); if (success == false) return false; } } return true; } // This is simular to GetChildData() but this function reads in // all the subgroups for the given node // Note that this function is designed to be call for the // root node (i.e. it gets all of group and RO data for a given // database table) public override bool RODB_GetGroupAndSubgroups(VlnXmlElement node, StringBuilder sb) { VlnStatusBar StatBar = new VlnStatusBar("Reading from the Database"); string tablename = node.GetAttribute("Table"); string strGetChildData = "SELECT * FROM " + tablename; strGetChildData = strGetChildData + " where (RecType = 3 or RecType = 5) AND ParentID <> '00000002' ORDER BY ParentID,RecID ASC"; HybridDictionary dicGroups = new HybridDictionary(); dicGroups.Add(node.GetAttribute("RecID").ToString(), node); StatBar.BarMax = RODB_GetNumberOfGroupRecords(tablename); //get number of groups StatBar.BarStepValue = 5; StatBar.StatMsg = node.InnerText; DBE.Command(strGetChildData); DBE.Reader(); // skip the first record - it's the node that we passed in if (!DBE.Read()) { DBE.ReaderClose(); StatBar.PerformStep(1); return false; } while (DBE.Read()) { string RecID = DBE.GetString(0); int RecType = DBE.GetInt32(1); string ParID = DBE.GetString(2); string AccPageID = DBE.GetString(3); string Info = DBE.GetString(5); if (!ParID.Equals(node.GetAttribute("RecID"))) { if (!dicGroups.Contains(ParID)) { if (sb.Length == 0) { sb.AppendLine("The following records were skipped."); sb.AppendLine("The parent for these RO's was missing."); sb.AppendLine(); } string strMBText = "RecID: " + RecID + "\n\n Table: " + tablename + "\n"; sb.AppendLine(strMBText); // MessageBox.Show(strMBText, "Warning - Orphan RO Record"); continue; // skip - no parent for this node } node = (VlnXmlElement)dicGroups[ParID]; node.SetAttribute("HasChild", "True"); node.SetAttribute("ChildLoaded", "True"); } else { node.SetAttribute("ChildLoaded", "True"); } if (RecType == (uint)RecordType.Group) { VlnXmlElement elem; StatBar.PerformStep(); try { XmlTextReader rdr = new XmlTextReader(Info, XmlNodeType.Element, null); XmlNode nd = ROXml.ReadNode(rdr); elem = (VlnXmlElement)nd; } catch (Exception e) { MessageBox.Show(e.Message, "Error reading Xml Group From data"); DBE.ReaderClose(); return false; } elem.MyROID = RecID; elem.SetAttribute("RecID", RecID); elem.SetAttribute("ParentID", node.GetAttribute("RecID")); elem.SetAttribute("Table", node.GetAttribute("Table")); if (!dicGroups.Contains(RecID)) { node.AppendChild(elem); dicGroups.Add(RecID, elem); } // The Parameter Display Data is stored along with the group. This data // was migrated over so that the data was not lost, but there is no // User Interface to get to it. So remove the xml elements from the // group part of tree. All valid group data only has one sublevel of // data defining the group name. XmlNode grp = (XmlNode)elem; XmlNode kid = grp.FirstChild; XmlNode tmpkid; while (kid != null) { tmpkid = kid.NextSibling; if (kid is VlnXmlElement) if (kid.ChildNodes.Count > 1) grp.RemoveChild(kid); kid = tmpkid; } } // Store data in the VlnXmlElement as an RO else if (RecType == (uint)RecordType.RRO) { //Create the reader. XmlNode ro; try { //B2022-043 &pos; was missing the ; if (Info != null) Info = Info.Replace("'", "\'"); // B2021-071: crash when getting/saving field names XmlTextReader roreader = new XmlTextReader(Info, XmlNodeType.Element, null); ro = ROXml.ReadNode(roreader); } catch (Exception e) { MessageBox.Show(e.Message, "Error reading Xml RRO From data"); DBE.ReaderClose(); return false; } VlnXmlElement elem = (VlnXmlElement)ro; node.AppendChild(ro); elem.MyROID = RecID; elem.SetAttribute("RecID", RecID); elem.SetAttribute("ParentID", node.GetAttribute("RecID")); elem.SetAttribute("Table", node.GetAttribute("Table")); elem.SetAttribute("AccPageID", AccPageID); } } DBE.ReaderClose(); dicGroups = null; StatBar.Dispose(); return true; } public override bool RODB_GetChildData(VlnXmlElement node, bool CheckChildCount) { ShowCount.GetChildData++; // get menu fields to display here. string tablename = node.GetAttribute("Table"); string strGetChildData = "SELECT RecID,RecType,AccPageID, Info FROM " + tablename + " where "; strGetChildData = strGetChildData + "(RecType = " + (uint)RecordType.Group + " or RecType = "; strGetChildData = strGetChildData + (uint)RecordType.RRO + ") and "; strGetChildData = strGetChildData + "ParentID='" + node.GetAttribute("RecID") + "'"; strGetChildData += " ORDER BY RecID ASC"; //bool readit=false; DBE.Command(strGetChildData); DBE.Reader(); while (DBE.Read()) { ShowCount.Children++; //get count for attribute element string RecID = DBE.GetString(0); int RecType = DBE.GetInt32(1); MyRecID = RecID; string AccPageID = DBE.GetString(2); string Info = DBE.GetString(3); //B2022-043 &pos; was missing the ; Info = Info.Replace("'", "\'"); // B2021-071: crash when getting/saving field names node.SetAttribute("HasChild", "True"); // Store data in the VlnXmlElement as a subgroup if (RecType == (uint)RecordType.Group) { VlnXmlElement elem; try { XmlTextReader rdr = new XmlTextReader(Info, XmlNodeType.Element, null); XmlNode nd = ROXml.ReadNode(rdr); elem = (VlnXmlElement)nd; } catch (Exception e) { MessageBox.Show(e.Message, "Error reading Xml Group From data"); DBE.ReaderClose(); return false; } node.AppendChild(elem); elem.MyROID = RecID; elem.SetAttribute("RecID", RecID); elem.SetAttribute("ParentID", node.GetAttribute("RecID")); elem.SetAttribute("Table", node.GetAttribute("Table")); // The Parameter Display Data is stored along with the group. This data // was migrated over so that the data was not lost, but there is no // User Interface to get to it. So remove the xml elements from the // group part of tree. All valid group data only has one sublevel of // data defining the group name. XmlNode grp = (XmlNode)elem; XmlNode kid = grp.FirstChild; XmlNode tmpkid; while (kid != null) { tmpkid = kid.NextSibling; if (kid is VlnXmlElement) if (kid.ChildNodes.Count > 1) grp.RemoveChild(kid); kid = tmpkid; } } // Store data in the VlnXmlElement as an RO else if (RecType == (uint)RecordType.RRO) { //Create the reader. XmlNode ro; try { XmlTextReader roreader = new XmlTextReader(Info, XmlNodeType.Element, null); ro = ROXml.ReadNode(roreader); } catch (Exception e) { MessageBox.Show(e.Message, "Error reading Xml RRO From data"); DBE.ReaderClose(); return false; } VlnXmlElement elem = (VlnXmlElement)ro; elem.MyROID = RecID; node.AppendChild(ro); elem.SetAttribute("RecID", RecID); elem.SetAttribute("ParentID", node.GetAttribute("RecID")); elem.SetAttribute("Table", node.GetAttribute("Table")); elem.SetAttribute("AccPageID", AccPageID); } } DBE.ReaderClose(); // get child count for each subgroup. if (CheckChildCount) { //A different approach - Look for grandchildren and set the haschildren attribute //Build a query to get a list of children with children string strGetSubCount = "SELECT R1.ParentID,COUNT(*) AS CNT FROM " + tablename + " R1 " + "INNER JOIN " + tablename + " R2 ON R1.ParentID = R2.RecID " + "WHERE (R1.RecType = " + (uint)RecordType.Group + " or R1.RecType=" + (uint)RecordType.RRO + ") " + "and R2.ParentID ='" + node.GetAttribute("RecID") + "' group by R1.ParentID;"; try { DBE.Command(strGetSubCount); DBE.Reader(); } catch (Exception e) { MessageBox.Show(e.Message, "Error reading data"); } //Build a Dictionary of Children with Children from the query results HybridDictionary dicChild = new HybridDictionary(); while (DBE.Read()) dicChild[DBE.GetString(0)] = DBE.GetInt32(1);//This adds the entry and sets the value DBE.CommandDispose(); DBE.ReaderClose(); XmlNode child = node.FirstChild; while (child != null) //Look at each child node { if (child is VlnXmlElement) { VlnXmlElement echild = (VlnXmlElement)child; if (echild.Name == "vlnGroup") { //If the dictionary contains an entry, the child has children echild.SetAttribute("HasChild", (dicChild.Contains(echild.GetAttribute("RecID"))) ? "True" : "False"); echild.SetAttribute("ChildLoaded", "False"); } } child = child.NextSibling; } dicChild = null; } node.SetAttribute("ChildLoaded", "True"); return true; } public override bool IsDuplicateAccPageID(VlnXmlElement ro, string newacc) { string strcheck, inacc = null; bool isdup = false; int indx = newacc.IndexOf("'"); if (indx >= 0) inacc = newacc.Insert(indx, "'"); else inacc = newacc; strcheck = "SELECT Count(RecID) AS CNT FROM " + ro.GetAttribute("Table") + " WHERE AccPageID = '" + inacc + "'"; if (ro.HasAttribute("RecID")) // new ro's don't have recid defined yet before this test. strcheck = strcheck + " AND (NOT RecID = '" + ro.GetAttribute("RecID") + "')"; try { DBE.Command(strcheck); DBE.Reader(); } catch (Exception e) { MessageBox.Show(e.Message, "Error on check"); return true; } if (DBE.Read()) { int cnt = DBE.GetInt32(0); if (cnt > 0) isdup = true; else isdup = false; } DBE.CommandDispose(); DBE.ReaderClose(); return isdup; } public override VlnXmlElement RODB_ReadRO(string tbl, string recid) { ShowCount.ReadRo++; VlnXmlElement retele = null; string readstr = "SELECT ParentID, AccPageID, Info FROM " + tbl + " WHERE " + ((recid == null) ? "RecType=3 and ParentID = '00000000'" : ("RecID = '" + recid + "'")); try { DBE.Command(readstr); DBE.Reader(); // With the additional check for a recid=null, in the WHERE statement (above), we now longer need to check and skip for a null recid - jsj 4-17-2015 //if (recid == null) // DBE.Read(); // skip the first parentID record of 00000000 if (DBE.Read()) { string ParentID = DBE.GetString(0); string AccPageID = DBE.GetString(1); string Info = DBE.GetString(2); // Store data in the VlnXmlElement XmlTextReader rdr = new XmlTextReader(Info, XmlNodeType.Element, null); XmlNode nd = ROXml.ReadNode(rdr); retele = (VlnXmlElement)nd; retele.SetAttribute("RecID", recid); retele.SetAttribute("ParentID", ParentID); retele.SetAttribute("Table", tbl); if (retele.Name != "vlnGroup") retele.SetAttribute("AccPageID", AccPageID); } DBE.CommandDispose(); DBE.ReaderClose(); // if this is a group, see if children. // if the DBE.Read() cannot find the RO, then retele remains a NULL // the null check was added to prevent a null reference error if (retele != null && retele.Name == "vlnGroup") { string strGetSubCount = "SELECT COUNT (RecID) as cnt FROM " + tbl + " WHERE "; strGetSubCount = strGetSubCount + "ParentID='" + recid + "'"; DBE.Command(strGetSubCount); DBE.Reader(); if (DBE.Read()) { int cnt = DBE.GetInt32(0); if (cnt > 0) retele.SetAttribute("HasChild", "True"); else retele.SetAttribute("HasChild", "False"); retele.SetAttribute("ChildLoaded", "False"); } DBE.CommandDispose(); DBE.ReaderClose(); } } catch (Exception e) { MessageBox.Show(e.Message, "Error on Read of RO"); return null; } return retele; } public override bool RODB_WriteRO(VlnXmlElement ro, bool movedRO = false) { bool success; if (ro.Name == "vlnGroup") { success = RODB_WriteGroup(ro, ro); return success; } else { string wraccid = null; string accid = ro.GetAttribute("AccPageID"); int quote = accid.IndexOf("'"); if (quote >= 0) wraccid = accid.Insert(quote, "'"); else wraccid = accid; string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now); string xmlstr = GenerateXmlString(ro, false); string strUpdate = "UPDATE " + ro.GetAttribute("Table") + " SET Info = '" + xmlstr + "'"; if (movedRO) { VlnXmlElement parent = (VlnXmlElement)ro.ParentNode; ro.SetAttribute("ParentID", parent.GetAttribute("RecID")); strUpdate += ", ParentID = '" + ro.GetAttribute("ParentID") + "'"; } strUpdate = strUpdate + ", ModDateTime = '" + dt + "', AccPageID = '" + wraccid + "' WHERE RecID='" + ro.GetAttribute("RecID") + "'"; try { DBE.Command(strUpdate); DBE.Reader(); success = true; } catch (Exception e) { MessageBox.Show(e.Message, "Database Write Error"); success = false; } DBE.ReaderClose(); DBE.CommandDispose(); } return success; } public override bool RODB_InsertRO(VlnXmlElement ro) { bool success = false; string strInsert = null; string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now); VlnXmlElement parent = (VlnXmlElement)ro.ParentNode; string lrecid = RODB_GetNextRecId(parent.GetAttribute("Table")); ro.SetAttribute("Table", parent.GetAttribute("Table")); ro.SetAttribute("RecID", lrecid); ro.SetAttribute("ParentID", parent.GetAttribute("RecID")); string haskids = parent.GetAttribute("HasChild"); if (haskids == "False" || haskids == "") { parent.SetAttribute("HasChild", "True"); parent.SetAttribute("ChildLoaded", "True"); } string xmlstr = GenerateXmlString(ro, false); string wraccid = null; if (ro.HasAttribute("AccPageID")) { string accid = ro.GetAttribute("AccPageID"); int quote = accid.IndexOf("'"); if (quote >= 0) wraccid = accid.Insert(quote, "'"); else wraccid = accid; } if (ro.Name == "vlnGroup") { // add attribute to flag that this does not have xml children, i.e. any // other subgroups or ros under it. ro.SetAttribute("HasChild", "False"); if (ro.HasAttribute("AccPageID")) { // Code is never reached, but this was noticed - this next line should likely be: // strInsert = "INSERT INTO " + parent.GetAttribute("Table") + "( RecID, RecType, ParentID, AccPageID, ModDateTime, Info ) "; strInsert = "INSERT INTO " + parent.GetAttribute("Table") + "( RecID, RecType, ParentID, ModDateTime, AccPageID, Info ) "; strInsert = strInsert + " VALUES ('" + ro.GetAttribute("RecID") + "'," + (uint)RecordType.Group + ",'" + ro.GetAttribute("ParentID"); strInsert = strInsert + "','" + wraccid + "','" + dt + "','" + xmlstr + "');"; } else { strInsert = "INSERT INTO " + parent.GetAttribute("Table") + "( RecID, RecType, ParentID, ModDateTime, Info ) "; strInsert = strInsert + " VALUES ('" + ro.GetAttribute("RecID") + "'," + (uint)RecordType.Group + ",'" + ro.GetAttribute("ParentID"); strInsert = strInsert + "','" + dt + "','" + xmlstr + "');"; } } else { strInsert = "INSERT INTO " + parent.GetAttribute("Table") + "( RecID, RecType, ParentID, AccPageId, ModDateTime, Info ) "; strInsert = strInsert + " VALUES ('" + ro.GetAttribute("RecID") + "'," + (uint)RecordType.RRO + ",'" + ro.GetAttribute("ParentID"); strInsert = strInsert + "','" + wraccid + "','" + dt + "','" + xmlstr + "');"; } try { DBE.Command(strInsert); DBE.Reader(); success = true; } catch (Exception e) { MessageBox.Show(e.Message, "Database Write Error"); } DBE.ReaderClose(); DBE.CommandDispose(); return success; } public override ushort RODB_GetFieldType(VlnXmlElement elem, string TableName, string Fld) { string strGetFields; string Info; string RecID; string name; string tmpname; ushort ftype = 0; ROField rof; if (TableName != lastTable) { lastTable = TableName; dicFldTypes = new HybridDictionary(); // select all of the field definition records in this table. // strGetFields = "SELECT RecID, Info from " + elem.GetAttribute("Table"); strGetFields = "SELECT RecID, Info from " + TableName; strGetFields = strGetFields + " where RecType = 2"; // + rtype.ToString(); DBE.Command(strGetFields); DBE.Reader(); // NOTE !!!! // This function does not handle Combo type fields (128). while (DBE.Read())// && ftype == 0) { RecID = DBE.GetString(0); Info = DBE.GetString(1); // it's defined in the local table if the first character is "<" which starts // the schema definition string. if ("<" == Info.Substring(0, 1)) { name = ParseEleName(Info); if (name != null) { tmpname = CvtUserFldToFld(name); //if (tmpname.Equals(Fld)) //{ rof = new ROField(name, RecID, null, 0); // uint tmpfldtype = rof.ParseFieldType(Info); ftype = GetFSTreturnType(System.Convert.ToUInt16(rof.ParseFieldType(Info)), tmpname, elem); dicFldTypes[tmpname] = ftype; //} } } else { // references master, but what we need is here: the field name and // the the field type. Just parse this from the info field. string parsename; // string strftype; // strftype = Info.Substring(9,3); parsename = Info.Substring(13, Info.Length - 13); //if (parsename.Equals(Fld)) //{ ftype = GetFSTreturnType(System.Convert.ToUInt16(Info.Substring(9, 3)), parsename, elem); dicFldTypes[parsename] = ftype; //} } } DBE.ReaderClose(); DBE.CommandDispose(); } ftype = ((ushort)dicFldTypes[Fld]); return ftype; } // For the given element's table, get all of the RO fields defined in this table. public override ArrayList RODB_GetFields(VlnXmlElement elem, uint rtype, bool refresh = false) { string table = elem.GetAttribute("Table"); if (!FieldDefinitions.ContainsKey(table)) FieldDefinitions.Add(table, RODB_GetFieldsFromDB(elem)); else if (refresh) { FieldDefinitions.Remove(table); FieldDefinitions.Add(table, RODB_GetFieldsFromDB(elem)); } return FieldDefinitions[table]; } private Dictionary _FieldDefinitions = null; public Dictionary FieldDefinitions { get { if (_FieldDefinitions == null) _FieldDefinitions = new Dictionary(); return _FieldDefinitions; } set { _FieldDefinitions = value; } } private ArrayList RODB_GetFieldsFromDB(VlnXmlElement elem) { ShowCount.GetFields++; ArrayList retlist = new ArrayList(); string strGetFields; string Info; string RecID; string name; uint ftype; ROField rof; // select all of the field definition records in this table. strGetFields = "SELECT RecID, Info from " + elem.GetAttribute("Table"); strGetFields = strGetFields + " where RecType = 2"; // + rtype.ToString(); DBE.Command(strGetFields); DBE.Reader(); while (DBE.Read()) { ShowCount.RoFields++; RecID = DBE.GetString(0); MyRecID = RecID; Info = DBE.GetString(1); // it's defined in the local table if the first character is "<" which starts // the schema definition string. if ("<" == Info.Substring(0, 1)) { name = ParseEleName(Info); if (name != null) { // what type of schema element? rof = new ROField(name, RecID, null, 0); ftype = rof.ParseFieldType(Info); rof.SetFieldType(ftype); retlist.Add((object)rof); } } else { // the recid points to a field definition (schema) in the master - use it to get // the schema element from the master and copy it. string parsename; string masterrecid; string strftype; masterrecid = Info.Substring(0, 8); strftype = Info.Substring(9, 3); ftype = System.Convert.ToUInt32(Info.Substring(9, 3)); parsename = CvtFldToUserFld(Info.Substring(13, Info.Length - 13)); // if we have a choice element, remove the last character (it was added to the // field names in order to allow for unique fields names for the choice items. if (ftype == (uint)FieldTypes.Combination) { string tparsename = parsename.Remove((parsename.Length) - 1, 1); // remove the choice string and the space parsename = tparsename; } rof = new ROField(parsename, RecID, masterrecid, ftype); retlist.Add((object)rof); } } DBE.ReaderClose(); DBE.CommandDispose(); return retlist; } public override string RODB_GetSchemaPiece(string Recid, string table) { string strGetSchemaPiece; string Info; strGetSchemaPiece = "SELECT Info From " + table + " where (RecID = '" + Recid + "')"; DBE.Command(strGetSchemaPiece); DBE.Reader(); if (DBE.Read()) Info = DBE.GetString(0); else Info = null; DBE.ReaderClose(); DBE.CommandDispose(); return Info?.Replace("'", "\'"); } public override bool RODB_NewSchemaPiece(string recid, string parentid, string table, string schpiece, uint rtype) { string strSchemaPiece; bool success = true; string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now); strSchemaPiece = "INSERT INTO " + table + " (RecID, RecType, ParentID, ModDateTime, Info) VALUES ('"; // B2004-016: at one point all fields are returned using a record type of 2 (Schema) - // but the insertion code did not account for this when inserting a type of 4 (GroupSchema) - i.e. // RecordType of 4 is no longer used, all fields (whether schema or group) are defined // as RecordTYpe of 2. //strSchemaPiece = strSchemaPiece + recid + "', " + rtype.ToString() + ", '" + parentid + "', '" + dt + "', '" + schpiece.Replace("\'","'") + "');"; strSchemaPiece = strSchemaPiece + recid + "', 2, '" + parentid + "', '" + dt + "', '" + schpiece.Replace("\'", "'") + "');"; DBE.Command(strSchemaPiece); try { DBE.Command(strSchemaPiece); DBE.Reader(); } catch (Exception e) { success = false; MessageBox.Show(e.Message, "Database Write Error"); } DBE.ReaderClose(); DBE.CommandDispose(); return success; } public override bool RODB_WriteSchemaPiece(string Recid, string table, string schpiece) { string strWriteSchemaPiece; bool success = true; string dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now); strWriteSchemaPiece = "UPDATE " + table + " SET " + table + ".Info = '" + schpiece.Replace("\'", "'") + "'"; strWriteSchemaPiece = strWriteSchemaPiece + ", ModDateTime = '" + dt + "' WHERE RecID = '" + Recid + "';"; try { DBE.Command(strWriteSchemaPiece); DBE.Reader(); } catch (Exception e) { success = false; MessageBox.Show(e.Message, "Database Update Error"); } DBE.ReaderClose(); DBE.CommandDispose(); return success; } public override bool RODB_ProcessRROFieldChange(VlnXmlElement child, string oldname, string newname, uint editlevel, VlnStatusMessage StatMsgWindow, bool combofield) { bool success; string str; string info; string tinfo1, tinfo2, tinfo3, tinfo4; string dt; string onameOpen, nnameOpen, onameClose, nnameClose, onameComma, nnameComma; string onameOpenX, nnameOpenX; onameOpen = "<" + oldname + ">"; nnameOpen = "<" + newname + ">"; onameOpenX = "<" + oldname + ">"; nnameOpenX = "<" + newname + ">"; onameComma = "<" + oldname + ","; // if format in attribute nnameComma = "<" + newname + ","; onameClose = ""; nnameClose = ""; dt = string.Format("{0:yyyyMMddHHmmss}", System.DateTime.Now); try { // first do the string replace for the group node. This will change any // attributes which may have changed. str = "SELECT Info From " + child.GetAttribute("Table") + " where (RecID = '" + child.GetAttribute("RecID") + "')"; DBE.Command(str); DBE.Reader(); if (DBE.Read()) info = DBE.GetString(0); else info = null; DBE.ReaderClose(); DBE.CommandDispose(); tinfo1 = info.Replace(onameOpen, nnameOpen); tinfo2 = tinfo1.Replace(onameClose, nnameClose); tinfo3 = tinfo2.Replace(onameComma, nnameComma); tinfo4 = tinfo3.Replace(onameOpenX, nnameOpenX); if (tinfo4 != info) { StatMsgWindow.StatusMessage = child.GetAttribute("MenuTitle"); str = "UPDATE " + child.GetAttribute("Table") + " SET Info = '" + tinfo4 + "'"; str = str + ", ModDateTime = '" + dt + "' WHERE RecID = '" + child.GetAttribute("RecID") + "';"; DBE.Command(str); DBE.Reader(); DBE.ReaderClose(); DBE.CommandDispose(); if (child.Name == "vlnGroup" && child.ParentNode.Name != "RO_Root") { XmlNodeList nodeList; nodeList = child.SelectNodes(oldname); foreach (XmlNode nod in nodeList) { XmlNode newnode = ROXml.CreateNode(XmlNodeType.Element, newname, ""); newnode.InnerXml = nod.InnerXml; child.ReplaceChild(newnode, nod); } } // if at top, replace strings in attributes for xml node. Only needs done // for top node because remainder are done in xml string replace for entire // node and written to database, but XML tree is deleted from memory and // reread in as needed. if (child.ParentNode.Name == "RO_Root") { string tmp, tmp1; tmp = child.GetAttribute("RetVal"); if (tmp != null) { tmp1 = tmp.Replace(onameOpen, nnameOpen); child.SetAttribute("RetVal", tmp1); } tmp = child.GetAttribute("MenuItem"); if (tmp != null) { string conameOpen = "<" + oldname + ","; string cnnameOpen = "<" + newname + ","; tmp1 = tmp.Replace(onameOpen, nnameOpen); string tmp2 = tmp1.Replace(conameOpen, cnnameOpen); child.SetAttribute("MenuItem", tmp2); } tmp = child.GetAttribute("AccPageID"); if (tmp != null) { tmp1 = tmp.Replace(onameOpen, nnameOpen); child.SetAttribute("AccPageID", tmp1); } tmp = child.GetAttribute("GroupMenuItem"); if (tmp != null) { tmp1 = tmp.Replace(onameOpen, nnameOpen); child.SetAttribute("GroupMenuItem", tmp1); } } } // if this was an RO field definition change, need to go through the XML for // the ROs too. if (editlevel == (uint)RecordType.Schema) { XmlNode chldnode = (XmlNode)child.FirstChild; VlnXmlElement echild; while (chldnode != null) { if (chldnode is VlnXmlElement) { echild = (VlnXmlElement)chldnode; if (echild.Name != "vlnGroup") { // If this is a group defintion subtree it will only have one // child, which is the text definition for the subgroup. Don't // include these in the tree. int levelCnt = chldnode.ChildNodes.Count; string TheMenuTitle = echild.GetAttribute("MenuTitle"); if ((levelCnt >= 1) && !TheMenuTitle.Equals("")) { // read record, do string replace in info & write record. Also, replace // the xml node with a node with the new name. str = "SELECT Info From " + echild.GetAttribute("Table") + " where (RecID = '" + echild.GetAttribute("RecID") + "')"; DBE.Command(str); DBE.Reader(); if (DBE.Read()) info = DBE.GetString(0); else info = null; DBE.ReaderClose(); DBE.CommandDispose(); if (info != null) info = info.Replace("\'", "&apos"); // B2021-071: crash when getting/saving field names if (combofield == false) { tinfo1 = info.Replace(onameOpen, nnameOpen); tinfo1 = DoReplaceParentChildField(tinfo1, onameOpen, nnameOpen); //C2021-0226 also update Parent/Child fields tinfo2 = tinfo1.Replace(onameClose, nnameClose); tinfo2 = DoReplaceParentChildField(tinfo2, onameClose, nnameClose); //C2021-0226 also update Parent/Child fields XmlNodeList nodeList; nodeList = echild.SelectNodes(oldname); foreach (XmlNode nod in nodeList) { XmlNode newnode = ROXml.CreateNode(XmlNodeType.Element, newname, ""); newnode.InnerXml = nod.InnerXml; echild.ReplaceChild(newnode, nod); } } else // need a,b,c,d yikes { string nma, nmb, nmc, nmd; nma = DoCmbFieldReplace(info, onameOpen, nnameOpen, "a"); nmb = DoCmbFieldReplace(nma, onameOpen, nnameOpen, "b"); nmc = DoCmbFieldReplace(nmb, onameOpen, nnameOpen, "c"); nmd = DoCmbFieldReplace(nmc, onameOpen, nnameOpen, "d"); nma = DoCmbFieldReplace(nmd, onameClose, nnameClose, "a"); nmb = DoCmbFieldReplace(nma, onameClose, nnameClose, "b"); nmc = DoCmbFieldReplace(nmb, onameClose, nnameClose, "c"); tinfo2 = DoCmbFieldReplace(nmc, onameClose, nnameClose, "d"); // replace the combo nodes with the new name, do a-d in case there are more than // one value field stored for this combo type for (char tmplet = 'a'; tmplet < 'e'; tmplet++) { XmlNodeList nodeList; nodeList = echild.SelectNodes(oldname + tmplet.ToString()); foreach (XmlNode nod in nodeList) { XmlNode newnode = ROXml.CreateNode(XmlNodeType.Element, newname + tmplet, ""); newnode.InnerXml = nod.InnerXml; echild.ReplaceChild(newnode, nod); } } } StatMsgWindow.StatusMessage = echild.GetAttribute("MenuTitle"); str = "UPDATE " + echild.GetAttribute("Table") + " SET Info = '" + tinfo2 + "'"; str = str + ", ModDateTime = '" + dt + "' WHERE RecID = '" + echild.GetAttribute("RecID") + "';"; DBE.Command(str); DBE.Reader(); DBE.ReaderClose(); DBE.CommandDispose(); } } } chldnode = chldnode.NextSibling; } } return true; } catch (Exception e) { success = false; MessageBox.Show(e.Message, "Database Update Error"); } return success; } public override bool RODB_UpdateFieldRecord(ROField myrof, VlnXmlElement myelem, string strschema, string oldname, string newname, uint editlevel, bool combofield) { bool success = false; // If this is a generic definition that was modified, i.e. from ROMaster, ask // the user whether to update the generic field definition or make it local. if (myrof.GetMasterRecID != null) { // if field is defined in the ROMaster, and we change the field name - // first check if it is using one of the names used to define new // setpoint or graphics databases. If so, make it a local change only! bool changegeneric = true; if (oldname != newname) { // if this is one of the standard names - don't let them save it. bool isused = RODB_CheckForStandardName(oldname); if (isused == true) { DialogResult result = MessageBox.Show($"The field name is used to define new Setpoints or Graphics Database.\n\n The update will be for this local (database) group only.\n\n Select OK to update local definition for all items in that table ({GetTopParentName(myelem)}).", "Field name modification.", MessageBoxButtons.OKCancel, MessageBoxIcon.Question); changegeneric = false; if(result == DialogResult.Cancel) return false; } } // if standard name, from above check, don't ask user if it is generic // or not. if (changegeneric != false) { // B2021-072: don't update name if user selects No on dialog. This is actually misleading, because Yes updates // Generic definition, i.e. all Groups, and No updates only Local definition, i.e. just this Group Added a // Cancel button to Cancel from this dialog and return to previous dialog. Added more information to the // dialog to inform user of this string dmsg = $"Update Generic definition?\r\n\r\n Yes - updates Generic definitions, \r\n No - updates Local definitions, \r\n Cancel - returns to previous dialog." + $"\r\n\r\n 'Generic' updates definition in ALL groups that use the name\r\n 'Local' updates definition only in the selected group, and will change the definition for all items in that table ({GetTopParentName(myelem)})."; System.Windows.Forms.DialogResult result = MessageBox.Show(dmsg, "Referenced Object Definition", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); if (result == DialogResult.Cancel) return false; if (result == DialogResult.No) changegeneric = false; } if (changegeneric == true) { success = RODB_WriteSchemaPiece(myrof.GetMasterRecID, "ROMaster", strschema); if (success != true) return false; // if change in fieldname, need to go through all of the databases to // adjust for it. if (oldname != newname) { VlnStatusMessage StatMsgWindow = new VlnStatusMessage("Status of RO Field Change"); // get root xml element // loop through each subgroup under root. VlnXmlElement topgroup; topgroup = (VlnXmlElement)ROXml.FirstChild.FirstChild; string sqlstr, recid, tbl; while (topgroup != null) { tbl = topgroup.GetAttribute("Table"); // get the recid of this field definition from the current table. sqlstr = "SELECT RecID FROM " + tbl; sqlstr = sqlstr + " WHERE (((" + tbl + ".RecType) = 2) AND ((" + tbl + ".Info) like ('"; sqlstr = sqlstr + myrof.GetMasterRecID + "%')));"; DBE.Command(sqlstr); DBE.Reader(); if (DBE.Read()) recid = DBE.GetString(0); else recid = null; DBE.ReaderClose(); DBE.CommandDispose(); if (recid != null) { // update the local field definition record StringBuilder strbld = new StringBuilder(); strbld.Append(myrof.GetMasterRecID); strbld.Append(" "); string strtype = myrof.GetFieldType.ToString(); strbld.Append(strtype.PadLeft(3, '0')); strbld.Append(" "); strbld.Append(myrof.MakeFieldName(myrof.GetFieldname)); success = RODB_WriteSchemaPiece(recid, topgroup.GetAttribute("Table"), strbld.ToString()); topgroup.SetAttribute("TreeNotData", "True"); // Update subelements too. if (success == true) success = RODB_UpdateFieldNames(topgroup, recid, oldname, newname, editlevel, false, StatMsgWindow, combofield); //topgroup = (VlnXmlElement)topgroup.NextSibling; } topgroup = (VlnXmlElement)topgroup.NextSibling; // B2021-071: infinite loop (moved this from above loop } StatMsgWindow.Dispose(); } } else // change local { // modify the current to have the definition local. success = RODB_WriteSchemaPiece(myrof.GetRecID, myelem.GetAttribute("Table"), strschema); // get top group node in tree. and from there process fields if // there was a change in fieldname text if (success == true && oldname != newname) { VlnStatusMessage StatMsgWindow = new VlnStatusMessage("Status of RO Field Change"); // get top node - the one immediately below the RO_Root. VlnXmlElement parent, startEle; parent = myelem; startEle = myelem; while (parent.Name != "RO_Root") { startEle = parent; parent = (VlnXmlElement)parent.ParentNode; } startEle.SetAttribute("TreeNotData", "True"); success = RODB_UpdateFieldNames(startEle, myrof.GetRecID, oldname, newname, editlevel, false, StatMsgWindow, combofield); StatMsgWindow.Dispose(); } } } else { success = RODB_WriteSchemaPiece(myrof.GetRecID, myelem.GetAttribute("Table"), strschema); // changed the fieldname, change xml text. if (success == true && oldname != newname) { VlnStatusMessage StatMsgWindow = new VlnStatusMessage("Status of RO Field Change"); // get top node - the one immediately below the RO_Root. VlnXmlElement parent, startEle; parent = myelem; startEle = myelem; while (parent.Name != "RO_Root") { startEle = parent; parent = (VlnXmlElement)parent.ParentNode; } startEle.SetAttribute("TreeNotData", "True"); success = RODB_UpdateFieldNames(startEle, myrof.GetRecID, oldname, newname, editlevel, false, StatMsgWindow, combofield); StatMsgWindow.Dispose(); } } return success; } //CSM-C2024-030 //Explain that Local is local to that table (and include Table name in wording displayed to user) public string GetTopParentName(VlnXmlElement elem) { VlnXmlElement nodetocheck = elem; while (nodetocheck.GetAttribute("ParentID") != "00000000" && nodetocheck.ParentNode != null) { nodetocheck = (VlnXmlElement) nodetocheck.ParentNode; } return nodetocheck.FirstChild.InnerText; } public override XmlSchema RODB_GetGroupSchema(VlnXmlElement elem) { XmlSchema myschema; VlnXmlElement parent; string entireschema; // if at top of tree, use this group schema information (fields in use). but if not, // the parent node defines the schema for the subgroups. if (elem.ParentNode.Name == "RO_Root") parent = elem; else parent = elem; // bug fix B2004-008 // need to use the current node's group definition all the time // parent = (VlnXmlElement) elem.ParentNode; while (parent != null) { if (parent.HasAttribute("GroupFieldsInUse") == true) break; // if we've looped to the top, just set the parent to null. (B2004-015) if (parent.ParentNode is VlnXmlElement) parent = (VlnXmlElement)parent.ParentNode; else parent = null; } if (parent == null) return null; // The group schema never gets saved as an attribute. it's always read in, because // it's not used as often as the ro schema. So for the Group Schema, we're looking // for 'GroupFieldsInUse'. // otherwise, read in schema definitions for those 'FieldsInUse'. string strGetSchemaPiece; entireschema = null; string GroupFieldsInUse = parent.GetAttribute("GroupFieldsInUse"); int strpos = 0; string Info; // For each item in use, get its xmlschema text. If unique to this group, the definition // exists local to this table. Otherwise, go back to the master. string recid; recid = GroupFieldsInUse.Substring(0, 8); while (recid != null) { strGetSchemaPiece = "SELECT Info From " + elem.GetAttribute("Table") + " where (RecID = '" + recid + "')"; DBE.Command(strGetSchemaPiece); DBE.Reader(); if (DBE.Read()) { Info = DBE.GetString(0); // if the info string has "<", i.e. an opening XML schema bracket, use // this text as the field definition, otherwise, go to the master to get // it. if ("<" == Info.Substring(0, 1)) { // use this field definition, else get from the master. entireschema = entireschema + Info; } else { string recidpart = Info.Substring(0, 8); strGetSchemaPiece = "SELECT Info From RoMaster where (RecID = '" + recidpart + "')"; DBE.ReaderClose(); DBE.CommandDispose(); DBE.Command(strGetSchemaPiece); DBE.Reader(); if (DBE.Read()) { Info = DBE.GetString(0); entireschema = entireschema + Info; } } DBE.ReaderClose(); DBE.CommandDispose(); strpos = strpos + 9; if (strpos > GroupFieldsInUse.Length) recid = null; else recid = GroupFieldsInUse.Substring(strpos, 8); } DBE.ReaderClose(); DBE.CommandDispose(); } entireschema = schemastart + entireschema + schemaend; XmlTextReader schemareader = new XmlTextReader(entireschema, XmlNodeType.Element, null); try { myschema = XmlSchema.Read(schemareader, null); return myschema; } catch (Exception e) { MessageBox.Show(e.Message, "Schema Error"); return null; } } public override XmlSchema RODB_GetSchema(VlnXmlElement elem) { XmlSchema myschema; VlnXmlElement parent; string entireschema; XmlNode nparent; nparent = elem; //.ParentNode; if (nparent is VlnXmlDocument) { // at top already. use this one. parent = elem; } else { parent = (VlnXmlElement)nparent; while (parent != null) { if (parent.HasAttribute("Schema") == true) break; if (parent.HasAttribute("FieldsInUse") == true) break; parent = (VlnXmlElement)parent.ParentNode; } } // if the schema has already been read in, just use it from the schema attribute, // otherwise, read in schema definitions for those 'FieldsInUse'. if (parent.HasAttribute("Schema")) { entireschema = parent.GetAttribute("Schema"); } else { string strGetSchemaPiece; entireschema = null; string FieldsInUse = parent.GetAttribute("FieldsInUse"); int strpos = 0; string Info; // For each item in use, get its xmlschema text. If unique to this group, the definition // exists local to this table. Otherwise, go back to the master. string recid; recid = FieldsInUse.Substring(0, 8); while (recid != null) { strGetSchemaPiece = "SELECT Info From " + elem.GetAttribute("Table") + " where (RecID = '" + recid + "')"; DBE.Command(strGetSchemaPiece); DBE.Reader(); if (DBE.Read()) { Info = DBE.GetString(0); // if the info string has "<", i.e. an opening XML schema bracket, use // this text as the field definition, otherwise, go to the master to get // it. if ("<" == Info.Substring(0, 1)) { // use this field definition, else get from the master. entireschema = entireschema + Info; } else { string recidpart = Info.Substring(0, 8); string localfieldname = Info.Substring(13, Info.Length - 13); strGetSchemaPiece = "SELECT Info From RoMaster where (RecID = '" + recidpart + "')"; DBE.ReaderClose(); DBE.CommandDispose(); DBE.Command(strGetSchemaPiece); DBE.Reader(); if (DBE.Read()) { // check if field name is same, if not, use local name. string goodname; Info = DBE.GetString(0); int indx1 = Info.IndexOf("\""); int indx2 = Info.IndexOf("\"", indx1 + 1); string masterfieldname = Info.Substring(indx1 + 1, indx2 - indx1 - 1); if (localfieldname != masterfieldname) goodname = Info.Replace(masterfieldname, localfieldname); else goodname = Info; entireschema = entireschema + goodname; } } DBE.ReaderClose(); DBE.CommandDispose(); strpos = strpos + 9; if (strpos > FieldsInUse.Length) recid = null; else recid = FieldsInUse.Substring(strpos, 8); } } DBE.ReaderClose(); DBE.CommandDispose(); entireschema = schemastart + entireschema + schemaend; } XmlTextReader schemareader = new XmlTextReader(entireschema, XmlNodeType.Element, null); try { myschema = XmlSchema.Read(schemareader, null); parent.SetAttribute("Schema", entireschema); return myschema; } catch (Exception e) { MessageBox.Show(e.Message, "Schema Read Error"); return null; } } public override int RODB_GetNumberOfROValueRecords(string tablename) { // get menu fields to display here. string strGetRoCount = "SELECT COUNT(RecType) AS NumRecs FROM " + tablename; strGetRoCount += " WHERE RecType = " + (uint)RecordType.RRO; DBE.Command(strGetRoCount); DBE.Reader(); DBE.Read(); int ROCnt = DBE.GetInt32(0); DBE.ReaderClose(); DBE.CommandDispose(); return ROCnt; } public override int RODB_GetNumberOfGroupRecords(string tablename) { // get menu fields to display here. string strGetGrpCount = "SELECT COUNT(RecType) AS NumRecs FROM " + tablename; strGetGrpCount += " WHERE RecType = " + (uint)RecordType.Group; DBE.Command(strGetGrpCount); DBE.Reader(); DBE.Read(); int GrpCnt = DBE.GetInt32(0); DBE.ReaderClose(); DBE.CommandDispose(); return GrpCnt; } } }