PageRenderTime 49ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/VisualStudio/Descriptors/ObjectDescriptor.cs

https://github.com/rykr/connector-net
C# | 1037 lines | 615 code | 102 blank | 320 comment | 170 complexity | 16df5cc431f0bae49f46f229ac6a25d7 MD5 | raw file
Possible License(s): GPL-2.0, AGPL-1.0, MPL-2.0-no-copyleft-exception
  1. // Copyright (C) 2006-2007 MySQL AB
  2. //
  3. // This file is part of MySQL Tools for Visual Studio.
  4. // MySQL Tools for Visual Studio is free software; you can redistribute it
  5. // and/or modify it under the terms of the GNU Lesser General Public
  6. // License version 2.1 as published by the Free Software Foundation
  7. //
  8. // This program is distributed in the hope that it will be useful,
  9. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. // GNU Lesser General Public License for more details.
  12. //
  13. // You should have received a copy of the GNU Lesser General Public License
  14. // along with this program; if not, write to the Free Software
  15. // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System;
  16. /*
  17. * This file contains implementation of the base class for all descriptors.
  18. */
  19. using System;
  20. using System.Collections.Generic;
  21. using System.Text;
  22. using System.Reflection;
  23. using System.Diagnostics;
  24. using MySql.Data.VisualStudio.Utils;
  25. using MySql.Data.VisualStudio.Properties;
  26. using System.Globalization;
  27. using System.Data;
  28. using System.Collections;
  29. namespace MySql.Data.VisualStudio.Descriptors
  30. {
  31. #region A commonly used enumeration
  32. /// <summary>
  33. /// Types of security
  34. /// </summary>
  35. public enum SecurityTypes
  36. {
  37. DEFINER,
  38. INVOKER
  39. }
  40. #endregion
  41. #region ObjectDescriptor class
  42. /// <summary>
  43. /// This is the base class for all descriptors objects. It fully implement common
  44. /// descriptor interface and uses reflection to get proper values from successor
  45. /// static fields. For several properties uses custom reflection attributes.
  46. /// </summary>
  47. public class ObjectDescriptor : IObjectDescriptor
  48. {
  49. #region IObjectDescriptor Members
  50. /// <summary>
  51. /// Amount of parts in the object identifier. Root objects have only three
  52. /// parts – catalog name, schema name and their own name.
  53. /// Read from IdLength custom attribute.
  54. /// </summary>
  55. public int IdLength
  56. {
  57. get
  58. {
  59. if (idLengthVal == -1)
  60. {
  61. // Get identifier length
  62. IdLengthAttribute idLength = ReflectionHelper.GetIdLengthAttribute(this.GetType());
  63. Debug.Assert(idLength != null, "Failed to get identifier length!");
  64. if (idLength == null)
  65. throw new NotSupportedException(Resources.Error_NotMarkedWithIdLength);
  66. this.idLengthVal = idLength.Length;
  67. }
  68. return idLengthVal;
  69. }
  70. }
  71. /// <summary>
  72. /// Type name how it was introduced in DataObject XML file.
  73. /// Read from ObjectDescriptor custom attribute.
  74. /// </summary>
  75. public string TypeName
  76. {
  77. get
  78. {
  79. if (typeNameVal == null)
  80. {
  81. // Get object type name
  82. ObjectDescriptorAttribute objectDescriptor = ReflectionHelper.GetObjectDescriptorAttribute(this.GetType());
  83. Debug.Assert(objectDescriptor != null, "Failed to get object type name!");
  84. if (objectDescriptor == null)
  85. throw new NotSupportedException(Resources.Error_NotMarkedAsDescriptor);
  86. typeNameVal = objectDescriptor.TypeName;
  87. }
  88. return typeNameVal;
  89. }
  90. }
  91. /// <summary>
  92. /// Returns array with object attributes names.
  93. /// </summary>
  94. public string[] ObjectAttributes
  95. {
  96. get
  97. {
  98. if (objectAttributesArray == null)
  99. LoadAttributes();
  100. return objectAttributesArray;
  101. }
  102. }
  103. /// <summary>
  104. /// Returns array with object identifier parts names.
  105. /// </summary>
  106. public string[] Identifier
  107. {
  108. get
  109. {
  110. if (identifierArray == null)
  111. LoadAttributes();
  112. return identifierArray;
  113. }
  114. }
  115. /// <summary>
  116. /// Returns name of the Schema attribute for this database object.
  117. /// </summary>
  118. public string SchemaAttributeName
  119. {
  120. get
  121. {
  122. if (schemaAttributeVal == null)
  123. LoadAttributes();
  124. return schemaAttributeVal;
  125. }
  126. }
  127. /// <summary>
  128. /// Returns name of the Name attribute for this database object.
  129. /// </summary>
  130. public string NameAttributeName
  131. {
  132. get
  133. {
  134. if (nameAttributeVal == null)
  135. LoadAttributes();
  136. return nameAttributeVal;
  137. }
  138. }
  139. /// <summary>
  140. /// Objects could not be droped by default. Returns false.
  141. /// </summary>
  142. public virtual bool CanBeDropped
  143. {
  144. get { return false; }
  145. }
  146. /// <summary>
  147. /// Objects could not be droped by default. Returns empty string.
  148. /// </summary>
  149. /// <param name="identifier">Database object identifier.</param>
  150. /// <returns>
  151. /// Objects could not be droped by default. Returns empty string.
  152. /// </returns>
  153. public virtual string BuildDropSql(object[] identifier)
  154. {
  155. Debug.Fail("Could not drop abstract object!");
  156. return String.Empty;
  157. }
  158. /// <summary>
  159. /// The lowest version of the MySQL Server where the descriptor's object
  160. /// appears
  161. /// </summary>
  162. public virtual Version RequiredVersion
  163. {
  164. get
  165. {
  166. // The default minimal version
  167. return new Version(4, 0);
  168. }
  169. }
  170. #endregion
  171. #region Protected properties
  172. /// <summary>
  173. /// Returns name of the FieldString attribute for this database object
  174. /// </summary>
  175. protected string FieldsStringName
  176. {
  177. get
  178. {
  179. if (fieldStringNameVal == null)
  180. LoadAttributes();
  181. return fieldStringNameVal;
  182. }
  183. }
  184. /// <summary>
  185. /// Returns a dictionary with known fields
  186. /// </summary>
  187. private Dictionary<string, FieldAttribute> Fields
  188. {
  189. get
  190. {
  191. if (fieldsDictionary == null)
  192. LoadAttributes();
  193. return fieldsDictionary;
  194. }
  195. }
  196. #endregion
  197. #region Enumeration SQL
  198. /// <summary>
  199. /// Returns enumeration SQL template for a given connection. May use server version to customize result.
  200. /// </summary>
  201. /// <param name="connection">The DataConnectionWrapper to be used for enumeration.</param>
  202. /// <returns>Returns enumeration SQL template for a given connection.</returns>
  203. protected virtual string GetEnumerateSqlTemplate(DataConnectionWrapper connection)
  204. {
  205. return EnumerateSqlTemplate;
  206. }
  207. /// <summary>
  208. /// Template string used to generate enumerate SQL query.
  209. /// Read from EnumerateSqlTemplate static field of successor.
  210. /// </summary>
  211. protected virtual string EnumerateSqlTemplate
  212. {
  213. get
  214. {
  215. if (String.IsNullOrEmpty(enumerateSqlTemplateVal))
  216. {
  217. // Get enumeration SQL template
  218. enumerateSqlTemplateVal = GetFieldValue("EnumerateSqlTemplate") as string;
  219. Debug.Assert(!String.IsNullOrEmpty(enumerateSqlTemplateVal), "Failed to get enumerate SQL template");
  220. if (String.IsNullOrEmpty(enumerateSqlTemplateVal))
  221. throw new NotSupportedException(Resources.Error_NoEnumerateSql);
  222. }
  223. return enumerateSqlTemplateVal;
  224. }
  225. }
  226. /// <summary>
  227. /// Returns default enumerate restrictions for a given connection. May use server version to customize result.
  228. /// </summary>
  229. /// <param name="connection">The DataConnectionWrapper to be used for enumeration.</param>
  230. /// <returns>Returns default enumerate restrictions for a given connection.</returns>
  231. protected virtual string[] GetDefaultRestrictions(DataConnectionWrapper connection)
  232. {
  233. return DefaultRestrictions;
  234. }
  235. /// <summary>
  236. /// String array with default restrictions values for enumeration query.
  237. /// Read from DefaultRestrictions static field of successor.
  238. /// </summary>
  239. protected virtual string[] DefaultRestrictions
  240. {
  241. get
  242. {
  243. if (defaultRestrictionsArray == null)
  244. {
  245. // Get default restrictions array
  246. defaultRestrictionsArray = GetFieldValue("DefaultRestrictions") as string[];
  247. Debug.Assert(defaultRestrictionsArray != null, "Failed to get default restrictions");
  248. if (defaultRestrictionsArray == null)
  249. throw new NotSupportedException(Resources.Error_NoDefaultRestrictions);
  250. }
  251. return defaultRestrictionsArray;
  252. }
  253. }
  254. /// <summary>
  255. /// Default fields to be used in ORDER BY clause for enumeration query.
  256. /// Read from DefaultSortFields static field of successor.
  257. /// </summary>
  258. protected virtual string DefaultSortString
  259. {
  260. get
  261. {
  262. if (defaultSortStringVal == null)
  263. {
  264. // Get default sort fields array
  265. defaultSortStringVal = GetFieldValue("DefaultSortString") as string;
  266. Debug.Assert(defaultSortStringVal != null, "Failed to get default sort fields");
  267. if (defaultSortStringVal == null)
  268. throw new NotSupportedException(Resources.Error_NoDefaultSort);
  269. }
  270. return defaultSortStringVal;
  271. }
  272. }
  273. #endregion
  274. #region Public static methods
  275. /// <summary>
  276. /// Returns multipart object identifier length for the given object type.
  277. /// </summary>
  278. /// <param name="typeName">Object type name.</param>
  279. /// <returns>Returns multipart object identifier length for the given object type.</returns>
  280. public static int GetIdentifierLength(string typeName)
  281. {
  282. if (typeName == null)
  283. throw new ArgumentNullException("typeName");
  284. IObjectDescriptor descriptor = ObjectDescriptorFactory.Instance.CreateDescriptor(typeName);
  285. if (descriptor == null)
  286. throw new NotSupportedException(String.Format(
  287. CultureInfo.CurrentCulture,
  288. Resources.Error_UnableToGetDescriptor,
  289. typeName));
  290. return descriptor.IdLength;
  291. }
  292. /// <summary>
  293. /// Enumerates database object of given type with given restrictions into
  294. /// DataTable.
  295. /// </summary>
  296. /// <param name="connection">The DataConnectionWrapper to be used for enumeration.</param>
  297. /// <param name="typeName">The type name for objects to be enumerated</param>
  298. /// <param name="restrictions">The restrictions to be putted on the retrieved objects set.</param>
  299. /// <returns>
  300. /// Returns DataTable which contains all database objects of given type which
  301. /// satisfy given restrictions.
  302. /// </returns>
  303. public static DataTable EnumerateObjects(DataConnectionWrapper connection, string typeName, object[] restrictions)
  304. {
  305. if (connection == null)
  306. throw new ArgumentNullException("connection");
  307. if (typeName == null)
  308. throw new ArgumentNullException("typeName");
  309. return EnumerateObjects(connection, typeName, restrictions, null);
  310. }
  311. /// <summary>
  312. /// Enumerates database object of given type with given restrictions into
  313. /// DataTable.
  314. /// </summary>
  315. /// <param name="connection">The DataConnectionWrapper to be used for enumeration.</param>
  316. /// <param name="typeName">The type name for objects to be enumerated</param>
  317. /// <param name="restrictions">The restrictions to be putted on the retrieved objects set.</param>
  318. /// <param name="sort">Sort expresion to append after ORDER BY clause.</param>
  319. /// <returns>
  320. /// Returns DataTable which contains all database objects of given type which
  321. /// satisfy given restrictions.
  322. /// </returns>
  323. public static DataTable EnumerateObjects(DataConnectionWrapper connection, string typeName, object[] restrictions, string sort)
  324. {
  325. if (connection == null)
  326. throw new ArgumentNullException("connection");
  327. if (typeName == null)
  328. throw new ArgumentNullException("typeName");
  329. // Get object descriptor for given type
  330. ObjectDescriptor descriptor = ObjectDescriptorFactory.Instance.CreateDescriptor(typeName) as ObjectDescriptor;
  331. if (descriptor == null)
  332. {
  333. Debug.Fail("Unsupported object type '" + typeName + "'");
  334. throw new NotSupportedException(String.Format(
  335. CultureInfo.CurrentCulture,
  336. Resources.Error_ObjectTypeNotSupported,
  337. typeName));
  338. }
  339. // Read objects
  340. DataTable result = descriptor.ReadTable(connection, restrictions, sort);
  341. if (result == null)
  342. {
  343. Debug.Fail("Failed to read data!");
  344. return null;
  345. }
  346. // Perform post-processing
  347. descriptor.PostProcessData(connection, result);
  348. result.AcceptChanges();
  349. // Return result
  350. return result;
  351. }
  352. /// <summary>
  353. /// Completes identifier for new object. Last element of id array considered as the name of new object. This
  354. /// method sequentially generates new names in form {template}{N} and checks for existing object with same name.
  355. /// To check it this method tries to enumerate objects with restriction to whole id.
  356. /// </summary>
  357. /// <param name="hierarchy">Server explorer facade object used to check for existen objects.</param>
  358. /// <param name="typeName">Object type name.</param>
  359. /// <param name="id">Array with object identifier.</param>
  360. /// <param name="template">Template for the new object identifier.</param>
  361. public static void CompleteNewObjectID(ServerExplorerFacade hierarchy, string typeName, ref object[] id, string template)
  362. {
  363. if (hierarchy == null)
  364. throw new ArgumentNullException("hierarchy");
  365. if (typeName == null)
  366. throw new ArgumentNullException("typeName");
  367. if (id == null)
  368. throw new ArgumentNullException("id");
  369. if (String.IsNullOrEmpty(template))
  370. throw new ArgumentException(Resources.Error_EmptyString, "template");
  371. // Retrieve connection information
  372. DataConnectionWrapper connection = hierarchy.Connection;
  373. Debug.Assert(connection != null, "Empty connection object!");
  374. if (connection == null)
  375. return;
  376. // Calculate "name" part of identifier (it is the last part)
  377. int nameIdPart = id.Length - 1;
  378. Debug.Assert(nameIdPart >= 0, "Could not complete empty identifier!");
  379. if (nameIdPart < 0)
  380. return;
  381. // Initialize search context
  382. int objectIndex = 0;
  383. DataTable objectTable = null;
  384. bool objectsFounded = false;
  385. // Generate object name in <typeName><N> style
  386. do
  387. {
  388. // Build exact identifier
  389. id[nameIdPart] = template + (++objectIndex).ToString(CultureInfo.InvariantCulture);
  390. objectsFounded = false;
  391. try
  392. {
  393. // Look for exisiting object with this identifier
  394. objectTable = EnumerateObjects(connection, typeName, id, null);
  395. objectsFounded = objectTable != null && objectTable.Rows != null && objectTable.Rows.Count > 0;
  396. }
  397. finally
  398. {
  399. // Release resources
  400. if (objectTable != null)
  401. objectTable.Dispose();
  402. objectTable = null;
  403. }
  404. // Look for registered document (may be second new object)
  405. objectsFounded = objectsFounded || hierarchy.HasDocument(typeName, id);
  406. }
  407. // Trying while objects are exists and objectIndex less when MaxObjectIndex
  408. while (objectsFounded && objectIndex < MaxObjectIndex);
  409. }
  410. #endregion
  411. #region Attributes
  412. /// <summary>
  413. /// List of known common attributes
  414. /// </summary>
  415. public static class Attributes
  416. {
  417. public const string Name = "{0}_NAME";
  418. public const string Comments = "{0}_COMMENT";
  419. }
  420. #endregion
  421. #region Enumeration and Post processing
  422. /// <summary>
  423. /// Reads table with Database Objects which satisfy given restriction. Base implementation
  424. /// uses direct SQL query to the INFORMATION_SCHEMA.
  425. /// </summary>
  426. /// <param name="connection">The DataConnectionWrapper to be used for enumeration.</param>
  427. /// <param name="restrictions">The restrictions to be putted on the retrieved objects set.</param>
  428. /// <param name="sort">Sort expresion to append after ORDER BY clause.</param>
  429. /// <returns>Returns table with Database Objects which satisfy given restriction.</returns>
  430. protected virtual DataTable ReadTable(DataConnectionWrapper connection, object[] restrictions, string sort)
  431. {
  432. if (connection == null)
  433. throw new ArgumentNullException("connection");
  434. if (connection.ServerVersion != null && RequiredVersion > connection.ServerVersion)
  435. // This object requires a higher version of the MySql Server
  436. return new DataTable();
  437. return connection.ExecuteSelectTable(BuildEnumerateSql(connection, restrictions, sort));
  438. }
  439. /// <summary>
  440. /// Builds enumerate SQL query for object of this type with given restrictions.
  441. /// </summary>
  442. /// <param name="connection">The DataConnectionWrapper to be used for enumeration.</param>
  443. /// <param name="restrictions">Restrictions to enumerated objects.</param>
  444. /// <param name="sort">Sort expression to use.</param>
  445. /// <returns>Enumerating SQL query string.</returns>
  446. protected virtual string BuildEnumerateSql(DataConnectionWrapper connection, object[] restrictions, string sort)
  447. {
  448. if (connection == null)
  449. throw new ArgumentNullException("connection");
  450. // Extract enumeration SQL information from the descriptor
  451. string sqlTemplate = GetEnumerateSqlTemplate(connection);
  452. string[] defaultRestrictions = GetDefaultRestrictions(connection);
  453. if (String.IsNullOrEmpty(sqlTemplate) || defaultRestrictions == null)
  454. {
  455. Debug.Fail("Failed to get enumeration SQL information for object type '" + TypeName + "'");
  456. throw new NotSupportedException(String.Format(
  457. CultureInfo.CurrentCulture,
  458. Resources.Error_UnableToGetEnumerationSql,
  459. TypeName));
  460. }
  461. // Get formated SQL
  462. string sqlStatement = FormatSqlString(sqlTemplate, restrictions, defaultRestrictions);
  463. // Check builded statement
  464. Debug.Assert(!String.IsNullOrEmpty(sqlStatement), "Failed to build enumeration statement!");
  465. if (String.IsNullOrEmpty(sqlStatement))
  466. throw new NotSupportedException(String.Format(
  467. CultureInfo.CurrentCulture,
  468. Resources.Error_UnableToGetEnumerationSql,
  469. TypeName));
  470. // Append ORDER BY if any
  471. string sortExpression = !String.IsNullOrEmpty(sort) ? sort : DefaultSortString;
  472. if (!String.IsNullOrEmpty(sortExpression))
  473. return String.Format("{0} ORDER BY {1}", sqlStatement, sortExpression);
  474. // Retrun results
  475. return sqlStatement;
  476. }
  477. /// <summary>
  478. /// Post process enumeration data. Base implementation adds primary key to
  479. /// the table and calculate fields
  480. /// </summary>
  481. /// <param name="connection">The DataConnectionWrapper to be used for enumeration</param>
  482. /// <param name="table">A table with data to post process</param>
  483. protected virtual void PostProcessData(DataConnectionWrapper connection, DataTable table)
  484. {
  485. if (connection == null)
  486. throw new ArgumentNullException("connection");
  487. if (table == null)
  488. throw new ArgumentNullException("table");
  489. // Adding table columns
  490. ExtendData(connection, table);
  491. // Validate read data
  492. if (!ValidateAttributesTable(table))
  493. throw new Exception(Resources.Error_AttributesAreMissing);
  494. // Add primary key
  495. AddPrimaryKey(table);
  496. }
  497. /// <summary>
  498. /// Exdents table data with additional information. Base implementation adds
  499. /// attributes of this database object
  500. /// </summary>
  501. /// <param name="connection">The DataConnectionWrapper to be used for enumeration.</param>
  502. /// <param name="table">Table with data to extend.</param>
  503. protected virtual void ExtendData(DataConnectionWrapper connection, DataTable table)
  504. {
  505. if (connection == null)
  506. throw new ArgumentNullException("connection");
  507. if (table == null)
  508. throw new ArgumentNullException("table");
  509. if (Fields == null || Fields.Count <= 0)
  510. // No known fields
  511. return;
  512. // Add columns for fields
  513. Debug.Assert(table.Columns != null, "Table has empty column collection!");
  514. Dictionary<string, FieldAttribute> added = new Dictionary<string, FieldAttribute>();
  515. foreach (KeyValuePair<string, FieldAttribute> field in Fields)
  516. {
  517. // If column is already exists, skip it
  518. if (table.Columns.Contains(field.Key))
  519. continue;
  520. // Add column for field
  521. AddFieldColumn(table, field.Key, field.Value.FieldType);
  522. // Add entry to added dictionary
  523. added[field.Key] = field.Value;
  524. }
  525. // Calculate field values for each row
  526. Dictionary<string, string> fieldValues;
  527. foreach (DataRow row in table.Rows)
  528. {
  529. // Parsing field string
  530. fieldValues = ExtractOptions(connection, row);
  531. if (fieldValues == null || fieldValues.Count <= 0)
  532. continue;
  533. // Adding a field
  534. foreach (KeyValuePair<string, FieldAttribute> field in added)
  535. {
  536. // Skip empty option names and columns which was in result before option adding.
  537. if (string.IsNullOrEmpty(field.Value.OptionName))
  538. continue;
  539. AddFieldValue(row, field.Key, field.Value.OptionName, field.Value.FieldType, fieldValues);
  540. }
  541. }
  542. }
  543. /// <summary>
  544. /// Extracts field values for given DataRow. Base implementation simply uses Parser
  545. /// </summary>
  546. /// <param name="connection">The DataConnectionWrapper to be used for enumeration</param>
  547. /// <param name="row">DataRow to extract values</param>
  548. /// <returns>Returns field values for a given DataRow</returns>
  549. protected virtual Dictionary<string, string> ExtractOptions(DataConnectionWrapper connection, DataRow row)
  550. {
  551. if (connection == null)
  552. throw new ArgumentNullException("connection");
  553. if (row == null)
  554. throw new ArgumentNullException("row");
  555. // Check a field string (if empty, returns empty dictionary)
  556. if (String.IsNullOrEmpty(FieldsStringName))
  557. return new Dictionary<string, string>();
  558. return Parser.ExtractFieldsDictionary(row, FieldsStringName);
  559. }
  560. /// <summary>
  561. /// Adds primary key to the table. Base implementation adds all
  562. /// known identifier parts.
  563. /// </summary>
  564. /// <param name="table">Table to add primary key</param>
  565. protected virtual void AddPrimaryKey(DataTable table)
  566. {
  567. DataColumn[] primaryKey = new DataColumn[Identifier.Length];
  568. for (int i = 0; i < Identifier.Length; i++)
  569. {
  570. primaryKey[i] = table.Columns[Identifier[i]];
  571. Debug.Assert(primaryKey[i] != null, "Failed to read primaruy key column!");
  572. }
  573. table.PrimaryKey = primaryKey;
  574. }
  575. /// <summary>
  576. /// Check table with object attributes for consistency.
  577. /// Typical only ensure that all attribute columns are present.
  578. /// </summary>
  579. /// <param name="table">DataTable object to check for consistency.</param>
  580. /// <returns>Returns true if table considered as consistent and false otherwise.</returns>
  581. protected virtual bool ValidateAttributesTable(DataTable table)
  582. {
  583. if (table == null)
  584. throw new ArgumentNullException("objectTable");
  585. // Get list of object attributes
  586. string[] objectAttributes = ObjectAttributes;
  587. Debug.Assert(objectAttributes != null, "Unable to get attributes list!");
  588. if (objectAttributes == null)
  589. return false;
  590. // Check, if table contains all necessary columns
  591. foreach (string attribute in objectAttributes)
  592. if (!table.Columns.Contains(attribute))
  593. return false;
  594. // All attributes are present
  595. return true;
  596. }
  597. #endregion
  598. #region Protected utility methods
  599. /// <summary>
  600. /// Renames column in the DataTable.
  601. /// </summary>
  602. /// <param name="oldName">Name of exists column.</param>
  603. /// <param name="newName">New name for a column.</param>
  604. /// <param name="table">DataTable to perform operation.</param>
  605. protected void RenameColumn(string oldName, string newName, DataTable table)
  606. {
  607. if (String.IsNullOrEmpty(oldName))
  608. throw new ArgumentException(Resources.Error_EmptyString, "oldName");
  609. if (String.IsNullOrEmpty(newName))
  610. throw new ArgumentException(Resources.Error_EmptyString, "newName");
  611. if (table == null)
  612. throw new ArgumentNullException("table");
  613. // Check table columns
  614. if (table.Columns == null || !table.Columns.Contains(oldName))
  615. {
  616. Debug.Fail("Table columns are empty or there is no column " + oldName);
  617. return;
  618. }
  619. // Extract proper column
  620. DataColumn column = table.Columns[oldName];
  621. if (column == null)
  622. {
  623. Debug.Fail("Failed to extract column with name " + oldName);
  624. return;
  625. }
  626. // Rename column
  627. column.ColumnName = newName;
  628. }
  629. #endregion
  630. #region Field processing private methods
  631. /// <summary>
  632. /// Creates new column in the DataTable for a field attribute
  633. /// </summary>
  634. /// <param name="table">DataTable to add column</param>
  635. /// <param name="columnName">Name of the column</param>
  636. /// <param name="attributeType">Type of the column</param>
  637. private static void AddFieldColumn(DataTable table, string columnName, TypeCode attributeType)
  638. {
  639. if (table == null)
  640. throw new ArgumentNullException("table");
  641. // Check, if table already has suh column
  642. if (table.Columns.Contains(columnName))
  643. return;
  644. switch (attributeType)
  645. {
  646. case TypeCode.Int16:
  647. case TypeCode.Int32:
  648. case TypeCode.Int64:
  649. table.Columns.Add(columnName, typeof(Int64));
  650. break;
  651. case TypeCode.Boolean:
  652. case TypeCode.String:
  653. table.Columns.Add(columnName, typeof(string));
  654. break;
  655. case TypeCode.DateTime:
  656. table.Columns.Add(columnName, typeof(DateTime));
  657. break;
  658. default:
  659. Debug.Fail("Unsupported type code is Sused!");
  660. table.Columns.Add(columnName, typeof(string));
  661. break;
  662. }
  663. }
  664. /// <summary>
  665. /// Adds a field value to the given data row. Choses field conversion type
  666. /// depending on give attribute type
  667. /// </summary>
  668. /// <param name="row">DataRow to add the field value</param>
  669. /// <param name="attributeName">Attribute name for which the value should be added</param>
  670. /// <param name="fieldName">Field name to look for value in the dictionary</param>
  671. /// <param name="attributeType">Attribute type to use for conversion</param>
  672. /// <param name="fieldValues">Dictionary with read field values</param>
  673. private static void AddFieldValue(DataRow row, string attributeName, string fieldName, TypeCode attributeType, Dictionary<string, string> fieldValues)
  674. {
  675. if (row == null)
  676. throw new ArgumentNullException("row");
  677. if (String.IsNullOrEmpty(attributeName))
  678. throw new ArgumentException(Resources.Error_EmptyString, "attributeName");
  679. if (String.IsNullOrEmpty(fieldName))
  680. throw new ArgumentException(Resources.Error_EmptyString, "fieldName");
  681. object value = null;
  682. // Select proper conversion depending on the attribute type
  683. switch (attributeType)
  684. {
  685. case TypeCode.Int16:
  686. case TypeCode.Int32:
  687. case TypeCode.Int64:
  688. // If a field has a valid value in the dictionary, copy it to the row
  689. value = GetIntField(fieldName, fieldValues);
  690. break;
  691. case TypeCode.Boolean:
  692. // If a field has a valid value in the dictionary, copy it to the row
  693. value = GetBoolField(fieldName, fieldValues);
  694. break;
  695. case TypeCode.String:
  696. // If a field has a valid value in the dictionary, copy it to the row
  697. value = GetStringField(fieldName, fieldValues);
  698. break;
  699. default:
  700. Debug.Fail("Unsuported type code used!");
  701. // If a field has a valid value in the dictionary, copy it to the row
  702. value = GetStringField(fieldName, fieldValues);
  703. break;
  704. }
  705. if (value != null)
  706. row[attributeName] = value;
  707. }
  708. /// <summary>
  709. /// Returns value of the field, interpreted as integer. If value is in the dictionary, tries
  710. /// to convert it to the Int64 and return result. Otherwise returns null.
  711. /// </summary>
  712. /// <param name="fieldName">Field name to look for value in the dictionary.</param>
  713. /// <param name="fieldValues">Dictionary with read field values</param>
  714. /// <returns>
  715. /// Returns value of the field, interpreted as integer. If value is in the dictionary, tries
  716. /// to convert it to the Int64 and return result. Otherwise returns null.
  717. /// </returns>
  718. private static object GetIntField(string fieldName, Dictionary<string, string> fieldValues)
  719. {
  720. if (fieldValues.ContainsKey(fieldName))
  721. {
  722. // Extract and validate string
  723. string stringVal = fieldValues[fieldName];
  724. if (!String.IsNullOrEmpty(stringVal))
  725. {
  726. // Parse string as integer
  727. Int64 intVal;
  728. if (Int64.TryParse(stringVal, out intVal))
  729. return intVal;
  730. }
  731. }
  732. return null;
  733. }
  734. /// <summary>
  735. /// Returns value of the field, interpreted as Boolean. If any value is in the dictionary,
  736. /// returns true string, otherwise returns false string.
  737. /// </summary>
  738. /// <param name="fieldName">Field name to look for value in the dictionary.</param>
  739. /// <param name="fieldValues">Dictionary with read field values.</param>
  740. /// <returns>
  741. /// Returns value of the field, interpreted as Boolean. If any value is in the dictionary,
  742. /// returns true string, otherwise returns false string.
  743. /// </returns>
  744. private static object GetBoolField(string fieldName, Dictionary<string, string> fieldValues)
  745. {
  746. return fieldValues.ContainsKey(fieldName) ? DataInterpreter.True : DataInterpreter.False;
  747. }
  748. /// <summary>
  749. /// Returns value of the field, interpreted as string. If nonempty value is in the dictionary,
  750. /// returns this value, otherwise returns null.
  751. /// </summary>
  752. /// <param name="fieldName">Field name to look for value in the dictionary.</param>
  753. /// <param name="fieldValues">Dictionary with read field values.</param>
  754. /// <returns>
  755. /// Returns value of the field, interpreted as string. If nonempty value is in the dictionary,
  756. /// returns this value, otherwise returns null.
  757. /// </returns>
  758. private static object GetStringField(string fieldName, Dictionary<string, string> fieldValues)
  759. {
  760. // If field has a valid value in the dictionary, copy it to the row
  761. if (fieldValues.ContainsKey(fieldName))
  762. {
  763. // Extract and validate string
  764. string stringVal = fieldValues[fieldName];
  765. if (!String.IsNullOrEmpty(stringVal))
  766. return stringVal;
  767. }
  768. return null;
  769. }
  770. #endregion
  771. #region Private methods
  772. /// <summary>
  773. /// This method formats a SQL string by specifying format arguments
  774. /// based on restrictions. Used to escape special characters (at this
  775. /// point only quotes).
  776. /// </summary>
  777. /// <param name="sql">SQL query template.</param>
  778. /// <param name="restrictions">Restrictions to be placed in the template.</param>
  779. /// <param name="defaultRestrictions">Default values for unspecified restrictions.</param>
  780. /// <returns>Formatted and escaped SQL query.</returns>
  781. private static string FormatSqlString(string sql, object[] restrictions, object[] defaultRestrictions)
  782. {
  783. Debug.Assert(sql != null);
  784. Debug.Assert(defaultRestrictions != null);
  785. object[] formatArgs = new object[defaultRestrictions.Length];
  786. //formatArgs[0] = (restrictions[0] as string).Replace("]", "]]");
  787. for (int i = 0; i < defaultRestrictions.Length; i++)
  788. {
  789. if (restrictions != null && restrictions.Length > i && restrictions[i] != null)
  790. {
  791. formatArgs[i] = QueryBuilder.EscapeAndQuoteString(restrictions[i].ToString());
  792. }
  793. else
  794. {
  795. formatArgs[i] = defaultRestrictions[i];
  796. }
  797. }
  798. return String.Format(CultureInfo.CurrentCulture, sql, formatArgs);
  799. }
  800. /// <summary>
  801. /// Used to get value for given static field of successor via Reflection.
  802. /// </summary>
  803. /// <param name="fieldName">Field name.</param>
  804. /// <returns>Returns value for given static field of successor.</returns>
  805. private object GetFieldValue(string fieldName)
  806. {
  807. Type type = this.GetType();
  808. return type.InvokeMember(
  809. fieldName,
  810. BindingFlags.GetField | BindingFlags.GetProperty
  811. | BindingFlags.NonPublic | BindingFlags.Static
  812. | BindingFlags.Public,
  813. null, null, new object[] { });
  814. }
  815. /// <summary>
  816. /// Loads sttributes names array
  817. /// </summary>
  818. private void LoadAttributes()
  819. {
  820. // Get array of object attributes names
  821. // Get current object type
  822. Type thisType = this.GetType();
  823. Debug.Assert(thisType != null);
  824. // Get attributes type
  825. Type attributes = thisType.GetNestedType("Attributes");
  826. Debug.Assert(attributes != null, "Attributes container class is undefined!");
  827. if (attributes == null)
  828. throw new NotSupportedException(Resources.Error_NoAttributesNestedClass);
  829. // Get fields array
  830. FieldInfo[] fields = attributes.GetFields();
  831. Debug.Assert(fields != null, "Unable to get fields list!");
  832. if (fields == null || fields.Length <= 0)
  833. throw new NotSupportedException(Resources.Error_EmptyAttributesNestedClass);
  834. // Parse and extract attributes information
  835. ParseAttributes(fields);
  836. // Validate and complete result
  837. if (String.IsNullOrEmpty(schemaAttributeVal))
  838. schemaAttributeVal = String.Empty;
  839. if (String.IsNullOrEmpty(nameAttributeVal))
  840. nameAttributeVal = String.Empty;
  841. if (String.IsNullOrEmpty(fieldStringNameVal))
  842. fieldStringNameVal = String.Empty;
  843. }
  844. /// <summary>
  845. /// Extarcts information about Database Object attributes from the array of FieldInfo.
  846. /// </summary>
  847. /// <param name="fields">Array with information about Database Object attributes.</param>
  848. private void ParseAttributes(FieldInfo[] fields)
  849. {
  850. // Initialize temporary data
  851. List<string> identifier = new List<string>();
  852. objectAttributesArray = new string[fields.Length];
  853. IdentifierAttribute identifierFlag;
  854. fieldsDictionary = new Dictionary<string, FieldAttribute>();
  855. FieldAttribute fieldMark;
  856. // Extracts value for each static field
  857. for (int i = 0; i < fields.Length; i++)
  858. {
  859. // Validate field type
  860. if (!fields[i].IsStatic || !typeof(String).IsAssignableFrom(fields[i].FieldType))
  861. {
  862. Debug.Fail("Unsupported attribute declaration found!");
  863. continue;
  864. }
  865. // Extract field value
  866. objectAttributesArray[i] = fields[i].GetValue(null) as string;
  867. if (String.IsNullOrEmpty(objectAttributesArray[i]))
  868. throw new NotSupportedException(Resources.Error_EmptyAttributeName);
  869. // Check for Identifier attribute
  870. identifierFlag = ReflectionHelper.GetIdentifierAttribute(fields[i]);
  871. if (identifierFlag != null)
  872. {
  873. identifier.Add(objectAttributesArray[i]);
  874. // Check if schema attribute
  875. if (identifierFlag.IsSchema)
  876. {
  877. Debug.Assert(String.IsNullOrEmpty(schemaAttributeVal), "Duplicate schema attribute!");
  878. schemaAttributeVal = objectAttributesArray[i];
  879. }
  880. // Check if name attribute
  881. if (identifierFlag.IsName)
  882. {
  883. Debug.Assert(String.IsNullOrEmpty(nameAttributeVal), "Duplicate name attribute!");
  884. nameAttributeVal = objectAttributesArray[i];
  885. }
  886. }
  887. // Check if this field is string with fields
  888. if (ReflectionHelper.GetFieldStringAttribute(fields[i]) != null)
  889. {
  890. Debug.Assert(String.IsNullOrEmpty(fieldStringNameVal), "Duplicate field string attribute!");
  891. fieldStringNameVal = objectAttributesArray[i];
  892. }
  893. // Check if this is a field
  894. fieldMark = ReflectionHelper.GetFieldAttribute(fields[i]);
  895. if (fieldMark != null)
  896. {
  897. Debug.Assert(!fieldsDictionary.ContainsKey(objectAttributesArray[i]), "This field already exists!");
  898. fieldsDictionary[objectAttributesArray[i]] = fieldMark;
  899. }
  900. }
  901. // Copy extracted identifier
  902. identifierArray = new string[identifier.Count];
  903. identifier.CopyTo(identifierArray);
  904. // Initialize default name
  905. if (String.IsNullOrEmpty(nameAttributeVal))
  906. {
  907. nameAttributeVal = String.Format(
  908. Attributes.Name,
  909. TypeName.ToUpperInvariant(),
  910. CultureInfo.InvariantCulture);
  911. }
  912. }
  913. #endregion
  914. #region Private variables to store properties
  915. private string enumerateSqlTemplateVal = null;
  916. private string[] defaultRestrictionsArray = null;
  917. private string defaultSortStringVal = null;
  918. private int idLengthVal = -1;
  919. private string typeNameVal = null;
  920. private string[] objectAttributesArray = null;
  921. private string[] identifierArray = null;
  922. private string nameAttributeVal = null;
  923. private string schemaAttributeVal = null;
  924. private string fieldStringNameVal = null;
  925. private Dictionary<string, FieldAttribute> fieldsDictionary;
  926. #endregion
  927. #region Constants
  928. /// <summary>
  929. /// Maximum object index for new identifier generator.
  930. /// </summary>
  931. private const int MaxObjectIndex = 50;
  932. #endregion
  933. }
  934. #endregion
  935. }