Index: build/manifests/jelix-lib.mn
===================================================================
--- build/manifests/jelix-lib.mn	(rÃ©vision 882)
+++ build/manifests/jelix-lib.mn	(copie de travail)
@@ -299,6 +299,7 @@
 cd lib/jelix/utils/
   jAppManager.class.php
   jClasses.class.php
+  jBinding.class.php
   jCmdUtils.class.php
   jCrypt.class.php
   jDatatype.class.php
Index: build/manifests/testapp.mn
===================================================================
--- build/manifests/testapp.mn	(rÃ©vision 882)
+++ build/manifests/testapp.mn	(copie de travail)
@@ -1,4 +1,5 @@
 cd testapp
+
   project.xml
   .htaccess
   application.init.php
@@ -85,6 +86,7 @@
 cd testapp/modules/jelix_tests/classes/tests
   foo.class.php
   foo.iface.php
+  bind.class.php
 cd testapp/modules/jelix_tests/locales/
   test_A.properties
   test_B.properties
@@ -172,6 +174,7 @@
   utils.jlog.html_cli.php
   utils.jdatetime.html_cli.php
   utils.jinifilemodifier.html_cli.php
+  utils.jclasses.html_cli.php
 
 cd testapp/var
   .htaccess
Index: testapp/modules/jelix_tests/tests/utils.jclasses.html_cli.php
===================================================================
--- testapp/modules/jelix_tests/tests/utils.jclasses.html_cli.php	(rÃ©vision 0)
+++ testapp/modules/jelix_tests/tests/utils.jclasses.html_cli.php	(rÃ©vision 0)
@@ -0,0 +1,131 @@
+<?php
+/**
+* @package     testapp
+* @subpackage  jelix_tests module
+* @author      Thiriot Christophe
+* @contributor
+* @copyright   2008 Thiriot Christophe
+* @link        http://www.jelix.org
+* @licence     GNU Lesser General Public Licence see LICENCE file or http://www.gnu.org/licenses/lgpl.html
+*/
+
+class UTjclasses extends UnitTestCase {
+
+    public function setUp() {
+        jClasses::resetBindings();
+    }
+
+    public function testClassNoBinding() {
+        $this->assertTrue(class_exists('jBinding'));
+        $class = jClasses::getBindedService('class:jelix_tests~myclass');
+        $this->assertTrue($class instanceof myclass);
+
+        // same test with an interface (raises an exception)
+        try {
+            $class = jClasses::getBindedService('iface:jelix_tests~test');
+            $this->fail('An interface without binding should raise a jException');
+        } catch (jException $e) {
+            $this->pass();
+        }
+    }
+
+    public function testBindingInCodeTo() {
+        jClasses::bind('jelix_tests~test')->to('jelix_tests~myclass');
+        $class = jClasses::getBindedService('jelix_tests~test');
+        $this->assertTrue($class instanceof myclass);
+
+        jClasses::bind('jelix_tests~test')->to('jelix_tests~myclass');
+        $classname = jClasses::getBinding('jelix_tests~test')->getClassName();
+        $this->assertTrue($classname === 'myclass');
+
+        try {
+            jClasses::bind('jelix_tests~test')->to('jelix_tests~notexistingclass');
+            $this->fail('A binding to a non existing class should raise an exception');
+        } catch (jExceptionSelector $e) {
+            $this->pass();
+        }
+    }
+
+    public function testBindingInCodeToInstance() {
+        $instance = jClasses::create('jelix_tests~myclass');
+        jClasses::bind('jelix_tests~test')->toInstance($instance);
+        $class = jClasses::getBindedService('jelix_tests~test');
+        $this->assertTrue($class instanceof myclass);
+
+        jClasses::bind('jelix_tests~test')->toInstance($instance);
+        $classname = jClasses::getBinding('jelix_tests~test')->getClassName();
+        $this->assertTrue($classname === 'myclass');
+    }
+
+    // test with binding in jelix config file + get class name + non existing binded class
+    public function testBindingInJelixConfigFile() {
+        global $gJConfig;
+        $oldgjconfig = clone $gJConfig;
+
+        $gJConfig->{jBinding::BINDINGS_CONFIG_SECTION}['jelix_tests*test'] = 'jelix_tests~myclass';
+        $class = jClasses::getBindedService('jelix_tests~test');
+        $this->assertTrue($class instanceof myclass);
+
+        // test with long selector and test with parse_ini_file
+        $gJConfig->{jBinding::BINDINGS_CONFIG_SECTION}['class:jelix_tests*myclass'] = 'jelix_tests~myclass';
+        $class = jClasses::getBindedService('class:jelix_tests~myclass');
+        $this->assertTrue($class instanceof myclass);
+
+        $gJConfig = $oldgjconfig;
+    }
+
+    // test with binding in DEFAULT IMPLEMENTATION constant + get class name + non existing binded class
+    public function testBindingInDefaultImplementation() {
+        $classname = jClasses::getBinding('iface:jelix_tests~tests/foo')->getClassName();
+        $this->assertEqual($classname, 'bind');
+        jClasses::resetBindings();
+
+        $class = jClasses::getBindedService('iface:jelix_tests~tests/foo');
+        $this->assertTrue($class instanceof bind);
+
+        jClasses::resetBindings();
+
+        try {
+            $class = jClasses::getBindedService('class:jelix_tests~test/bind');
+            $this->fail('A non existing default implementation should raise an exception');
+        } catch (jExceptionSelector $e) {
+            $this->pass();
+        }
+    }
+
+    // getBindedService should give the same instance if called twice
+    public function testBindedServiceCalledTwice(){
+        $obj1 = jClasses::getBindedService('class:jelix_tests~myclass');
+        $obj2 = jClasses::getBindedService('class:jelix_tests~myclass');
+        $this->assertTrue($obj1 === $obj2);
+
+        jClasses::bind('jelix_tests~test')->to('jelix_tests~myclass');
+        $obj1 = jClasses::getBindedService('jelix_tests~test');
+        $obj2 = jClasses::getBindedService('jelix_tests~test');
+        $this->assertTrue($obj1 === $obj2);
+    }
+
+    // createBinded called twice should give different instances if called twice
+    public function testcreateBindedCalledTwice(){
+        $obj1 = jClasses::createBinded('class:jelix_tests~myclass');
+        $obj2 = jClasses::createBinded('class:jelix_tests~myclass');
+        $this->assertTrue($obj1 !== $obj2);
+
+        jClasses::bind('jelix_tests~test')->to('jelix_tests~myclass');
+        $obj1 = jClasses::createBinded('jelix_tests~test');
+        $obj2 = jClasses::createBinded('jelix_tests~test');
+        $this->assertTrue($obj1 !== $obj2);
+    }
+
+    // createBinded called twice with a bind()->toInstance() before give diffent instances , too.
+    // then toInstance has no effect... 
+    public function testCreateBindedWithToInstanceCalledTwice(){
+        $instance = new StdClass();
+        jClasses::bind('jelix_tests~test')->toInstance($instance);
+        $obj1 = jClasses::createBinded('jelix_tests~test');
+        $obj2 = jClasses::createBinded('jelix_tests~test');
+        $this->assertTrue($obj1 !== $obj2);
+    }
+}
+
+?>
Index: testapp/modules/jelix_tests/classes/tests/foo.iface.php
===================================================================
--- testapp/modules/jelix_tests/classes/tests/foo.iface.php	(rÃ©vision 882)
+++ testapp/modules/jelix_tests/classes/tests/foo.iface.php	(copie de travail)
@@ -1,7 +1,7 @@
 <?php
 
 interface foo {
-
+    const JBINDING_BINDED_IMPLEMENTATION = 'jelix_tests~tests/bind';
 }
 
-?>
\ Pas de fin de ligne Ã  la fin du fichier
+?>
Index: testapp/modules/jelix_tests/classes/tests/bind.class.php
===================================================================
--- testapp/modules/jelix_tests/classes/tests/bind.class.php	(rÃ©vision 0)
+++ testapp/modules/jelix_tests/classes/tests/bind.class.php	(rÃ©vision 0)
@@ -0,0 +1,7 @@
+<?php
+
+class bind {
+    const JBINDING_BINDED_IMPLEMENTATION = 'jelix_tests~nonexistingclass';
+}
+
+?>
Index: lib/jelix/core/jSelector.class.php
===================================================================
--- lib/jelix/core/jSelector.class.php	(rÃ©vision 882)
+++ lib/jelix/core/jSelector.class.php	(copie de travail)
@@ -34,7 +34,11 @@
      * @param string $selstr  the selector. It should be a full selector : "type:module~resource" (not "module~resource")
      * @return jISelector the corresponding selector
      */
-    static public function create ($selstr){
+    static public function create ($selstr, $defaulttype=false){
+        if (is_string($defaulttype) && strpos($selstr, ':') === false) {
+            $selstr = "$defaulttype:$selstr";
+        }
+
         if(preg_match("/^([a-z]{3,5})\:([\w~\/\.]+)$/", $selstr, $m)){
             $cname='jSelector'.$m[1];
             if(class_exists($cname)){
Index: lib/jelix/core-modules/jelix/locales/en_EN/errors.ISO-8859-15.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/en_EN/errors.ISO-8859-15.properties	(rÃ©vision 882)
+++ lib/jelix/core-modules/jelix/locales/en_EN/errors.ISO-8859-15.properties	(copie de travail)
@@ -109,5 +109,8 @@
 acl.driver.notfound = (370) Driver %s for jAcl not found
 acl.action.right.needed = (371) The user is not allowed to access to this page
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: invalid date/time (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/en_EN/errors.ISO-8859-1.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/en_EN/errors.ISO-8859-1.properties	(rÃ©vision 882)
+++ lib/jelix/core-modules/jelix/locales/en_EN/errors.ISO-8859-1.properties	(copie de travail)
@@ -110,5 +110,8 @@
 acl.driver.notfound = (370) Driver %s for jAcl not found
 acl.action.right.needed = (371) The user is not allowed to access to this page
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: invalid date/time (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/en_EN/errors.UTF-8.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/en_EN/errors.UTF-8.properties	(rÃ©vision 882)
+++ lib/jelix/core-modules/jelix/locales/en_EN/errors.UTF-8.properties	(copie de travail)
@@ -110,5 +110,8 @@
 acl.driver.notfound = (370) Driver %s for jAcl not found
 acl.action.right.needed = (371) The user is not allowed to access to this page
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: invalid date/time (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/en_US/errors.ISO-8859-15.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/en_US/errors.ISO-8859-15.properties	(rÃ©vision 882)
+++ lib/jelix/core-modules/jelix/locales/en_US/errors.ISO-8859-15.properties	(copie de travail)
@@ -109,5 +109,8 @@
 acl.driver.notfound = (370) Driver %s for jAcl not found
 acl.action.right.needed = (371) The user is not allowed to access to this page
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: invalid date/time (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/en_US/errors.ISO-8859-1.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/en_US/errors.ISO-8859-1.properties	(rÃ©vision 882)
+++ lib/jelix/core-modules/jelix/locales/en_US/errors.ISO-8859-1.properties	(copie de travail)
@@ -109,5 +109,8 @@
 acl.driver.notfound = (370) Driver %s for jAcl not found
 acl.action.right.needed = (371) The user is not allowed to access to this page
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: invalid date/time (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/en_US/errors.UTF-8.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/en_US/errors.UTF-8.properties	(rÃ©vision 882)
+++ lib/jelix/core-modules/jelix/locales/en_US/errors.UTF-8.properties	(copie de travail)
@@ -110,5 +110,8 @@
 acl.driver.notfound = (370) Driver %s for jAcl not found
 acl.action.right.needed = (371) The user is not allowed to access to this page
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: invalid date/time (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/fr_FR/errors.ISO-8859-15.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/fr_FR/errors.ISO-8859-15.properties	(rÃ©vision 882)
+++ lib/jelix/core-modules/jelix/locales/fr_FR/errors.ISO-8859-15.properties	(copie de travail)
@@ -109,5 +109,8 @@
 acl.driver.notfound = (370) Driver %s pour jAcl non trouvé
 acl.action.right.needed = (371) L'utilisateur n'a pas les droits pour cette action
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: date/heure invalide (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/fr_FR/errors.ISO-8859-1.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/fr_FR/errors.ISO-8859-1.properties	(rÃ©vision 882)
+++ lib/jelix/core-modules/jelix/locales/fr_FR/errors.ISO-8859-1.properties	(copie de travail)
@@ -109,5 +109,8 @@
 acl.driver.notfound = (370) Driver %s pour jAcl non trouvé
 acl.action.right.needed = (371) L'utilisateur n'a pas les droits pour cette action
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: date/heure invalide (%d-%d-%d %d:%d:%d)
Index: lib/jelix/core-modules/jelix/locales/fr_FR/errors.UTF-8.properties
===================================================================
--- lib/jelix/core-modules/jelix/locales/fr_FR/errors.UTF-8.properties	(rÃ©vision 882)
+++ lib/jelix/core-modules/jelix/locales/fr_FR/errors.UTF-8.properties	(copie de travail)
@@ -109,5 +109,8 @@
 acl.driver.notfound = (370) Driver %s pour jAcl non trouvÃ©
 acl.action.right.needed = (371) L'utilisateur n'a pas les droits pour cette action
 
+#---- bindings
+bindings.nobinding = (380) Binding for %s not defined
+
 #---- datetime
 datetime.invalid = (400)jDateTime: date/heure invalide (%d-%d-%d %d:%d:%d)
Index: lib/jelix/utils/jClasses.class.php
===================================================================
--- lib/jelix/utils/jClasses.class.php	(rÃ©vision 882)
+++ lib/jelix/utils/jClasses.class.php	(copie de travail)
@@ -15,10 +15,12 @@
 * @subpackage  utils
 * @static
 */
-class jClasses {
+class jClasses{
 
     static protected $_instances = array();
 
+    static protected $_bindings = array();
+
     private function __construct(){}
 
     /**
@@ -34,6 +36,17 @@
     }
 
     /**
+     * Shortcut to corresponding jBinding::getInstance() but without singleton
+     * The binding is recreated each time (be careful about performance)
+     * 
+     * @param string $selector  Selector to a bindable class|interface
+     * @return object           Corresponding instance
+     */
+    static public function createBinded($selector){
+        return self::getBinding($selector, false);
+    }
+
+    /**
      * alias of create method
      * @see jClasses::create()
      */
@@ -50,9 +63,9 @@
     static public function getService($selector){
         $sel = new jSelectorClass($selector);
         $s = $sel->toString();
-        if (isset(self::$_instances[$s])) {
+        if (isset(self::$_instances[$s])){
             return self::$_instances[$s];
-        } else {
+        } else{
             $o = self::create($selector);
             self::$_instances[$s]=$o;
             return $o;
@@ -60,10 +73,65 @@
     }
 
     /**
+     * Shortcut to corresponding jBinding::getInstance() 
+     * 
+     * @param string $selector  Selector to a bindable class|interface
+     * @return object           Corresponding instance
+     */
+    static public function getBindedService($selector){
+        return self::getBinding($selector)->getInstance();
+    }
+
+    /**
+     * Alias of self::getBinding method. Better for use like this : jClasses::bind('selector').to('classselector')
+     * 
+     * @param  string $selector 
+     * @return jBinding
+     * @see jClasses::bind
+     */
+    static public function bind($selector){
+        return self::getBinding($selector);
+    }
+
+    /**
+     * Get the binding corresponding to the specified selector.
+     * Better for use like this : jClasses::getBinding($selector)->getClassName()
+     * 
+     * @param string $selector
+     * @param bool   $singleton if this binding should be a singleton or not
+     * @return jBinding
+     * @see jClasses::bind
+     */
+    static public function getBinding($selector, $singleton=true){
+        $osel = jSelectorFactory::create($selector, 'iface');
+        $s    = $osel->toString(true);
+
+        $binding = null;
+        if ($singleton === false || !isset(self::$_bindings[$s])){
+            $binding = new jBinding($osel);
+            if ($singleton === true) self::$_bindings[$s] = $binding;
+        } else{
+            $binding = self::$_bindings[$s];
+        }
+
+
+        return $binding;
+    }
+
+    /**
+     * Reset the defined bindings (should only use it for unit tests)
+     * 
+     * @return void
+     */
+    static public function resetBindings(){
+        self::$_bindings = array();
+    }
+
+    /**
      * only include a class
      * @param string $selector the jelix selector correponding to the class
      */
-    static public function inc($selector) {
+    static public function inc($selector){
         $sel = new jSelectorClass($selector);
         require_once($sel->getPath());
     }
@@ -73,7 +141,7 @@
      * @param string $selector the jelix selector correponding to the interface
      * @since 1.0b2
      */
-    static public function incIface($selector) {
+    static public function incIface($selector){
         $sel = new jSelectorInterface($selector);
         require_once($sel->getPath());
     }
Index: lib/jelix/utils/jBinding.class.php
===================================================================
--- lib/jelix/utils/jBinding.class.php	(rÃ©vision 0)
+++ lib/jelix/utils/jBinding.class.php	(rÃ©vision 0)
@@ -0,0 +1,171 @@
+<?php
+/**
+ * @package     jelix
+ * @subpackage  utils
+ * @author      Christophe THIRIOT
+ * @copyright   2008 Christophe THIRIOT 
+ * @link        http://www.jelix.org
+ * @licence     GNU Lesser General Public Licence see LICENCE file or http://www.gnu.org/licenses/lgpl.html
+ */
+
+/**
+ * Services binding for jelix
+ *
+ * @package     jelix
+ * @subpackage  utils
+ */
+class jBinding
+{
+    /**
+     *  Section in the jelix config file where bindings are specified
+     */
+    const BINDINGS_CONFIG_SECTION = 'Bindings';
+
+    /**
+     * Name of the constant setting the default binded implementation
+     */
+    const BINDED_IMPLEMENTATION_LABEL = 'JBINDING_BINDED_IMPLEMENTATION';
+
+    /**
+     * Jelix selector separator in code
+     */
+    const CODE_SELECTOR_SEPARATOR = '~';
+
+    /**
+     * Jelix selector separator in jelix config file 
+     */
+    const CONFIG_SELECTOR_SEPARATOR = '*';
+
+    /**
+     * @var jSelectorInterface|jSelectorClass Called selector 
+     */
+    protected $fromselector = null;
+
+    /**
+     * selector to the binded class (string form) 
+     */
+    protected $toselector = null;
+
+    /**
+     * resulting binded instance
+     */
+    protected $instance = null;
+
+    /**
+     * __constructor 
+     * @param jSelectorInterface|jSelectorClass
+     * @return void
+     */
+    public function __construct($selector) {
+        require_once($selector->getPath());
+        $this->fromselector = $selector;
+    }
+
+    /**
+     * Bind the selector to the class specified
+     * Even if this instance is already defined (BE CAREFUL !!! singleton is bypassed)
+     * 
+     * @param string $toselector 
+     * @return jBinding $this
+     */
+    public function to($toselector) {
+        $this->toselector = new jSelectorClass($toselector);
+        $this->instance   = null;
+        return $this;
+    }
+
+    /**
+     * Bind the selector to the specified instance
+     * Even if this instance is already defined (BE CAREFUL !!! singleton is bypassed)
+     * 
+     * @param  mixed    $instance 
+     * @return jBinding $this
+     */
+    public function toInstance($instance) {
+        $this->instance   = $instance;
+        $this->toselector = null;
+        return $this;
+    }
+
+    /**
+     * Get the binded instance
+     * 
+     * @return mixed
+     */
+    public function getInstance() {
+        if ($this->instance === null){
+            if ($this->toselector === null) {
+                $this->toselector = $this->_getClassSelector();                
+            }
+            $this->instance = jClasses::create($this->toselector->toString());
+        }
+        return $this->instance;
+    }
+
+    /**
+     * Get the name of the binded class without creating this class
+     * 
+     * @return string
+     */
+    public function getClassName() {
+        $class_name = null;
+        if ($this->instance !== null) {
+            $class_name = get_class($this->instance);
+        } elseif ($this->toselector !== null) {
+            $class_name = $this->toselector->className;
+        } else {
+            $class_name = $this->_getClassSelector()->className;
+        }
+        return $class_name;
+    }
+
+    /**
+     * Get the selector to the binded class 
+     * Protected because this does not work if called after a simple __construct() and a toInstance()
+     * 
+     * @return string
+     */
+    protected function _getClassSelector() {
+        $class_selector = null;
+
+        // the instance is not already created
+        if ($this->toselector === null && $this->instance === null) {
+            $str_selector      = $this->fromselector->toString();
+            $str_selector_long = $this->fromselector->toString(true);
+
+            // 1) verify that a default implementation is specified in the jelix config file
+            global $gJConfig;
+            if (isset($gJConfig->{self::BINDINGS_CONFIG_SECTION})) {
+                $conf = $gJConfig->{self::BINDINGS_CONFIG_SECTION};
+                
+                // No '~' allowed as key of a ini file, we use '*' instead
+                $conf_selector      = str_replace(self::CODE_SELECTOR_SEPARATOR, self::CONFIG_SELECTOR_SEPARATOR, $str_selector);
+                $conf_selector_long = str_replace(self::CODE_SELECTOR_SEPARATOR, self::CONFIG_SELECTOR_SEPARATOR, $str_selector_long);
+                // get the binding corresponding to selector, long or not
+                $str_fromselector = null;
+                if (isset($conf[$conf_selector])) {
+                    $str_fromselector = $conf_selector;
+                } elseif (isset($conf[$conf_selector_long])){
+                    $str_fromselector = $conf_selector_long;
+                }
+
+                if ($str_fromselector !== null) {
+                    $this->fromselector = jSelectorFactory::create($str_selector_long, 'iface');
+                    return $this->toselector = new jSelectorClass($conf[$str_fromselector]);
+                }
+            }
+
+            // 2) see if a default implementation is specified in the source class
+            $class_selector = @constant($this->fromselector->className . '::' . jBinding::BINDED_IMPLEMENTATION_LABEL);
+            if ($class_selector!==null) return $this->toselector = new jSelectorClass($class_selector);
+
+            // 3) If the source is a class, then use it as the default implementation
+            if (false === ($this->fromselector instanceof jSelectorClass)) {
+                return $this->toselector = new jSelectorClass($str_selector);
+            }
+            
+            throw new jException('jelix~errors.binding.nobinding', array($this->fromselector->toString(true)));
+        }
+    }
+}
+?>
