// Persist library : Persistence layer
// Copyright (C) 2003 Vincent Daron
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using System.IO;
using System.Xml;
using System.Collections;
using System.Collections.Specialized;
using Persist;
using Persist.Sql;
using Persist.Maps;
using Persist.Criteria;
using Persist.Converters;
using Persist.IdGenerators;
namespace Persist.Config{
/// <summary>
/// IConfigurationStore implementation based on a Xml File
/// </summary>
public class XMLConfigurationStore : IConfigurationStore
{
private XmlDocument theXmlDocument = new XmlDocument();
private XmlNode theRoot;
private NameValueCollection theClassMapType = new NameValueCollection();
/// <summary>
/// Contructor
/// </summary>
/// <param name="fileName">the XML File Name</param>
public XMLConfigurationStore(string fileName)
{
theXmlDocument.Load(fileName);
}
/// <summary>
/// Create XmlConfigurationStore Based on a Stream
/// </summary>
/// <param name="stream">the Stream</param>
public XMLConfigurationStore(Stream stream)
{
if(stream == null)throw new ArgumentNullException("stream","Stream cannot be null");
theXmlDocument.Load(stream);
}
/// <summary>
/// Read Configuration from XmlFile
/// </summary>
/// <returns>the Configuration</returns>
public void LoadConfiguration(Configuration configuration)
{
theRoot = theXmlDocument.SelectSingleNode("Persist");
if(theRoot == null)
{
throw new ConfigurationException("Invalid XML document. No Persist definition found");
}
foreach(XmlNode node in theRoot.SelectNodes("Database"))
{
GetRelationalDatabase(configuration, node);
}
foreach(XmlNode node in theRoot.SelectNodes("Converter"))
{
GetConverter(configuration,node);
}
foreach(XmlNode node in theRoot.SelectNodes("IdGenerator"))
{
GetIdGenerator(configuration,node);
}
foreach(XmlNode node in theRoot.SelectNodes("Class"))
{
ClassMap cm = GetClassMap(node);
configuration.AddClassMap(cm);
theClassMapType.Add(cm.Name,cm.TypeName.Substring(0,cm.TypeName.IndexOf(',')));
}
foreach(XmlNode node in theRoot.SelectNodes("Association"))
{
ProcessAssociation(configuration,node);
}
}
/// <summary>
/// Save the configuration to the file (incomplete for now)
/// </summary>
/// <param name="configuration">the configuration to save</param>
public void SaveConfiguration(Configuration configuration, string fileName)
{
theXmlDocument = new XmlDocument();
XmlNode root = theXmlDocument.CreateNode(XmlNodeType.Element,"Persist","");
XmlAttribute assemblyName = theXmlDocument.CreateAttribute("assemblyName");
assemblyName.Value = configuration.AssemblyName;
root.Attributes.Append(assemblyName);
foreach(RelationalDatabaseHolder db in configuration.Databases)
{
root.AppendChild(GetRelationalDatabaseNode(db));
}
foreach(IdGeneratorHolder idGeneratorHolder in configuration.IdGenerators)
{
root.AppendChild(GetIdGeneratorNode(idGeneratorHolder));
}
foreach(ConverterHolder converterHolder in configuration.Converters)
{
root.AppendChild(GetConverterNode(converterHolder));
}
foreach(ClassMap cm in configuration.ClassMaps)
{
root.AppendChild(GetClassMapNode(cm));
}
foreach(ClassMap cm in configuration.ClassMaps)
{
foreach(UniDirectionalAssociationMap associationMap in cm.AssociationMaps)
{
root.AppendChild(GetUniDirectionalAssociationMapNode(associationMap));
}
}
theXmlDocument.AppendChild(root);
theXmlDocument.Save(fileName);
}
private string GetAttributeValue(XmlNode node,string attributeName)
{
string result = null;
System.Xml.XmlAttribute attr = node.Attributes[attributeName];
if(attr != null)
{
result = attr.Value;
}
return result;
}
private void GetRelationalDatabase(Configuration configuration, XmlNode relationalDatabaseNode)
{
string relationalDatabaseName = GetAttributeValue(relationalDatabaseNode, "name");
string relationalDatabaseTypeName = GetAttributeValue(relationalDatabaseNode, "type");
if(relationalDatabaseName == null)
{
throw new ConfigurationException("Error in Relational Database definition.No name attribute specified.");
}
if(relationalDatabaseTypeName == null)
{
throw new ConfigurationException("Error in Relational Database definition.No type attribute specified.");
}
NameValueCollection parameters = GetParameters(relationalDatabaseNode);
configuration.AddRelationalDatabase(relationalDatabaseName,relationalDatabaseTypeName,parameters);
}
private void GetIdGenerator(Configuration configuration, XmlNode idGeneratorNode)
{
string idGeneratorName = GetAttributeValue(idGeneratorNode, "name");
string idGeneratorTypeName = GetAttributeValue(idGeneratorNode, "type");
string singleInstanceString = GetAttributeValue(idGeneratorNode, "singleInstance");
bool singleInstance = false;
if(idGeneratorName == null)
{
throw new ConfigurationException("Error in IdGenerator definition.No name attribute specified.");
}
if(idGeneratorTypeName == null)
{
throw new ConfigurationException("Error in IdGenerator definition.No type attribute specified.");
}
if(singleInstanceString != null)
{
singleInstance = bool.Parse(singleInstanceString);
}
NameValueCollection parameters = GetParameters(idGeneratorNode);
configuration.AddIdGenerator(idGeneratorName,idGeneratorTypeName,singleInstance,parameters);
}
private NameValueCollection GetParameters(XmlNode node)
{
NameValueCollection parameters = new NameValueCollection();
string parameterKey;
string parameterValue;
foreach(XmlNode parameter in node.SelectNodes("Parameters/Add"))
{
parameterKey = GetAttributeValue(parameter,"key");
parameterValue = GetAttributeValue(parameter,"value");
if((parameterKey == null)||(parameterValue == null))
{
throw new ConfigurationException("Parameter incomplete for node :"+node.Value+" , specified key and value attribute");
}
parameters.Add(parameterKey,parameterValue);
}
return parameters;
}
private void GetConverter(Configuration configuration,XmlNode node)
{
string name = GetAttributeValue(node, "name");
string typeName = GetAttributeValue(node, "type");
if(name == null)
{
throw new ConfigurationException("Converter name is not specified");
}
if(typeName == null)
{
throw new ConfigurationException("Converter type name is not specified");
}
NameValueCollection parameters = GetParameters(node);
// Put converter to the PersistenceManagerFactory
configuration.AddConverter(name,new ConverterHolder(name,typeName,parameters));
}
private ClassMap GetClassMap(XmlNode node)
{
string name = GetAttributeValue(node, "name");
string typeName = GetAttributeValue(node, "type");
string table = GetAttributeValue(node, "table");
string databaseName = GetAttributeValue(node, "database");
if(name == null)
{
throw new ConfigurationException("Error in class map definition. name attribute is not specified.");
}
if(typeName == null)
{
throw new ConfigurationException("Error in class map definition. type attribute is not specified.");
}
if(typeName.IndexOf(',') < 0)
{
throw new ConfigurationException("Error in class map definition, type attibute do not contains assembly name");
}
if(table == null)
{
throw new ConfigurationException("Error in class map definition for type " + typeName + ". table attribute is not specified.");
}
if(databaseName == null)
{
throw new ConfigurationException("Error in class map definition for type " + typeName + ". database attribute is not specified.");
}
// Create current class map
ClassMap cm = new ClassMap(name,typeName,databaseName);
// Create database map for the current table
DatabaseMap dm = new DatabaseMap(databaseName);
// Create current table map
TableMap tableMap = new TableMap();
tableMap.Name = table;
tableMap.DatabaseMap = dm;
cm.Table = tableMap;
foreach(XmlNode attribute in node.SelectNodes("Attribute"))
{
AttributeMap am = GetAttributeMap(attribute, cm, tableMap);
if(am.IsOptimistCheckAttribute)
{
cm.OptimistLockAttributeMap = am;
}
cm.AddAttributeMap(am);
}
return cm;
}
private AttributeMap GetAttributeMap(XmlNode node, ClassMap clm, TableMap tableMap)
{
string attributeName = GetAttributeValue(node, "name");
string column = GetAttributeValue(node, "column");
string proxy = GetAttributeValue(node, "proxy");
string optimistLock = GetAttributeValue(node, "optimistlock");
string type = GetAttributeValue(node, "type");
string key = GetAttributeValue(node,"key");
string idGeneratorName = GetAttributeValue(node,"idgenerator");
string converterName = GetAttributeValue(node,"converter");
if(attributeName != null)
{
AttributeMap am = new AttributeMap(attributeName);
// Set column map for this attribute map
if(column != null)
{
if(type == null)
{
throw new ConfigurationException("type of column "+column+" of type "+clm.TypeName+" not specified");
}
// Create column map for this attribute map
ColumnMap cm = new ColumnMap(column, tableMap);
cm.DbType = (System.Data.DbType)Enum.Parse(typeof(System.Data.DbType),type,false);
cm.ConverterName = converterName;
if(key != null)
{
if(key.ToLower() == "primary")
{
cm.KeyType = ColumnMap.KeyTypeEnum.Primary;
cm.IdGeneratorName = idGeneratorName;
}
else if(key.ToLower() == "foreign")
{
cm.KeyType = ColumnMap.KeyTypeEnum.Foreign;
}
}
if(proxy == "true")
{
am.IsProxy = true;
}
if(optimistLock == "true")
{
am.IsOptimistCheckAttribute = true;
}
am.ColumnMap = cm;
}
return am;
}
else
{
throw new ConfigurationException("Error in attribute map definition. Attribute name is not specified.");
}
}
private void ProcessIdGenerator(Configuration configuration, XmlNode node, RelationalDatabase database)
{
string name = GetAttributeValue(node, "name");
string typeName = GetAttributeValue(node, "type");
string singeltonValue = GetAttributeValue(node, "singleton");
if(name == null || name.Length == 0)
{
throw new ConfigurationException("ID generator name is not specified");
}
if(typeName == null || typeName.Length == 0)
{
throw new ConfigurationException("ID generator type name is not specified");
}
bool isSingleInstance = false;
if((singeltonValue != null)&&(singeltonValue.Length > 0))
{
isSingleInstance = bool.Parse(singeltonValue);
}
// get ID generator parameters
NameValueCollection parameters = GetParameters(node);
// Put ID generator instance to the relational database
configuration.AddIdGenerator(name,typeName,isSingleInstance,parameters);
//database.AddIdGenerator(name, typeName, isSingleInstance, parameters);
}
private void ProcessAssociation(Configuration configuration, XmlNode node)
{
string fromClass = GetAttributeValue(node, "from");
string toClass = GetAttributeValue(node, "to");
string target = GetAttributeValue(node, "target");
string cardinality = GetAttributeValue(node, "cardinality");
string isDeleteAutomatic=GetAttributeValue(node, "delete-automatic");
string isSaveAutomatic=GetAttributeValue(node, "save-automatic");
string isRetrieveAutomatic=GetAttributeValue(node, "retrieve-automatic");
string isInverse=GetAttributeValue(node, "inverse");
if(fromClass == null || toClass == null || target == null)
{
if(fromClass == null)
{
throw new ConfigurationException("Error in association definition. from-class is not specified.");
}
else if(toClass == null)
{
throw new ConfigurationException("Error in association definition. to-class is not specified.");
}
else
{
throw new ConfigurationException("Error in association definition. Target is not specified.");
}
}
else
{
if(theClassMapType[fromClass] == null)
{
throw new ConfigurationException("Error in association definition. Class map was not found for '" + fromClass + "' from-class.");
}
ClassMap fromClassMap = configuration.GetClassMap(theClassMapType[fromClass]);
if(theClassMapType[toClass] == null)
{
throw new ConfigurationException("Error in association definition. Class map was not found for '" + toClass + "' to-class.");
}
ClassMap toClassMap = configuration.GetClassMap(theClassMapType[toClass]);
UniDirectionalAssociationMap am = new UniDirectionalAssociationMap();
am.ForClass = toClassMap;
am.TargetName = target;
am.Target = fromClassMap.GetAttributeMap(target);
if(am.Target == null)
{
throw new ConfigurationException("Error in association definition. Attribute map was not found for target : '" + target + "' in class "+fromClass);
}
am.IsDeleteAutomatic = isDeleteAutomatic == "true";
am.IsSaveAutomatic = isSaveAutomatic == "true";
am.IsRetrieveAutomatic = isRetrieveAutomatic == "true";
am.IsInverse = isInverse == "true";
if(cardinality != null)
{
if(cardinality == "1..1")
{
am.Cardinality = UniDirectionalAssociationMap.CardinalityEnum.ONE_TO_ONE;
}
else if(cardinality == "1..*")
{
am.Cardinality = UniDirectionalAssociationMap.CardinalityEnum.ONE_TO_MANY;
}
}
foreach(XmlNode child in node.SelectNodes("Entry"))
{
string fromAttribute = GetAttributeValue(child, "from-attribute");
string toAttribute = GetAttributeValue(child, "to-attribute");
if(fromAttribute != null && toAttribute != null)
{
if(am.IsInverse)
{
if(toClassMap.GetAttributeMap(fromAttribute) == null)
{
throw new ConfigurationException("Error in association map entry definition. "+"Attribute map was not found for '"+fromAttribute+"' from-attribute in " + toClass + " class");
}
if(fromClassMap.GetAttributeMap(toAttribute) == null)
{
throw new ConfigurationException("Error in association map entry definition. "+"Attribute map was not found for '"+toAttribute+"' to-attribute in " + fromClass + " class");
}
UDAMapEntry entry = new UDAMapEntry(toClassMap.GetAttributeMap(fromAttribute),fromClassMap.GetAttributeMap(toAttribute));
am.AddEntry(entry);
}
else
{
if(fromClassMap.GetAttributeMap(fromAttribute) == null)
{
throw new ConfigurationException("Error in association map entry definition. "+"Attribute map was not found for '"+fromAttribute+"' from-attribute in " + fromClass + " class");
}
if(toClassMap.GetAttributeMap(toAttribute) == null)
{
throw new ConfigurationException("Error in association map entry definition. "+"Attribute map was not found for '"+toAttribute+"' to-attribute in " + toClass + " class");
}
UDAMapEntry entry = new UDAMapEntry(fromClassMap.GetAttributeMap(fromAttribute),toClassMap.GetAttributeMap(toAttribute));
am.AddEntry(entry);
}
}
else
{
if(fromAttribute == null)
{
throw new ConfigurationException("Error in association map entry definition. from-attribute in not specified.");
}
else
{
throw new ConfigurationException("Error in association map entry definition. to-attribute in not specified.");
}
}
}
// get order attributes
// ArrayList orderAttributes = new ArrayList();
// foreach(XmlNode OrderByNode in node.SelectNodes("OrderBy"))
// {
// string attributeName = GetAttributeValue(OrderByNode, "name");
// string ascend = GetAttributeValue(OrderByNode, "ascend");
// if(attributeName == null)
// throw new Exception("Error in order attribute specification. attribute name is not specified.");
// AttributeMap orderAttribute = am.ForClass.GetAttributeMap(attributeName);
// if(orderAttribute == null)
// throw new Exception("Order attribute with name " + attributeName + " not found.");
//
// bool isAscend = ascend == "true";
//
// orderAttributes.Add(new OrderEntry(orderAttribute, isAscend));
// }
//
// if(orderAttributes.Count > 0)
// {
// am.OrderAttributes = (OrderEntry[])orderAttributes.ToArray(typeof(OrderEntry));
// }
fromClassMap.AddAssociationMap(am);
}
}
private XmlElement GetUniDirectionalAssociationMapNode(UniDirectionalAssociationMap assocationMap)
{
XmlElement element = theXmlDocument.CreateElement("Association");
XmlAttribute fromClass = theXmlDocument.CreateAttribute("from");
XmlAttribute toClass = theXmlDocument.CreateAttribute("to");
XmlAttribute target = theXmlDocument.CreateAttribute("target");
XmlAttribute cardinality = theXmlDocument.CreateAttribute("cardinality");
XmlAttribute isDeleteAutomatic=theXmlDocument.CreateAttribute("delete-automatic");
XmlAttribute isSaveAutomatic=theXmlDocument.CreateAttribute("save-automatic");
XmlAttribute isRetrieveAutomatic=theXmlDocument.CreateAttribute("retrieve-automatic");
XmlAttribute isInverse=theXmlDocument.CreateAttribute("inverse");
fromClass.Value = assocationMap.ForClass.Name;
element.Attributes.Append(fromClass);
// toClass.Value = assocationMap.T
return element;
}
private XmlElement GetAttributeMapNode(AttributeMap attributeMap)
{
XmlElement element = theXmlDocument.CreateElement("Attribute");
XmlAttribute attributeName = theXmlDocument.CreateAttribute("name");
attributeName.Value = attributeMap.Name;
element.Attributes.Append(attributeName);
if(attributeMap.ColumnMap != null)
{
XmlAttribute column = theXmlDocument.CreateAttribute("column");
column.Value = attributeMap.ColumnMap.Name;
element.Attributes.Append(column);
if(attributeMap.IsProxy)
{
XmlAttribute proxy = theXmlDocument.CreateAttribute("proxy");
proxy.Value = attributeMap.IsProxy.ToString();
element.Attributes.Append(proxy);
}
if(attributeMap.IsOptimistCheckAttribute)
{
XmlAttribute optimistLock = theXmlDocument.CreateAttribute("optimistlock");
optimistLock.Value = attributeMap.IsOptimistCheckAttribute.ToString();
element.Attributes.Append(optimistLock);
}
XmlAttribute type = theXmlDocument.CreateAttribute("type");
type.Value = attributeMap.ColumnMap.DbType.ToString();
element.Attributes.Append(type);
if(attributeMap.ColumnMap.KeyType != ColumnMap.KeyTypeEnum.None)
{
XmlAttribute key = theXmlDocument.CreateAttribute("key");
key.Value = attributeMap.ColumnMap.KeyType.ToString();
element.Attributes.Append(key);
}
if(attributeMap.ColumnMap.KeyType == ColumnMap.KeyTypeEnum.Primary)
{
XmlAttribute idGeneratorName = theXmlDocument.CreateAttribute("idgenerator");
idGeneratorName.Value = attributeMap.ColumnMap.IdGeneratorName;
element.Attributes.Append(idGeneratorName);
}
}
return element;
}
private XmlNode GetIdGeneratorNode(IdGeneratorHolder idGeneratorHolder)
{
XmlElement element = theXmlDocument.CreateElement("IdGenerator");
XmlAttribute name = theXmlDocument.CreateAttribute("name");
name.Value = idGeneratorHolder.Name;
element.Attributes.Append(name);
XmlAttribute typeName = theXmlDocument.CreateAttribute("type");
typeName.Value = idGeneratorHolder.TypeName;
element.Attributes.Append(typeName);
XmlAttribute singleInstance = theXmlDocument.CreateAttribute("singleInstance");
singleInstance.Value = idGeneratorHolder.SingleInstance.ToString();
element.Attributes.Append(singleInstance);
if(idGeneratorHolder.Parameters.Count > 0)
{
element.AppendChild(GetParametersNodes(idGeneratorHolder.Parameters));
}
return element;
}
private XmlNode GetRelationalDatabaseNode(RelationalDatabaseHolder relationalDatabase)
{
XmlAttribute typeAttribute = theXmlDocument.CreateAttribute("type");
XmlAttribute nameAttribute = theXmlDocument.CreateAttribute("name");
XmlElement element = theXmlDocument.CreateElement("Database");
nameAttribute.Value = relationalDatabase.Name;
element.Attributes.Append(nameAttribute);
typeAttribute.Value = relationalDatabase.TypeName;
element.Attributes.Append(typeAttribute);
element.AppendChild(GetParametersNodes(relationalDatabase.Parameters));
return element;
}
private XmlNode GetClassMapNode(ClassMap classMap)
{
XmlElement element = theXmlDocument.CreateElement("Class");
XmlAttribute name = theXmlDocument.CreateAttribute("name");
name.Value = classMap.Name;
element.Attributes.Append(name);
XmlAttribute typeName = theXmlDocument.CreateAttribute("type");
typeName.Value = classMap.TypeName;
element.Attributes.Append(typeName);
XmlAttribute table = theXmlDocument.CreateAttribute("table");
table.Value = classMap.Table.Name;
element.Attributes.Append(table);
XmlAttribute databaseName = theXmlDocument.CreateAttribute("database");
databaseName.Value = classMap.RelationalDatabase.Name;
element.Attributes.Append(databaseName);
foreach(AttributeMap attributeMap in classMap.AttributeMaps.Values)
{
element.AppendChild( GetAttributeMapNode(attributeMap) );
}
return element;
}
private XmlNode GetParametersNodes(NameValueCollection parameters)
{
XmlElement element = theXmlDocument.CreateElement("Parameters");
foreach(string key in parameters.AllKeys)
{
XmlElement addElement = theXmlDocument.CreateElement("Add");
XmlAttribute keyAttribute = theXmlDocument.CreateAttribute("key");
keyAttribute.Value = key;
addElement.Attributes.Append(keyAttribute);
XmlAttribute valueAttribute = theXmlDocument.CreateAttribute("value");
valueAttribute.Value = parameters[key];
addElement.Attributes.Append(valueAttribute);
element.AppendChild(addElement);
}
return element;
}
private XmlNode GetConverterNode(ConverterHolder converterHolder)
{
XmlElement element = theXmlDocument.CreateElement("Converter");
XmlAttribute name = theXmlDocument.CreateAttribute("name");
name.Value = converterHolder.Name;
element.Attributes.Append(name);
XmlAttribute typeName = theXmlDocument.CreateAttribute("type");
typeName.Value = converterHolder.TypeName;
element.Attributes.Append(typeName);
if(converterHolder.Parameters.Count > 0)
{
element.AppendChild(GetParametersNodes(converterHolder.Parameters));
}
return element;
}
}
}
|