summaryrefslogtreecommitdiff
path: root/framework/Data/SqlMap/DataMapper
diff options
context:
space:
mode:
authorwei <>2006-12-04 00:02:23 +0000
committerwei <>2006-12-04 00:02:23 +0000
commit18ea316c553f7ccfc18b73f0c987de007f11b275 (patch)
treeb8fa0d14de75582b48c3e4d12050c84f07805920 /framework/Data/SqlMap/DataMapper
parentb69ca04f50ffd538239342f3bfd1e77ffc6156c0 (diff)
Fixed #469
Diffstat (limited to 'framework/Data/SqlMap/DataMapper')
-rw-r--r--framework/Data/SqlMap/DataMapper/TLazyLoadList.php145
-rw-r--r--framework/Data/SqlMap/DataMapper/TPropertyAccess.php130
-rw-r--r--framework/Data/SqlMap/DataMapper/TSqlMapCache.php230
-rw-r--r--framework/Data/SqlMap/DataMapper/TSqlMapException.php99
-rw-r--r--framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php158
-rw-r--r--framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php183
-rw-r--r--framework/Data/SqlMap/DataMapper/messages.txt66
7 files changed, 1011 insertions, 0 deletions
diff --git a/framework/Data/SqlMap/DataMapper/TLazyLoadList.php b/framework/Data/SqlMap/DataMapper/TLazyLoadList.php
new file mode 100644
index 00000000..770c217a
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/TLazyLoadList.php
@@ -0,0 +1,145 @@
+<?php
+/**
+ * TLazyLoadList, TObjectProxy classes file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.SqlMap.DataMapper
+ */
+
+/**
+ * TLazyLoadList executes mapped statements when the proxy collection is first accessed.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.DataMapper
+ * @since 3.1
+ */
+class TLazyLoadList
+{
+ private $_param;
+ private $_target;
+ private $_propertyName='';
+ private $_statement='';
+ private $_loaded=false;
+ private $_innerList;
+ private $_connection;
+
+ /**
+ * Create a new proxy list that will execute the mapped statement when any
+ * of the list's method are accessed for the first time.
+ * @param TMappedStatement statement to be executed to load the data.
+ * @param mixed parameter value for the statement.
+ * @param object result object that contains the lazy collection.
+ * @param string property of the result object to set the loaded collection.
+ */
+ protected function __construct($mappedStatement, $param, $target, $propertyName)
+ {
+ $this->_param = $param;
+ $this->_target = $target;
+ $this->_statement = $mappedStatement;
+ $this->_connection=$mappedStatement->getManager()->getDbConnection();
+ $this->_propertyName = $propertyName;
+ }
+
+ /**
+ * Create a new instance of a lazy collection.
+ * @param TMappedStatement statement to be executed to load the data.
+ * @param mixed parameter value for the statement.
+ * @param object result object that contains the lazy collection.
+ * @param string property of the result object to set the loaded collection.
+ * @return TObjectProxy proxied collection object.
+ */
+ public static function newInstance($mappedStatement, $param, $target, $propertyName)
+ {
+ $handler = new self($mappedStatement, $param, $target, $propertyName);
+ $statement = $mappedStatement->getStatement();
+ $registry=$mappedStatement->getManager()->getTypeHandlers();
+ $list = $statement->createInstanceOfListClass($registry);
+ if(!is_object($list))
+ throw new TSqlMapExecutionException('sqlmap_invalid_lazyload_list',$statement->getID());
+ return new TObjectProxy($handler, $list);
+ }
+
+ /**
+ * Relay the method call to the underlying collection.
+ * @param string method name.
+ * @param array method parameters.
+ */
+ public function intercept($method, $arguments)
+ {
+ return call_user_func_array(array($this->_innerList, $method), $arguments);
+ }
+
+ /**
+ * Load the data by executing the mapped statement.
+ */
+ protected function fetchListData()
+ {
+ if($this->_loaded == false)
+ {
+ $this->_innerList = $this->_statement->executeQueryForList($this->_connection,$this->_param);
+ $this->_loaded = true;
+ //replace the target property with real list
+ TPropertyAccess::set($this->_target, $this->_propertyName, $this->_innerList);
+ }
+ }
+
+ /**
+ * Try to fetch the data when any of the proxy collection method is called.
+ * @param string method name.
+ * @return boolean true if the underlying collection has the corresponding method name.
+ */
+ public function hasMethod($method)
+ {
+ $this->fetchListData();
+ if(is_object($this->_innerList))
+ return in_array($method, get_class_methods($this->_innerList));
+ return false;
+ }
+}
+
+/**
+ * TObjectProxy sets up a simple object that intercepts method calls to a
+ * particular object and relays the call to handler object.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.DataMapper
+ * @since 3.1
+ */
+class TObjectProxy
+{
+ private $_object;
+ private $_handler;
+
+ /**
+ * @param object handler to method calls.
+ * @param object the object to by proxied.
+ */
+ public function __construct($handler, $object)
+ {
+ $this->_handler = $handler;
+ $this->_object = $object;
+ }
+
+ /**
+ * Relay the method call to the handler object (if able to be handled), otherwise
+ * it calls the proxied object's method.
+ * @param string method name called
+ * @param array method arguments
+ * @return mixed method return value.
+ */
+ public function __call($method,$params)
+ {
+ if($this->_handler->hasMethod($method))
+ return $this->_handler->intercept($method, $params);
+ else
+ return call_user_func_array(array($this->_object, $method), $params);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/DataMapper/TPropertyAccess.php b/framework/Data/SqlMap/DataMapper/TPropertyAccess.php
new file mode 100644
index 00000000..fea94fa4
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/TPropertyAccess.php
@@ -0,0 +1,130 @@
+<?php
+
+class TPropertyAccess
+{
+ private $_obj;
+ private $_performance=false;
+
+ public function __construct($obj,$performance=false)
+ {
+ $this->_obj = $obj;
+ $this->_performance=$performance;
+ }
+
+ public function __get($name)
+ {
+ return self::get($this->_obj,$name,$this->_performance);
+ }
+
+ public function __set($name,$value)
+ {
+ self::set($this->_obj,$name,$value,$this->_performance);
+ }
+
+ /**
+ * Evaluates the data value at the specified field.
+ * - If the data is an array, then the field is treated as an array index
+ * and the corresponding element value is returned;
+ * - If the data is a TMap or TList object, then the field is treated as a key
+ * into the map or list, and the corresponding value is returned.
+ * - If the data is an object, the field is treated as a property or subproperty
+ * defined with getter methods. For example, if the object has a method called
+ * getMyValue(), then field 'MyValue' will retrive the result of this method call.
+ * If getMyValue() returns an object which contains a method getMySubValue(),
+ * then field 'MyValue.MySubValue' will return that method call result.
+ * @param mixed data containing the field value, can be an array, TMap, TList or object.
+ * @param mixed field value
+ * @return mixed value at the specified field
+ * @throw TInvalidDataValueException if field or data is invalid
+ */
+ public static function get($object,$path)
+ {
+ if(!is_array($object) && !is_object($object))
+ return $object;
+ $properties = explode('.', $path);
+ foreach($properties as $prop)
+ {
+ if(is_array($object) || $object instanceof ArrayAccess)
+ {
+ if(array_key_exists($prop, $object))
+ $object = $object[$prop];
+ else
+ throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
+ }
+ else if(is_object($object))
+ {
+ $getter = 'get'.$prop;
+ if(is_callable(array($object,$getter)))
+ $object = $object->{$getter}();
+ else if(in_array($prop, array_keys(get_object_vars($object))))
+ $object = $object->{$prop};
+ else
+ throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
+ }
+ else
+ throw new TInvalidPropertyException('sqlmap_invalid_property',$path);
+ }
+ return $object;
+ }
+
+ public static function has($object, $path)
+ {
+ if(!is_array($object) && !is_object($object))
+ return false;
+ $properties = explode('.', $path);
+ foreach($properties as $prop)
+ {
+ if(is_array($object) || $object instanceof ArrayAccess)
+ {
+ if(array_key_exists($prop, $object))
+ $object = $object[$prop];
+ else
+ return false;
+ }
+ else if(is_object($object))
+ {
+ $getter = 'get'.$prop;
+ if(is_callable(array($object,$getter)))
+ $object = $object->{$getter}();
+ else if(in_array($prop, array_keys(get_object_vars($object))))
+ $object = $object->{$prop};
+ return false;
+ }
+ else
+ return false;
+ }
+ return true;
+ }
+
+ public static function set(&$originalObject, $path, $value)
+ {
+ $properties = explode('.', $path);
+ $prop = array_pop($properties);
+ if(count($properties) > 0)
+ $object = self::get($originalObject, implode('.',$properties));
+ else
+ $object = &$originalObject;
+
+ //var_dump($object);
+ if(is_array($object) || $object instanceof ArrayAccess)
+ {
+ $object[$prop] = $value;
+ }
+ else if(is_object($object))
+ {
+ $setter = 'set'.$prop;
+ if(is_callable(array($object, $setter)))
+ {
+ if($object->{$setter}($value) === null)
+ $object->{$prop} = $value;
+ }
+ else
+ $object->{$prop} = $value;
+ }
+ else
+ throw new TInvalidPropertyException('sqlmap_invalid_property_type',$path);
+ }
+
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapCache.php b/framework/Data/SqlMap/DataMapper/TSqlMapCache.php
new file mode 100644
index 00000000..5cb9cbcb
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/TSqlMapCache.php
@@ -0,0 +1,230 @@
+<?php
+/**
+ * TSqlMapCache class file contains FIFO, LRU, and GLOBAL cache implementations.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2007 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.DataAccess.SQLMap
+ */
+
+interface ISqLMapCache
+{
+ public function remove($key);
+
+ public function flush();
+
+ public function get($key);
+
+ public function set($key, $value);
+}
+
+/**
+ * Allow different implementation of caching strategy. See <tt>TSqlMapFifoCache</tt>
+ * for a first-in-first-out implementation. See <tt>TSqlMapLruCache</tt> for
+ * a least-recently-used cache implementation.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.DataAccess.SQLMap
+ * @since 3.0
+ */
+abstract class TSqlMapCache implements ISqlMapCache
+{
+ protected $_keyList;
+ protected $_cache;
+ protected $_cacheSize = 100;
+
+ /**
+ * Create a new cache with limited cache size.
+ * @param integer maxium number of items to cache.
+ */
+ public function __construct($cacheSize=100)
+ {
+ $this->_cache = new TMap;
+ $this->_cacheSize = intval($cacheSize);
+ $this->_keyList = new TList;
+ }
+
+ public function setCacheSize($value)
+ {
+ $this->_cacheSize=TPropertyValue::ensureInteger($value,100);
+ }
+
+ public function getCacheSize()
+ {
+ return $this->_cacheSize;
+ }
+
+ /**
+ * @return object the object removed if exists, null otherwise.
+ */
+ public function remove($key)
+ {
+ $object = $this->get($key);
+ $this->_cache->remove($key);
+ $this->_keyList->remove($key);
+ return $object;
+ }
+
+ /**
+ * Clears the cache.
+ */
+ public function flush()
+ {
+ $this->_keyList->clear();
+ $this->_cache->clear();
+ }
+
+}
+
+/**
+ * First-in-First-out cache implementation, removes
+ * object that was first added when the cache is full.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.DataAccess.SQLMap
+ * @since 3.0
+ */
+class TSqlMapFifoCache extends TSqlMapCache
+{
+ /**
+ * @return mixed Gets a cached object with the specified key.
+ */
+ public function get($key)
+ {
+ return $this->_cache->itemAt($key);
+ }
+
+ /**
+ * Adds an item with the specified key and value into cached data.
+ * @param string cache key
+ * @param mixed value to cache.
+ */
+ public function set($key, $value)
+ {
+ $this->_cache->add($key, $value);
+ $this->_keyList->add($key);
+ if($this->_keyList->getCount() > $this->_cacheSize)
+ {
+ $oldestKey = $this->_keyList->removeAt(0);
+ $this->_cache->remove($oldestKey);
+ }
+ }
+}
+
+/**
+ * Least recently used cache implementation, removes
+ * object that was accessed last when the cache is full.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.DataAccess.SQLMap
+ * @since 3.0
+ */
+class TSqlMapLruCache extends TSqlMapCache
+{
+ /**
+ * @return mixed Gets a cached object with the specified key.
+ */
+ public function get($key)
+ {
+ if($this->_keyList->contains($key))
+ {
+ $this->_keyList->remove($key);
+ $this->_keyList->add($key);
+ return $this->_cache->itemAt($key);
+ }
+ else
+ return null;
+ }
+
+ /**
+ * Adds an item with the specified key and value into cached data.
+ * @param string cache key
+ * @param mixed value to cache.
+ */
+ public function set($key, $value)
+ {
+ $this->_cache->add($key, $value);
+ $this->_keyList->add($key);
+ if($this->_keyList->getCount() > $this->_cacheSize)
+ {
+ $oldestKey = $this->_keyList->removeAt(0);
+ $this->_cache->remove($oldestKey);
+ }
+ }
+}
+
+class TSqlMapApplicationCache implements ISqlMapCache
+{
+ private $_cache;
+ private $_expiry=0;
+ private $_property=array();
+ private $_cacheModelID;
+
+ public function __sleep()
+ {
+ $this->_cache = null;
+ return array_keys(get_object_vars($this));
+ }
+
+ public function remove($key)
+ {
+ $this->getCache()->delete($key);
+ }
+
+ public function flush()
+ {
+ $this->getCache()->flush();
+ }
+
+ public function get($key)
+ {
+ $result = $this->getCache()->get($key);
+ return $result === false ? null : $result;
+ }
+
+ public function set($key, $value)
+ {
+ $this->getCache()->set($key, $value, $this->_expiry);
+ }
+
+ public function configure($model, $properties)
+ {
+ $this->_property = $properties;
+ $this->_cacheModelID = $model->getID();
+ }
+
+ protected function getCache()
+ {
+ if(is_null($this->_cache))
+ $this->initialize();
+ return $this->_cache;
+ }
+
+ protected function initialize()
+ {
+ if(isset($this->_property['expiry']))
+ $this->_expiry = intval($this->_property['expiry']);
+
+ if(isset($this->_property['cacheModule']))
+ {
+ $id = $this->_property['cacheModule'];
+ $this->_cache = Prado::getApplication()->getModule($id);
+ }
+ else
+ {
+ $this->_cache = Prado::getApplication()->getCache();
+ }
+
+ if(!($this->_cache instanceof ICache))
+ throw new TSqlMapConfigurationException(
+ 'sqlmap_invalid_prado_cache', $this->_cacheModelID);
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapException.php b/framework/Data/SqlMap/DataMapper/TSqlMapException.php
new file mode 100644
index 00000000..9a2db478
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/TSqlMapException.php
@@ -0,0 +1,99 @@
+<?php
+
+class TSqlMapException extends TException
+{
+ /**
+ * Constructor.
+ * @param string error message. This can be a string that is listed
+ * in the message file. If so, the message in the preferred language
+ * will be used as the error message. Any rest parameters will be used
+ * to replace placeholders ({0}, {1}, {2}, etc.) in the message.
+ */
+ public function __construct($errorMessage)
+ {
+ $this->setErrorCode($errorMessage);
+ $errorMessage=$this->translateErrorMessage($errorMessage);
+ $args=func_get_args();
+ array_shift($args);
+ $n=count($args);
+ $tokens=array();
+ for($i=0;$i<$n;++$i)
+ {
+ if($args[$i] instanceof SimpleXmlElement)
+ $tokens['{'.$i.'}']=$this->implodeNode($args[$i]);
+ else
+ $tokens['{'.$i.'}']=TPropertyValue::ensureString($args[$i]);
+ }
+ parent::__construct(strtr($errorMessage,$tokens));
+ }
+
+ protected function implodeNode($node)
+ {
+ $attributes=array();
+ foreach($node->attributes() as $k=>$v)
+ $attributes[]=$k.'="'.(string)$v.'"';
+ return '<'.$node->getName().' '.implode(' ',$attributes).'>';
+ }
+
+ /**
+ * @return string path to the error message file
+ */
+ protected function getErrorMessageFile()
+ {
+ $lang=Prado::getPreferredLanguage();
+ $dir=dirname(__FILE__);
+ $msgFile=$dir.'/messages-'.$lang.'.txt';
+ if(!is_file($msgFile))
+ $msgFile=$dir.'/messages.txt';
+ return $msgFile;
+ }
+}
+
+class TSqlMapConfigurationException extends TSqlMapException
+{
+
+}
+
+class TUndefinedAttributeException extends TSqlMapConfigurationException
+{
+ public function __construct($attr, $node, $object, $file)
+ {
+ parent::__construct(
+ 'sqlmap_undefined_attribute', get_class($object), $attr,
+ htmlentities($node->asXml()),$file);
+ }
+}
+
+class TSqlMapExecutionException extends TSqlMapException
+{
+}
+
+class TSqlMapQueryExecutionException extends TSqlMapExecutionException
+{
+ protected $parent;
+ public function __construct($statement, $exception)
+ {
+ $this->parent = $exception;
+ parent::__construct('sqlmap_query_execution_error',
+ $statement->getID(), $exception->getMessage());
+ }
+}
+
+class TSqlMapUndefinedException extends TSqlMapException
+{
+
+}
+
+class TSqlMapDuplicateException extends TSqlMapException
+{
+}
+
+class TSqlMapConnectionException extends TSqlMapException
+{
+}
+
+class TInvalidPropertyException extends TSqlMapException
+{
+
+}
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php b/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php
new file mode 100644
index 00000000..b15a1d4c
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/TSqlMapPagedList.php
@@ -0,0 +1,158 @@
+<?php
+
+Prado::using('System.Collections.TPagedList');
+
+/**
+ * TSQLMapPagedList
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Web.UI.WebControls
+ * @since 3.0
+ */
+class TSqlMapPagedList extends TPagedList
+{
+ private $_statement;
+ private $_parameter;
+ private $_prevPageList;
+ private $_nextPageList;
+ private $_delegate=null;
+
+ public function __construct(IMappedStatement $statement,
+ $parameter, $pageSize, $delegate=null)
+ {
+ parent::__construct();
+ parent::setCustomPaging(true);
+ $this->initialize($statement,$parameter, $pageSize);
+ $this->_delegate=$delegate;
+ }
+
+ protected function initialize($statement, $parameter, $pageSize)
+ {
+ $this->_statement = $statement;
+ $this->_parameter = $parameter;
+ $this->setPageSize($pageSize);
+ $this->attachEventHandler('OnFetchData', array($this, 'fetchDataFromStatement'));
+ $this->gotoPage(0);
+ }
+
+ public function setCustomPaging($value)
+ {
+ throw new TDataMapperException('sqlmap_must_enable_custom_paging');
+ }
+
+ protected function fetchDataFromStatement($sender, $param)
+ {
+ $limit = $this->getOffsetAndLimit($param);
+ $connection = $this->_statement->getManager()->getDbConnection();
+ $data = $this->_statement->executeQueryForList($connection,
+ $this->_parameter, null, $limit[0], $limit[1], $this->_delegate);
+ $this->populateData($param, $data);
+ }
+
+ public function nextPage()
+ {
+ if($this->getIsNextPageAvailable())
+ return parent::nextPage();
+ else
+ return false;
+ }
+
+ public function previousPage()
+ {
+ if($this->getIsPreviousPageAvailable())
+ return parent::previousPage();
+ else
+ return false;
+ }
+
+ protected function populateData($param, $data)
+ {
+ $total = $data instanceof TList ? $data->getCount() : count($data);
+ $pageSize = $this->getPageSize();
+ if($total < 1)
+ {
+ $param->setData($data);
+ $this->_prevPageList = null;
+ $this->_nextPageList = null;
+ return;
+ }
+
+ if($param->getNewPageIndex() < 1)
+ {
+ $this->_prevPageList = null;
+ if($total <= $pageSize)
+ {
+ $param->setData($data);
+ $this->_nextPageList = null;
+ }
+ else
+ {
+ $param->setData($this->sublist($data, 0, $pageSize));
+ $this->_nextPageList = $this->sublist($data, $pageSize,$total);
+ }
+ }
+ else
+ {
+ if($total <= $pageSize)
+ {
+ $this->_prevPageList = $this->sublist($data, 0, $total);
+ $param->setData(array());
+ $this->_nextPageList = null;
+ }
+ else if($total <= $pageSize*2)
+ {
+ $this->_prevPageList = $this->sublist($data, 0, $pageSize);
+ $param->setData($this->sublist($data, $pageSize, $total));
+ $this->_nextPageList = null;
+ }
+ else
+ {
+ $this->_prevPageList = $this->sublist($data, 0, $pageSize);
+ $param->setData($this->sublist($data, $pageSize, $pageSize*2));
+ $this->_nextPageList = $this->sublist($data, $pageSize*2, $total);
+ }
+ }
+ }
+
+ protected function sublist($data, $from, $to)
+ {
+ $array = array();
+ for($i = $from; $i<$to; $i++)
+ $array[] = $data[$i];
+ return $array;
+ }
+
+ protected function getOffsetAndLimit($param)
+ {
+ $index = $param->getNewPageIndex();
+ $pageSize = $this->getPageSize();
+ if($index < 1)
+ return array($index, $pageSize*2);
+ else
+ return array(($index-1)*$pageSize, $pageSize*3);
+ }
+
+ public function getIsNextPageAvailable()
+ {
+ return !is_null($this->_nextPageList);
+ }
+
+ public function getIsPreviousPageAvailable()
+ {
+ return !is_null($this->_prevPageList);
+ }
+
+ public function getIsLastPage()
+ {
+ return is_null($this->_nextPageList)
+ || $this->_nextPageList->getCount() < 1;
+ }
+
+ public function getIsMiddlePage()
+ {
+ return !($this->getIsFirstPage() || $this->getIsLastPage());
+ }
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php b/framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php
new file mode 100644
index 00000000..c26c0401
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/TSqlMapTypeHandlerRegistry.php
@@ -0,0 +1,183 @@
+<?php
+
+/**
+ * TTypeHandlerFactory provides type handler classes to convert database field type
+ * to PHP types and vice versa.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.DataMapper
+ * @since 3.1
+ */
+class TSqlMapTypeHandlerRegistry
+{
+ private $_typeHandlers=array();
+
+ /**
+ * @param string database field type
+ * @return TSqlMapTypeHandler type handler for give database field type.
+ */
+ public function getDbTypeHandler($dbType='NULL')
+ {
+ foreach($this->_typeHandlers as $handler)
+ if($handler->getDbType()===$dbType)
+ return $handler;
+ }
+
+ /**
+ * @param string type handler class name
+ * @return TSqlMapTypeHandler type handler
+ */
+ public function getTypeHandler($class)
+ {
+ if(isset($this->_typeHandlers[$class]))
+ return $this->_typeHandlers[$class];
+ }
+
+ /**
+ * @param TSqlMapTypeHandler registers a new type handler
+ */
+ public function registerTypeHandler(TSqlMapTypeHandler $handler)
+ {
+ $this->_typeHandlers[$handler->getType()] = $handler;
+ }
+
+ /**
+ * Creates a new instance of a particular class (for PHP primative types,
+ * their corresponding default value for given type is used).
+ * @param string PHP type name
+ * @return mixed default type value, if no type is specified null is returned.
+ * @throws TSqlMapException if class name is not found.
+ */
+ public function createInstanceOf($type='')
+ {
+ if(strlen($type) > 0)
+ {
+ switch(strtolower($type))
+ {
+ case 'string': return '';
+ case 'array': return array();
+ case 'float': case 'double': case 'decimal': return 0.0;
+ case 'integer': case 'int': return 0;
+ case 'bool': case 'boolean': return false;
+ }
+
+ if(class_exists('Prado', false))
+ return Prado::createComponent($type);
+ else if(class_exists($type, false)) //NO auto loading
+ return new $type;
+ else
+ throw new TSqlMapException('sqlmap_unable_to_find_class', $type);
+ }
+ }
+
+ /**
+ * Converts the value to given type using PHP's settype() function.
+ * @param string PHP primative type.
+ * @param mixed value to be casted
+ * @return mixed type casted value.
+ */
+ public function convertToType($type, $value)
+ {
+ switch(strtolower($type))
+ {
+ case 'integer': case 'int':
+ $type = 'integer'; break;
+ case 'float': case 'double': case 'decimal':
+ $type = 'float'; break;
+ case 'boolean': case 'bool':
+ $type = 'boolean'; break;
+ case 'string' :
+ $type = 'string'; break;
+ default:
+ return $value;
+ }
+ settype($value, $type);
+ return $value;
+ }
+}
+
+/**
+ * A simple interface for implementing custom type handlers.
+ *
+ * Using this interface, you can implement a type handler that
+ * will perform customized processing before parameters are set
+ * on and after values are retrieved from the database.
+ * Using a custom type handler you can extend
+ * the framework to handle types that are not supported, or
+ * handle supported types in a different way. For example,
+ * you might use a custom type handler to implement proprietary
+ * BLOB support (e.g. Oracle), or you might use it to handle
+ * booleans using "Y" and "N" instead of the more typical 0/1.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.SqlMap.DataMapper
+ * @since 3.1
+ */
+abstract class TSqlMapTypeHandler extends TComponent
+{
+ private $_dbType='NULL';
+ private $_type;
+ /**
+ * @param string database field type.
+ */
+ public function setDbType($value)
+ {
+ $this->_dbType=$value;
+ }
+
+ /**
+ * @return string database field type.
+ */
+ public function getDbType()
+ {
+ return $this->_dbType;
+ }
+
+ public function getType()
+ {
+ if($this->_type===null)
+ return get_class($this);
+ else
+ return $this->_type;
+ }
+
+ public function setType($value)
+ {
+ $this->_type=$value;
+ }
+
+ /**
+ * Performs processing on a value before it is used to set
+ * the parameter of a IDbCommand.
+ * @param object The interface for setting the value.
+ * @param object The value to be set.
+ */
+ public abstract function getParameter($object);
+
+
+ /**
+ * Performs processing on a value before after it has been retrieved
+ * from a database
+ * @param object The interface for getting the value.
+ * @return mixed The processed value.
+ */
+ public abstract function getResult($string);
+
+
+ /**
+ * Casts the string representation of a value into a type recognized by
+ * this type handler. This method is used to translate nullValue values
+ * into types that can be appropriately compared. If your custom type handler
+ * cannot support nullValues, or if there is no reasonable string representation
+ * for this type (e.g. File type), you can simply return the String representation
+ * as it was passed in. It is not recommended to return null, unless null was passed
+ * in.
+ * @param array result row.
+ * @return mixed
+ */
+ public abstract function createNewInstance($row=null);
+}
+
+?> \ No newline at end of file
diff --git a/framework/Data/SqlMap/DataMapper/messages.txt b/framework/Data/SqlMap/DataMapper/messages.txt
new file mode 100644
index 00000000..0923d606
--- /dev/null
+++ b/framework/Data/SqlMap/DataMapper/messages.txt
@@ -0,0 +1,66 @@
+
+# TSqlMapManager.php
+sqlmap_contains_no_statement = Unable to find SQLMap statement '{0}'.
+sqlmap_already_contains_statement = Duplicate SQLMap statement found, '{0}' already exists.
+sqlmap_contains_no_result_map = Unable to find SQLMap result map '{0}'.
+sqlmap_already_contains_result_map = Duplicate SQLMap result map found, '{0}' already exists.
+sqlmap_contains_no_parameter_map = Unable to find SQLMap parameter map '{0}'.
+sqlmap_already_contains_parameter_map = Duplicate SQLMap parameter map found, '{0}' already exists.
+sqlmap_cache_model_already_exists = This SQLMap already contains cache model '{0}'.
+sqlmap_unable_to_find_cache_model = Unable to find cache model '{0}' in this SQLMap.
+
+# TTypeHandlerFactory.php
+sqlmap_dbtype_handler_not_found = Type handler for dbType='{0}' not found.
+sqlmap_type_handler_class_not_found = Type handler class '{0}' not found.
+sqlmap_unable_to_find_class = Unable to find class '{0}'.
+
+# TSqlMapXmlConfig.php
+sqlmap_node_class_undef = Missing attribute 'class' in tag '{0}' in configuration file '{1}'.
+sqlmap_unable_to_find_parent_result_map = Unable to find parent SQLMap result map named '{2}' in file {1} near '{0}'.
+sqlmap_undefined_discriminator = The <discriminator> tag not found in ResultMap '{0}' for sub-map '{2}' in file '{1}'.
+sqlmap_unable_to_find_parent_sql = Unable to find parent sql statement extension '{0}' near '{2}' in file {1}.
+sqlmap_invalid_property = Invalid property '{0}' for class '{1}' for tag '{2}' in configuration file '{3}'.
+
+
+# TInlineParameterMapParser.php
+sqlmap_undefined_property_inline_map = Invalid attribute '{0}' in '{3}' for inline parameter in statement '{2}' in file {1}.
+
+# TSqlMapCacheModel.php
+sqlmap_unable_to_find_implemenation = Unable to find cache implementation class '{0}'.
+
+# TResultProperty.php
+sqlmap_error_in_result_property_from_handler = For result map '{0}', error in getting result from type handler '{2}', with value '{1}'.
+
+# TParameterMap.php
+sqlmap_index_must_be_string_or_int = Invalid index '{0}', must be an integes or string to get a SqlMap parameter map property.
+sqlmap_unable_to_get_property_for_parameter = Unable to find property '{1}' in object '{2}' for parameter map '{0}'.
+sqlmap_error_in_parameter_from_handler = For parameter map '{0}', error in getting parameter from type handler '{2}' with value '{1}': '{3}'.
+
+# MISC
+sqlmap_type_handler_class_undef = Unable to find type handler class named '{1}' in sqlmap configuration file '{0}'.
+sqlmap_type_handler_callback_undef = Attributes 'type' and 'callback' must be defined in typeHandler tag in configuration file '{0}'.
+
+sqlmap_undefined_attribute = {0} attribute '{1}' is not defined for {2} in file {3}.
+sqlmap_unable_to_find_parent_parameter_map = Unable to find parent parameter map extension '{0}' in file {1}.
+sqlmap_unable_to_find_result_mapping = Unable to resolve SQLMap result mapping '{0}' in Result Map '{2}' using configuration file {1}.
+
+sqlmap_undefined_input_property = Undefined array index '{0}' in retrieving property in SQLMap parameter map '{1}'.
+sqlmap_can_not_instantiate = Type handler '{0}' can not create new objects.
+sqlmap_cannot_execute_query_for_map = SQLMap statement class {0} can not query for map in statement '{1}'.
+sqlmap_cannot_execute_update = SQLMap statement class {0} can not execute update query in statement '{1}'.
+sqlmap_cannot_execute_insert = SQLMap statement class {0} can not execute insert in statement '{1}'.
+sqlmap_cannot_execute_query_for_list = SQLMap statement class {0} can not query for list in statement '{1}'.
+sqlmap_cannot_execute_query_for_object = SQLMap statement class {0} can not query for object in statement '{1}'.
+sqlmap_execution_error_no_record = No record set found in executing statement '{0}': '{1}'.
+sqlmap_unable_to_create_new_instance = Unable to create a new instance of '{0}' using type hander '{1}' for SQLMap statement with ID '{2}'.
+sqlmap_invalid_property_type = Invalid object type, must be 'Object', unable to set property in path '{0}'.
+
+sqlmap_unable_to_find_config = Unable to find SQLMap configuration file '{0}'.
+sqlmap_unable_to_find_groupby = Unable to find data in result set with column '{0}' in result map with ID '{1}'.
+sqlmap_invalid_lazyload_list = Invalid type to lazy load, must specify a valid ListClass in statement '{0}'.
+sqlmap_unable_to_find_resource = 'Unable to find SQLMap configuration file '{0}'.
+sqlmap_query_execution_error = Error in executing SQLMap statement '{0}' : '{1}'.
+sqlmap_invalid_delegate = Invalid callback row delegate '{1}' in mapped statement '{0}'.
+sqlmap_invalid_prado_cache = Unable to find Prado cache module for SQLMap cache '{0}'.
+
+sqlmap_non_groupby_array_list_type = Expecting GroupBy property in result map '{0}' since {1}::{2} is an array or TList type. \ No newline at end of file