/********************************************************
* ADO.NET 2.0 Data Provider for SQLite Version 3.X
* Written by Joe Mistachkin (joe@mistachkin.com)
*
* Released to the public domain, use at your own risk!
********************************************************/
using System;
using System.Collections.Generic;
using System.Data.Common;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using Microsoft.VisualStudio.Shell;
namespace SQLite.Designer
{
///
/// This class keeps track of the options configured on a per-solution file
/// basis pertaining to the System.Data.SQLite design-time components.
///
[Guid("5cf5656c-ccbe-4162-8780-0cbee936b90c")]
internal static class SQLiteOptions
{
#region Private Constants
///
/// This is the name of the setting containing the configured ADO.NET
/// provider name.
///
private static readonly string ProviderNameKey = "ProviderName";
///////////////////////////////////////////////////////////////////////
///
/// This is the name of the environment variable that will be checked
/// prior to setting the initial default value for the configured
/// ADO.NET provider name, thus allowing the default value to be
/// overridden via the environment.
///
private static readonly string ProviderNameEnvVarName =
"ProviderName_SQLiteDesigner";
///////////////////////////////////////////////////////////////////////
///
/// This is the legacy provider name used by the System.Data.SQLite
/// design-time components. It is also the default value for the
/// associated option key.
///
private static readonly string LegacyProviderName = "System.Data.SQLite";
///////////////////////////////////////////////////////////////////////
#if NET_40 || NET_45 || NET_451
///
/// This is the provider name used when Entity Framework 6.x support is
/// required for use with the System.Data.SQLite design-time components.
/// This provider name is only available when this class is compiled for
/// the .NET Framework 4.0 or later.
///
private static readonly string Ef6ProviderName = "System.Data.SQLite.EF6";
#endif
#endregion
///////////////////////////////////////////////////////////////////////
#region Private Static Data
///
/// This is used to synchronize access to the static dictionary of
/// options and objects.
///
private static readonly object syncRoot = new object();
///
/// This dictionary contains the key/value pairs representing the
/// per-solution options configured for the current solution. When
/// a new solution is loaded by Visual Studio, this dictionary must
/// be reset.
///
private static Dictionary options;
///
/// This dictionary contains the
/// objects cached by this class.
///
private static Dictionary
dbProviderFactories;
#endregion
///////////////////////////////////////////////////////////////////////
#region Private Static Methods
///
/// This method initializes (or resets) the per-solution configuration
/// options.
///
///
/// Non-zero to reset the options if they are already initialized.
/// When this method is called from the
/// constructor, this value should always be true.
///
private static void Initialize(
bool reset
)
{
lock (syncRoot)
{
if (options != null)
options.Clear();
else
options = new Dictionary();
string key = ProviderNameKey;
string value = Environment.GetEnvironmentVariable(
ProviderNameEnvVarName);
if (IsValidValue(key, value))
options[key] = value;
#if !NET_40 && !NET_45 && !NET_451
else
options[key] = LegacyProviderName;
#endif
}
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Public Static Methods
#region Provider Name Handling
///
/// This method determines the name of the ADO.NET provider for the
/// System.Data.SQLite design-time components to use.
///
///
/// The configured ADO.NET provider name for System.Data.SQLite -OR-
/// the default ADO.NET provider name for System.Data.SQLite in the
/// event of any failure. This method cannot return null.
///
public static string GetProviderName()
{
#if NET_40 || NET_45 || NET_451
return GetProviderName(Ef6ProviderName);
#else
return GetProviderName(LegacyProviderName);
#endif
}
///////////////////////////////////////////////////////////////////////
///
/// This method determines the name of the ADO.NET provider for the
/// System.Data.SQLite design-time components to use.
///
///
/// The value to return from this method if the name of the ADO.NET
/// provider is unavailable or cannot be determined.
///
///
/// The configured ADO.NET provider name for System.Data.SQLite -OR-
/// the default ADO.NET provider name for System.Data.SQLite in the
/// event of any failure.
///
private static string GetProviderName(
string @default
)
{
string key = ProviderNameKey;
string value;
if (GetValue(key, out value) && IsValidValue(key, value))
return value;
return @default;
}
///////////////////////////////////////////////////////////////////////
///
/// This method attempts to set the name of the ADO.NET provider for
/// the System.Data.SQLite design-time components to use.
///
///
/// The ADO.NET provider name to use.
///
///
/// Non-zero upon success; otherwise, zero. All ADO.NET provider names
/// unknown to this class are rejected.
///
public static bool SetProviderName(
string value
)
{
string key = ProviderNameKey;
if (IsValidValue(key, value))
return SetValue(key, value);
return false;
}
///////////////////////////////////////////////////////////////////////
#region User-Interface Handling
///
/// This method attempts to select the configured ADO.NET provider name
/// in the specified . This method will only
/// work correctly when called from the user-interface thread.
///
///
/// The object where the selection is to be
/// modified.
///
///
/// Non-zero upon success; otherwise, zero.
///
public static bool SelectProviderName(
ComboBox comboBox
)
{
if (comboBox == null)
return false;
string value = GetProviderName(null);
for (int index = 0; index < comboBox.Items.Count; index++)
{
object item = comboBox.Items[index];
if (item == null)
continue;
if ((value == null) || String.Equals(
item.ToString(), value, StringComparison.Ordinal))
{
comboBox.SelectedIndex = index;
return true;
}
}
return false;
}
///////////////////////////////////////////////////////////////////////
///
/// Determines if the specified ADO.NET provider name appears to be
/// usable.
///
///
/// The invariant name of the ADO.NET provider to create. Using null
/// or an empty string will cause this method to return false.
///
///
/// Non-zero if the was created or
/// queried successfully; otherwise, zero.
///
private static bool CheckProviderName(
string name
)
{
DbProviderFactory dbProviderFactory; /* NOT USED */
return GetProviderFactory(name, true, out dbProviderFactory);
}
///////////////////////////////////////////////////////////////////////
///
/// Gets the cached object associated
/// with the specified name, creating a new instance if necessary.
///
///
/// The invariant name of the ADO.NET provider to create. Using null
/// or an empty string will cause this method to return false.
///
///
/// Non-zero to create a new instance of the ADO.NET provider, if
/// necessary.
///
///
/// The newly created object or null
/// if it is unavailable or could not be created.
///
///
/// Non-zero if the was created or
/// queried successfully; otherwise, zero.
///
public static bool GetProviderFactory(
string name,
bool create,
out DbProviderFactory dbProviderFactory
)
{
lock (syncRoot)
{
dbProviderFactory = null;
if (dbProviderFactories == null)
{
dbProviderFactories =
new Dictionary();
}
if (String.IsNullOrEmpty(name))
return false;
if (dbProviderFactories.TryGetValue(
name, out dbProviderFactory))
{
return (dbProviderFactory != null);
}
if (!create)
return false;
dbProviderFactory = null; /* NOTE: Pedantic. */
try
{
dbProviderFactory = DbProviderFactories.GetFactory(name);
}
catch
{
// do nothing.
}
dbProviderFactories.Add(name, dbProviderFactory);
return (dbProviderFactory != null);
}
}
///////////////////////////////////////////////////////////////////////
///
/// This method populates the specified item
/// list with the recognized ADO.NET provider names. This method will
/// only work correctly when called from the user-interface thread.
///
///
/// The property value containing the
/// list of items to be modified. This value cannot be null.
///
///
/// The number of items actually added to the list, which may be zero.
///
public static int AddProviderNames(
ComboBox.ObjectCollection items
)
{
int result = 0;
if (items == null)
return result;
IList names = new List();
#if NET_40 || NET_45 || NET_451
names.Add(Ef6ProviderName);
#endif
names.Add(LegacyProviderName);
foreach (string name in names)
{
if (CheckProviderName(name))
{
items.Add(name);
result++;
}
}
return result;
}
#endregion
#endregion
///////////////////////////////////////////////////////////////////////
#region Hard-Coded Default Value Handling
///
/// This method determines if the specified key/value pair represents
/// the default value for that option.
///
///
/// The name ("key") of the configuration option.
///
///
/// The value of the configuration option.
///
///
/// Non-zero if the key/value pair represents its default value.
///
public static bool IsDefaultValue(
string key,
string value
)
{
if (String.Equals(
key, ProviderNameKey, StringComparison.Ordinal) &&
String.Equals(
value, LegacyProviderName, StringComparison.Ordinal))
{
return true;
}
return false;
}
///////////////////////////////////////////////////////////////////////
///
/// This method determines if the specified key/value pair is valid
/// and supported by this class.
///
///
/// The name ("key") of the configuration option.
///
///
/// The value of the configuration option.
///
///
/// Non-zero if the key/value pair represents a valid option key and
/// value supported by this class.
///
public static bool IsValidValue(
string key,
string value
)
{
if (String.Equals(
key, ProviderNameKey, StringComparison.Ordinal) &&
(String.Equals(
value, LegacyProviderName, StringComparison.Ordinal)
#if NET_40 || NET_45 || NET_451
|| String.Equals(
value, Ef6ProviderName, StringComparison.Ordinal)
#endif
))
{
return true;
}
return false;
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Core Option Handling
///
/// This method returns the current list of option keys supported by
/// the System.Data.SQLite design-time components.
///
///
/// An of strings containing the list of
/// option keys supported by the System.Data.SQLite design-time
/// components -OR- null in the event of any failure.
///
public static IEnumerable GetKeys(
bool reset
)
{
lock (syncRoot) /* TRANSACTIONAL */
{
Initialize(reset);
return (options != null) ?
new List(options.Keys) : null;
}
}
///////////////////////////////////////////////////////////////////////
///
/// This method determines if the specified option key is supported by
/// this class.
///
///
/// The name ("key") of the configuration option.
///
///
/// Non-zero if the specified option key is supported by this class.
///
public static bool HaveKey(
string key
)
{
lock (syncRoot)
{
if ((key == null) || (options == null))
return false;
return options.ContainsKey(key);
}
}
///////////////////////////////////////////////////////////////////////
///
/// This method attempts to query and return the current value of the
/// specified option key.
///
///
/// The name ("key") of the configuration option.
///
///
/// Upon success, the current value for the configuration option;
/// otherwise, null.
///
///
/// Non-zero for success; otherwise, zero.
///
public static bool GetValue(
string key,
out string value
)
{
lock (syncRoot)
{
value = null;
if ((key == null) || (options == null))
return false;
if (options.TryGetValue(key, out value))
return true;
return false;
}
}
///////////////////////////////////////////////////////////////////////
///
/// This method attempts to set the value of the specified option key.
///
///
/// The name ("key") of the configuration option.
///
///
/// The new value for the configuration option.
///
///
/// Non-zero for success; otherwise, zero.
///
public static bool SetValue(
string key,
string value
)
{
lock (syncRoot)
{
if ((key == null) || (options == null))
return false;
options[key] = value;
return true;
}
}
#endregion
///////////////////////////////////////////////////////////////////////
#region Stream Handling
///
/// This method attempts to read an option value from the specified
/// stream. The stream must be readable. After this method returns,
/// the stream may no longer be usable.
///
///
/// The stream to read the option value from.
///
///
/// Upon success, the read value for the configuration option;
/// otherwise, null.
///
///
/// Non-zero for success; otherwise, zero.
///
public static bool ReadValue(
Stream stream,
out string value
)
{
value = null;
if ((stream == null) || !stream.CanRead)
return false;
try
{
using (StreamReader streamReader = new StreamReader(stream))
{
value = streamReader.ReadToEnd();
return true;
}
}
catch (Exception)
{
// do nothing.
}
return false;
}
///////////////////////////////////////////////////////////////////////
///
/// This method attempts to write an option value to the specified
/// stream. The stream must be writable. After this method returns,
/// the stream may no longer be usable.
///
///
/// The stream to write the option value to.
///
///
/// The option value to be written. This value may be null.
///
///
/// Non-zero for success; otherwise, zero.
///
public static bool WriteValue(
Stream stream,
string value
)
{
if ((stream == null) || !stream.CanWrite)
return false;
try
{
using (StreamWriter streamWriter = new StreamWriter(stream))
{
streamWriter.Write(value);
return true;
}
}
catch (Exception)
{
// do nothing.
}
return false;
}
#endregion
#endregion
}
}