From 903ae8a581fac1e6917fc3e31d2ad8fb91df80c3 Mon Sep 17 00:00:00 2001
From: ctrlaltca <>
Date: Thu, 12 Jul 2012 11:21:01 +0000
Subject: standardize the use of unix eol; use svn properties to enforce native
 eol

---
 .../Exceptions/TActiveRecordException.php          |  98 +--
 .../Relations/TActiveRecordBelongsTo.php           | 274 ++++----
 .../Relations/TActiveRecordHasMany.php             | 240 +++----
 .../Relations/TActiveRecordHasManyAssociation.php  | 750 ++++++++++-----------
 .../ActiveRecord/Relations/TActiveRecordHasOne.php | 288 ++++----
 .../Relations/TActiveRecordRelation.php            | 508 +++++++-------
 .../Relations/TActiveRecordRelationContext.php     | 458 ++++++-------
 .../Data/ActiveRecord/Scaffold/TScaffoldBase.php   | 414 ++++++------
 .../ActiveRecord/Scaffold/TScaffoldEditView.php    | 616 ++++++++---------
 .../ActiveRecord/Scaffold/TScaffoldListView.php    | 608 ++++++++---------
 .../Data/ActiveRecord/Scaffold/TScaffoldSearch.php | 296 ++++----
 .../Data/ActiveRecord/Scaffold/TScaffoldView.php   | 286 ++++----
 .../Data/ActiveRecord/TActiveRecordConfig.php      | 400 +++++------
 .../Data/ActiveRecord/TActiveRecordCriteria.php    |  72 +-
 .../Data/ActiveRecord/TActiveRecordManager.php     | 324 ++++-----
 15 files changed, 2816 insertions(+), 2816 deletions(-)

(limited to 'framework/Data/ActiveRecord')

diff --git a/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php b/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php
index a5d6405e..bcfbfbcb 100644
--- a/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php
+++ b/framework/Data/ActiveRecord/Exceptions/TActiveRecordException.php
@@ -1,49 +1,49 @@
-<?php
-/**
- * TActiveRecordException class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord
- */
-
-/**
- * Base exception class for Active Records.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord
- * @since 3.1
- */
-class TActiveRecordException extends TDbException
-{
-	/**
-	 * @return string path to the error message file
-	 */
-	protected function getErrorMessageFile()
-	{
-		$lang=Prado::getPreferredLanguage();
-		$path = dirname(__FILE__);
-		$msgFile=$path.'/messages-'.$lang.'.txt';
-		if(!is_file($msgFile))
-			$msgFile=$path.'/messages.txt';
-		return $msgFile;
-	}
-}
-
-/**
- * TActiveRecordConfigurationException class.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord
- * @since 3.1
- */
-class TActiveRecordConfigurationException extends TActiveRecordException
-{
-
-}
-
+<?php
+/**
+ * TActiveRecordException class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ */
+
+/**
+ * Base exception class for Active Records.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ * @since 3.1
+ */
+class TActiveRecordException extends TDbException
+{
+	/**
+	 * @return string path to the error message file
+	 */
+	protected function getErrorMessageFile()
+	{
+		$lang=Prado::getPreferredLanguage();
+		$path = dirname(__FILE__);
+		$msgFile=$path.'/messages-'.$lang.'.txt';
+		if(!is_file($msgFile))
+			$msgFile=$path.'/messages.txt';
+		return $msgFile;
+	}
+}
+
+/**
+ * TActiveRecordConfigurationException class.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ * @since 3.1
+ */
+class TActiveRecordConfigurationException extends TActiveRecordException
+{
+
+}
+
diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php b/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php
index 68510042..f89660a6 100644
--- a/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php
+++ b/framework/Data/ActiveRecord/Relations/TActiveRecordBelongsTo.php
@@ -1,138 +1,138 @@
-<?php
-/**
- * TActiveRecordBelongsTo class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php
+/**
+ * TActiveRecordBelongsTo class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
  * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * Loads base active record relationship class.
- */
-Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
-
-/**
- * Implements the foreign key relationship (TActiveRecord::BELONGS_TO) between
- * the source objects and the related foreign object. Consider the
- * <b>entity</b> relationship between a Team and a Player.
- * <code>
- * +------+            +--------+
- * | Team | 1 <----- * | Player |
- * +------+            +--------+
- * </code>
- * Where one team may have 0 or more players and each player belongs to only
- * one team. We may model Team-Player <b>object</b> relationship as active record as follows.
- * <code>
- * class TeamRecord extends TActiveRecord
- * {
- *     // see TActiveRecordHasMany for detailed definition.
- * }
- * class PlayerRecord extends TActiveRecord
- * {
- *     const TABLE='player';
- *     public $player_id; //primary key
- *     public $team_name; //foreign key player.team_name <-> team.name
- * 	   public $age;
- *     public $team; //foreign object TeamRecord
- *
- *     public static $RELATIONS = array
- *     (
- *			'team' => array(self::BELONGS_TO, 'TeamRecord')
- *     );
- *
- *	   public static function finder($className=__CLASS__)
- *	   {
- *		   return parent::finder($className);
- *	   }
- * }
- * </code>
- * The static <tt>$RELATIONS</tt> property of PlayerRecord defines that the
- * property <tt>$team</tt> belongs to a <tt>TeamRecord</tt>.
- *
- * The team object may be fetched as follows.
- * <code>
- * $players = PlayerRecord::finder()->with_team()->findAll();
- * </code>
- * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
- * name, in this case, <tt>team</tt>) fetchs the corresponding TeamRecords using
- * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
- * arguments as other finder methods of TActiveRecord, e.g.
- * <tt>with_team('location = ?', 'Madrid')</tt>.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-class TActiveRecordBelongsTo extends TActiveRecordRelation
-{
-	/**
-	 * Get the foreign key index values from the results and make calls to the
-	 * database to find the corresponding foreign objects.
-	 * @param array original results.
-	 */
-	protected function collectForeignObjects(&$results)
-	{
-		$fkeys = $this->getRelationForeignKeys();
-
-		$properties = array_keys($fkeys);
-		$fields = array_values($fkeys);
-		$indexValues = $this->getIndexValues($properties, $results);
-		$fkObjects = $this->findForeignObjects($fields, $indexValues);
-		$this->populateResult($results,$properties,$fkObjects,$fields);
-	}
-
-	/**
-	 * @return array foreign key field names as key and object properties as value.
-	 * @since 3.1.2
-	 */
-	public function getRelationForeignKeys()
-	{
-		$fkObject = $this->getContext()->getForeignRecordFinder();
-		return $this->findForeignKeys($this->getSourceRecord(),$fkObject);
-	}
-
-	/**
-	 * Sets the foreign objects to the given property on the source object.
-	 * @param TActiveRecord source object.
-	 * @param array foreign objects.
-	 */
-	protected function setObjectProperty($source, $properties, &$collections)
-	{
-		$hash = $this->getObjectHash($source, $properties);
-		$prop = $this->getContext()->getProperty();
-		if(isset($collections[$hash]) && count($collections[$hash]) > 0)
-		{
-			if(count($collections[$hash]) > 1)
-				throw new TActiveRecordException('ar_belongs_to_multiple_result');
-			$source->$prop=$collections[$hash][0];
-		}
-		else
-			$source->$prop=null;
-	}
-
-	/**
-	 * Updates the source object first.
-	 * @return boolean true if all update are success (including if no update was required), false otherwise .
-	 */
-	public function updateAssociatedRecords()
-	{
-		$obj = $this->getContext()->getSourceRecord();
-		$fkObject = $obj->getColumnValue($this->getContext()->getProperty());
-		if($fkObject!==null)
-		{
-			$fkObject->save();
-			$source = $this->getSourceRecord();
-			$fkeys = $this->findForeignKeys($source, $fkObject);
-			foreach($fkeys as $srcKey => $fKey)
-				$source->setColumnValue($srcKey, $fkObject->getColumnValue($fKey));
-			return true;
-		}
-		return false;
-	}
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ */
+
+/**
+ * Loads base active record relationship class.
+ */
+Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
+
+/**
+ * Implements the foreign key relationship (TActiveRecord::BELONGS_TO) between
+ * the source objects and the related foreign object. Consider the
+ * <b>entity</b> relationship between a Team and a Player.
+ * <code>
+ * +------+            +--------+
+ * | Team | 1 <----- * | Player |
+ * +------+            +--------+
+ * </code>
+ * Where one team may have 0 or more players and each player belongs to only
+ * one team. We may model Team-Player <b>object</b> relationship as active record as follows.
+ * <code>
+ * class TeamRecord extends TActiveRecord
+ * {
+ *     // see TActiveRecordHasMany for detailed definition.
+ * }
+ * class PlayerRecord extends TActiveRecord
+ * {
+ *     const TABLE='player';
+ *     public $player_id; //primary key
+ *     public $team_name; //foreign key player.team_name <-> team.name
+ * 	   public $age;
+ *     public $team; //foreign object TeamRecord
+ *
+ *     public static $RELATIONS = array
+ *     (
+ *			'team' => array(self::BELONGS_TO, 'TeamRecord')
+ *     );
+ *
+ *	   public static function finder($className=__CLASS__)
+ *	   {
+ *		   return parent::finder($className);
+ *	   }
+ * }
+ * </code>
+ * The static <tt>$RELATIONS</tt> property of PlayerRecord defines that the
+ * property <tt>$team</tt> belongs to a <tt>TeamRecord</tt>.
+ *
+ * The team object may be fetched as follows.
+ * <code>
+ * $players = PlayerRecord::finder()->with_team()->findAll();
+ * </code>
+ * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
+ * name, in this case, <tt>team</tt>) fetchs the corresponding TeamRecords using
+ * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
+ * arguments as other finder methods of TActiveRecord, e.g.
+ * <tt>with_team('location = ?', 'Madrid')</tt>.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ * @since 3.1
+ */
+class TActiveRecordBelongsTo extends TActiveRecordRelation
+{
+	/**
+	 * Get the foreign key index values from the results and make calls to the
+	 * database to find the corresponding foreign objects.
+	 * @param array original results.
+	 */
+	protected function collectForeignObjects(&$results)
+	{
+		$fkeys = $this->getRelationForeignKeys();
+
+		$properties = array_keys($fkeys);
+		$fields = array_values($fkeys);
+		$indexValues = $this->getIndexValues($properties, $results);
+		$fkObjects = $this->findForeignObjects($fields, $indexValues);
+		$this->populateResult($results,$properties,$fkObjects,$fields);
+	}
+
+	/**
+	 * @return array foreign key field names as key and object properties as value.
+	 * @since 3.1.2
+	 */
+	public function getRelationForeignKeys()
+	{
+		$fkObject = $this->getContext()->getForeignRecordFinder();
+		return $this->findForeignKeys($this->getSourceRecord(),$fkObject);
+	}
+
+	/**
+	 * Sets the foreign objects to the given property on the source object.
+	 * @param TActiveRecord source object.
+	 * @param array foreign objects.
+	 */
+	protected function setObjectProperty($source, $properties, &$collections)
+	{
+		$hash = $this->getObjectHash($source, $properties);
+		$prop = $this->getContext()->getProperty();
+		if(isset($collections[$hash]) && count($collections[$hash]) > 0)
+		{
+			if(count($collections[$hash]) > 1)
+				throw new TActiveRecordException('ar_belongs_to_multiple_result');
+			$source->$prop=$collections[$hash][0];
+		}
+		else
+			$source->$prop=null;
+	}
+
+	/**
+	 * Updates the source object first.
+	 * @return boolean true if all update are success (including if no update was required), false otherwise .
+	 */
+	public function updateAssociatedRecords()
+	{
+		$obj = $this->getContext()->getSourceRecord();
+		$fkObject = $obj->getColumnValue($this->getContext()->getProperty());
+		if($fkObject!==null)
+		{
+			$fkObject->save();
+			$source = $this->getSourceRecord();
+			$fkeys = $this->findForeignKeys($source, $fkObject);
+			foreach($fkeys as $srcKey => $fKey)
+				$source->setColumnValue($srcKey, $fkObject->getColumnValue($fKey));
+			return true;
+		}
+		return false;
+	}
+}
+
diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php
index f5bc4438..6c5628b8 100644
--- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php
+++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasMany.php
@@ -1,121 +1,121 @@
-<?php
-/**
- * TActiveRecordHasMany class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php
+/**
+ * TActiveRecordHasMany class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
  * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * Loads base active record relations class.
- */
-Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
-
-/**
- * Implements TActiveRecord::HAS_MANY relationship between the source object having zero or
- * more foreign objects. Consider the <b>entity</b> relationship between a Team and a Player.
- * <code>
- * +------+            +--------+
- * | Team | 1 <----- * | Player |
- * +------+            +--------+
- * </code>
- * Where one team may have 0 or more players and each player belongs to only
- * one team. We may model Team-Player <b>object</b> relationship as active record as follows.
- * <code>
- * class TeamRecord extends TActiveRecord
- * {
- *     const TABLE='team';
- *     public $name; //primary key
- *     public $location;
- *
- *     public $players=array(); //list of players
- *
- *     public static $RELATIONS=array
- *     (
- *         'players' => array(self::HAS_MANY, 'PlayerRecord')
- *     );
- *
- *	   public static function finder($className=__CLASS__)
- *	   {
- *		   return parent::finder($className);
- *	   }
- * }
- * class PlayerRecord extends TActiveRecord
- * {
- *     // see TActiveRecordBelongsTo for detailed definition
- * }
- * </code>
- * The static <tt>$RELATIONS</tt> property of TeamRecord defines that the
- * property <tt>$players</tt> has many <tt>PlayerRecord</tt>s.
- *
- * The players list may be fetched as follows.
- * <code>
- * $team = TeamRecord::finder()->with_players()->findAll();
- * </code>
- * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
- * name, in this case, <tt>players</tt>) fetchs the corresponding PlayerRecords using
- * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
- * arguments as other finder methods of TActiveRecord, e.g. <tt>with_players('age < ?', 35)</tt>.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-class TActiveRecordHasMany extends TActiveRecordRelation
-{
-	/**
-	 * Get the foreign key index values from the results and make calls to the
-	 * database to find the corresponding foreign objects.
-	 * @param array original results.
-	 */
-	protected function collectForeignObjects(&$results)
-	{
-		$fkeys = $this->getRelationForeignKeys();
-
-		$properties = array_values($fkeys);
-		$fields = array_keys($fkeys);
-
-		$indexValues = $this->getIndexValues($properties, $results);
-		$fkObjects = $this->findForeignObjects($fields,$indexValues);
-		$this->populateResult($results,$properties,$fkObjects,$fields);
-	}
-
-	/**
-	 * @return array foreign key field names as key and object properties as value.
-	 * @since 3.1.2
-	 */
-	public function getRelationForeignKeys()
-	{
-		$fkObject = $this->getContext()->getForeignRecordFinder();
-		return $this->findForeignKeys($fkObject, $this->getSourceRecord());
-	}
-
-	/**
-	 * Updates the associated foreign objects.
-	 * @return boolean true if all update are success (including if no update was required), false otherwise .
-	 */
-	public function updateAssociatedRecords()
-	{
-		$obj = $this->getContext()->getSourceRecord();
-		$fkObjects = &$obj->{$this->getContext()->getProperty()};
-		$success=true;
-		if(($total = count($fkObjects))> 0)
-		{
-			$source = $this->getSourceRecord();
-			$fkeys = $this->findForeignKeys($fkObjects[0], $source);
-			for($i=0;$i<$total;$i++)
-			{
-				foreach($fkeys as $fKey => $srcKey)
-					$fkObjects[$i]->setColumnValue($fKey, $source->getColumnValue($srcKey));
-				$success = $fkObjects[$i]->save() && $success;
-			}
-		}
-		return $success;
-	}
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ */
+
+/**
+ * Loads base active record relations class.
+ */
+Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
+
+/**
+ * Implements TActiveRecord::HAS_MANY relationship between the source object having zero or
+ * more foreign objects. Consider the <b>entity</b> relationship between a Team and a Player.
+ * <code>
+ * +------+            +--------+
+ * | Team | 1 <----- * | Player |
+ * +------+            +--------+
+ * </code>
+ * Where one team may have 0 or more players and each player belongs to only
+ * one team. We may model Team-Player <b>object</b> relationship as active record as follows.
+ * <code>
+ * class TeamRecord extends TActiveRecord
+ * {
+ *     const TABLE='team';
+ *     public $name; //primary key
+ *     public $location;
+ *
+ *     public $players=array(); //list of players
+ *
+ *     public static $RELATIONS=array
+ *     (
+ *         'players' => array(self::HAS_MANY, 'PlayerRecord')
+ *     );
+ *
+ *	   public static function finder($className=__CLASS__)
+ *	   {
+ *		   return parent::finder($className);
+ *	   }
+ * }
+ * class PlayerRecord extends TActiveRecord
+ * {
+ *     // see TActiveRecordBelongsTo for detailed definition
+ * }
+ * </code>
+ * The static <tt>$RELATIONS</tt> property of TeamRecord defines that the
+ * property <tt>$players</tt> has many <tt>PlayerRecord</tt>s.
+ *
+ * The players list may be fetched as follows.
+ * <code>
+ * $team = TeamRecord::finder()->with_players()->findAll();
+ * </code>
+ * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
+ * name, in this case, <tt>players</tt>) fetchs the corresponding PlayerRecords using
+ * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
+ * arguments as other finder methods of TActiveRecord, e.g. <tt>with_players('age < ?', 35)</tt>.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ * @since 3.1
+ */
+class TActiveRecordHasMany extends TActiveRecordRelation
+{
+	/**
+	 * Get the foreign key index values from the results and make calls to the
+	 * database to find the corresponding foreign objects.
+	 * @param array original results.
+	 */
+	protected function collectForeignObjects(&$results)
+	{
+		$fkeys = $this->getRelationForeignKeys();
+
+		$properties = array_values($fkeys);
+		$fields = array_keys($fkeys);
+
+		$indexValues = $this->getIndexValues($properties, $results);
+		$fkObjects = $this->findForeignObjects($fields,$indexValues);
+		$this->populateResult($results,$properties,$fkObjects,$fields);
+	}
+
+	/**
+	 * @return array foreign key field names as key and object properties as value.
+	 * @since 3.1.2
+	 */
+	public function getRelationForeignKeys()
+	{
+		$fkObject = $this->getContext()->getForeignRecordFinder();
+		return $this->findForeignKeys($fkObject, $this->getSourceRecord());
+	}
+
+	/**
+	 * Updates the associated foreign objects.
+	 * @return boolean true if all update are success (including if no update was required), false otherwise .
+	 */
+	public function updateAssociatedRecords()
+	{
+		$obj = $this->getContext()->getSourceRecord();
+		$fkObjects = &$obj->{$this->getContext()->getProperty()};
+		$success=true;
+		if(($total = count($fkObjects))> 0)
+		{
+			$source = $this->getSourceRecord();
+			$fkeys = $this->findForeignKeys($fkObjects[0], $source);
+			for($i=0;$i<$total;$i++)
+			{
+				foreach($fkeys as $fKey => $srcKey)
+					$fkObjects[$i]->setColumnValue($fKey, $source->getColumnValue($srcKey));
+				$success = $fkObjects[$i]->save() && $success;
+			}
+		}
+		return $success;
+	}
+}
+
diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php
index fe368f9c..28ebb317 100644
--- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php
+++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasManyAssociation.php
@@ -1,376 +1,376 @@
-<?php
-/**
- * TActiveRecordHasManyAssociation class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php
+/**
+ * TActiveRecordHasManyAssociation class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
  * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * Loads base active record relations class.
- */
-Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
-
-/**
- * Implements the M-N (many to many) relationship via association table.
- * Consider the <b>entity</b> relationship between Articles and Categories
- * via the association table <tt>Article_Category</tt>.
- * <code>
- * +---------+            +------------------+            +----------+
- * | Article | * -----> * | Article_Category | * <----- * | Category |
- * +---------+            +------------------+            +----------+
- * </code>
- * Where one article may have 0 or more categories and each category may have 0
- * or more articles. We may model Article-Category <b>object</b> relationship
- * as active record as follows.
- * <code>
- * class ArticleRecord
- * {
- *     const TABLE='Article';
- *     public $article_id;
- *
- *     public $Categories=array(); //foreign object collection.
- *
- *     public static $RELATIONS = array
- *     (
- *         'Categories' => array(self::MANY_TO_MANY, 'CategoryRecord', 'Article_Category')
- *     );
- *
- *     public static function finder($className=__CLASS__)
- *     {
- *         return parent::finder($className);
- *     }
- * }
- * class CategoryRecord
- * {
- *     const TABLE='Category';
- *     public $category_id;
- *
- *     public $Articles=array();
- *
- *     public static $RELATIONS = array
- *     (
- *         'Articles' => array(self::MANY_TO_MANY, 'ArticleRecord', 'Article_Category')
- *     );
- *
- *     public static function finder($className=__CLASS__)
- *     {
- *         return parent::finder($className);
- *     }
- * }
- * </code>
- *
- * The static <tt>$RELATIONS</tt> property of ArticleRecord defines that the
- * property <tt>$Categories</tt> has many <tt>CategoryRecord</tt>s. Similar, the
- * static <tt>$RELATIONS</tt> property of CategoryRecord defines many ArticleRecords.
- *
- * The articles with categories list may be fetched as follows.
- * <code>
- * $articles = TeamRecord::finder()->withCategories()->findAll();
- * </code>
- * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
- * name, in this case, <tt>Categories</tt>) fetchs the corresponding CategoryRecords using
- * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
- * arguments as other finder methods of TActiveRecord.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-class TActiveRecordHasManyAssociation extends TActiveRecordRelation
-{
-	private $_association;
-	private $_sourceTable;
-	private $_foreignTable;
-	private $_association_columns=array();
-
-	/**
-	* Get the foreign key index values from the results and make calls to the
-	* database to find the corresponding foreign objects using association table.
-	* @param array original results.
-	*/
-	protected function collectForeignObjects(&$results)
-	{
-		list($sourceKeys, $foreignKeys) = $this->getRelationForeignKeys();
-		$properties = array_values($sourceKeys);
-		$indexValues = $this->getIndexValues($properties, $results);
-		$this->fetchForeignObjects($results, $foreignKeys,$indexValues,$sourceKeys);
-	}
-
-	/**
-	 * @return array 2 arrays of source keys and foreign keys from the association table.
-	 */
-	public function getRelationForeignKeys()
-	{
-		$association = $this->getAssociationTable();
-		$sourceKeys = $this->findForeignKeys($association, $this->getSourceRecord(), true);
-		$fkObject = $this->getContext()->getForeignRecordFinder();
-		$foreignKeys = $this->findForeignKeys($association, $fkObject);
-		return array($sourceKeys, $foreignKeys);
-	}
-
-	/**
-	 * @return TDbTableInfo association table information.
-	 */
-	protected function getAssociationTable()
-	{
-		if($this->_association===null)
-		{
-			$gateway = $this->getSourceRecord()->getRecordGateway();
-			$conn = $this->getSourceRecord()->getDbConnection();
-			//table name may include the fk column name separated with a dot.
-			$table = explode('.', $this->getContext()->getAssociationTable());
-			if(count($table)>1)
-			{
-				$columns = preg_replace('/^\((.*)\)/', '\1', $table[1]);
-				$this->_association_columns = preg_split('/\s*[, ]\*/',$columns);
-			}
-			$this->_association = $gateway->getTableInfo($conn, $table[0]);
-		}
-		return $this->_association;
-	}
-
-	/**
-	 * @return TDbTableInfo source table information.
-	 */
-	protected function getSourceTable()
-	{
-		if($this->_sourceTable===null)
-		{
-			$gateway = $this->getSourceRecord()->getRecordGateway();
-			$this->_sourceTable = $gateway->getRecordTableInfo($this->getSourceRecord());
-		}
-		return $this->_sourceTable;
-	}
-
-	/**
-	 * @return TDbTableInfo foreign table information.
-	 */
-	protected function getForeignTable()
-	{
-		if($this->_foreignTable===null)
-		{
-			$gateway = $this->getSourceRecord()->getRecordGateway();
-			$fkObject = $this->getContext()->getForeignRecordFinder();
-			$this->_foreignTable = $gateway->getRecordTableInfo($fkObject);
-		}
-		return $this->_foreignTable;
-	}
-
-	/**
-	 * @return TDataGatewayCommand
-	 */
-	protected function getCommandBuilder()
-	{
-		return $this->getSourceRecord()->getRecordGateway()->getCommand($this->getSourceRecord());
-	}
-
-	/**
-	 * @return TDataGatewayCommand
-	 */
-	protected function getForeignCommandBuilder()
-	{
-		$obj = $this->getContext()->getForeignRecordFinder();
-		return $this->getSourceRecord()->getRecordGateway()->getCommand($obj);
-	}
-
-
-	/**
-	 * Fetches the foreign objects using TActiveRecord::findAllByIndex()
-	 * @param array field names
-	 * @param array foreign key index values.
-	 */
-	protected function fetchForeignObjects(&$results,$foreignKeys,$indexValues,$sourceKeys)
-	{
-		$criteria = $this->getCriteria();
-		$finder = $this->getContext()->getForeignRecordFinder();
-		$type = get_class($finder);
-		$command = $this->createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys);
-		$srcProps = array_keys($sourceKeys);
-		$collections=array();
-		foreach($this->getCommandBuilder()->onExecuteCommand($command, $command->query()) as $row)
-		{
-			$hash = $this->getObjectHash($row, $srcProps);
-			foreach($srcProps as $column)
-				unset($row[$column]);
-			$obj = $this->createFkObject($type,$row,$foreignKeys);
-			$collections[$hash][] = $obj;
-		}
-		$this->setResultCollection($results, $collections, array_values($sourceKeys));
-	}
-
-	/**
-	 * @param string active record class name.
-	 * @param array row data
-	 * @param array foreign key column names
-	 * @return TActiveRecord
-	 */
-	protected function createFkObject($type,$row,$foreignKeys)
-	{
-		$obj = TActiveRecord::createRecord($type, $row);
-		if(count($this->_association_columns) > 0)
-		{
-			$i=0;
-			foreach($foreignKeys as $ref=>$fk)
-				$obj->setColumnValue($ref, $row[$this->_association_columns[$i++]]);
-		}
-		return $obj;
-	}
-
-	/**
-	 * @param TSqlCriteria
-	 * @param TTableInfo association table info
-	 * @param array field names
-	 * @param array field values
-	 */
-	public function createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys)
-	{
-		$innerJoin = $this->getAssociationJoin($foreignKeys,$indexValues,$sourceKeys);
-		$fkTable = $this->getForeignTable()->getTableFullName();
-		$srcColumns = $this->getSourceColumns($sourceKeys);
-		if(($where=$criteria->getCondition())===null)
-			$where='1=1';
-		$sql = "SELECT {$fkTable}.*, {$srcColumns} FROM {$fkTable} {$innerJoin} WHERE {$where}";
-
-		$parameters = $criteria->getParameters()->toArray();
-		$ordering = $criteria->getOrdersBy();
-		$limit = $criteria->getLimit();
-		$offset = $criteria->getOffset();
-
-		$builder = $this->getForeignCommandBuilder()->getBuilder();
-		$command = $builder->applyCriterias($sql,$parameters,$ordering,$limit,$offset);
-		$this->getCommandBuilder()->onCreateCommand($command, $criteria);
-		return $command;
-	}
-
-	/**
-	 * @param array source table column names.
-	 * @return string comma separated source column names.
-	 */
-	protected function getSourceColumns($sourceKeys)
-	{
-		$columns=array();
-		$table = $this->getAssociationTable();
-		$tableName = $table->getTableFullName();
-		$columnNames = array_merge(array_keys($sourceKeys),$this->_association_columns);
-		foreach($columnNames as $name)
-			$columns[] = $tableName.'.'.$table->getColumn($name)->getColumnName();
-		return implode(', ', $columns);
-	}
-
-	/**
-	 * SQL inner join for M-N relationship via association table.
-	 * @param array foreign table column key names.
-	 * @param array source table index values.
-	 * @param array source table column names.
-	 * @return string inner join condition for M-N relationship via association table.
-	 */
-	protected function getAssociationJoin($foreignKeys,$indexValues,$sourceKeys)
-	{
-		$refInfo= $this->getAssociationTable();
-		$fkInfo = $this->getForeignTable();
-
-		$refTable = $refInfo->getTableFullName();
-		$fkTable = $fkInfo->getTableFullName();
-
-		$joins = array();
-		$hasAssociationColumns = count($this->_association_columns) > 0;
-		$i=0;
-		foreach($foreignKeys as $ref=>$fk)
-		{
-			if($hasAssociationColumns)
-				$refField = $refInfo->getColumn($this->_association_columns[$i++])->getColumnName();
-			else
-				$refField = $refInfo->getColumn($ref)->getColumnName();
-			$fkField = $fkInfo->getColumn($fk)->getColumnName();
-			$joins[] = "{$fkTable}.{$fkField} = {$refTable}.{$refField}";
-		}
-		$joinCondition = implode(' AND ', $joins);
-		$index = $this->getCommandBuilder()->getIndexKeyCondition($refInfo,array_keys($sourceKeys), $indexValues);
-		return "INNER JOIN {$refTable} ON ({$joinCondition}) AND {$index}";
-	}
-
-	/**
-	 * Updates the associated foreign objects.
-	 * @return boolean true if all update are success (including if no update was required), false otherwise .
-	 */
-	public function updateAssociatedRecords()
-	{
-		$obj = $this->getContext()->getSourceRecord();
-		$fkObjects = &$obj->{$this->getContext()->getProperty()};
-		$success=true;
-		if(($total = count($fkObjects))> 0)
-		{
-			$source = $this->getSourceRecord();
-			$builder = $this->getAssociationTableCommandBuilder();
-			for($i=0;$i<$total;$i++)
-				$success = $fkObjects[$i]->save() && $success;
-			return $this->updateAssociationTable($obj, $fkObjects, $builder) && $success;
-		}
-		return $success;
-	}
-
-	/**
-	 * @return TDbCommandBuilder
-	 */
-	protected function getAssociationTableCommandBuilder()
-	{
-		$conn = $this->getContext()->getSourceRecord()->getDbConnection();
-		return $this->getAssociationTable()->createCommandBuilder($conn);
-	}
-
-	private function hasAssociationData($builder,$data)
-	{
-		$condition=array();
-		$table = $this->getAssociationTable();
-		foreach($data as $name=>$value)
-			$condition[] = $table->getColumn($name)->getColumnName().' = ?';
-		$command = $builder->createCountCommand(implode(' AND ', $condition),array_values($data));
-		$result = $this->getCommandBuilder()->onExecuteCommand($command, intval($command->queryScalar()));
-		return intval($result) > 0;
-	}
-
-	private function addAssociationData($builder,$data)
-	{
-		$command = $builder->createInsertCommand($data);
-		return $this->getCommandBuilder()->onExecuteCommand($command, $command->execute()) > 0;
-	}
-
-	private function updateAssociationTable($obj,$fkObjects, $builder)
-	{
-		$source = $this->getSourceRecordValues($obj);
-		$foreignKeys = $this->findForeignKeys($this->getAssociationTable(), $fkObjects[0]);
-		$success=true;
-		foreach($fkObjects as $fkObject)
-		{
-			$data = array_merge($source, $this->getForeignObjectValues($foreignKeys,$fkObject));
-			if(!$this->hasAssociationData($builder,$data))
-				$success = $this->addAssociationData($builder,$data) && $success;
-		}
-		return $success;
-	}
-
-	private function getSourceRecordValues($obj)
-	{
-		$sourceKeys = $this->findForeignKeys($this->getAssociationTable(), $obj);
-		$indexValues = $this->getIndexValues(array_values($sourceKeys), $obj);
-		$data = array();
-		$i=0;
-		foreach($sourceKeys as $name=>$srcKey)
-			$data[$name] = $indexValues[0][$i++];
-		return $data;
-	}
-
-	private function getForeignObjectValues($foreignKeys,$fkObject)
-	{
-		$data=array();
-		foreach($foreignKeys as $name=>$fKey)
-			$data[$name] = $fkObject->getColumnValue($fKey);
-		return $data;
-	}
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ */
+
+/**
+ * Loads base active record relations class.
+ */
+Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
+
+/**
+ * Implements the M-N (many to many) relationship via association table.
+ * Consider the <b>entity</b> relationship between Articles and Categories
+ * via the association table <tt>Article_Category</tt>.
+ * <code>
+ * +---------+            +------------------+            +----------+
+ * | Article | * -----> * | Article_Category | * <----- * | Category |
+ * +---------+            +------------------+            +----------+
+ * </code>
+ * Where one article may have 0 or more categories and each category may have 0
+ * or more articles. We may model Article-Category <b>object</b> relationship
+ * as active record as follows.
+ * <code>
+ * class ArticleRecord
+ * {
+ *     const TABLE='Article';
+ *     public $article_id;
+ *
+ *     public $Categories=array(); //foreign object collection.
+ *
+ *     public static $RELATIONS = array
+ *     (
+ *         'Categories' => array(self::MANY_TO_MANY, 'CategoryRecord', 'Article_Category')
+ *     );
+ *
+ *     public static function finder($className=__CLASS__)
+ *     {
+ *         return parent::finder($className);
+ *     }
+ * }
+ * class CategoryRecord
+ * {
+ *     const TABLE='Category';
+ *     public $category_id;
+ *
+ *     public $Articles=array();
+ *
+ *     public static $RELATIONS = array
+ *     (
+ *         'Articles' => array(self::MANY_TO_MANY, 'ArticleRecord', 'Article_Category')
+ *     );
+ *
+ *     public static function finder($className=__CLASS__)
+ *     {
+ *         return parent::finder($className);
+ *     }
+ * }
+ * </code>
+ *
+ * The static <tt>$RELATIONS</tt> property of ArticleRecord defines that the
+ * property <tt>$Categories</tt> has many <tt>CategoryRecord</tt>s. Similar, the
+ * static <tt>$RELATIONS</tt> property of CategoryRecord defines many ArticleRecords.
+ *
+ * The articles with categories list may be fetched as follows.
+ * <code>
+ * $articles = TeamRecord::finder()->withCategories()->findAll();
+ * </code>
+ * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
+ * name, in this case, <tt>Categories</tt>) fetchs the corresponding CategoryRecords using
+ * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
+ * arguments as other finder methods of TActiveRecord.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ * @since 3.1
+ */
+class TActiveRecordHasManyAssociation extends TActiveRecordRelation
+{
+	private $_association;
+	private $_sourceTable;
+	private $_foreignTable;
+	private $_association_columns=array();
+
+	/**
+	* Get the foreign key index values from the results and make calls to the
+	* database to find the corresponding foreign objects using association table.
+	* @param array original results.
+	*/
+	protected function collectForeignObjects(&$results)
+	{
+		list($sourceKeys, $foreignKeys) = $this->getRelationForeignKeys();
+		$properties = array_values($sourceKeys);
+		$indexValues = $this->getIndexValues($properties, $results);
+		$this->fetchForeignObjects($results, $foreignKeys,$indexValues,$sourceKeys);
+	}
+
+	/**
+	 * @return array 2 arrays of source keys and foreign keys from the association table.
+	 */
+	public function getRelationForeignKeys()
+	{
+		$association = $this->getAssociationTable();
+		$sourceKeys = $this->findForeignKeys($association, $this->getSourceRecord(), true);
+		$fkObject = $this->getContext()->getForeignRecordFinder();
+		$foreignKeys = $this->findForeignKeys($association, $fkObject);
+		return array($sourceKeys, $foreignKeys);
+	}
+
+	/**
+	 * @return TDbTableInfo association table information.
+	 */
+	protected function getAssociationTable()
+	{
+		if($this->_association===null)
+		{
+			$gateway = $this->getSourceRecord()->getRecordGateway();
+			$conn = $this->getSourceRecord()->getDbConnection();
+			//table name may include the fk column name separated with a dot.
+			$table = explode('.', $this->getContext()->getAssociationTable());
+			if(count($table)>1)
+			{
+				$columns = preg_replace('/^\((.*)\)/', '\1', $table[1]);
+				$this->_association_columns = preg_split('/\s*[, ]\*/',$columns);
+			}
+			$this->_association = $gateway->getTableInfo($conn, $table[0]);
+		}
+		return $this->_association;
+	}
+
+	/**
+	 * @return TDbTableInfo source table information.
+	 */
+	protected function getSourceTable()
+	{
+		if($this->_sourceTable===null)
+		{
+			$gateway = $this->getSourceRecord()->getRecordGateway();
+			$this->_sourceTable = $gateway->getRecordTableInfo($this->getSourceRecord());
+		}
+		return $this->_sourceTable;
+	}
+
+	/**
+	 * @return TDbTableInfo foreign table information.
+	 */
+	protected function getForeignTable()
+	{
+		if($this->_foreignTable===null)
+		{
+			$gateway = $this->getSourceRecord()->getRecordGateway();
+			$fkObject = $this->getContext()->getForeignRecordFinder();
+			$this->_foreignTable = $gateway->getRecordTableInfo($fkObject);
+		}
+		return $this->_foreignTable;
+	}
+
+	/**
+	 * @return TDataGatewayCommand
+	 */
+	protected function getCommandBuilder()
+	{
+		return $this->getSourceRecord()->getRecordGateway()->getCommand($this->getSourceRecord());
+	}
+
+	/**
+	 * @return TDataGatewayCommand
+	 */
+	protected function getForeignCommandBuilder()
+	{
+		$obj = $this->getContext()->getForeignRecordFinder();
+		return $this->getSourceRecord()->getRecordGateway()->getCommand($obj);
+	}
+
+
+	/**
+	 * Fetches the foreign objects using TActiveRecord::findAllByIndex()
+	 * @param array field names
+	 * @param array foreign key index values.
+	 */
+	protected function fetchForeignObjects(&$results,$foreignKeys,$indexValues,$sourceKeys)
+	{
+		$criteria = $this->getCriteria();
+		$finder = $this->getContext()->getForeignRecordFinder();
+		$type = get_class($finder);
+		$command = $this->createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys);
+		$srcProps = array_keys($sourceKeys);
+		$collections=array();
+		foreach($this->getCommandBuilder()->onExecuteCommand($command, $command->query()) as $row)
+		{
+			$hash = $this->getObjectHash($row, $srcProps);
+			foreach($srcProps as $column)
+				unset($row[$column]);
+			$obj = $this->createFkObject($type,$row,$foreignKeys);
+			$collections[$hash][] = $obj;
+		}
+		$this->setResultCollection($results, $collections, array_values($sourceKeys));
+	}
+
+	/**
+	 * @param string active record class name.
+	 * @param array row data
+	 * @param array foreign key column names
+	 * @return TActiveRecord
+	 */
+	protected function createFkObject($type,$row,$foreignKeys)
+	{
+		$obj = TActiveRecord::createRecord($type, $row);
+		if(count($this->_association_columns) > 0)
+		{
+			$i=0;
+			foreach($foreignKeys as $ref=>$fk)
+				$obj->setColumnValue($ref, $row[$this->_association_columns[$i++]]);
+		}
+		return $obj;
+	}
+
+	/**
+	 * @param TSqlCriteria
+	 * @param TTableInfo association table info
+	 * @param array field names
+	 * @param array field values
+	 */
+	public function createCommand($criteria, $foreignKeys,$indexValues,$sourceKeys)
+	{
+		$innerJoin = $this->getAssociationJoin($foreignKeys,$indexValues,$sourceKeys);
+		$fkTable = $this->getForeignTable()->getTableFullName();
+		$srcColumns = $this->getSourceColumns($sourceKeys);
+		if(($where=$criteria->getCondition())===null)
+			$where='1=1';
+		$sql = "SELECT {$fkTable}.*, {$srcColumns} FROM {$fkTable} {$innerJoin} WHERE {$where}";
+
+		$parameters = $criteria->getParameters()->toArray();
+		$ordering = $criteria->getOrdersBy();
+		$limit = $criteria->getLimit();
+		$offset = $criteria->getOffset();
+
+		$builder = $this->getForeignCommandBuilder()->getBuilder();
+		$command = $builder->applyCriterias($sql,$parameters,$ordering,$limit,$offset);
+		$this->getCommandBuilder()->onCreateCommand($command, $criteria);
+		return $command;
+	}
+
+	/**
+	 * @param array source table column names.
+	 * @return string comma separated source column names.
+	 */
+	protected function getSourceColumns($sourceKeys)
+	{
+		$columns=array();
+		$table = $this->getAssociationTable();
+		$tableName = $table->getTableFullName();
+		$columnNames = array_merge(array_keys($sourceKeys),$this->_association_columns);
+		foreach($columnNames as $name)
+			$columns[] = $tableName.'.'.$table->getColumn($name)->getColumnName();
+		return implode(', ', $columns);
+	}
+
+	/**
+	 * SQL inner join for M-N relationship via association table.
+	 * @param array foreign table column key names.
+	 * @param array source table index values.
+	 * @param array source table column names.
+	 * @return string inner join condition for M-N relationship via association table.
+	 */
+	protected function getAssociationJoin($foreignKeys,$indexValues,$sourceKeys)
+	{
+		$refInfo= $this->getAssociationTable();
+		$fkInfo = $this->getForeignTable();
+
+		$refTable = $refInfo->getTableFullName();
+		$fkTable = $fkInfo->getTableFullName();
+
+		$joins = array();
+		$hasAssociationColumns = count($this->_association_columns) > 0;
+		$i=0;
+		foreach($foreignKeys as $ref=>$fk)
+		{
+			if($hasAssociationColumns)
+				$refField = $refInfo->getColumn($this->_association_columns[$i++])->getColumnName();
+			else
+				$refField = $refInfo->getColumn($ref)->getColumnName();
+			$fkField = $fkInfo->getColumn($fk)->getColumnName();
+			$joins[] = "{$fkTable}.{$fkField} = {$refTable}.{$refField}";
+		}
+		$joinCondition = implode(' AND ', $joins);
+		$index = $this->getCommandBuilder()->getIndexKeyCondition($refInfo,array_keys($sourceKeys), $indexValues);
+		return "INNER JOIN {$refTable} ON ({$joinCondition}) AND {$index}";
+	}
+
+	/**
+	 * Updates the associated foreign objects.
+	 * @return boolean true if all update are success (including if no update was required), false otherwise .
+	 */
+	public function updateAssociatedRecords()
+	{
+		$obj = $this->getContext()->getSourceRecord();
+		$fkObjects = &$obj->{$this->getContext()->getProperty()};
+		$success=true;
+		if(($total = count($fkObjects))> 0)
+		{
+			$source = $this->getSourceRecord();
+			$builder = $this->getAssociationTableCommandBuilder();
+			for($i=0;$i<$total;$i++)
+				$success = $fkObjects[$i]->save() && $success;
+			return $this->updateAssociationTable($obj, $fkObjects, $builder) && $success;
+		}
+		return $success;
+	}
+
+	/**
+	 * @return TDbCommandBuilder
+	 */
+	protected function getAssociationTableCommandBuilder()
+	{
+		$conn = $this->getContext()->getSourceRecord()->getDbConnection();
+		return $this->getAssociationTable()->createCommandBuilder($conn);
+	}
+
+	private function hasAssociationData($builder,$data)
+	{
+		$condition=array();
+		$table = $this->getAssociationTable();
+		foreach($data as $name=>$value)
+			$condition[] = $table->getColumn($name)->getColumnName().' = ?';
+		$command = $builder->createCountCommand(implode(' AND ', $condition),array_values($data));
+		$result = $this->getCommandBuilder()->onExecuteCommand($command, intval($command->queryScalar()));
+		return intval($result) > 0;
+	}
+
+	private function addAssociationData($builder,$data)
+	{
+		$command = $builder->createInsertCommand($data);
+		return $this->getCommandBuilder()->onExecuteCommand($command, $command->execute()) > 0;
+	}
+
+	private function updateAssociationTable($obj,$fkObjects, $builder)
+	{
+		$source = $this->getSourceRecordValues($obj);
+		$foreignKeys = $this->findForeignKeys($this->getAssociationTable(), $fkObjects[0]);
+		$success=true;
+		foreach($fkObjects as $fkObject)
+		{
+			$data = array_merge($source, $this->getForeignObjectValues($foreignKeys,$fkObject));
+			if(!$this->hasAssociationData($builder,$data))
+				$success = $this->addAssociationData($builder,$data) && $success;
+		}
+		return $success;
+	}
+
+	private function getSourceRecordValues($obj)
+	{
+		$sourceKeys = $this->findForeignKeys($this->getAssociationTable(), $obj);
+		$indexValues = $this->getIndexValues(array_values($sourceKeys), $obj);
+		$data = array();
+		$i=0;
+		foreach($sourceKeys as $name=>$srcKey)
+			$data[$name] = $indexValues[0][$i++];
+		return $data;
+	}
+
+	private function getForeignObjectValues($foreignKeys,$fkObject)
+	{
+		$data=array();
+		foreach($foreignKeys as $name=>$fKey)
+			$data[$name] = $fkObject->getColumnValue($fKey);
+		return $data;
+	}
+}
diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php
index bc895901..a762e196 100644
--- a/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php
+++ b/framework/Data/ActiveRecord/Relations/TActiveRecordHasOne.php
@@ -1,145 +1,145 @@
-<?php
-/**
- * TActiveRecordHasOne class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php
+/**
+ * TActiveRecordHasOne class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
  * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * Loads base active record relationship class.
- */
-Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
-
-/**
- * TActiveRecordHasOne models the object relationship that a record (the source object)
- * property is an instance of foreign record object having a foreign key
- * related to the source object. The HAS_ONE relation is very similar to the
- * HAS_MANY relationship (in fact, it is equivalent in the entities relationship point of view).
- *
- * The difference of HAS_ONE from HAS_MANY is that the foreign object is singular.
- * That is, HAS_MANY will return a collection of records while HAS_ONE returns the
- * corresponding record.
- *
- * Consider the <b>entity</b> relationship between a Car and a Engine.
- * <code>
- * +-----+            +--------+
- * | Car | 1 <----- 1 | Engine |
- * +-----+            +--------+
- * </code>
- * Where each engine belongs to only one car, that is, the Engine entity has
- * a foreign key to the Car's primary key. We may model
- * Engine-Car <b>object</b> relationship as active record as follows.
- * <code>
- * class CarRecord extends TActiveRecord
- * {
- *     const TABLE='car';
- *     public $car_id; //primary key
- *     public $colour;
- *
- *     public $engine; //engine foreign object
- *
- *     public static $RELATIONS=array
- *     (
- *         'engine' => array(self::HAS_ONE, 'EngineRecord')
- *     );
- *
- *	   public static function finder($className=__CLASS__)
- *	   {
- *		   return parent::finder($className);
- *	   }
- * }
- * class EngineRecord extends TActiveRecord
- * {
- *     const TABLE='engine';
- *     public $engine_id;
- *     public $capacity;
- *     public $car_id; //foreign key to cars
- *
- *	   public static function finder($className=__CLASS__)
- *	   {
- *		   return parent::finder($className);
- *	   }
- * }
- * </code>
- * The static <tt>$RELATIONS</tt> property of CarRecord defines that the
- * property <tt>$engine</tt> that will reference an <tt>EngineRecord</tt> instance.
- *
- * The car record with engine property list may be fetched as follows.
- * <code>
- * $cars = CarRecord::finder()->with_engine()->findAll();
- * </code>
- * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
- * name, in this case, <tt>engine</tt>) fetchs the corresponding EngineRecords using
- * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
- * arguments as other finder methods of TActiveRecord, e.g. <tt>with_engine('capacity < ?', 3.8)</tt>.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-class TActiveRecordHasOne extends TActiveRecordRelation
-{
-	/**
-	 * Get the foreign key index values from the results and make calls to the
-	 * database to find the corresponding foreign objects.
-	 * @param array original results.
-	 */
-	protected function collectForeignObjects(&$results)
-	{
-		$fkeys = $this->getRelationForeignKeys();
-		$properties = array_values($fkeys);
-		$fields = array_keys($fkeys);
-
-		$indexValues = $this->getIndexValues($properties, $results);
-		$fkObjects = $this->findForeignObjects($fields,$indexValues);
-		$this->populateResult($results,$properties,$fkObjects,$fields);
-	}
-
-	/**
-	 * @return array foreign key field names as key and object properties as value.
-	 * @since 3.1.2
-	 */
-	public function getRelationForeignKeys()
-	{
-		$fkObject = $this->getContext()->getForeignRecordFinder();
-		return $this->findForeignKeys($fkObject, $this->getSourceRecord());
-	}
-
-	/**
-	 * Sets the foreign objects to the given property on the source object.
-	 * @param TActiveRecord source object.
-	 * @param array foreign objects.
-	 */
-	protected function setObjectProperty($source, $properties, &$collections)
-	{
-		$hash = $this->getObjectHash($source, $properties);
-		$prop = $this->getContext()->getProperty();
-		if(isset($collections[$hash]) && count($collections[$hash]) > 0)
-		{
-			if(count($collections[$hash]) > 1)
-				throw new TActiveRecordException('ar_belongs_to_multiple_result');
-			$source->setColumnValue($prop, $collections[$hash][0]);
-		}
-	}
-
-	/**
-	 * Updates the associated foreign objects.
-	 * @return boolean true if all update are success (including if no update was required), false otherwise .
-	 */
-	public function updateAssociatedRecords()
-	{
-		$fkObject = $this->getContext()->getPropertyValue();
-		$source = $this->getSourceRecord();
-		$fkeys = $this->findForeignKeys($fkObject, $source);
-		foreach($fkeys as $fKey => $srcKey)
-			$fkObject->setColumnValue($fKey, $source->getColumnValue($srcKey));
-		return $fkObject->save();
-	}
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ */
+
+/**
+ * Loads base active record relationship class.
+ */
+Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelation');
+
+/**
+ * TActiveRecordHasOne models the object relationship that a record (the source object)
+ * property is an instance of foreign record object having a foreign key
+ * related to the source object. The HAS_ONE relation is very similar to the
+ * HAS_MANY relationship (in fact, it is equivalent in the entities relationship point of view).
+ *
+ * The difference of HAS_ONE from HAS_MANY is that the foreign object is singular.
+ * That is, HAS_MANY will return a collection of records while HAS_ONE returns the
+ * corresponding record.
+ *
+ * Consider the <b>entity</b> relationship between a Car and a Engine.
+ * <code>
+ * +-----+            +--------+
+ * | Car | 1 <----- 1 | Engine |
+ * +-----+            +--------+
+ * </code>
+ * Where each engine belongs to only one car, that is, the Engine entity has
+ * a foreign key to the Car's primary key. We may model
+ * Engine-Car <b>object</b> relationship as active record as follows.
+ * <code>
+ * class CarRecord extends TActiveRecord
+ * {
+ *     const TABLE='car';
+ *     public $car_id; //primary key
+ *     public $colour;
+ *
+ *     public $engine; //engine foreign object
+ *
+ *     public static $RELATIONS=array
+ *     (
+ *         'engine' => array(self::HAS_ONE, 'EngineRecord')
+ *     );
+ *
+ *	   public static function finder($className=__CLASS__)
+ *	   {
+ *		   return parent::finder($className);
+ *	   }
+ * }
+ * class EngineRecord extends TActiveRecord
+ * {
+ *     const TABLE='engine';
+ *     public $engine_id;
+ *     public $capacity;
+ *     public $car_id; //foreign key to cars
+ *
+ *	   public static function finder($className=__CLASS__)
+ *	   {
+ *		   return parent::finder($className);
+ *	   }
+ * }
+ * </code>
+ * The static <tt>$RELATIONS</tt> property of CarRecord defines that the
+ * property <tt>$engine</tt> that will reference an <tt>EngineRecord</tt> instance.
+ *
+ * The car record with engine property list may be fetched as follows.
+ * <code>
+ * $cars = CarRecord::finder()->with_engine()->findAll();
+ * </code>
+ * The method <tt>with_xxx()</tt> (where <tt>xxx</tt> is the relationship property
+ * name, in this case, <tt>engine</tt>) fetchs the corresponding EngineRecords using
+ * a second query (not by using a join). The <tt>with_xxx()</tt> accepts the same
+ * arguments as other finder methods of TActiveRecord, e.g. <tt>with_engine('capacity < ?', 3.8)</tt>.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ * @since 3.1
+ */
+class TActiveRecordHasOne extends TActiveRecordRelation
+{
+	/**
+	 * Get the foreign key index values from the results and make calls to the
+	 * database to find the corresponding foreign objects.
+	 * @param array original results.
+	 */
+	protected function collectForeignObjects(&$results)
+	{
+		$fkeys = $this->getRelationForeignKeys();
+		$properties = array_values($fkeys);
+		$fields = array_keys($fkeys);
+
+		$indexValues = $this->getIndexValues($properties, $results);
+		$fkObjects = $this->findForeignObjects($fields,$indexValues);
+		$this->populateResult($results,$properties,$fkObjects,$fields);
+	}
+
+	/**
+	 * @return array foreign key field names as key and object properties as value.
+	 * @since 3.1.2
+	 */
+	public function getRelationForeignKeys()
+	{
+		$fkObject = $this->getContext()->getForeignRecordFinder();
+		return $this->findForeignKeys($fkObject, $this->getSourceRecord());
+	}
+
+	/**
+	 * Sets the foreign objects to the given property on the source object.
+	 * @param TActiveRecord source object.
+	 * @param array foreign objects.
+	 */
+	protected function setObjectProperty($source, $properties, &$collections)
+	{
+		$hash = $this->getObjectHash($source, $properties);
+		$prop = $this->getContext()->getProperty();
+		if(isset($collections[$hash]) && count($collections[$hash]) > 0)
+		{
+			if(count($collections[$hash]) > 1)
+				throw new TActiveRecordException('ar_belongs_to_multiple_result');
+			$source->setColumnValue($prop, $collections[$hash][0]);
+		}
+	}
+
+	/**
+	 * Updates the associated foreign objects.
+	 * @return boolean true if all update are success (including if no update was required), false otherwise .
+	 */
+	public function updateAssociatedRecords()
+	{
+		$fkObject = $this->getContext()->getPropertyValue();
+		$source = $this->getSourceRecord();
+		$fkeys = $this->findForeignKeys($fkObject, $source);
+		foreach($fkeys as $fKey => $srcKey)
+			$fkObject->setColumnValue($fKey, $source->getColumnValue($srcKey));
+		return $fkObject->save();
+	}
+}
+
diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php
index 39482635..c59532d9 100644
--- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php
+++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelation.php
@@ -1,254 +1,254 @@
-<?php
-/**
- * TActiveRecordRelation class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * Load active record relationship context.
- */
-Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext');
-
-/**
- * Base class for active record relationships.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-abstract class TActiveRecordRelation
-{
-	private $_context;
-	private $_criteria;
-
-	public function __construct(TActiveRecordRelationContext $context, $criteria)
-	{
-		$this->_context = $context;
-		$this->_criteria = $criteria;
-	}
-
-	/**
-	 * @return TActiveRecordRelationContext
-	 */
-	protected function getContext()
-	{
-		return $this->_context;
-	}
-
-	/**
-	 * @return TActiveRecordCriteria
-	 */
-	protected function getCriteria()
-	{
-		return $this->_criteria;
-	}
-
-	/**
-	 * @return TActiveRecord
-	 */
-	protected function getSourceRecord()
-	{
-		return $this->getContext()->getSourceRecord();
-	}
-
-	abstract protected function collectForeignObjects(&$results);
-
-	/**
-	 * Dispatch the method calls to the source record finder object. When
-	 * an instance of TActiveRecord or an array of TActiveRecord is returned
-	 * the corresponding foreign objects are also fetched and assigned.
-	 *
-	 * Multiple relationship calls can be chain together.
-	 *
-	 * @param string method name called
-	 * @param array method arguments
-	 * @return mixed TActiveRecord or array of TActiveRecord results depending on the method called.
-	 */
-	public function __call($method,$args)
-	{
-		static $stack=array();
-
-		$results = call_user_func_array(array($this->getSourceRecord(),$method),$args);
-		$validArray = is_array($results) && count($results) > 0;
-		if($validArray || $results instanceof ArrayAccess || $results instanceof TActiveRecord)
-		{
-			$this->collectForeignObjects($results);
-			while($obj = array_pop($stack))
-				$obj->collectForeignObjects($results);
-		}
-		else if($results instanceof TActiveRecordRelation)
-			$stack[] = $this; //call it later
-		else if($results === null || !$validArray)
-			$stack = array();
-		return $results;
-	}
-
-	/**
-	 * Fetch results for current relationship.
-	 * @return boolean always true.
-	 */
-	public function fetchResultsInto($obj)
-	{
-		$this->collectForeignObjects($obj);
-		return true;
-	}
-
-	/**
-	 * Returns foreign keys in $fromRecord with source column names as key
-	 * and foreign column names in the corresponding $matchesRecord as value.
-	 * The method returns the first matching foreign key between these 2 records.
-	 * @param TActiveRecord $fromRecord
-	 * @param TActiveRecord $matchesRecord
-	 * @return array foreign keys with source column names as key and foreign column names as value.
-	 */
-	protected function findForeignKeys($from, $matchesRecord, $loose=false)
-	{
-		$gateway = $matchesRecord->getRecordGateway();
-		$recordTableInfo = $gateway->getRecordTableInfo($matchesRecord);
-		$matchingTableName = strtolower($recordTableInfo->getTableName());
-		$matchingFullTableName = strtolower($recordTableInfo->getTableFullName());
-		$tableInfo=$from;
-		if($from instanceof TActiveRecord)
-			$tableInfo = $gateway->getRecordTableInfo($from);
-		//find first non-empty FK
-		foreach($tableInfo->getForeignKeys() as $fkeys)
-		{
-			$fkTable = strtolower($fkeys['table']);
-			if($fkTable===$matchingTableName || $fkTable===$matchingFullTableName)
-			{
-				$hasFkField = !$loose && $this->getContext()->hasFkField();
-				$key = $hasFkField ? $this->getFkFields($fkeys['keys']) : $fkeys['keys'];
-				if(!empty($key))
-					return $key;
-			}
-		}
-
-		//none found
-		$matching = $gateway->getRecordTableInfo($matchesRecord)->getTableFullName();
-		throw new TActiveRecordException('ar_relations_missing_fk',
-			$tableInfo->getTableFullName(), $matching);
-	}
-
-	/**
-	 * @return array foreign key field names as key and object properties as value.
-	 * @since 3.1.2
-	 */
-	abstract public function getRelationForeignKeys();
-
-	/**
-	 * Find matching foreign key fields from the 3rd element of an entry in TActiveRecord::$RELATION.
-	 * Assume field names consist of [\w-] character sets. Prefix to the field names ending with a dot
-	 * are ignored.
-	 */
-	private function getFkFields($fkeys)
-	{
-		$matching = array();
-		preg_match_all('/\s*(\S+\.)?([\w-]+)\s*/', $this->getContext()->getFkField(), $matching);
-		$fields = array();
-		foreach($fkeys as $fkName => $field)
-		{
-			if(in_array($fkName, $matching[2]))
-				$fields[$fkName] = $field;
-		}
-		return $fields;
-	}
-
-	/**
-	 * @param mixed object or array to be hashed
-	 * @param array name of property for hashing the properties.
-	 * @return string object hash using crc32 and serialize.
-	 */
-	protected function getObjectHash($obj, $properties)
-	{
-		$ids=array();
-		foreach($properties as $property)
-			$ids[] = is_object($obj) ? (string)$obj->getColumnValue($property) : (string)$obj[$property];
-		return serialize($ids);
-	}
-
-	/**
-	 * Fetches the foreign objects using TActiveRecord::findAllByIndex()
-	 * @param array field names
-	 * @param array foreign key index values.
-	 * @return TActiveRecord[] foreign objects.
-	 */
-	protected function findForeignObjects($fields, $indexValues)
-	{
-		$finder = $this->getContext()->getForeignRecordFinder();
-		return $finder->findAllByIndex($this->_criteria, $fields, $indexValues);
-	}
-
-	/**
-	 * Obtain the foreign key index values from the results.
-	 * @param array property names
-	 * @param array TActiveRecord results
-	 * @return array foreign key index values.
-	 */
-	protected function getIndexValues($keys, $results)
-	{
-		if(!is_array($results) && !$results instanceof ArrayAccess)
-			$results = array($results);
-		$values=array();
-		foreach($results as $result)
-		{
-			$value = array();
-			foreach($keys as $name)
-				$value[] = $result->getColumnValue($name);
-			$values[] = $value;
-		}
-		return $values;
-	}
-
-	/**
-	 * Populate the results with the foreign objects found.
-	 * @param array source results
-	 * @param array source property names
-	 * @param array foreign objects
-	 * @param array foreign object field names.
-	 */
-	protected function populateResult(&$results,$properties,&$fkObjects,$fields)
-	{
-		$collections=array();
-		foreach($fkObjects as $fkObject)
-			$collections[$this->getObjectHash($fkObject, $fields)][]=$fkObject;
-		$this->setResultCollection($results, $collections, $properties);
-	}
-
-	/**
-	 * Populates the result array with foreign objects (matched using foreign key hashed property values).
-	 * @param array $results
-	 * @param array $collections
-	 * @param array property names
-	 */
-	protected function setResultCollection(&$results, &$collections, $properties)
-	{
-		if(is_array($results) || $results instanceof ArrayAccess)
-		{
-			for($i=0,$k=count($results);$i<$k;$i++)
-				$this->setObjectProperty($results[$i], $properties, $collections);
-		}
-		else
-			$this->setObjectProperty($results, $properties, $collections);
-	}
-
-	/**
-	 * Sets the foreign objects to the given property on the source object.
-	 * @param TActiveRecord source object.
-	 * @param array source properties
-	 * @param array foreign objects.
-	 */
-	protected function setObjectProperty($source, $properties, &$collections)
-	{
-		$hash = $this->getObjectHash($source, $properties);
-		$prop = $this->getContext()->getProperty();
-		$source->$prop=isset($collections[$hash]) ? $collections[$hash] : array();
-	}
-}
-
+<?php
+/**
+ * TActiveRecordRelation class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ */
+
+/**
+ * Load active record relationship context.
+ */
+Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordRelationContext');
+
+/**
+ * Base class for active record relationships.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ * @since 3.1
+ */
+abstract class TActiveRecordRelation
+{
+	private $_context;
+	private $_criteria;
+
+	public function __construct(TActiveRecordRelationContext $context, $criteria)
+	{
+		$this->_context = $context;
+		$this->_criteria = $criteria;
+	}
+
+	/**
+	 * @return TActiveRecordRelationContext
+	 */
+	protected function getContext()
+	{
+		return $this->_context;
+	}
+
+	/**
+	 * @return TActiveRecordCriteria
+	 */
+	protected function getCriteria()
+	{
+		return $this->_criteria;
+	}
+
+	/**
+	 * @return TActiveRecord
+	 */
+	protected function getSourceRecord()
+	{
+		return $this->getContext()->getSourceRecord();
+	}
+
+	abstract protected function collectForeignObjects(&$results);
+
+	/**
+	 * Dispatch the method calls to the source record finder object. When
+	 * an instance of TActiveRecord or an array of TActiveRecord is returned
+	 * the corresponding foreign objects are also fetched and assigned.
+	 *
+	 * Multiple relationship calls can be chain together.
+	 *
+	 * @param string method name called
+	 * @param array method arguments
+	 * @return mixed TActiveRecord or array of TActiveRecord results depending on the method called.
+	 */
+	public function __call($method,$args)
+	{
+		static $stack=array();
+
+		$results = call_user_func_array(array($this->getSourceRecord(),$method),$args);
+		$validArray = is_array($results) && count($results) > 0;
+		if($validArray || $results instanceof ArrayAccess || $results instanceof TActiveRecord)
+		{
+			$this->collectForeignObjects($results);
+			while($obj = array_pop($stack))
+				$obj->collectForeignObjects($results);
+		}
+		else if($results instanceof TActiveRecordRelation)
+			$stack[] = $this; //call it later
+		else if($results === null || !$validArray)
+			$stack = array();
+		return $results;
+	}
+
+	/**
+	 * Fetch results for current relationship.
+	 * @return boolean always true.
+	 */
+	public function fetchResultsInto($obj)
+	{
+		$this->collectForeignObjects($obj);
+		return true;
+	}
+
+	/**
+	 * Returns foreign keys in $fromRecord with source column names as key
+	 * and foreign column names in the corresponding $matchesRecord as value.
+	 * The method returns the first matching foreign key between these 2 records.
+	 * @param TActiveRecord $fromRecord
+	 * @param TActiveRecord $matchesRecord
+	 * @return array foreign keys with source column names as key and foreign column names as value.
+	 */
+	protected function findForeignKeys($from, $matchesRecord, $loose=false)
+	{
+		$gateway = $matchesRecord->getRecordGateway();
+		$recordTableInfo = $gateway->getRecordTableInfo($matchesRecord);
+		$matchingTableName = strtolower($recordTableInfo->getTableName());
+		$matchingFullTableName = strtolower($recordTableInfo->getTableFullName());
+		$tableInfo=$from;
+		if($from instanceof TActiveRecord)
+			$tableInfo = $gateway->getRecordTableInfo($from);
+		//find first non-empty FK
+		foreach($tableInfo->getForeignKeys() as $fkeys)
+		{
+			$fkTable = strtolower($fkeys['table']);
+			if($fkTable===$matchingTableName || $fkTable===$matchingFullTableName)
+			{
+				$hasFkField = !$loose && $this->getContext()->hasFkField();
+				$key = $hasFkField ? $this->getFkFields($fkeys['keys']) : $fkeys['keys'];
+				if(!empty($key))
+					return $key;
+			}
+		}
+
+		//none found
+		$matching = $gateway->getRecordTableInfo($matchesRecord)->getTableFullName();
+		throw new TActiveRecordException('ar_relations_missing_fk',
+			$tableInfo->getTableFullName(), $matching);
+	}
+
+	/**
+	 * @return array foreign key field names as key and object properties as value.
+	 * @since 3.1.2
+	 */
+	abstract public function getRelationForeignKeys();
+
+	/**
+	 * Find matching foreign key fields from the 3rd element of an entry in TActiveRecord::$RELATION.
+	 * Assume field names consist of [\w-] character sets. Prefix to the field names ending with a dot
+	 * are ignored.
+	 */
+	private function getFkFields($fkeys)
+	{
+		$matching = array();
+		preg_match_all('/\s*(\S+\.)?([\w-]+)\s*/', $this->getContext()->getFkField(), $matching);
+		$fields = array();
+		foreach($fkeys as $fkName => $field)
+		{
+			if(in_array($fkName, $matching[2]))
+				$fields[$fkName] = $field;
+		}
+		return $fields;
+	}
+
+	/**
+	 * @param mixed object or array to be hashed
+	 * @param array name of property for hashing the properties.
+	 * @return string object hash using crc32 and serialize.
+	 */
+	protected function getObjectHash($obj, $properties)
+	{
+		$ids=array();
+		foreach($properties as $property)
+			$ids[] = is_object($obj) ? (string)$obj->getColumnValue($property) : (string)$obj[$property];
+		return serialize($ids);
+	}
+
+	/**
+	 * Fetches the foreign objects using TActiveRecord::findAllByIndex()
+	 * @param array field names
+	 * @param array foreign key index values.
+	 * @return TActiveRecord[] foreign objects.
+	 */
+	protected function findForeignObjects($fields, $indexValues)
+	{
+		$finder = $this->getContext()->getForeignRecordFinder();
+		return $finder->findAllByIndex($this->_criteria, $fields, $indexValues);
+	}
+
+	/**
+	 * Obtain the foreign key index values from the results.
+	 * @param array property names
+	 * @param array TActiveRecord results
+	 * @return array foreign key index values.
+	 */
+	protected function getIndexValues($keys, $results)
+	{
+		if(!is_array($results) && !$results instanceof ArrayAccess)
+			$results = array($results);
+		$values=array();
+		foreach($results as $result)
+		{
+			$value = array();
+			foreach($keys as $name)
+				$value[] = $result->getColumnValue($name);
+			$values[] = $value;
+		}
+		return $values;
+	}
+
+	/**
+	 * Populate the results with the foreign objects found.
+	 * @param array source results
+	 * @param array source property names
+	 * @param array foreign objects
+	 * @param array foreign object field names.
+	 */
+	protected function populateResult(&$results,$properties,&$fkObjects,$fields)
+	{
+		$collections=array();
+		foreach($fkObjects as $fkObject)
+			$collections[$this->getObjectHash($fkObject, $fields)][]=$fkObject;
+		$this->setResultCollection($results, $collections, $properties);
+	}
+
+	/**
+	 * Populates the result array with foreign objects (matched using foreign key hashed property values).
+	 * @param array $results
+	 * @param array $collections
+	 * @param array property names
+	 */
+	protected function setResultCollection(&$results, &$collections, $properties)
+	{
+		if(is_array($results) || $results instanceof ArrayAccess)
+		{
+			for($i=0,$k=count($results);$i<$k;$i++)
+				$this->setObjectProperty($results[$i], $properties, $collections);
+		}
+		else
+			$this->setObjectProperty($results, $properties, $collections);
+	}
+
+	/**
+	 * Sets the foreign objects to the given property on the source object.
+	 * @param TActiveRecord source object.
+	 * @param array source properties
+	 * @param array foreign objects.
+	 */
+	protected function setObjectProperty($source, $properties, &$collections)
+	{
+		$hash = $this->getObjectHash($source, $properties);
+		$prop = $this->getContext()->getProperty();
+		$source->$prop=isset($collections[$hash]) ? $collections[$hash] : array();
+	}
+}
+
diff --git a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php
index 8826bb69..dbcc097b 100644
--- a/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php
+++ b/framework/Data/ActiveRecord/Relations/TActiveRecordRelationContext.php
@@ -1,230 +1,230 @@
-<?php
-/**
- * TActiveRecordRelationContext class.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php
+/**
+ * TActiveRecordRelationContext class.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
  * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- */
-
-/**
- * TActiveRecordRelationContext holds information regarding record relationships
- * such as record relation property name, query criteria and foreign object record
- * class names.
- *
- * This class is use internally by passing a context to the TActiveRecordRelation
- * constructor.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Relations
- * @since 3.1
- */
-class TActiveRecordRelationContext
-{
-	private $_property;
-	private $_record;
-	private $_relation; //data from an entry of TActiveRecord::$RELATION
-	private $_fkeys;
-
-	public function __construct($record, $property=null, $relation=null)
-	{
-		$this->_record=$record;
-		$this->_property=$property;
-		$this->_relation=$relation;
-	}
-
-	/**
-	 * @return boolean true if the relation is defined in TActiveRecord::$RELATIONS
-	 * @since 3.1.2
-	 */
-	public function hasRecordRelation()
-	{
-		return $this->_relation!==null;
-	}
-
-	public function getPropertyValue()
-	{
-		$obj = $this->getSourceRecord();
-		return $obj->getColumnValue($this->getProperty());
-	}
-
-	/**
-	 * @return string name of the record property that the relationship results will be assigned to.
-	 */
-	public function getProperty()
-	{
-		return $this->_property;
-	}
-
-	/**
-	 * @return TActiveRecord the active record instance that queried for its related records.
-	 */
-	public function getSourceRecord()
-	{
-		return $this->_record;
-	}
-
-	/**
-	 * @return array foreign key of this relations, the keys is dependent on the
-	 * relationship type.
-	 * @since 3.1.2
-	 */
-	public function getRelationForeignKeys()
-	{
-		if($this->_fkeys===null)
-			$this->_fkeys=$this->getRelationHandler()->getRelationForeignKeys();
-		return $this->_fkeys;
-	}
-
-	/**
-	 * @return string HAS_MANY, HAS_ONE, or BELONGS_TO
-	 */
-	public function getRelationType()
-	{
-		return $this->_relation[0];
-	}
-
-	/**
-	 * @return string foreign record class name.
-	 */
-	public function getForeignRecordClass()
-	{
-		return $this->_relation[1];
-	}
-
-	/**
-	 * @return string foreign key field names, comma delimited.
-	 * @since 3.1.2
-	 */
-	public function getFkField()
-	{
-		return $this->_relation[2];
-	}
-
-	/**
-	 * @return string the query condition for the relation as specified in RELATIONS
-	 * @since 3.1.2
-	 */
-	public function getCondition()
-	{
-		return isset($this->_relation[3])?$this->_relation[3]:null;
-	}
-
-	/**
-	 * @return array the query parameters for the relation as specified in RELATIONS
-	 * @since 3.1.2
-	 */
-	public function getParameters()
-	{
-		return isset($this->_relation[4])?$this->_relation[4]:array();
-	}
-
-	/**
-	 * @return boolean true if the 3rd element of an TActiveRecord::$RELATION entry is set.
-	 * @since 3.1.2
-	 */
-	public function hasFkField()
-	{
-		$notManyToMany = $this->getRelationType() !== TActiveRecord::MANY_TO_MANY;
-		return $notManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]);
-	}
-
-	/**
-	 * @return string the M-N relationship association table name.
-	 */
-	public function getAssociationTable()
-	{
-		return $this->_relation[2];
-	}
-
-	/**
-	 * @return boolean true if the relationship is HAS_MANY and requires an association table.
-	 */
-	public function hasAssociationTable()
-	{
-		$isManyToMany = $this->getRelationType() === TActiveRecord::MANY_TO_MANY;
-		return $isManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]);
-	}
-
-	/**
-	 * @return TActiveRecord corresponding relationship foreign object finder instance.
-	 */
-	public function getForeignRecordFinder()
-	{
-		return TActiveRecord::finder($this->getForeignRecordClass());
-	}
-
-	/**
-	 * Creates and return the TActiveRecordRelation handler for specific relationships.
-	 * An instance of TActiveRecordHasOne, TActiveRecordBelongsTo, TActiveRecordHasMany,
-	 * or TActiveRecordHasManyAssocation will be returned.
-	 * @param TActiveRecordCriteria search criteria
-	 * @return TActiveRecordRelation record relationship handler instnace.
-	 * @throws TActiveRecordException if property is not defined or missing.
-	 */
-	public function getRelationHandler($criteria=null)
-	{
-		if(!$this->hasRecordRelation())
-		{
-			throw new TActiveRecordException('ar_undefined_relation_prop',
-				$this->_property, get_class($this->_record), 'RELATIONS');
-		}
-		if($criteria===null)
-			$criteria = new TActiveRecordCriteria($this->getCondition(), $this->getParameters());
-		switch($this->getRelationType())
-		{
-			case TActiveRecord::HAS_MANY:
-				Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasMany');
-				return new TActiveRecordHasMany($this, $criteria);
-			case TActiveRecord::MANY_TO_MANY:
-				Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasManyAssociation');
-				return new TActiveRecordHasManyAssociation($this, $criteria);
-			case TActiveRecord::HAS_ONE:
-				Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasOne');
-				return new TActiveRecordHasOne($this, $criteria);
-			case TActiveRecord::BELONGS_TO:
-				Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordBelongsTo');
-				return new TActiveRecordBelongsTo($this, $criteria);
-			default:
-				throw new TActiveRecordException('ar_invalid_relationship');
-		}
-	}
-
-	/**
-	 * @return TActiveRecordRelationCommand
-	 */
-	public function updateAssociatedRecords($updateBelongsTo=false)
-	{
-		$success=true;
-		foreach($this->_record->getRecordRelations() as $data)
-		{
-			list($property, $relation) = $data;
-			$belongsTo = $relation[0]==TActiveRecord::BELONGS_TO;
-			if(($updateBelongsTo && $belongsTo) || (!$updateBelongsTo && !$belongsTo))
-			{
-				$obj = $this->getSourceRecord();
-				if(!$this->isEmptyFkObject($obj->getColumnValue($property)))
-				{
-					$context = new TActiveRecordRelationContext($this->getSourceRecord(),$property,$relation);
-					$success = $context->getRelationHandler()->updateAssociatedRecords() && $success;
-				}
-			}
-		}
-		return $success;
-	}
-
-	protected function isEmptyFkObject($obj)
-	{
-		if(is_object($obj))
-			return $obj instanceof TList ? $obj->count() === 0 : false;
-		else if(is_array($obj))
-			return count($obj)===0;
-		else
-			return empty($obj);
-	}
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ */
+
+/**
+ * TActiveRecordRelationContext holds information regarding record relationships
+ * such as record relation property name, query criteria and foreign object record
+ * class names.
+ *
+ * This class is use internally by passing a context to the TActiveRecordRelation
+ * constructor.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Relations
+ * @since 3.1
+ */
+class TActiveRecordRelationContext
+{
+	private $_property;
+	private $_record;
+	private $_relation; //data from an entry of TActiveRecord::$RELATION
+	private $_fkeys;
+
+	public function __construct($record, $property=null, $relation=null)
+	{
+		$this->_record=$record;
+		$this->_property=$property;
+		$this->_relation=$relation;
+	}
+
+	/**
+	 * @return boolean true if the relation is defined in TActiveRecord::$RELATIONS
+	 * @since 3.1.2
+	 */
+	public function hasRecordRelation()
+	{
+		return $this->_relation!==null;
+	}
+
+	public function getPropertyValue()
+	{
+		$obj = $this->getSourceRecord();
+		return $obj->getColumnValue($this->getProperty());
+	}
+
+	/**
+	 * @return string name of the record property that the relationship results will be assigned to.
+	 */
+	public function getProperty()
+	{
+		return $this->_property;
+	}
+
+	/**
+	 * @return TActiveRecord the active record instance that queried for its related records.
+	 */
+	public function getSourceRecord()
+	{
+		return $this->_record;
+	}
+
+	/**
+	 * @return array foreign key of this relations, the keys is dependent on the
+	 * relationship type.
+	 * @since 3.1.2
+	 */
+	public function getRelationForeignKeys()
+	{
+		if($this->_fkeys===null)
+			$this->_fkeys=$this->getRelationHandler()->getRelationForeignKeys();
+		return $this->_fkeys;
+	}
+
+	/**
+	 * @return string HAS_MANY, HAS_ONE, or BELONGS_TO
+	 */
+	public function getRelationType()
+	{
+		return $this->_relation[0];
+	}
+
+	/**
+	 * @return string foreign record class name.
+	 */
+	public function getForeignRecordClass()
+	{
+		return $this->_relation[1];
+	}
+
+	/**
+	 * @return string foreign key field names, comma delimited.
+	 * @since 3.1.2
+	 */
+	public function getFkField()
+	{
+		return $this->_relation[2];
+	}
+
+	/**
+	 * @return string the query condition for the relation as specified in RELATIONS
+	 * @since 3.1.2
+	 */
+	public function getCondition()
+	{
+		return isset($this->_relation[3])?$this->_relation[3]:null;
+	}
+
+	/**
+	 * @return array the query parameters for the relation as specified in RELATIONS
+	 * @since 3.1.2
+	 */
+	public function getParameters()
+	{
+		return isset($this->_relation[4])?$this->_relation[4]:array();
+	}
+
+	/**
+	 * @return boolean true if the 3rd element of an TActiveRecord::$RELATION entry is set.
+	 * @since 3.1.2
+	 */
+	public function hasFkField()
+	{
+		$notManyToMany = $this->getRelationType() !== TActiveRecord::MANY_TO_MANY;
+		return $notManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]);
+	}
+
+	/**
+	 * @return string the M-N relationship association table name.
+	 */
+	public function getAssociationTable()
+	{
+		return $this->_relation[2];
+	}
+
+	/**
+	 * @return boolean true if the relationship is HAS_MANY and requires an association table.
+	 */
+	public function hasAssociationTable()
+	{
+		$isManyToMany = $this->getRelationType() === TActiveRecord::MANY_TO_MANY;
+		return $isManyToMany && isset($this->_relation[2]) && !empty($this->_relation[2]);
+	}
+
+	/**
+	 * @return TActiveRecord corresponding relationship foreign object finder instance.
+	 */
+	public function getForeignRecordFinder()
+	{
+		return TActiveRecord::finder($this->getForeignRecordClass());
+	}
+
+	/**
+	 * Creates and return the TActiveRecordRelation handler for specific relationships.
+	 * An instance of TActiveRecordHasOne, TActiveRecordBelongsTo, TActiveRecordHasMany,
+	 * or TActiveRecordHasManyAssocation will be returned.
+	 * @param TActiveRecordCriteria search criteria
+	 * @return TActiveRecordRelation record relationship handler instnace.
+	 * @throws TActiveRecordException if property is not defined or missing.
+	 */
+	public function getRelationHandler($criteria=null)
+	{
+		if(!$this->hasRecordRelation())
+		{
+			throw new TActiveRecordException('ar_undefined_relation_prop',
+				$this->_property, get_class($this->_record), 'RELATIONS');
+		}
+		if($criteria===null)
+			$criteria = new TActiveRecordCriteria($this->getCondition(), $this->getParameters());
+		switch($this->getRelationType())
+		{
+			case TActiveRecord::HAS_MANY:
+				Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasMany');
+				return new TActiveRecordHasMany($this, $criteria);
+			case TActiveRecord::MANY_TO_MANY:
+				Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasManyAssociation');
+				return new TActiveRecordHasManyAssociation($this, $criteria);
+			case TActiveRecord::HAS_ONE:
+				Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordHasOne');
+				return new TActiveRecordHasOne($this, $criteria);
+			case TActiveRecord::BELONGS_TO:
+				Prado::using('System.Data.ActiveRecord.Relations.TActiveRecordBelongsTo');
+				return new TActiveRecordBelongsTo($this, $criteria);
+			default:
+				throw new TActiveRecordException('ar_invalid_relationship');
+		}
+	}
+
+	/**
+	 * @return TActiveRecordRelationCommand
+	 */
+	public function updateAssociatedRecords($updateBelongsTo=false)
+	{
+		$success=true;
+		foreach($this->_record->getRecordRelations() as $data)
+		{
+			list($property, $relation) = $data;
+			$belongsTo = $relation[0]==TActiveRecord::BELONGS_TO;
+			if(($updateBelongsTo && $belongsTo) || (!$updateBelongsTo && !$belongsTo))
+			{
+				$obj = $this->getSourceRecord();
+				if(!$this->isEmptyFkObject($obj->getColumnValue($property)))
+				{
+					$context = new TActiveRecordRelationContext($this->getSourceRecord(),$property,$relation);
+					$success = $context->getRelationHandler()->updateAssociatedRecords() && $success;
+				}
+			}
+		}
+		return $success;
+	}
+
+	protected function isEmptyFkObject($obj)
+	{
+		if(is_object($obj))
+			return $obj instanceof TList ? $obj->count() === 0 : false;
+		else if(is_array($obj))
+			return count($obj)===0;
+		else
+			return empty($obj);
+	}
+}
+
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php
index d2e58bc3..68f5806a 100644
--- a/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php
+++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldBase.php
@@ -1,207 +1,207 @@
-<?php
-/**
- * TScaffoldBase class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Include the base Active Record class.
- */
-Prado::using('System.Data.ActiveRecord.TActiveRecord');
-
-/**
- * Base class for Active Record scaffold views.
- *
- * Provides common properties for all scaffold views (such as, TScaffoldListView,
- * TScaffoldEditView, TScaffoldListView and TScaffoldView).
- *
- * During the OnPrRender stage the default css style file (filename style.css)
- * is published and registered. To override the default style, provide your own stylesheet
- * file explicitly.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-abstract class TScaffoldBase extends TTemplateControl
-{
-	/**
-	 * @var TActiveRecord record instance (may be new or retrieved from db)
-	 */
-	private $_record;
-
-	/**
-	 * @return TDbMetaData table/view information
-	 */
-	protected function getTableInfo()
-	{
-		$finder = $this->getRecordFinder();
-		$gateway = $finder->getRecordManager()->getRecordGateWay();
-		return $gateway->getRecordTableInfo($finder);
-	}
-
-	/**
-	 * @param TActiveRecord record instance
-	 * @return array record property values
-	 */
-	protected function getRecordPropertyValues($record)
-	{
-		$data = array();
-		foreach($this->getTableInfo()->getColumns() as $name=>$column)
-			$data[] = $record->getColumnValue($name);
-		return $data;
-	}
-
-	/**
-	 * @param TActiveRecord record instance
-	 * @return array record primary key values.
-	 */
-	protected function getRecordPkValues($record)
-	{
-		$data=array();
-		foreach($this->getTableInfo()->getColumns() as $name=>$column)
-		{
-			if($column->getIsPrimaryKey())
-				$data[] = $record->getColumnValue($name);
-		}
-		return $data;
-	}
-
-	/**
-	 * Name of the Active Record class to be viewed or scaffolded.
-	 * @return string Active Record class name.
-	 */
-	public function getRecordClass()
-	{
-		return $this->getViewState('RecordClass');
-	}
-
-	/**
-	 * Name of the Active Record class to be viewed or scaffolded.
-	 * @param string Active Record class name.
-	 */
-	public function setRecordClass($value)
-	{
-		$this->setViewState('RecordClass', $value);
-	}
-
-	/**
-	 * Copy the view details from another scaffold view instance.
-	 * @param TScaffoldBase scaffold view.
-	 */
-	protected function copyFrom(TScaffoldBase $obj)
-	{
-		$this->_record = $obj->_record;
-		$this->setRecordClass($obj->getRecordClass());
-		$this->setEnableDefaultStyle($obj->getEnableDefaultStyle());
-	}
-
-	/**
-	 * Unset the current record instance and table information.
-	 */
-	protected function clearRecordObject()
-	{
-		$this->_record=null;
-	}
-
-	/**
-	 * Gets the current Active Record instance. Creates new instance if the
-	 * primary key value is null otherwise the record is fetched from the db.
-	 * @param array primary key value
-	 * @return TActiveRecord record instance
-	 */
-	protected function getRecordObject($pk=null)
-	{
-		if($this->_record===null)
-		{
-			if($pk!==null)
-			{
-				$this->_record=$this->getRecordFinder()->findByPk($pk);
-				if($this->_record===null)
-					throw new TConfigurationException('scaffold_invalid_record_pk',
-						$this->getRecordClass(), $pk);
-			}
-			else
-			{
-				$class = $this->getRecordClass();
-				if($class!==null)
-					$this->_record=Prado::createComponent($class);
-				else
-				{
-					throw new TConfigurationException('scaffold_invalid_record_class',
-						$this->getRecordClass(),$this->getID());
-				}
-			}
-		}
-		return $this->_record;
-	}
-
-	/**
-	 * @param TActiveRecord Active Record instance.
-	 */
-	protected function setRecordObject(TActiveRecord $value)
-	{
-		$this->_record=$value;
-	}
-
-	/**
-	 * @return TActiveRecord Active Record finder instance
-	 */
-	protected function getRecordFinder()
-	{
-		return TActiveRecord::finder($this->getRecordClass());
-	}
-
-	/**
-	 * @return string default scaffold stylesheet name
-	 */
-	public function getDefaultStyle()
-	{
-		return $this->getViewState('DefaultStyle', 'style');
-	}
-
-	/**
-	 * @param string default scaffold stylesheet name
-	 */
-	public function setDefaultStyle($value)
-	{
-		$this->setViewState('DefaultStyle', TPropertyValue::ensureString($value), 'style');
-	}
-
-	/**
-	 * @return boolean enable default stylesheet, default is true.
-	 */
-	public function getEnableDefaultStyle()
-	{
-		return $this->getViewState('EnableDefaultStyle', true);
-	}
-
-	/**
-	 * @param boolean enable default stylesheet, default is true.
-	 */
-	public function setEnableDefaultStyle($value)
-	{
-		return $this->setViewState('EnableDefaultStyle', TPropertyValue::ensureBoolean($value), true);
-	}
-
-	/**
-	 * Publish the default stylesheet file.
-	 */
-	public function onPreRender($param)
-	{
-		parent::onPreRender($param);
-		if($this->getEnableDefaultStyle())
-		{
-			$url = $this->publishAsset($this->getDefaultStyle().'.css');
-			$this->getPage()->getClientScript()->registerStyleSheetFile($url,$url);
-		}
-	}
-}
-
+<?php
+/**
+ * TScaffoldBase class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ */
+
+/**
+ * Include the base Active Record class.
+ */
+Prado::using('System.Data.ActiveRecord.TActiveRecord');
+
+/**
+ * Base class for Active Record scaffold views.
+ *
+ * Provides common properties for all scaffold views (such as, TScaffoldListView,
+ * TScaffoldEditView, TScaffoldListView and TScaffoldView).
+ *
+ * During the OnPrRender stage the default css style file (filename style.css)
+ * is published and registered. To override the default style, provide your own stylesheet
+ * file explicitly.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ * @since 3.1
+ */
+abstract class TScaffoldBase extends TTemplateControl
+{
+	/**
+	 * @var TActiveRecord record instance (may be new or retrieved from db)
+	 */
+	private $_record;
+
+	/**
+	 * @return TDbMetaData table/view information
+	 */
+	protected function getTableInfo()
+	{
+		$finder = $this->getRecordFinder();
+		$gateway = $finder->getRecordManager()->getRecordGateWay();
+		return $gateway->getRecordTableInfo($finder);
+	}
+
+	/**
+	 * @param TActiveRecord record instance
+	 * @return array record property values
+	 */
+	protected function getRecordPropertyValues($record)
+	{
+		$data = array();
+		foreach($this->getTableInfo()->getColumns() as $name=>$column)
+			$data[] = $record->getColumnValue($name);
+		return $data;
+	}
+
+	/**
+	 * @param TActiveRecord record instance
+	 * @return array record primary key values.
+	 */
+	protected function getRecordPkValues($record)
+	{
+		$data=array();
+		foreach($this->getTableInfo()->getColumns() as $name=>$column)
+		{
+			if($column->getIsPrimaryKey())
+				$data[] = $record->getColumnValue($name);
+		}
+		return $data;
+	}
+
+	/**
+	 * Name of the Active Record class to be viewed or scaffolded.
+	 * @return string Active Record class name.
+	 */
+	public function getRecordClass()
+	{
+		return $this->getViewState('RecordClass');
+	}
+
+	/**
+	 * Name of the Active Record class to be viewed or scaffolded.
+	 * @param string Active Record class name.
+	 */
+	public function setRecordClass($value)
+	{
+		$this->setViewState('RecordClass', $value);
+	}
+
+	/**
+	 * Copy the view details from another scaffold view instance.
+	 * @param TScaffoldBase scaffold view.
+	 */
+	protected function copyFrom(TScaffoldBase $obj)
+	{
+		$this->_record = $obj->_record;
+		$this->setRecordClass($obj->getRecordClass());
+		$this->setEnableDefaultStyle($obj->getEnableDefaultStyle());
+	}
+
+	/**
+	 * Unset the current record instance and table information.
+	 */
+	protected function clearRecordObject()
+	{
+		$this->_record=null;
+	}
+
+	/**
+	 * Gets the current Active Record instance. Creates new instance if the
+	 * primary key value is null otherwise the record is fetched from the db.
+	 * @param array primary key value
+	 * @return TActiveRecord record instance
+	 */
+	protected function getRecordObject($pk=null)
+	{
+		if($this->_record===null)
+		{
+			if($pk!==null)
+			{
+				$this->_record=$this->getRecordFinder()->findByPk($pk);
+				if($this->_record===null)
+					throw new TConfigurationException('scaffold_invalid_record_pk',
+						$this->getRecordClass(), $pk);
+			}
+			else
+			{
+				$class = $this->getRecordClass();
+				if($class!==null)
+					$this->_record=Prado::createComponent($class);
+				else
+				{
+					throw new TConfigurationException('scaffold_invalid_record_class',
+						$this->getRecordClass(),$this->getID());
+				}
+			}
+		}
+		return $this->_record;
+	}
+
+	/**
+	 * @param TActiveRecord Active Record instance.
+	 */
+	protected function setRecordObject(TActiveRecord $value)
+	{
+		$this->_record=$value;
+	}
+
+	/**
+	 * @return TActiveRecord Active Record finder instance
+	 */
+	protected function getRecordFinder()
+	{
+		return TActiveRecord::finder($this->getRecordClass());
+	}
+
+	/**
+	 * @return string default scaffold stylesheet name
+	 */
+	public function getDefaultStyle()
+	{
+		return $this->getViewState('DefaultStyle', 'style');
+	}
+
+	/**
+	 * @param string default scaffold stylesheet name
+	 */
+	public function setDefaultStyle($value)
+	{
+		$this->setViewState('DefaultStyle', TPropertyValue::ensureString($value), 'style');
+	}
+
+	/**
+	 * @return boolean enable default stylesheet, default is true.
+	 */
+	public function getEnableDefaultStyle()
+	{
+		return $this->getViewState('EnableDefaultStyle', true);
+	}
+
+	/**
+	 * @param boolean enable default stylesheet, default is true.
+	 */
+	public function setEnableDefaultStyle($value)
+	{
+		return $this->setViewState('EnableDefaultStyle', TPropertyValue::ensureBoolean($value), true);
+	}
+
+	/**
+	 * Publish the default stylesheet file.
+	 */
+	public function onPreRender($param)
+	{
+		parent::onPreRender($param);
+		if($this->getEnableDefaultStyle())
+		{
+			$url = $this->publishAsset($this->getDefaultStyle().'.css');
+			$this->getPage()->getClientScript()->registerStyleSheetFile($url,$url);
+		}
+	}
+}
+
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php
index c0547627..166e3360 100644
--- a/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php
+++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldEditView.php
@@ -1,309 +1,309 @@
-<?php
-/**
- * TScaffoldEditView class and IScaffoldEditRenderer interface file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php
+/**
+ * TScaffoldEditView class and IScaffoldEditRenderer interface file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
  * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Load scaffold base.
- */
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
-
-/**
- * Template control for editing an Active Record instance.
- * The <b>RecordClass</b> determines the Active Record class to be edited.
- * A particular record can be edited by specifying the {@link setRecordPk RecordPk}
- * value (may be an array for composite keys).
- *
- * The default editor input controls are created based on the column types.
- * The editor layout can be specified by a renderer by set the value
- * of the {@link setEditRenderer EditRenderer} property to the class name of a
- * class that implements TScaffoldEditRenderer. A renderer is an external
- * template control that implements IScaffoldEditRenderer.
- *
- * The <b>Data</b> of the IScaffoldEditRenderer will be set as the current Active
- * Record to be edited. The <b>UpdateRecord()</b> method of IScaffoldEditRenderer
- * is called when request to save the record is requested.
- *
- * Validators in the custom external editor template should have the
- * {@link TBaseValidator::setValidationGroup ValidationGroup} property set to the
- * value of the {@link getValidationGroup} of the TScaffoldEditView instance
- * (the edit view instance is the <b>Parent</b> of the IScaffoldEditRenderer in most
- * cases.
- *
- * Cosmetic changes to the default editor should be done using Cascading Stylesheets.
- * For example, a particular field/property can be hidden by specifying "display:none" for
- * the corresponding style (each field/property has unique Css class name as "property_xxx", where
- * xxx is the property name).
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-class TScaffoldEditView extends TScaffoldBase
-{
-	/**
-	 * @var IScaffoldEditRenderer custom scaffold edit renderer
-	 */
-	private $_editRenderer;
-
-	/**
-	 * Initialize the editor form if it is Visible.
-	 */
-	public function onLoad($param)
-	{
-		if($this->getVisible())
-			$this->initializeEditForm();
-	}
-
-	/**
-	 * @return string the class name for scaffold editor. Defaults to empty, meaning not set.
-	 */
-	public function getEditRenderer()
-	{
-		return $this->getViewState('EditRenderer', '');
-	}
-
-	/**
-	 * @param string the class name for scaffold editor. Defaults to empty, meaning not set.
-	 */
-	public function setEditRenderer($value)
-	{
-		$this->setViewState('EditRenderer', $value, '');
-	}
-
-	/**
-	 * @param array Active Record primary key value to be edited.
-	 */
-	public function setRecordPk($value)
-	{
-		$this->clearRecordObject();
-		$val = TPropertyValue::ensureArray($value);
-		$this->setViewState('PK', count($val) > 0 ? $val : null);
-	}
-
-	/**
-	 * @return array Active Record primary key value.
-	 */
-	public function getRecordPk()
-	{
-		return $this->getViewState('PK');
-	}
-
-	/**
-	 * @return TActiveRecord current Active Record instance
-	 */
-	protected function getCurrentRecord()
-	{
-		return $this->getRecordObject($this->getRecordPk());
-	}
-
-	/**
-	 * Initialize the editor form
-	 */
-	public function initializeEditForm()
-	{
-		$record = $this->getCurrentRecord();
-		$classPath = $this->getEditRenderer();
-		if($classPath === '')
-		{
-			$columns = $this->getTableInfo()->getColumns();
-			$this->getInputRepeater()->setDataSource($columns);
-			$this->getInputRepeater()->dataBind();
-		}
-		else
-		{
-			if($this->_editRenderer===null)
-				$this->createEditRenderer($record, $classPath);
-			else
-				$this->_editRenderer->setData($record);
-		}
-	}
-
-	/**
-	 * Instantiate the external edit renderer.
-	 * @param TActiveRecord record to be edited
-	 * @param string external edit renderer class name.
-	 * @throws TConfigurationException raised when renderer is not an
-	 * instance of IScaffoldEditRenderer.
-	 */
-	protected function createEditRenderer($record, $classPath)
-	{
-		$this->_editRenderer = Prado::createComponent($classPath);
-		if($this->_editRenderer instanceof IScaffoldEditRenderer)
-		{
-			$index = $this->getControls()->remove($this->getInputRepeater());
-			$this->getControls()->insertAt($index,$this->_editRenderer);
-			$this->_editRenderer->setData($record);
-		}
-		else
-		{
-			throw new TConfigurationException(
-				'scaffold_invalid_edit_renderer', $this->getID(), get_class($record));
-		}
-	}
-
-	/**
-	 * Initialize the default editor using the scaffold input builder.
-	 */
-	protected function createRepeaterEditItem($sender, $param)
-	{
-		$type = $param->getItem()->getItemType();
-		if($type==TListItemType::Item || $type==TListItemType::AlternatingItem)
-		{
-			$item = $param->getItem();
-			$column = $item->getDataItem();
-			if($column===null)
-				return;
-
-			$record = $this->getCurrentRecord();
-			$builder = $this->getScaffoldInputBuilder($record);
-			$builder->createScaffoldInput($this, $item, $column, $record);
-		}
-	}
-
-	/**
-	 * Bubble the command name event. Stops bubbling when the page validator false.
-	 * Otherwise, the bubble event is continued.
-	 */
-	public function bubbleEvent($sender, $param)
-	{
-		switch(strtolower($param->getCommandName()))
-		{
-			case 'save':
-				return $this->doSave() ? false : true;
-			case 'clear':
-				$this->setRecordPk(null);
-				$this->initializeEditForm();
-				return false;
-			default:
-				return false;
-		}
-	}
-
-	/**
-	 * Check the validators, then tries to save the record.
-	 * @return boolean true if the validators are true, false otherwise.
-	 */
-	protected function doSave()
-	{
-		if($this->getPage()->getIsValid())
-		{
-			$record = $this->getCurrentRecord();
-			if($this->_editRenderer===null)
-			{
-				$table = $this->getTableInfo();
-				$builder = $this->getScaffoldInputBuilder($record);
-				foreach($this->getInputRepeater()->getItems() as $item)
-				{
-					$column = $table->getColumn($item->getCustomData());
-					$builder->loadScaffoldInput($this, $item, $column, $record);
-				}
-			}
-			else
-			{
-				$this->_editRenderer->updateRecord($record);
-			}
-			$record->save();
-			return true;
-		}
-		else if($this->_editRenderer!==null)
-		{
-			//preserve the form data.
-			$this->_editRenderer->updateRecord($this->getCurrentRecord());
-		}
-
-		return false;
-	}
-
-	/**
-	 * @return TRepeater default editor input controls repeater
-	 */
-	protected function getInputRepeater()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_repeater');
-	}
-
-	/**
-	 * @return TButton Button triggered to save the Active Record.
-	 */
-	public function getSaveButton()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_save');
-	}
-
-	/**
-	 * @return TButton Button to clear the editor inputs.
-	 */
-	public function getClearButton()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_clear');
-	}
-
-	/**
-	 * @return TButton Button to cancel the edit action (e.g. hide the edit view).
-	 */
-	public function getCancelButton()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_cancel');
-	}
-
-	/**
-	 * Create the default scaffold editor control factory.
-	 * @param TActiveRecord record instance.
-	 * @return TScaffoldInputBase scaffold editor control factory.
-	 */
-	protected function getScaffoldInputBuilder($record)
-	{
-		static $_builders=array();
-		$class = get_class($record);
-		if(!isset($_builders[$class]))
-		{
-			Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputBase');
-			$_builders[$class] = TScaffoldInputBase::createInputBuilder($record);
-		}
-		return $_builders[$class];
-	}
-
-	/**
-	 * @return string editor validation group name.
-	 */
-	public function getValidationGroup()
-	{
-		return 'group_'.$this->getUniqueID();
-	}
-}
-
-/**
- * IScaffoldEditRenderer interface.
- *
- * IScaffoldEditRenderer defines the interface that an edit renderer
- * needs to implement. Besides the {@link getData Data} property, an edit
- * renderer also needs to provide {@link updateRecord updateRecord} method
- * that is called before the save() method is called on the TActiveRecord.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-interface IScaffoldEditRenderer extends IDataRenderer
-{
-	/**
-	 * This method should update the record with the user input data.
-	 * @param TActiveRecord record to be saved.
-	 */
-	public function updateRecord($record);
-}
-
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ */
+
+/**
+ * Load scaffold base.
+ */
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
+
+/**
+ * Template control for editing an Active Record instance.
+ * The <b>RecordClass</b> determines the Active Record class to be edited.
+ * A particular record can be edited by specifying the {@link setRecordPk RecordPk}
+ * value (may be an array for composite keys).
+ *
+ * The default editor input controls are created based on the column types.
+ * The editor layout can be specified by a renderer by set the value
+ * of the {@link setEditRenderer EditRenderer} property to the class name of a
+ * class that implements TScaffoldEditRenderer. A renderer is an external
+ * template control that implements IScaffoldEditRenderer.
+ *
+ * The <b>Data</b> of the IScaffoldEditRenderer will be set as the current Active
+ * Record to be edited. The <b>UpdateRecord()</b> method of IScaffoldEditRenderer
+ * is called when request to save the record is requested.
+ *
+ * Validators in the custom external editor template should have the
+ * {@link TBaseValidator::setValidationGroup ValidationGroup} property set to the
+ * value of the {@link getValidationGroup} of the TScaffoldEditView instance
+ * (the edit view instance is the <b>Parent</b> of the IScaffoldEditRenderer in most
+ * cases.
+ *
+ * Cosmetic changes to the default editor should be done using Cascading Stylesheets.
+ * For example, a particular field/property can be hidden by specifying "display:none" for
+ * the corresponding style (each field/property has unique Css class name as "property_xxx", where
+ * xxx is the property name).
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ * @since 3.1
+ */
+class TScaffoldEditView extends TScaffoldBase
+{
+	/**
+	 * @var IScaffoldEditRenderer custom scaffold edit renderer
+	 */
+	private $_editRenderer;
+
+	/**
+	 * Initialize the editor form if it is Visible.
+	 */
+	public function onLoad($param)
+	{
+		if($this->getVisible())
+			$this->initializeEditForm();
+	}
+
+	/**
+	 * @return string the class name for scaffold editor. Defaults to empty, meaning not set.
+	 */
+	public function getEditRenderer()
+	{
+		return $this->getViewState('EditRenderer', '');
+	}
+
+	/**
+	 * @param string the class name for scaffold editor. Defaults to empty, meaning not set.
+	 */
+	public function setEditRenderer($value)
+	{
+		$this->setViewState('EditRenderer', $value, '');
+	}
+
+	/**
+	 * @param array Active Record primary key value to be edited.
+	 */
+	public function setRecordPk($value)
+	{
+		$this->clearRecordObject();
+		$val = TPropertyValue::ensureArray($value);
+		$this->setViewState('PK', count($val) > 0 ? $val : null);
+	}
+
+	/**
+	 * @return array Active Record primary key value.
+	 */
+	public function getRecordPk()
+	{
+		return $this->getViewState('PK');
+	}
+
+	/**
+	 * @return TActiveRecord current Active Record instance
+	 */
+	protected function getCurrentRecord()
+	{
+		return $this->getRecordObject($this->getRecordPk());
+	}
+
+	/**
+	 * Initialize the editor form
+	 */
+	public function initializeEditForm()
+	{
+		$record = $this->getCurrentRecord();
+		$classPath = $this->getEditRenderer();
+		if($classPath === '')
+		{
+			$columns = $this->getTableInfo()->getColumns();
+			$this->getInputRepeater()->setDataSource($columns);
+			$this->getInputRepeater()->dataBind();
+		}
+		else
+		{
+			if($this->_editRenderer===null)
+				$this->createEditRenderer($record, $classPath);
+			else
+				$this->_editRenderer->setData($record);
+		}
+	}
+
+	/**
+	 * Instantiate the external edit renderer.
+	 * @param TActiveRecord record to be edited
+	 * @param string external edit renderer class name.
+	 * @throws TConfigurationException raised when renderer is not an
+	 * instance of IScaffoldEditRenderer.
+	 */
+	protected function createEditRenderer($record, $classPath)
+	{
+		$this->_editRenderer = Prado::createComponent($classPath);
+		if($this->_editRenderer instanceof IScaffoldEditRenderer)
+		{
+			$index = $this->getControls()->remove($this->getInputRepeater());
+			$this->getControls()->insertAt($index,$this->_editRenderer);
+			$this->_editRenderer->setData($record);
+		}
+		else
+		{
+			throw new TConfigurationException(
+				'scaffold_invalid_edit_renderer', $this->getID(), get_class($record));
+		}
+	}
+
+	/**
+	 * Initialize the default editor using the scaffold input builder.
+	 */
+	protected function createRepeaterEditItem($sender, $param)
+	{
+		$type = $param->getItem()->getItemType();
+		if($type==TListItemType::Item || $type==TListItemType::AlternatingItem)
+		{
+			$item = $param->getItem();
+			$column = $item->getDataItem();
+			if($column===null)
+				return;
+
+			$record = $this->getCurrentRecord();
+			$builder = $this->getScaffoldInputBuilder($record);
+			$builder->createScaffoldInput($this, $item, $column, $record);
+		}
+	}
+
+	/**
+	 * Bubble the command name event. Stops bubbling when the page validator false.
+	 * Otherwise, the bubble event is continued.
+	 */
+	public function bubbleEvent($sender, $param)
+	{
+		switch(strtolower($param->getCommandName()))
+		{
+			case 'save':
+				return $this->doSave() ? false : true;
+			case 'clear':
+				$this->setRecordPk(null);
+				$this->initializeEditForm();
+				return false;
+			default:
+				return false;
+		}
+	}
+
+	/**
+	 * Check the validators, then tries to save the record.
+	 * @return boolean true if the validators are true, false otherwise.
+	 */
+	protected function doSave()
+	{
+		if($this->getPage()->getIsValid())
+		{
+			$record = $this->getCurrentRecord();
+			if($this->_editRenderer===null)
+			{
+				$table = $this->getTableInfo();
+				$builder = $this->getScaffoldInputBuilder($record);
+				foreach($this->getInputRepeater()->getItems() as $item)
+				{
+					$column = $table->getColumn($item->getCustomData());
+					$builder->loadScaffoldInput($this, $item, $column, $record);
+				}
+			}
+			else
+			{
+				$this->_editRenderer->updateRecord($record);
+			}
+			$record->save();
+			return true;
+		}
+		else if($this->_editRenderer!==null)
+		{
+			//preserve the form data.
+			$this->_editRenderer->updateRecord($this->getCurrentRecord());
+		}
+
+		return false;
+	}
+
+	/**
+	 * @return TRepeater default editor input controls repeater
+	 */
+	protected function getInputRepeater()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_repeater');
+	}
+
+	/**
+	 * @return TButton Button triggered to save the Active Record.
+	 */
+	public function getSaveButton()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_save');
+	}
+
+	/**
+	 * @return TButton Button to clear the editor inputs.
+	 */
+	public function getClearButton()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_clear');
+	}
+
+	/**
+	 * @return TButton Button to cancel the edit action (e.g. hide the edit view).
+	 */
+	public function getCancelButton()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_cancel');
+	}
+
+	/**
+	 * Create the default scaffold editor control factory.
+	 * @param TActiveRecord record instance.
+	 * @return TScaffoldInputBase scaffold editor control factory.
+	 */
+	protected function getScaffoldInputBuilder($record)
+	{
+		static $_builders=array();
+		$class = get_class($record);
+		if(!isset($_builders[$class]))
+		{
+			Prado::using('System.Data.ActiveRecord.Scaffold.InputBuilder.TScaffoldInputBase');
+			$_builders[$class] = TScaffoldInputBase::createInputBuilder($record);
+		}
+		return $_builders[$class];
+	}
+
+	/**
+	 * @return string editor validation group name.
+	 */
+	public function getValidationGroup()
+	{
+		return 'group_'.$this->getUniqueID();
+	}
+}
+
+/**
+ * IScaffoldEditRenderer interface.
+ *
+ * IScaffoldEditRenderer defines the interface that an edit renderer
+ * needs to implement. Besides the {@link getData Data} property, an edit
+ * renderer also needs to provide {@link updateRecord updateRecord} method
+ * that is called before the save() method is called on the TActiveRecord.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ * @since 3.1
+ */
+interface IScaffoldEditRenderer extends IDataRenderer
+{
+	/**
+	 * This method should update the record with the user input data.
+	 * @param TActiveRecord record to be saved.
+	 */
+	public function updateRecord($record);
+}
+
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php
index 953420e3..bed5bf88 100644
--- a/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php
+++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldListView.php
@@ -1,306 +1,306 @@
-<?php
-/**
- * TScaffoldListView class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php
+/**
+ * TScaffoldListView class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
  * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Load the scaffold base class.
- */
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
-
-/**
- * TScaffoldListView displays a list of Active Records.
- *
- * The {@link getHeader Header} property is a TRepeater displaying the
- * Active Record property/field names. The {@link getSort Sort} property
- * is a drop down list displaying the combination of properties and its possible
- * ordering. The {@link getPager Pager} property is a TPager control displaying
- * the links and/or buttons that navigate to different pages in the Active Record data.
- * The {@link getList List} property is a TRepeater that renders a row of
- * Active Record data.
- *
- * Custom rendering of the each Active Record can be achieved by specifying
- * the ItemTemplate or AlternatingItemTemplate property of the main {@linnk getList List}
- * repeater.
- *
- * The TScaffoldListView will listen for two command events named "delete" and
- * "edit". A "delete" command will delete a the record for the row where the
- * "delete" command is originates. An "edit" command will push
- * the record data to be edited by a TScaffoldEditView with ID specified by the
- * {@link setEditViewID EditViewID}.
- *
- * Additional {@link setSearchCondition SearchCondition} and
- * {@link setSearchParameters SearchParameters} (takes array values) can be
- * specified to customize the records to be shown. The {@link setSearchCondition SearchCondition}
- * will be used as the Condition property of TActiveRecordCriteria, and similarly
- * the {@link setSearchParameters SearchParameters} will be the corresponding
- * Parameters property of TActiveRecordCriteria.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-class TScaffoldListView extends TScaffoldBase
-{
-	/**
-	 * Initialize the sort drop down list and the column names repeater.
-	 */
-	protected function initializeSort()
-	{
-		$table = $this->getTableInfo();
-		$sorts = array('Sort By', str_repeat('-',15));
-		$headers = array();
-		foreach($table->getColumns() as $name=>$colum)
-		{
-			$fname = ucwords(str_replace('_', ' ', $name));
-			$sorts[$name.' ASC'] = $fname .' Ascending';
-			$sorts[$name.' DESC'] = $fname .' Descending';
-			$headers[] = $fname ;
-		}
-		$this->_sort->setDataSource($sorts);
-		$this->_sort->dataBind();
-		$this->_header->setDataSource($headers);
-		$this->_header->dataBind();
-	}
-
-	/**
-	 * Loads and display the data.
-	 */
-	public function onPreRender($param)
-	{
-		parent::onPreRender($param);
-		if(!$this->getPage()->getIsPostBack() || $this->getViewState('CurrentClass')!=$this->getRecordClass())
-		{
-			$this->initializeSort();
-			$this->setViewState('CurrentClass', $this->getRecordClass());
-		}
-		$this->loadRecordData();
-	}
-
-	/**
-	 * Fetch the records and data bind it to the list.
-	 */
-	protected function loadRecordData()
-	{
-		$search = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters());
-		$this->_list->setVirtualItemCount($this->getRecordFinder()->count($search));
-		$finder = $this->getRecordFinder();
-		$criteria = $this->getRecordCriteria();
-		$this->_list->setDataSource($finder->findAll($criteria));
-		$this->_list->dataBind();
-	}
-
-	/**
-	 * @return TActiveRecordCriteria sort/search/paging criteria
-	 */
-	protected function getRecordCriteria()
-	{
-		$total = $this->_list->getVirtualItemCount();
-		$limit = $this->_list->getPageSize();
-		$offset = $this->_list->getCurrentPageIndex()*$limit;
-		if($offset + $limit > $total)
-			$limit = $total - $offset;
-		$criteria = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters());
-		if($limit > 0)
-		{
-			$criteria->setLimit($limit);
-			if($offset <= $total)
-				$criteria->setOffset($offset);
-		}
-		$order = explode(' ',$this->_sort->getSelectedValue(), 2);
-		if(is_array($order) && count($order) === 2)
-			$criteria->OrdersBy[$order[0]] = $order[1];
-		return $criteria;
-	}
-
-	/**
-	 * @param string search condition, the SQL string after the WHERE clause.
-	 */
-	public function setSearchCondition($value)
-	{
-		$this->setViewState('SearchCondition', $value);
-	}
-
-	/**
-	 * @param string SQL search condition for list display.
-	 */
-	public function getSearchCondition()
-	{
-		return $this->getViewState('SearchCondition');
-	}
-
-	/**
-	 * @param array search parameters
-	 */
-	public function setSearchParameters($value)
-	{
-		$this->setViewState('SearchParameters', TPropertyValue::ensureArray($value),array());
-	}
-
-	/**
-	 * @return array search parameters
-	 */
-	public function getSearchParameters()
-	{
-		return $this->getViewState('SearchParameters', array());
-	}
-
-	/**
-	 * Continue bubbling the "edit" command, "delete" command is handled in this class.
-	 */
-	public function bubbleEvent($sender, $param)
-	{
-		switch(strtolower($param->getCommandName()))
-		{
-			case 'delete':
-				return $this->deleteRecord($sender, $param);
-			case 'edit':
-				$this->initializeEdit($sender, $param);
-		}
-		$this->raiseBubbleEvent($this, $param);
-		return true;
-	}
-
-	/**
-	 * Initialize the edit view control form when EditViewID is set.
-	 */
-	protected function initializeEdit($sender, $param)
-	{
-		if(($ctrl=$this->getEditViewControl())!==null)
-		{
-			if($param instanceof TRepeaterCommandEventParameter)
-			{
-				$pk = $param->getItem()->getCustomData();
-				$ctrl->setRecordPk($pk);
-				$ctrl->initializeEditForm();
-			}
-		}
-	}
-
-	/**
-	 * Deletes an Active Record.
-	 */
-	protected function deleteRecord($sender, $param)
-	{
-		if($param instanceof TRepeaterCommandEventParameter)
-		{
-			$pk = $param->getItem()->getCustomData();
-			$this->getRecordFinder()->deleteByPk($pk);
-		}
-	}
-
-	/**
-	 * Initialize the default display for each Active Record item.
-	 */
-	protected function listItemCreated($sender, $param)
-	{
-		$item = $param->getItem();
-		if($item instanceof IItemDataRenderer)
-		{
-			$type = $item->getItemType();
-			if($type==TListItemType::Item || $type==TListItemType::AlternatingItem)
-				$this->populateField($sender, $param);
-		}
-	}
-
-	/**
-	 * Sets the Record primary key to the current repeater item's CustomData.
-	 * Binds the inner repeater with properties of the current Active Record.
-	 */
-	protected function populateField($sender, $param)
-	{
-		$item = $param->getItem();
-		if(($data = $item->getData()) !== null)
-		{
-			$item->setCustomData($this->getRecordPkValues($data));
-			if(($prop = $item->findControl('_properties'))!==null)
-			{
-				$item->_properties->setDataSource($this->getRecordPropertyValues($data));
-				$item->_properties->dataBind();
-			}
-		}
-	}
-
-	/**
-	 * Updates repeater page index with the pager new index value.
-	 */
-	protected function pageChanged($sender, $param)
-	{
-		$this->_list->setCurrentPageIndex($param->getNewPageIndex());
-	}
-
-	/**
-	 * @return TRepeater Repeater control for Active Record instances.
-	 */
-	public function getList()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_list');
-	}
-
-	/**
-	 * @return TPager List pager control.
-	 */
-	public function getPager()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_pager');
-	}
-
-	/**
-	 * @return TDropDownList Control that displays and controls the record ordering.
-	 */
-	public function getSort()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_sort');
-	}
-
-	/**
-	 * @return TRepeater Repeater control for record property names.
-	 */
-	public function getHeader()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_header');
-	}
-
-	/**
-	 * @return string TScaffoldEditView control ID for editing selected Active Record.
-	 */
-	public function getEditViewID()
-	{
-		return $this->getViewState('EditViewID');
-	}
-
-	/**
-	 * @param string TScaffoldEditView control ID for editing selected Active Record.
-	 */
-	public function setEditViewID($value)
-	{
-		$this->setViewState('EditViewID', $value);
-	}
-
-	/**
-	 * @return TScaffoldEditView control for editing selected Active Record, null if EditViewID is not set.
-	 */
-	protected function getEditViewControl()
-	{
-		if(($id=$this->getEditViewID())!==null)
-		{
-			$ctrl = $this->getParent()->findControl($id);
-			if($ctrl===null)
-				throw new TConfigurationException('scaffold_unable_to_find_edit_view', $id);
-			return $ctrl;
-		}
-	}
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ */
+
+/**
+ * Load the scaffold base class.
+ */
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
+
+/**
+ * TScaffoldListView displays a list of Active Records.
+ *
+ * The {@link getHeader Header} property is a TRepeater displaying the
+ * Active Record property/field names. The {@link getSort Sort} property
+ * is a drop down list displaying the combination of properties and its possible
+ * ordering. The {@link getPager Pager} property is a TPager control displaying
+ * the links and/or buttons that navigate to different pages in the Active Record data.
+ * The {@link getList List} property is a TRepeater that renders a row of
+ * Active Record data.
+ *
+ * Custom rendering of the each Active Record can be achieved by specifying
+ * the ItemTemplate or AlternatingItemTemplate property of the main {@linnk getList List}
+ * repeater.
+ *
+ * The TScaffoldListView will listen for two command events named "delete" and
+ * "edit". A "delete" command will delete a the record for the row where the
+ * "delete" command is originates. An "edit" command will push
+ * the record data to be edited by a TScaffoldEditView with ID specified by the
+ * {@link setEditViewID EditViewID}.
+ *
+ * Additional {@link setSearchCondition SearchCondition} and
+ * {@link setSearchParameters SearchParameters} (takes array values) can be
+ * specified to customize the records to be shown. The {@link setSearchCondition SearchCondition}
+ * will be used as the Condition property of TActiveRecordCriteria, and similarly
+ * the {@link setSearchParameters SearchParameters} will be the corresponding
+ * Parameters property of TActiveRecordCriteria.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ * @since 3.1
+ */
+class TScaffoldListView extends TScaffoldBase
+{
+	/**
+	 * Initialize the sort drop down list and the column names repeater.
+	 */
+	protected function initializeSort()
+	{
+		$table = $this->getTableInfo();
+		$sorts = array('Sort By', str_repeat('-',15));
+		$headers = array();
+		foreach($table->getColumns() as $name=>$colum)
+		{
+			$fname = ucwords(str_replace('_', ' ', $name));
+			$sorts[$name.' ASC'] = $fname .' Ascending';
+			$sorts[$name.' DESC'] = $fname .' Descending';
+			$headers[] = $fname ;
+		}
+		$this->_sort->setDataSource($sorts);
+		$this->_sort->dataBind();
+		$this->_header->setDataSource($headers);
+		$this->_header->dataBind();
+	}
+
+	/**
+	 * Loads and display the data.
+	 */
+	public function onPreRender($param)
+	{
+		parent::onPreRender($param);
+		if(!$this->getPage()->getIsPostBack() || $this->getViewState('CurrentClass')!=$this->getRecordClass())
+		{
+			$this->initializeSort();
+			$this->setViewState('CurrentClass', $this->getRecordClass());
+		}
+		$this->loadRecordData();
+	}
+
+	/**
+	 * Fetch the records and data bind it to the list.
+	 */
+	protected function loadRecordData()
+	{
+		$search = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters());
+		$this->_list->setVirtualItemCount($this->getRecordFinder()->count($search));
+		$finder = $this->getRecordFinder();
+		$criteria = $this->getRecordCriteria();
+		$this->_list->setDataSource($finder->findAll($criteria));
+		$this->_list->dataBind();
+	}
+
+	/**
+	 * @return TActiveRecordCriteria sort/search/paging criteria
+	 */
+	protected function getRecordCriteria()
+	{
+		$total = $this->_list->getVirtualItemCount();
+		$limit = $this->_list->getPageSize();
+		$offset = $this->_list->getCurrentPageIndex()*$limit;
+		if($offset + $limit > $total)
+			$limit = $total - $offset;
+		$criteria = new TActiveRecordCriteria($this->getSearchCondition(), $this->getSearchParameters());
+		if($limit > 0)
+		{
+			$criteria->setLimit($limit);
+			if($offset <= $total)
+				$criteria->setOffset($offset);
+		}
+		$order = explode(' ',$this->_sort->getSelectedValue(), 2);
+		if(is_array($order) && count($order) === 2)
+			$criteria->OrdersBy[$order[0]] = $order[1];
+		return $criteria;
+	}
+
+	/**
+	 * @param string search condition, the SQL string after the WHERE clause.
+	 */
+	public function setSearchCondition($value)
+	{
+		$this->setViewState('SearchCondition', $value);
+	}
+
+	/**
+	 * @param string SQL search condition for list display.
+	 */
+	public function getSearchCondition()
+	{
+		return $this->getViewState('SearchCondition');
+	}
+
+	/**
+	 * @param array search parameters
+	 */
+	public function setSearchParameters($value)
+	{
+		$this->setViewState('SearchParameters', TPropertyValue::ensureArray($value),array());
+	}
+
+	/**
+	 * @return array search parameters
+	 */
+	public function getSearchParameters()
+	{
+		return $this->getViewState('SearchParameters', array());
+	}
+
+	/**
+	 * Continue bubbling the "edit" command, "delete" command is handled in this class.
+	 */
+	public function bubbleEvent($sender, $param)
+	{
+		switch(strtolower($param->getCommandName()))
+		{
+			case 'delete':
+				return $this->deleteRecord($sender, $param);
+			case 'edit':
+				$this->initializeEdit($sender, $param);
+		}
+		$this->raiseBubbleEvent($this, $param);
+		return true;
+	}
+
+	/**
+	 * Initialize the edit view control form when EditViewID is set.
+	 */
+	protected function initializeEdit($sender, $param)
+	{
+		if(($ctrl=$this->getEditViewControl())!==null)
+		{
+			if($param instanceof TRepeaterCommandEventParameter)
+			{
+				$pk = $param->getItem()->getCustomData();
+				$ctrl->setRecordPk($pk);
+				$ctrl->initializeEditForm();
+			}
+		}
+	}
+
+	/**
+	 * Deletes an Active Record.
+	 */
+	protected function deleteRecord($sender, $param)
+	{
+		if($param instanceof TRepeaterCommandEventParameter)
+		{
+			$pk = $param->getItem()->getCustomData();
+			$this->getRecordFinder()->deleteByPk($pk);
+		}
+	}
+
+	/**
+	 * Initialize the default display for each Active Record item.
+	 */
+	protected function listItemCreated($sender, $param)
+	{
+		$item = $param->getItem();
+		if($item instanceof IItemDataRenderer)
+		{
+			$type = $item->getItemType();
+			if($type==TListItemType::Item || $type==TListItemType::AlternatingItem)
+				$this->populateField($sender, $param);
+		}
+	}
+
+	/**
+	 * Sets the Record primary key to the current repeater item's CustomData.
+	 * Binds the inner repeater with properties of the current Active Record.
+	 */
+	protected function populateField($sender, $param)
+	{
+		$item = $param->getItem();
+		if(($data = $item->getData()) !== null)
+		{
+			$item->setCustomData($this->getRecordPkValues($data));
+			if(($prop = $item->findControl('_properties'))!==null)
+			{
+				$item->_properties->setDataSource($this->getRecordPropertyValues($data));
+				$item->_properties->dataBind();
+			}
+		}
+	}
+
+	/**
+	 * Updates repeater page index with the pager new index value.
+	 */
+	protected function pageChanged($sender, $param)
+	{
+		$this->_list->setCurrentPageIndex($param->getNewPageIndex());
+	}
+
+	/**
+	 * @return TRepeater Repeater control for Active Record instances.
+	 */
+	public function getList()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_list');
+	}
+
+	/**
+	 * @return TPager List pager control.
+	 */
+	public function getPager()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_pager');
+	}
+
+	/**
+	 * @return TDropDownList Control that displays and controls the record ordering.
+	 */
+	public function getSort()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_sort');
+	}
+
+	/**
+	 * @return TRepeater Repeater control for record property names.
+	 */
+	public function getHeader()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_header');
+	}
+
+	/**
+	 * @return string TScaffoldEditView control ID for editing selected Active Record.
+	 */
+	public function getEditViewID()
+	{
+		return $this->getViewState('EditViewID');
+	}
+
+	/**
+	 * @param string TScaffoldEditView control ID for editing selected Active Record.
+	 */
+	public function setEditViewID($value)
+	{
+		$this->setViewState('EditViewID', $value);
+	}
+
+	/**
+	 * @return TScaffoldEditView control for editing selected Active Record, null if EditViewID is not set.
+	 */
+	protected function getEditViewControl()
+	{
+		if(($id=$this->getEditViewID())!==null)
+		{
+			$ctrl = $this->getParent()->findControl($id);
+			if($ctrl===null)
+				throw new TConfigurationException('scaffold_unable_to_find_edit_view', $id);
+			return $ctrl;
+		}
+	}
+}
 
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php
index bd679c94..40335085 100644
--- a/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php
+++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldSearch.php
@@ -1,150 +1,150 @@
-<?php
-/**
- * TScaffoldSearch class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php
+/**
+ * TScaffoldSearch class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
  * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Import the scaffold base.
- */
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
-
-/**
- * TScaffoldSearch provide a simple textbox and a button that is used
- * to perform search on a TScaffoldListView with ID given by {@link setListViewID ListViewID}.
- *
- * The {@link getSearchText SearchText} property is a TTextBox and the
- * {@link getSearchButton SearchButton} property is a TButton with label value "Search".
- *
- * Searchable fields of the Active Record can be restricted by specifying
- * a comma delimited string of allowable fields in the
- * {@link setSearchableFields SearchableFields} property. The default is null,
- * meaning that most text type fields are searched (the default searchable fields
- * are database dependent).
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.1
- */
-class TScaffoldSearch extends TScaffoldBase
-{
-	/**
-	 * @var TScaffoldListView the scaffold list view.
-	 */
-	private $_list;
-
-	/**
-	 * @return TScaffoldListView the scaffold list view this search box belongs to.
-	 */
-	protected function getListView()
-	{
-		if($this->_list===null && ($id = $this->getListViewID()) !== null)
-		{
-			$this->_list = $this->getParent()->findControl($id);
-			if($this->_list ===null)
-				throw new TConfigurationException('scaffold_unable_to_find_list_view', $id);
-		}
-		return $this->_list;
-	}
-
-	/**
-	 * @param string ID of the TScaffoldListView this search control belongs to.
-	 */
-	public function setListViewID($value)
-	{
-		$this->setViewState('ListViewID', $value);
-	}
-
-	/**
-	 * @return string ID of the TScaffoldListView this search control belongs to.
-	 */
-	public function getListViewID()
-	{
-		return $this->getViewState('ListViewID');
-	}
-
-	/**
-	 * Sets the SearchCondition of the TScaffoldListView as the search terms
-	 * given by the text of the search text box.
-	 */
-	public function bubbleEvent($sender, $param)
-	{
-		if(strtolower($param->getCommandName())==='search')
-		{
-			if(($list = $this->getListView()) !== null)
-			{
-				$list->setSearchCondition($this->createSearchCondition());
-				return false;
-			}
-		}
-		$this->raiseBubbleEvent($this, $param);
-		return true;
-	}
-
-	/**
-	 * @return string the search criteria for the search terms in the search text box.
-	 */
-	protected function createSearchCondition()
-	{
-		$table = $this->getTableInfo();
-		if(strlen($str=$this->getSearchText()->getText()) > 0)
-		{
-			$builder = $table->createCommandBuilder($this->getRecordFinder()->getDbConnection());
-			return $builder->getSearchExpression($this->getFields(), $str);
-		}
-	}
-
-	/**
-	 * @return array list of fields to be searched.
-	 */
-	protected function getFields()
-	{
-		if(strlen(trim($str=$this->getSearchableFields()))>0)
-			$fields = preg_split('/\s*,\s*/', $str);
-		else
-			$fields = $this->getTableInfo()->getColumns()->getKeys();
-		return $fields;
-	}
-
-	/**
-	 * @return string comma delimited list of fields that may be searched.
-	 */
-	public function getSearchableFields()
-	{
-		return $this->getViewState('SearchableFields','');
-	}
-
-	/**
-	 * @param string comma delimited list of fields that may be searched.
-	 */
-	public function setSearchableFields($value)
-	{
-		$this->setViewState('SearchableFields', $value, '');
-	}
-
-	/**
-	 * @return TButton button with default label "Search".
-	 */
-	public function getSearchButton()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_search');
-	}
-
-	/**
-	 * @return TTextBox search text box.
-	 */
-	public function getSearchText()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_textbox');
-	}
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ */
+
+/**
+ * Import the scaffold base.
+ */
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
+
+/**
+ * TScaffoldSearch provide a simple textbox and a button that is used
+ * to perform search on a TScaffoldListView with ID given by {@link setListViewID ListViewID}.
+ *
+ * The {@link getSearchText SearchText} property is a TTextBox and the
+ * {@link getSearchButton SearchButton} property is a TButton with label value "Search".
+ *
+ * Searchable fields of the Active Record can be restricted by specifying
+ * a comma delimited string of allowable fields in the
+ * {@link setSearchableFields SearchableFields} property. The default is null,
+ * meaning that most text type fields are searched (the default searchable fields
+ * are database dependent).
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ * @since 3.1
+ */
+class TScaffoldSearch extends TScaffoldBase
+{
+	/**
+	 * @var TScaffoldListView the scaffold list view.
+	 */
+	private $_list;
+
+	/**
+	 * @return TScaffoldListView the scaffold list view this search box belongs to.
+	 */
+	protected function getListView()
+	{
+		if($this->_list===null && ($id = $this->getListViewID()) !== null)
+		{
+			$this->_list = $this->getParent()->findControl($id);
+			if($this->_list ===null)
+				throw new TConfigurationException('scaffold_unable_to_find_list_view', $id);
+		}
+		return $this->_list;
+	}
+
+	/**
+	 * @param string ID of the TScaffoldListView this search control belongs to.
+	 */
+	public function setListViewID($value)
+	{
+		$this->setViewState('ListViewID', $value);
+	}
+
+	/**
+	 * @return string ID of the TScaffoldListView this search control belongs to.
+	 */
+	public function getListViewID()
+	{
+		return $this->getViewState('ListViewID');
+	}
+
+	/**
+	 * Sets the SearchCondition of the TScaffoldListView as the search terms
+	 * given by the text of the search text box.
+	 */
+	public function bubbleEvent($sender, $param)
+	{
+		if(strtolower($param->getCommandName())==='search')
+		{
+			if(($list = $this->getListView()) !== null)
+			{
+				$list->setSearchCondition($this->createSearchCondition());
+				return false;
+			}
+		}
+		$this->raiseBubbleEvent($this, $param);
+		return true;
+	}
+
+	/**
+	 * @return string the search criteria for the search terms in the search text box.
+	 */
+	protected function createSearchCondition()
+	{
+		$table = $this->getTableInfo();
+		if(strlen($str=$this->getSearchText()->getText()) > 0)
+		{
+			$builder = $table->createCommandBuilder($this->getRecordFinder()->getDbConnection());
+			return $builder->getSearchExpression($this->getFields(), $str);
+		}
+	}
+
+	/**
+	 * @return array list of fields to be searched.
+	 */
+	protected function getFields()
+	{
+		if(strlen(trim($str=$this->getSearchableFields()))>0)
+			$fields = preg_split('/\s*,\s*/', $str);
+		else
+			$fields = $this->getTableInfo()->getColumns()->getKeys();
+		return $fields;
+	}
+
+	/**
+	 * @return string comma delimited list of fields that may be searched.
+	 */
+	public function getSearchableFields()
+	{
+		return $this->getViewState('SearchableFields','');
+	}
+
+	/**
+	 * @param string comma delimited list of fields that may be searched.
+	 */
+	public function setSearchableFields($value)
+	{
+		$this->setViewState('SearchableFields', $value, '');
+	}
+
+	/**
+	 * @return TButton button with default label "Search".
+	 */
+	public function getSearchButton()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_search');
+	}
+
+	/**
+	 * @return TTextBox search text box.
+	 */
+	public function getSearchText()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_textbox');
+	}
+}
 
diff --git a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php
index 69bc1f81..40eee21d 100644
--- a/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php
+++ b/framework/Data/ActiveRecord/Scaffold/TScaffoldView.php
@@ -1,143 +1,143 @@
-<?php
-/**
- * TScaffoldView class.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
- * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- */
-
-/**
- * Import scaffold base, list, edit and search controls.
- */
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldListView');
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldEditView');
-Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldSearch');
-
-/**
- * TScaffoldView is a composite control consisting of TScaffoldListView
- * with a TScaffoldSearch. In addition, it will display a TScaffoldEditView
- * when an "edit" command is raised from the TScaffoldListView (when the
- * edit button is clicked). Futher more, the "add" button can be clicked
- * that shows an empty data TScaffoldListView for creating new records.
- *
- * The {@link getListView ListView} property gives a TScaffoldListView for
- * display the record data. The {@link getEditView EditView} is the
- * TScaffoldEditView that renders the
- * inputs for editing and adding records. The {@link getSearchControl SearchControl}
- * is a TScaffoldSearch responsible to the search user interface.
- *
- * Set the {@link setRecordClass RecordClass} property to the name of
- * the Active Record class to be displayed/edited/added.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord.Scaffold
- * @since 3.0
- */
-class TScaffoldView extends TScaffoldBase
-{
-	/**
-	 * Copy basic record details to the list/edit/search controls.
-	 */
-	public function onPreRender($param)
-	{
-		parent::onPreRender($param);
-		$this->getListView()->copyFrom($this);
-		$this->getEditView()->copyFrom($this);
-		$this->getSearchControl()->copyFrom($this);
-	}
-
-	/**
-	 * @return TScaffoldListView scaffold list view.
-	 */
-	public function getListView()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_listView');
-	}
-
-	/**
-	 * @return TScaffoldEditView scaffold edit view.
-	 */
-	public function getEditView()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_editView');
-	}
-
-	/**
-	 * @return TScaffoldSearch scaffold search textbox and button.
-	 */
-	public function getSearchControl()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_search');
-	}
-
-	/**
-	 * @return TButton "Add new record" button.
-	 */
-	public function getAddButton()
-	{
-		$this->ensureChildControls();
-		return $this->getRegisteredObject('_newButton');
-	}
-
-	/**
-	 * Handle the "edit" and "new" commands by displaying the edit view.
-	 * Default command shows the list view.
-	 */
-	public function bubbleEvent($sender,$param)
-	{
-		switch(strtolower($param->getCommandName()))
-		{
-			case 'edit':
-				return $this->showEditView($sender, $param);
-			case 'new':
-				return $this->showAddView($sender, $param);
-			default:
-				return $this->showListView($sender, $param);
-		}
-		return false;
-	}
-
-	/**
-	 * Shows the edit record view.
-	 */
-	protected function showEditView($sender, $param)
-	{
-		$this->getListView()->setVisible(false);
-		$this->getEditView()->setVisible(true);
-		$this->_panForNewButton->setVisible(false);
-		$this->_panForSearch->setVisible(false);
-		$this->getEditView()->getCancelButton()->setVisible(true);
-		$this->getEditView()->getClearButton()->setVisible(false);
-	}
-
-	/**
-	 * Shows the view for listing the records.
-	 */
-	protected function showListView($sender, $param)
-	{
-		$this->getListView()->setVisible(true);
-		$this->getEditView()->setVisible(false);
-		$this->_panForNewButton->setVisible(true);
-		$this->_panForSearch->setVisible(true);
-	}
-
-	/**
-	 * Shows the add record view.
-	 */
-	protected function showAddView($sender, $param)
-	{
-		$this->getEditView()->setRecordPk(null);
-		$this->getEditView()->initializeEditForm();
-		$this->showEditView($sender, $param);
-	}
-}
-
+<?php
+/**
+ * TScaffoldView class.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
+ * @copyright Copyright &copy; 2005-2012 PradoSoft
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ */
+
+/**
+ * Import scaffold base, list, edit and search controls.
+ */
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldBase');
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldListView');
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldEditView');
+Prado::using('System.Data.ActiveRecord.Scaffold.TScaffoldSearch');
+
+/**
+ * TScaffoldView is a composite control consisting of TScaffoldListView
+ * with a TScaffoldSearch. In addition, it will display a TScaffoldEditView
+ * when an "edit" command is raised from the TScaffoldListView (when the
+ * edit button is clicked). Futher more, the "add" button can be clicked
+ * that shows an empty data TScaffoldListView for creating new records.
+ *
+ * The {@link getListView ListView} property gives a TScaffoldListView for
+ * display the record data. The {@link getEditView EditView} is the
+ * TScaffoldEditView that renders the
+ * inputs for editing and adding records. The {@link getSearchControl SearchControl}
+ * is a TScaffoldSearch responsible to the search user interface.
+ *
+ * Set the {@link setRecordClass RecordClass} property to the name of
+ * the Active Record class to be displayed/edited/added.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord.Scaffold
+ * @since 3.0
+ */
+class TScaffoldView extends TScaffoldBase
+{
+	/**
+	 * Copy basic record details to the list/edit/search controls.
+	 */
+	public function onPreRender($param)
+	{
+		parent::onPreRender($param);
+		$this->getListView()->copyFrom($this);
+		$this->getEditView()->copyFrom($this);
+		$this->getSearchControl()->copyFrom($this);
+	}
+
+	/**
+	 * @return TScaffoldListView scaffold list view.
+	 */
+	public function getListView()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_listView');
+	}
+
+	/**
+	 * @return TScaffoldEditView scaffold edit view.
+	 */
+	public function getEditView()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_editView');
+	}
+
+	/**
+	 * @return TScaffoldSearch scaffold search textbox and button.
+	 */
+	public function getSearchControl()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_search');
+	}
+
+	/**
+	 * @return TButton "Add new record" button.
+	 */
+	public function getAddButton()
+	{
+		$this->ensureChildControls();
+		return $this->getRegisteredObject('_newButton');
+	}
+
+	/**
+	 * Handle the "edit" and "new" commands by displaying the edit view.
+	 * Default command shows the list view.
+	 */
+	public function bubbleEvent($sender,$param)
+	{
+		switch(strtolower($param->getCommandName()))
+		{
+			case 'edit':
+				return $this->showEditView($sender, $param);
+			case 'new':
+				return $this->showAddView($sender, $param);
+			default:
+				return $this->showListView($sender, $param);
+		}
+		return false;
+	}
+
+	/**
+	 * Shows the edit record view.
+	 */
+	protected function showEditView($sender, $param)
+	{
+		$this->getListView()->setVisible(false);
+		$this->getEditView()->setVisible(true);
+		$this->_panForNewButton->setVisible(false);
+		$this->_panForSearch->setVisible(false);
+		$this->getEditView()->getCancelButton()->setVisible(true);
+		$this->getEditView()->getClearButton()->setVisible(false);
+	}
+
+	/**
+	 * Shows the view for listing the records.
+	 */
+	protected function showListView($sender, $param)
+	{
+		$this->getListView()->setVisible(true);
+		$this->getEditView()->setVisible(false);
+		$this->_panForNewButton->setVisible(true);
+		$this->_panForSearch->setVisible(true);
+	}
+
+	/**
+	 * Shows the add record view.
+	 */
+	protected function showAddView($sender, $param)
+	{
+		$this->getEditView()->setRecordPk(null);
+		$this->getEditView()->initializeEditForm();
+		$this->showEditView($sender, $param);
+	}
+}
+
diff --git a/framework/Data/ActiveRecord/TActiveRecordConfig.php b/framework/Data/ActiveRecord/TActiveRecordConfig.php
index 478786d3..821c4223 100644
--- a/framework/Data/ActiveRecord/TActiveRecordConfig.php
+++ b/framework/Data/ActiveRecord/TActiveRecordConfig.php
@@ -1,201 +1,201 @@
-<?php
-/**
- * TActiveRecordConfig class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php
+/**
+ * TActiveRecordConfig class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
  * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord
- */
-
-Prado::using('System.Data.TDataSourceConfig');
-Prado::using('System.Data.ActiveRecord.TActiveRecordManager');
-
-/**
- * TActiveRecordConfig module configuration class.
- *
- * Database configuration for the default ActiveRecord manager instance.
- *
- * Example: application.xml configuration
- * <code>
- * <modules>
- * 	<module class="System.Data.ActiveRecord.TActiveRecordConfig" EnableCache="true">
- * 		<database ConnectionString="mysql:host=localhost;dbname=test"
- * 			Username="dbuser" Password="dbpass" />
- * 	</module>
- * </modules>
- * </code>
- *
- * MySQL database definition:
- * <code>
- * CREATE TABLE `blogs` (
- *  `blog_id` int(10) unsigned NOT NULL auto_increment,
- *  `blog_name` varchar(255) NOT NULL,
- *  `blog_author` varchar(255) NOT NULL,
- *  PRIMARY KEY  (`blog_id`)
- * ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
- * </code>
- *
- * Record php class:
- * <code>
- * class Blogs extends TActiveRecord
- * {
- * 	public $blog_id;
- *	public $blog_name;
- *	public $blog_author;
- *
- *	public static function finder($className=__CLASS__)
- *	{
- *		return parent::finder($className);
- *	}
- * }
- * </code>
- *
- * Usage example:
- * <code>
- * class Home extends TPage
- * {
- * 	function onLoad($param)
- * 	{
- * 		$blogs = Blogs::finder()->findAll();
- *      print_r($blogs);
- * 	}
- * }
- * </code>
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord
- * @since 3.1
- */
-class TActiveRecordConfig extends TDataSourceConfig
-{
-	const DEFAULT_MANAGER_CLASS = 'System.Data.ActiveRecord.TActiveRecordManager';
-	const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway';
-
-	/**
-	 * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS}
-	 * @var string
-	 */
-	private $_managerClass = self::DEFAULT_MANAGER_CLASS;
-
-	/**
-	 * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
-	 * @var string
-	 */
-	private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS;
-
-	/**
-	 * @var TActiveRecordManager
-	 */
-	private $_manager = null;
-
-	private $_enableCache=false;
-
-	/**
-	 * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'
-	 *
-	 * @var TActiveRecordInvalidFinderResult
-	 * @since 3.1.5
-	 */
-	private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null;
-
-	/**
-	 * Initialize the active record manager.
-	 * @param TXmlDocument xml configuration.
-	 */
-	public function init($xml)
-	{
-		parent::init($xml);
-		$manager = $this -> getManager();
-		if($this->getEnableCache())
-			$manager->setCache($this->getApplication()->getCache());
-		$manager->setDbConnection($this->getDbConnection());
-		$manager->setInvalidFinderResult($this->getInvalidFinderResult());
-		$manager->setGatewayClass($this->getGatewayClass());
-	}
-
-	/**
-	 * @return TActiveRecordManager
-	 */
-	public function getManager() {
-		if($this->_manager === null)
-			$this->_manager = Prado::createComponent($this -> getManagerClass());
-		return TActiveRecordManager::getInstance($this->_manager);
-	}
-
-	/**
-	 * Set implementation class of ActiveRecordManager
-	 * @param string $value
-	 */
-	public function setManagerClass($value)
-	{
-		$this->_managerClass = TPropertyValue::ensureString($value);
-	}
-
-	/**
-	 * @return string the implementation class of ActiveRecordManager. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS}
-	 */
-	public function getManagerClass()
-	{
-		return $this->_managerClass;
-	}
-
-	/**
-	 * Set implementation class of ActiveRecordGateway
-	 * @param string $value
-	 */
-	public function setGatewayClass($value)
-	{
-		$this->_gatewayClass = TPropertyValue::ensureString($value);
-	}
-
-	/**
-	 * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
-	 */
-	public function getGatewayClass()
-	{
-		return $this->_gatewayClass;
-	}
-
-	/**
-	 * Set true to cache the table meta data.
-	 * @param boolean true to cache sqlmap instance.
-	 */
-	public function setEnableCache($value)
-	{
-		$this->_enableCache = TPropertyValue::ensureBoolean($value);
-	}
-
-	/**
-	 * @return boolean true if table meta data should be cached, false otherwise.
-	 */
-	public function getEnableCache()
-	{
-		return $this->_enableCache;
-	}
-
-	/**
-	 * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'.
-	 * @see setInvalidFinderResult
-	 * @since 3.1.5
-	 */
-	public function getInvalidFinderResult()
-	{
-		return $this->_invalidFinderResult;
-	}
-
-	/**
-	 * Define the way an active record finder react if an invalid magic-finder invoked
-	 *
-	 * @param TActiveRecordInvalidFinderResult
-	 * @see getInvalidFinderResult
-	 * @since 3.1.5
-	 */
-	public function setInvalidFinderResult($value)
-	{
-		$this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult');
-	}
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ */
+
+Prado::using('System.Data.TDataSourceConfig');
+Prado::using('System.Data.ActiveRecord.TActiveRecordManager');
+
+/**
+ * TActiveRecordConfig module configuration class.
+ *
+ * Database configuration for the default ActiveRecord manager instance.
+ *
+ * Example: application.xml configuration
+ * <code>
+ * <modules>
+ * 	<module class="System.Data.ActiveRecord.TActiveRecordConfig" EnableCache="true">
+ * 		<database ConnectionString="mysql:host=localhost;dbname=test"
+ * 			Username="dbuser" Password="dbpass" />
+ * 	</module>
+ * </modules>
+ * </code>
+ *
+ * MySQL database definition:
+ * <code>
+ * CREATE TABLE `blogs` (
+ *  `blog_id` int(10) unsigned NOT NULL auto_increment,
+ *  `blog_name` varchar(255) NOT NULL,
+ *  `blog_author` varchar(255) NOT NULL,
+ *  PRIMARY KEY  (`blog_id`)
+ * ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+ * </code>
+ *
+ * Record php class:
+ * <code>
+ * class Blogs extends TActiveRecord
+ * {
+ * 	public $blog_id;
+ *	public $blog_name;
+ *	public $blog_author;
+ *
+ *	public static function finder($className=__CLASS__)
+ *	{
+ *		return parent::finder($className);
+ *	}
+ * }
+ * </code>
+ *
+ * Usage example:
+ * <code>
+ * class Home extends TPage
+ * {
+ * 	function onLoad($param)
+ * 	{
+ * 		$blogs = Blogs::finder()->findAll();
+ *      print_r($blogs);
+ * 	}
+ * }
+ * </code>
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ * @since 3.1
+ */
+class TActiveRecordConfig extends TDataSourceConfig
+{
+	const DEFAULT_MANAGER_CLASS = 'System.Data.ActiveRecord.TActiveRecordManager';
+	const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway';
+
+	/**
+	 * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS}
+	 * @var string
+	 */
+	private $_managerClass = self::DEFAULT_MANAGER_CLASS;
+
+	/**
+	 * Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
+	 * @var string
+	 */
+	private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS;
+
+	/**
+	 * @var TActiveRecordManager
+	 */
+	private $_manager = null;
+
+	private $_enableCache=false;
+
+	/**
+	 * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'
+	 *
+	 * @var TActiveRecordInvalidFinderResult
+	 * @since 3.1.5
+	 */
+	private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null;
+
+	/**
+	 * Initialize the active record manager.
+	 * @param TXmlDocument xml configuration.
+	 */
+	public function init($xml)
+	{
+		parent::init($xml);
+		$manager = $this -> getManager();
+		if($this->getEnableCache())
+			$manager->setCache($this->getApplication()->getCache());
+		$manager->setDbConnection($this->getDbConnection());
+		$manager->setInvalidFinderResult($this->getInvalidFinderResult());
+		$manager->setGatewayClass($this->getGatewayClass());
+	}
+
+	/**
+	 * @return TActiveRecordManager
+	 */
+	public function getManager() {
+		if($this->_manager === null)
+			$this->_manager = Prado::createComponent($this -> getManagerClass());
+		return TActiveRecordManager::getInstance($this->_manager);
+	}
+
+	/**
+	 * Set implementation class of ActiveRecordManager
+	 * @param string $value
+	 */
+	public function setManagerClass($value)
+	{
+		$this->_managerClass = TPropertyValue::ensureString($value);
+	}
+
+	/**
+	 * @return string the implementation class of ActiveRecordManager. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_MANAGER_CLASS}
+	 */
+	public function getManagerClass()
+	{
+		return $this->_managerClass;
+	}
+
+	/**
+	 * Set implementation class of ActiveRecordGateway
+	 * @param string $value
+	 */
+	public function setGatewayClass($value)
+	{
+		$this->_gatewayClass = TPropertyValue::ensureString($value);
+	}
+
+	/**
+	 * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordConfig::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
+	 */
+	public function getGatewayClass()
+	{
+		return $this->_gatewayClass;
+	}
+
+	/**
+	 * Set true to cache the table meta data.
+	 * @param boolean true to cache sqlmap instance.
+	 */
+	public function setEnableCache($value)
+	{
+		$this->_enableCache = TPropertyValue::ensureBoolean($value);
+	}
+
+	/**
+	 * @return boolean true if table meta data should be cached, false otherwise.
+	 */
+	public function getEnableCache()
+	{
+		return $this->_enableCache;
+	}
+
+	/**
+	 * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'.
+	 * @see setInvalidFinderResult
+	 * @since 3.1.5
+	 */
+	public function getInvalidFinderResult()
+	{
+		return $this->_invalidFinderResult;
+	}
+
+	/**
+	 * Define the way an active record finder react if an invalid magic-finder invoked
+	 *
+	 * @param TActiveRecordInvalidFinderResult
+	 * @see getInvalidFinderResult
+	 * @since 3.1.5
+	 */
+	public function setInvalidFinderResult($value)
+	{
+		$this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult');
+	}
+}
diff --git a/framework/Data/ActiveRecord/TActiveRecordCriteria.php b/framework/Data/ActiveRecord/TActiveRecordCriteria.php
index d15f83ee..cdd7f964 100644
--- a/framework/Data/ActiveRecord/TActiveRecordCriteria.php
+++ b/framework/Data/ActiveRecord/TActiveRecordCriteria.php
@@ -1,39 +1,39 @@
 <?php
-/**
- * TActiveRecordCriteria class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+/**
+ * TActiveRecordCriteria class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
  * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord
- */
-
-Prado::using('System.Data.DataGateway.TSqlCriteria');
-
-/**
- * Search criteria for Active Record.
- *
- * Criteria object for active record finder methods. Usage:
- * <code>
- * $criteria = new TActiveRecordCriteria;
- * $criteria->Condition = 'username = :name AND password = :pass';
- * $criteria->Parameters[':name'] = 'admin';
- * $criteria->Parameters[':pass'] = 'prado';
- * $criteria->OrdersBy['level'] = 'desc';
- * $criteria->OrdersBy['name'] = 'asc';
- * $criteria->Limit = 10;
- * $criteria->Offset = 20;
- * </code>
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord
- * @since 3.1
- */
-class TActiveRecordCriteria extends TSqlCriteria
-{
-
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ */
+
+Prado::using('System.Data.DataGateway.TSqlCriteria');
+
+/**
+ * Search criteria for Active Record.
+ *
+ * Criteria object for active record finder methods. Usage:
+ * <code>
+ * $criteria = new TActiveRecordCriteria;
+ * $criteria->Condition = 'username = :name AND password = :pass';
+ * $criteria->Parameters[':name'] = 'admin';
+ * $criteria->Parameters[':pass'] = 'prado';
+ * $criteria->OrdersBy['level'] = 'desc';
+ * $criteria->OrdersBy['name'] = 'asc';
+ * $criteria->Limit = 10;
+ * $criteria->Offset = 20;
+ * </code>
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ * @since 3.1
+ */
+class TActiveRecordCriteria extends TSqlCriteria
+{
+
+}
 
diff --git a/framework/Data/ActiveRecord/TActiveRecordManager.php b/framework/Data/ActiveRecord/TActiveRecordManager.php
index 00979d1c..6cca76e7 100644
--- a/framework/Data/ActiveRecord/TActiveRecordManager.php
+++ b/framework/Data/ActiveRecord/TActiveRecordManager.php
@@ -1,163 +1,163 @@
-<?php
-/**
- * TActiveRecordManager class file.
- *
- * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
- * @link http://www.pradosoft.com/
+<?php
+/**
+ * TActiveRecordManager class file.
+ *
+ * @author Wei Zhuo <weizhuo[at]gmail[dot]com>
+ * @link http://www.pradosoft.com/
  * @copyright Copyright &copy; 2005-2012 PradoSoft
- * @license http://www.pradosoft.com/license/
- * @version $Id$
- * @package System.Data.ActiveRecord
- */
-
-Prado::using('System.Data.TDbConnection');
-Prado::using('System.Data.ActiveRecord.TActiveRecord');
-Prado::using('System.Data.ActiveRecord.Exceptions.TActiveRecordException');
-Prado::using('System.Data.ActiveRecord.TActiveRecordGateway');
-
-/**
- * TActiveRecordManager provides the default DB connection,
- * default active record gateway, and table meta data inspector.
- *
- * The default connection can be set as follows:
- * <code>
- * TActiveRecordManager::getInstance()->setDbConnection($conn);
- * </code>
- * All new active record created after setting the
- * {@link DbConnection setDbConnection()} will use that connection unless
- * the custom ActiveRecord class overrides the ActiveRecord::getDbConnection().
- *
- * Set the {@link setCache Cache} property to an ICache object to allow
- * the active record gateway to cache the table meta data information.
- *
- * @author Wei Zhuo <weizho[at]gmail[dot]com>
- * @version $Id$
- * @package System.Data.ActiveRecord
- * @since 3.1
- */
-class TActiveRecordManager extends TComponent
-{
-	const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway';
-
-	/**
-	 * Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
-	 * @var string
-	 */
-	private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS;
-
-	private $_gateway;
-	private $_meta=array();
-	private $_connection;
-
-	private $_cache;
-
-	/**
-	 * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'
-	 *
-	 * @var TActiveRecordInvalidFinderResult
-	 * @since 3.1.5
-	 */
-	private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null;
-
-	/**
-	 * @return ICache application cache.
-	 */
-	public function getCache()
-	{
-		return $this->_cache;
-	}
-
-	/**
-	 * @param ICache application cache
-	 */
-	public function setCache($value)
-	{
-		$this->_cache=$value;
-	}
-
-	/**
-	 * @param TDbConnection default database connection
-	 */
-	public function setDbConnection($conn)
-	{
-		$this->_connection=$conn;
-	}
-
-	/**
-	 * @return TDbConnection default database connection
-	 */
-	public function getDbConnection()
-	{
-		return $this->_connection;
-	}
-
-	/**
-	 * @return TActiveRecordManager static instance of record manager.
-	 */
-	public static function getInstance($self=null)
-	{
-		static $instance;
-		if($self!==null)
-			$instance=$self;
-		else if($instance===null)
-			$instance = new self;
-		return $instance;
-	}
-
-	/**
-	 * @return TActiveRecordGateway record gateway.
-	 */
-	public function getRecordGateway()
-	{
-		if($this->_gateway === null) {
-			$this->_gateway = $this->createRecordGateway();
-		}
-		return $this->_gateway;
-	}
-
-	/**
-	 * @return TActiveRecordGateway default record gateway.
-	 */
-	protected function createRecordGateway()
-	{
-		return Prado::createComponent($this->getGatewayClass(), $this);
-	}
-
-	/**
-	 * Set implementation class of ActiveRecordGateway
-	 * @param string $value
-	 */
-	public function setGatewayClass($value)
-	{
-		$this->_gatewayClass = (string)$value;
-	}
-
-	/**
-	 * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
-	 */
-	public function getGatewayClass()
-	{
-		return $this->_gatewayClass;
-	}
-
-	/**
-	 * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'.
-	 * @since 3.1.5
-	 * @see setInvalidFinderResult
-	 */
-	public function getInvalidFinderResult()
-	{
-		return $this->_invalidFinderResult;
-	}
-
-	/**
-	 * Define the way an active record finder react if an invalid magic-finder invoked
-	 * @param TActiveRecordInvalidFinderResult
-	 * @since 3.1.5
-	 * @see getInvalidFinderResult
-	 */
-	public function setInvalidFinderResult($value)
-	{
-		$this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult');
-	}
-}
+ * @license http://www.pradosoft.com/license/
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ */
+
+Prado::using('System.Data.TDbConnection');
+Prado::using('System.Data.ActiveRecord.TActiveRecord');
+Prado::using('System.Data.ActiveRecord.Exceptions.TActiveRecordException');
+Prado::using('System.Data.ActiveRecord.TActiveRecordGateway');
+
+/**
+ * TActiveRecordManager provides the default DB connection,
+ * default active record gateway, and table meta data inspector.
+ *
+ * The default connection can be set as follows:
+ * <code>
+ * TActiveRecordManager::getInstance()->setDbConnection($conn);
+ * </code>
+ * All new active record created after setting the
+ * {@link DbConnection setDbConnection()} will use that connection unless
+ * the custom ActiveRecord class overrides the ActiveRecord::getDbConnection().
+ *
+ * Set the {@link setCache Cache} property to an ICache object to allow
+ * the active record gateway to cache the table meta data information.
+ *
+ * @author Wei Zhuo <weizho[at]gmail[dot]com>
+ * @version $Id$
+ * @package System.Data.ActiveRecord
+ * @since 3.1
+ */
+class TActiveRecordManager extends TComponent
+{
+	const DEFAULT_GATEWAY_CLASS = 'System.Data.ActiveRecord.TActiveRecordGateway';
+
+	/**
+	 * Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
+	 * @var string
+	 */
+	private $_gatewayClass = self::DEFAULT_GATEWAY_CLASS;
+
+	private $_gateway;
+	private $_meta=array();
+	private $_connection;
+
+	private $_cache;
+
+	/**
+	 * Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'
+	 *
+	 * @var TActiveRecordInvalidFinderResult
+	 * @since 3.1.5
+	 */
+	private $_invalidFinderResult = TActiveRecordInvalidFinderResult::Null;
+
+	/**
+	 * @return ICache application cache.
+	 */
+	public function getCache()
+	{
+		return $this->_cache;
+	}
+
+	/**
+	 * @param ICache application cache
+	 */
+	public function setCache($value)
+	{
+		$this->_cache=$value;
+	}
+
+	/**
+	 * @param TDbConnection default database connection
+	 */
+	public function setDbConnection($conn)
+	{
+		$this->_connection=$conn;
+	}
+
+	/**
+	 * @return TDbConnection default database connection
+	 */
+	public function getDbConnection()
+	{
+		return $this->_connection;
+	}
+
+	/**
+	 * @return TActiveRecordManager static instance of record manager.
+	 */
+	public static function getInstance($self=null)
+	{
+		static $instance;
+		if($self!==null)
+			$instance=$self;
+		else if($instance===null)
+			$instance = new self;
+		return $instance;
+	}
+
+	/**
+	 * @return TActiveRecordGateway record gateway.
+	 */
+	public function getRecordGateway()
+	{
+		if($this->_gateway === null) {
+			$this->_gateway = $this->createRecordGateway();
+		}
+		return $this->_gateway;
+	}
+
+	/**
+	 * @return TActiveRecordGateway default record gateway.
+	 */
+	protected function createRecordGateway()
+	{
+		return Prado::createComponent($this->getGatewayClass(), $this);
+	}
+
+	/**
+	 * Set implementation class of ActiveRecordGateway
+	 * @param string $value
+	 */
+	public function setGatewayClass($value)
+	{
+		$this->_gatewayClass = (string)$value;
+	}
+
+	/**
+	 * @return string the implementation class of ActiveRecordGateway. Defaults to {@link TActiveRecordManager::DEFAULT_GATEWAY_CLASS DEFAULT_GATEWAY_CLASS}
+	 */
+	public function getGatewayClass()
+	{
+		return $this->_gatewayClass;
+	}
+
+	/**
+	 * @return TActiveRecordInvalidFinderResult Defaults to '{@link TActiveRecordInvalidFinderResult::Null Null}'.
+	 * @since 3.1.5
+	 * @see setInvalidFinderResult
+	 */
+	public function getInvalidFinderResult()
+	{
+		return $this->_invalidFinderResult;
+	}
+
+	/**
+	 * Define the way an active record finder react if an invalid magic-finder invoked
+	 * @param TActiveRecordInvalidFinderResult
+	 * @since 3.1.5
+	 * @see getInvalidFinderResult
+	 */
+	public function setInvalidFinderResult($value)
+	{
+		$this->_invalidFinderResult = TPropertyValue::ensureEnum($value, 'TActiveRecordInvalidFinderResult');
+	}
+}
-- 
cgit v1.2.3