diff --git a/LICENSE.TXT b/LICENSE.TXT
new file mode 100755
index 0000000..648d1e4
--- /dev/null
+++ b/LICENSE.TXT
@@ -0,0 +1,48 @@
+-------------------------------------------------------------------------------
+
+ Sabel 1.2 Beta2 - Rapid Web Application Development Framework
+
+ The New BSD License
+
+ Copyright (c) 2004-2009
+ Mori Reo All rights reserved.
+
+-------------------------------------------------------------------------------
+
+ Authors:
+
+ Mori Reo
+ Ebine Yutaka
+ Hamanaka Kazuhiro
+ Hosokawa Yasuko
+
+-------------------------------------------------------------------------------
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+-------------------------------------------------------------------------------
+
+ For more information on the Sabel project,
+ please see
+
+-------------------------------------------------------------------------------
diff --git a/Sabel.php b/Sabel.php
new file mode 100755
index 0000000..71e66bd
--- /dev/null
+++ b/Sabel.php
@@ -0,0 +1,204 @@
+
+ * @author Ebine Yutaka
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+final class Sabel
+{
+ /**
+ * @var array
+ */
+ private static $readableFiles = array();
+
+ /**
+ * @var int
+ */
+ private static $readableFilesNum = 0;
+
+ /**
+ * @var array
+ */
+ private static $required = array();
+
+ /**
+ * @var array
+ */
+ private static $fileUsing = array();
+
+ public static function getPath()
+ {
+ return dirname(__FILE__);
+ }
+
+ public static function using($className)
+ {
+ if (class_exists($className, false)) {
+ return true;
+ } else {
+ return self::autoload($className);
+ }
+ }
+
+ public static function autoload($className)
+ {
+ if (isset(self::$required[$className])) return true;
+
+ if (isset(self::$readableFiles[$className])) {
+ require (self::$readableFiles[$className]);
+ self::$required[$className] = 1;
+ return true;
+ } elseif ($path = self::getFilePath($className)) {
+ require ($path);
+
+ self::$required[$className] = 1;
+ self::$readableFiles[$className] = $path;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public static function fileUsing($path, $once = false)
+ {
+ if ($once && isset(self::$fileUsing[$path])) return true;
+
+ if (isset(self::$readableFiles[$path])) {
+ $readable = true;
+ } elseif (is_readable($path)) {
+ $readable = true;
+ self::$readableFiles[$path] = $path;
+ } else {
+ $readable = false;
+ }
+
+ if ($readable) {
+ ($once) ? require_once ($path) : require ($path);
+ self::$fileUsing[$path] = 1;
+ return true;
+ }
+
+ return false;
+ }
+
+ private static function getFilePath($className)
+ {
+ static $includePath = null;
+ static $paths = null;
+
+ $exp = explode("_", $className);
+
+ if (count($exp) === 1) {
+ $path = $exp[0] . ".php";
+ } else {
+ $class = array_pop($exp);
+ $prePath = implode("/", array_map("lcfirst", $exp));
+ $path = $prePath . DIRECTORY_SEPARATOR . $class . ".php";
+ }
+
+ if ($includePath === null) {
+ $includePath = get_include_path();
+ } elseif (($incPath = get_include_path()) !== $includePath) {
+ $includePath = $incPath;
+ $paths = null;
+ }
+
+ if ($paths === null) {
+ $paths = explode(PATH_SEPARATOR, $includePath);
+ }
+
+ foreach ($paths as $p) {
+ $fullPath = $p . DIRECTORY_SEPARATOR . $path;
+ if (is_readable($fullPath)) return $fullPath;
+ }
+
+ return false;
+ }
+
+ public static function main()
+ {
+ $SABEL = "sabel" . DIRECTORY_SEPARATOR;
+
+ require ($SABEL . "Object.php");
+ require ($SABEL . "Logger.php");
+ require ($SABEL . "Bus.php");
+ require ($SABEL . "Config.php");
+ require ($SABEL . "Context.php");
+ require ($SABEL . "Request.php");
+ require ($SABEL . "Session.php");
+ require ($SABEL . "Response.php");
+ require ($SABEL . "View.php");
+
+ require ($SABEL . "functions" . DIRECTORY_SEPARATOR . "core.php");
+ require ($SABEL . "functions" . DIRECTORY_SEPARATOR . "db.php");
+
+ $BUS = $SABEL . "bus" . DIRECTORY_SEPARATOR;
+ $MAP = $SABEL . "map" . DIRECTORY_SEPARATOR;
+ $CTRL = $SABEL . "controller" . DIRECTORY_SEPARATOR;
+ $RESP = $SABEL . "response" . DIRECTORY_SEPARATOR;
+ $SESSION = $SABEL . "session" . DIRECTORY_SEPARATOR;
+ $VIEW = $SABEL . "view" . DIRECTORY_SEPARATOR;
+ $UTIL = $SABEL . "util" . DIRECTORY_SEPARATOR;
+
+ require ($BUS . "Config.php");
+ require ($BUS . "Processor.php");
+
+ require ($MAP . "Configurator.php");
+ require ($MAP . "Candidate.php");
+ require ($MAP . "Destination.php");
+ require ($MAP . "config" . DIRECTORY_SEPARATOR . "Route.php");
+
+ require ($RESP . "Object.php");
+ require ($RESP . "Status.php");
+ require ($RESP . "Redirector.php");
+ require ($RESP . "Header.php");
+
+ require ($SESSION . "Abstract.php");
+ require ($SESSION . "PHP.php");
+
+ require ($VIEW . "Renderer.php");
+ require ($VIEW . "Object.php");
+ require ($VIEW . "Location.php");
+ require ($VIEW . "location" . DIRECTORY_SEPARATOR . "File.php");
+
+ require ($UTIL . "HashList.php");
+ require ($UTIL . "VariableCache.php");
+
+ require ($SABEL . "request" . DIRECTORY_SEPARATOR . "Object.php");
+ require ($SABEL . "controller" . DIRECTORY_SEPARATOR . "Page.php");
+ require ($SABEL . "exception" . DIRECTORY_SEPARATOR . "Runtime.php");
+ require ($SABEL . "logger" . DIRECTORY_SEPARATOR . "Interface.php");
+ }
+
+ public static function init()
+ {
+ $path = "sabel" . DIRECTORY_SEPARATOR . "Sabel";
+ $cache = Sabel_Util_VariableCache::create($path);
+
+ if ($files = $cache->read("readableFiles")) {
+ self::$readableFiles = $files;
+ self::$readableFilesNum = count($files);
+ }
+ }
+
+ public static function shutdown()
+ {
+ if (self::$readableFilesNum !== count(self::$readableFiles)) {
+ $path = "sabel" . DIRECTORY_SEPARATOR . "Sabel";
+ $cache = Sabel_Util_VariableCache::create($path);
+ $cache->write("readableFiles", self::$readableFiles);
+ $cache->save();
+ }
+ }
+}
+
+/* register autoload method */
+spl_autoload_register(array("Sabel", "autoload"));
+
+Sabel::main();
diff --git a/SabelAllTests.php b/SabelAllTests.php
new file mode 100755
index 0000000..fdd6cae
--- /dev/null
+++ b/SabelAllTests.php
@@ -0,0 +1,139 @@
+addTest(Test_DB_Statement_Tests::suite());
+ $suite->addTest(Test_DB_Storage_Tests::suite());
+ $suite->addTest(Test_DB_Tests::suite());
+ return $suite;
+ }
+
+ $suite->addTest(Test_Object::suite());
+ $suite->addTest(Test_ValueObject::suite());
+ $suite->addTest(Test_Console::suite());
+ $suite->addTest(Test_Bus_Tests::suite());
+ $suite->addTest(Test_Map_Tests::suite());
+ $suite->addTest(Test_Request_Tests::suite());
+ $suite->addTest(Test_Response_Tests::suite());
+ $suite->addTest(Test_Controller_Tests::suite());
+ $suite->addTest(Test_Util_Tests::suite());
+ $suite->addTest(Test_View_Tests::suite());
+ $suite->addTest(Test_Cache_Tests::suite());
+ $suite->addTest(Test_Session_Tests::suite());
+ $suite->addTest(Test_Processor_Tests::suite());
+
+ $suite->addTest(Test_Annotation::suite());
+ $suite->addTest(Test_Reflection::suite());
+ $suite->addTest(Test_Container::suite());
+ //$suite->addTest(Test_Aspect_Tests::suite());
+ $suite->addTest(Test_Exception::suite());
+
+ $suite->addTest(Test_I18n_Gettext::suite());
+ $suite->addTest(Test_Cookie_Tests::suite());
+ $suite->addTest(Test_Mail_Tests::suite());
+ $suite->addTest(Test_XML_Tests::suite());
+
+ $suite->addTest(Test_Application::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/Annotation.php b/Test/Annotation.php
new file mode 100755
index 0000000..86342d5
--- /dev/null
+++ b/Test/Annotation.php
@@ -0,0 +1,116 @@
+
+ */
+class Test_Annotation extends SabelTestCase
+{
+ private $reader = null;
+
+ public static function suite()
+ {
+ return self::createSuite("Test_Annotation");
+ }
+
+ public function setUp()
+ {
+ $this->reader = Sabel_Annotation_Reader::create();
+ }
+
+ public function testClassAnnotation()
+ {
+ $annotations = $this->reader->readClassAnnotation("TestAnnotation");
+ $this->assertEquals("class", $annotations["annotation"][0][0]);
+ }
+
+ public function testBasic()
+ {
+ $annotation = $this->reader->readMethodAnnotation("TestAnnotation", "testMethod");
+ $this->assertEquals("value", $annotation["param"][0][0]);
+ }
+
+ public function testMultipleValue()
+ {
+ $annotation = $this->reader->readMethodAnnotation("TestAnnotation", "testMethod");
+ $this->assertEquals("hoge", $annotation["array"][0][0]);
+ $this->assertEquals("fuga", $annotation["array"][0][1]);
+ }
+
+ public function testIgnoreSpace()
+ {
+ $annotation = $this->reader->readMethodAnnotation("TestAnnotation", "testMethod2");
+ $this->assertEquals("value", $annotation["ignoreSpace"][0][0]);
+ }
+
+ public function testQuotedValue()
+ {
+ $annotation = $this->reader->readMethodAnnotation("TestAnnotation", "testMethod2");
+
+ $this->assertEquals("hoge", $annotation["array"][0][0]);
+ $this->assertEquals(' test"a" ', $annotation["array"][0][1]);
+ $this->assertEquals("a: index", $annotation["array"][0][2]);
+ $this->assertEquals("fuga", $annotation["array"][1][0]);
+ $this->assertEquals(" test'a' ", $annotation["array"][1][1]);
+ $this->assertEquals("c: index, a: index", $annotation["array"][1][2]);
+ }
+
+ public function testEmptyValue()
+ {
+ $annotation = $this->reader->readMethodAnnotation("TestAnnotation", "testMethod3");
+ $this->assertTrue(isset($annotation["emptyValue"]));
+ $this->assertNull($annotation["emptyValue"][0]);
+ }
+
+ public function testPropertyAnnotation()
+ {
+ $annotations = $this->reader->readPropertyAnnotation("TestAnnotation", "var");
+ $this->assertEquals("int", $annotations["var"][0][0]);
+ }
+}
+
+/**
+ * class annotation
+ *
+ * @annotation class
+ */
+class TestAnnotation
+{
+ /**
+ * @var int
+ */
+ protected $var = 10;
+
+ /**
+ * this is annotation test
+ *
+ * @param value
+ * @array hoge fuga
+ */
+ public function testMethod($test, $test = null)
+ {
+
+ }
+
+ /**
+ * this is annotation test
+ *
+ * @ignoreSpace value
+ * @array hoge ' test"a" ' "a: index"
+ * @array fuga " test'a' " 'c: index, a: index'
+ */
+ public function testMethod2()
+ {
+
+ }
+
+ /**
+ * @emptyValue
+ */
+ public function testMethod3()
+ {
+
+ }
+}
diff --git a/Test/Application.php b/Test/Application.php
new file mode 100755
index 0000000..b273050
--- /dev/null
+++ b/Test/Application.php
@@ -0,0 +1,230 @@
+
+ */
+class Test_Application extends SabelTestCase
+{
+ private static $setup = false;
+
+ public static function suite()
+ {
+ return self::createSuite("Test_Application");
+ }
+
+ public function setUp()
+ {
+ if (self::$setup) return;
+
+ $dir = TEST_APP_DIR . DS . LIB_DIR_NAME . DS . "processor" . DS;
+
+ Sabel::fileUsing($dir . "Addon.php", true);
+ Sabel::fileUsing($dir . "Session.php", true);
+ Sabel::fileUsing($dir . "Executer.php", true);
+ Sabel::fileUsing($dir . "Helper.php", true);
+ Sabel::fileUsing($dir . "Initializer.php", true);
+ Sabel::fileUsing($dir . "Controller.php", true);
+ Sabel::fileUsing($dir . "Response.php", true);
+ Sabel::fileUsing($dir . "Request.php", true);
+ Sabel::fileUsing($dir . "Router.php", true);
+ Sabel::fileUsing($dir . "View.php", true);
+
+ unshift_include_paths(array(MODULES_DIR_NAME,
+ LIB_DIR_NAME,
+ MODULES_DIR_NAME . DS . "models",
+ ADDON_DIR_NAME), TEST_APP_DIR . DS);
+
+ self::$setup = true;
+ }
+
+ public function testResponses()
+ {
+ $bus = $this->getBus("index/index");
+ $bus->run(new AppBusConfig());
+
+ $responses = $bus->get("response")->getResponses();
+ $this->assertEquals("10", $responses["hoge"]);
+ $this->assertEquals("20", $responses["fuga"]);
+ $this->assertFalse(isset($responses["foo"]));
+ }
+
+ public function testHtml()
+ {
+ $bus = $this->getBus("index/hoge");
+ $bus->run(new AppBusConfig());
+
+ $lines = $this->toHtmlLines($bus->get("result"));
+
+ $this->assertEquals("", $lines[0]);
+ $this->assertEquals("", $lines[1]);
+ $this->assertEquals("yamada
", $lines[2]);
+ $this->assertEquals("", $lines[3]);
+ $this->assertEquals("", $lines[4]);
+ }
+
+ public function testInstanceOfController()
+ {
+ $bus = $this->getBus("");
+ $bus->run(new AppBusConfig());
+ $this->assertTrue($bus->get("controller") instanceof Index_Controllers_Index);
+
+ $bus = $this->getBus("main");
+ $bus->run(new AppBusConfig());
+ $this->assertTrue($bus->get("controller") instanceof Index_Controllers_Main);
+
+ $bus = $this->getBus("manage/login/prepare");
+ $bus->run(new AppBusConfig());
+ $this->assertTrue($bus->get("controller") instanceof Manage_Controllers_Login);
+ }
+
+ public function testUriParameters()
+ {
+ $bus = $this->getBus("manage/index/index/1/2");
+ $bus->run(new AppBusConfig());
+ $lines = $this->toHtmlLines($bus->get("result"));
+
+ $this->assertEquals("", $lines[0]);
+ $this->assertEquals("", $lines[1]);
+ $this->assertEquals("manage ", $lines[2]);
+ $this->assertEquals("param1: 1
", $lines[3]);
+ $this->assertEquals("param2: 2
", $lines[4]);
+ $this->assertEquals("", $lines[5]);
+ $this->assertEquals("", $lines[6]);
+
+ $bus = $this->getBus("manage/index/index/100/200");
+ $bus->run(new AppBusConfig());
+ $lines = $this->toHtmlLines($bus->get("result"));
+
+ $this->assertEquals("param1: 100
", $lines[3]);
+ $this->assertEquals("param2: 200
", $lines[4]);
+ }
+
+ public function testRedirect()
+ {
+ $bus = $this->getBus("manage/index/index/abcde/2");
+ $bus->run(new AppBusConfig());
+
+ $controller = $bus->get("controller");
+ $this->assertTrue($controller->isRedirected());
+ $this->assertEquals("/manage/login/prepare", $controller->getResponse()->getRedirector()->getUri());
+ }
+
+ public function testNotFound()
+ {
+ $bus = $this->getBus("manage/hoge/fuga");
+ $bus->run(new AppBusConfig());
+
+ $response = $bus->get("response");
+ $this->assertEquals(Sabel_Response::NOT_FOUND, $response->getStatus()->getCode());
+ $headers = $response->outputHeader();
+ $this->assertEquals("HTTP/1.0 404 Not Found", $headers[0]);
+ }
+
+ public function testServerError()
+ {
+ $bus = $this->getBus("manage/index/refuse");
+ $bus->run(new AppBusConfig());
+
+ $response = $bus->get("response");
+ $this->assertEquals(Sabel_Response::INTERNAL_SERVER_ERROR, $response->getStatus()->getCode());
+ $headers = $response->outputHeader();
+ $this->assertEquals("HTTP/1.0 500 Internal Server Error", $headers[0]);
+ }
+
+ public function testInternalRequest()
+ {
+ $bus = $this->getBus("main/foo");
+ $bus->run(new AppBusConfig());
+ $this->assertEquals("foo bar", $bus->get("response")->getResponse("bar"));
+ }
+
+ protected function toHtmlLines($result)
+ {
+ $html = str_replace(array("\r\n", "\r"), "\n", trim($result));
+ return array_map("trim", explode("\n", $html));
+ }
+
+ protected function getBus($uri)
+ {
+ $bus = new Sabel_Bus();
+ $bus->set("request", new Sabel_Request_Object($uri));
+ $bus->set("session", Sabel_Session_InMemory::create());
+ return $bus;
+ }
+}
+
+class AppBusConfig extends Sabel_Bus_Config
+{
+ protected $processors = array("addon" => "TestProcessor_Addon",
+ "request" => "TestProcessor_Request",
+ "response" => "TestProcessor_Response",
+ "router" => "TestProcessor_Router",
+ "session" => "TestProcessor_Session",
+ "controller" => "TestProcessor_Controller",
+ "helper" => "TestProcessor_Helper",
+ "initializer" => "TestProcessor_Initializer",
+ "executer" => "TestProcessor_Executer",
+ "view" => "TestProcessor_View");
+
+ protected $configs = array("map" => "AppTestMapConfig",
+ "addon" => "AppTestAddonConfig",
+ "database" => "AppTestDbConfig");
+}
+
+class AppTestMapConfig extends Sabel_Map_Configurator
+{
+ public function configure()
+ {
+ $this->route("manage")
+ ->uri("manage/:controller/:action/:param1/:param2")
+ ->module("manage")
+ ->defaults(array(":controller" => "index",
+ ":action" => "index",
+ ":param1" => null,
+ ":param2" => null));
+
+ $this->route("default")
+ ->uri(":controller/:action")
+ ->module("index")
+ ->defaults(array(":controller" => "index",
+ ":action" => "index"));
+ }
+}
+
+class AppBusConfig2 extends AppBusConfig
+{
+ protected $configs = array("map" => "AppTestMapConfig2",
+ "addon" => "AppTestAddonConfig",
+ "database" => "AppTestDbConfig");
+}
+
+class AppTestMapConfig2 extends Sabel_Map_Configurator
+{
+ public function configure()
+ {
+ $this->route("all")
+ ->uri(":uri[]")
+ ->module("index")
+ ->controller("index")
+ ->action("index")
+ ->defaults(array(":uri" => array()));
+ }
+}
+
+class AppTestAddonConfig implements Sabel_Config
+{
+ public function configure() { return array(); }
+}
+
+class AppTestDbConfig implements Sabel_Config
+{
+ public function configure() { return array(); }
+}
diff --git a/Test/Aspect/Config.php b/Test/Aspect/Config.php
new file mode 100755
index 0000000..1f20c01
--- /dev/null
+++ b/Test/Aspect/Config.php
@@ -0,0 +1,23 @@
+configure();
+ }
+}
+
+class Sabel_Test_Aspect_Config extends Sabel_Aspect_Config implements Sabel_Config
+{
+ public function configure()
+ {
+ $this->aspect("default");
+ }
+}
\ No newline at end of file
diff --git a/Test/Aspect/Introduction.php b/Test/Aspect/Introduction.php
new file mode 100755
index 0000000..94bac4c
--- /dev/null
+++ b/Test/Aspect/Introduction.php
@@ -0,0 +1,147 @@
+
+ */
+class Test_Aspect_Introduction extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Aspect_Introduction");
+ }
+
+ private $weaver = null;
+
+ public function setUp()
+ {
+ $this->weaver = new Sabel_Aspect_Weaver("Sabel_Test_Aspect_Person");
+ }
+
+ public function testIntroduceLockable()
+ {
+ $mixin = new Sabel_Test_Aspect_LockMixin();
+
+ $this->weaver->addAdvisor(new Sabel_Test_Aspect_LockMixinAdvisor($mixin));
+
+ $person = $this->weaver->getProxy();
+ $person->__checkTargetMethod(false);
+
+ $person->lock();
+
+ try {
+ $person->setAge(28);
+ $this->assertFalse(true);
+ } catch (Sabel_Test_Aspect_LockedException $e) {
+ $this->assertEquals("locked", $e->getMessage());
+ }
+
+ $person->unlock();
+ $person->setAge(29);
+ $this->assertEquals(29, $person->getAge());
+ }
+}
+
+/**
+ * target class
+ */
+class Sabel_Test_Aspect_Person
+{
+ private $age = 0;
+
+ public function setAge($age)
+ {
+ $this->age = $age;
+ }
+
+ public function getAge()
+ {
+ return $this->age;
+ }
+}
+
+/**
+ * lockable interface
+ */
+interface Sabel_Test_Aspect_Lockable
+{
+ public function lock();
+ public function unlock();
+ public function locked();
+}
+
+class Sabel_Test_Aspect_LockMixin extends Sabel_Aspect_Introduction_DelegatingInterceptor
+ implements Sabel_Test_Aspect_Lockable
+{
+ private $locked = false;
+
+ public function lock()
+ {
+ $this->locked = true;
+ }
+
+ public function unlock()
+ {
+ $this->locked = false;
+ }
+
+ public function locked()
+ {
+ return $this->locked;
+ }
+
+ public function invoke(Sabel_Aspect_MethodInvocation $invocation)
+ {
+ $method = $invocation->getMethod()->getName();
+
+ if (in_array($method, get_class_methods($this))) {
+ $this->$method();
+ }
+
+ if (preg_match("/set+/", $invocation->getMethod()->getName())) {
+ if ($this->locked()) {
+ throw new Sabel_Test_Aspect_LockedException("locked");
+ }
+ }
+
+ return parent::invoke($invocation);
+ }
+}
+
+class Sabel_Test_Aspect_LockedException extends Sabel_Exception_Runtime {}
+
+
+class Sabel_Test_Aspect_LockMixinAdvisor extends Sabel_Aspect_Introduction_DefaultAdvisor
+{
+}
+
+
+class TrueClassMatcher implements Sabel_Aspect_Matcher_Class
+{
+ public function matches($class)
+ {
+ return true;
+ }
+}
+
+class TrueMethodMatcher implements Sabel_Aspect_Matcher_Method
+{
+ public function matches($method, $class)
+ {
+ return true;
+ }
+}
+
+class TrueMatchPointcut implements Sabel_Aspect_Pointcut
+{
+ public function getClassMatcher()
+ {
+ return new TrueClassMatcher();
+ }
+
+ public function getMethodMatcher()
+ {
+ return new TrueMethodMatcher();
+ }
+}
diff --git a/Test/Aspect/Matcher.php b/Test/Aspect/Matcher.php
new file mode 100755
index 0000000..9b7f930
--- /dev/null
+++ b/Test/Aspect/Matcher.php
@@ -0,0 +1,31 @@
+
+ */
+class Test_Aspect_Matcher extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Aspect_Matcher");
+ }
+
+ public function testRegexMethodMatcher()
+ {
+ $matcher = new Sabel_Aspect_Matcher_RegexMethod();
+ $matcher->setPattern("/set+/");
+ $this->assertTrue($matcher->matches("setX", ""));
+ }
+
+ public function testRegexClassMatcher()
+ {
+ $matcher = new Sabel_Aspect_Matcher_RegexClass();
+ $matcher->setPattern("/Sabel_+/");
+ $this->assertTrue($matcher->matches("Sabel_Test", ""));
+
+ $matcher->setPattern("/Sabel_+/");
+ $this->assertFalse($matcher->matches("Test_Test", ""));
+ }
+}
\ No newline at end of file
diff --git a/Test/Aspect/Pointcuts.php b/Test/Aspect/Pointcuts.php
new file mode 100755
index 0000000..b402d29
--- /dev/null
+++ b/Test/Aspect/Pointcuts.php
@@ -0,0 +1,50 @@
+
+ */
+class Test_Aspect_Pointcuts extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Aspect_Pointcuts");
+ }
+
+ protected $target = null;
+ protected $pointcuts = null;
+
+ public function setUp()
+ {
+ $this->target = new Sabel_Tests_Aspect_TargetClass();
+ $this->pointcuts = new Sabel_Aspect_DefaultPointcuts();
+ }
+
+ public function testPointcuts()
+ {
+ $match = $this->pointcuts->matches(new StaticPointcut(), "setX", $this->target);
+
+ $this->assertTrue($match);
+ }
+
+ public function testRegexMatcherPointcuts()
+ {
+ $pointcuts = $this->pointcuts;
+ $target = $this->target;
+
+
+ $pointcut = new Sabel_Aspect_Pointcut_DefaultRegex();
+ $pointcut->setClassMatchPattern("/Sabel+/");
+ $pointcut->setMethodMatchPattern("/set+/");
+
+ $match = $pointcuts->matches($pointcut, "setX", $target);
+ $this->assertTrue($match);
+
+ $match = $pointcuts->matches($pointcut, "setY", $target);
+ $this->assertTrue($match);
+
+ $match = $pointcuts->matches($pointcut, "getY", $target);
+ $this->assertFalse($match);
+ }
+}
\ No newline at end of file
diff --git a/Test/Aspect/Proxy.php b/Test/Aspect/Proxy.php
new file mode 100755
index 0000000..4be5b33
--- /dev/null
+++ b/Test/Aspect/Proxy.php
@@ -0,0 +1,416 @@
+
+ */
+class Test_Aspect_Proxy extends SabelTestCase
+{
+ protected $weaver = null;
+
+ public static function suite()
+ {
+ return self::createSuite("Test_Aspect_Proxy");
+ }
+
+ public function setUp()
+ {
+ $this->weaver = new Sabel_Aspect_Weaver("Sabel_Tests_Aspect_TargetClass");
+ }
+
+ public function testRegexMethodMatcher()
+ {
+ $matcher = new Sabel_Aspect_Matcher_RegexMethod();
+ $matcher->setPattern("/set+/");
+ $this->assertTrue($matcher->matches("setX", ""));
+ }
+
+ public function testRegexClassMatcher()
+ {
+ $matcher = new Sabel_Aspect_Matcher_RegexClass();
+ $matcher->setPattern("/Sabel_+/");
+ $this->assertTrue($matcher->matches("Sabel_Test", ""));
+
+ $matcher->setPattern("/Sabel_+/");
+ $this->assertFalse($matcher->matches("Test_Test", ""));
+ }
+
+ public function testNonExistTargetClass()
+ {
+ try {
+ $this->weaver->setTarget("Non_Exist_Target_Class");
+ } catch (Sabel_Exception_Runtime $e) {
+ $this->assertTrue(true);
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testWeaverWithInterceptor()
+ {
+ $weaver = $this->weaver;
+
+ $interceptor = new Sabel_Aspect_Interceptor_Debug();
+ $advisor = new MyStaticMethodMatcherPointcutAdvisor();
+ $advisor->setAdvice($interceptor);
+
+ $weaver->addAdvisor($advisor);
+
+ $interceptor = new Sabel_Aspect_Interceptor_SimpleTrace();
+ $advisor = new MyStaticMethodMatcherPointcutAdvisor();
+ $advisor->setAdvice($interceptor);
+
+ $weaver->addAdvisor($advisor);
+
+ $target = $weaver->getProxy();
+
+ $result = $target->getX("arg", "arg2");
+ $this->assertEquals("X", $result);
+
+ $result = $target->getY("arg", "arg2");
+ $this->assertEquals("Y", $result);
+ }
+
+ public function testAopWeaverWithoutInterceptors()
+ {
+ $weaver = $this->weaver;
+
+ $target = $weaver->getProxy();
+
+ $result = $target->getX("arg", "arg2");
+ $this->assertEquals("X", $result);
+
+ $result = $target->getY("arg", "arg2");
+ $this->assertEquals("Y", $result);
+ }
+
+ public function testMultipleAdvice()
+ {
+ defineClass("ResultInterceptor", '
+ class %s implements Sabel_Aspect_MethodInterceptor
+ {
+ public function invoke(Sabel_Aspect_MethodInvocation $invocation)
+ {
+ return "adviced " . $invocation->proceed();
+ }
+ }
+ ');
+
+ $weaver = $this->weaver;
+
+ $weaver->setTarget("Sabel_Tests_Aspect_TargetClass");
+
+ $advisor = new Sabel_Aspect_Advisor_RegexMatcherPointcut();
+ $advisor->setClassMatchPattern("/.+/U");
+ $advisor->setMethodMatchPattern("/get+/");
+
+ $advisor->addAdvice(new ResultInterceptor());
+
+ $weaver->addAdvisor($advisor);
+
+ $target = $weaver->getProxy();
+
+ $this->assertEquals("adviced X", $target->getX());
+ $this->assertEquals("adviced Y", $target->getY());
+
+ $advisor->addAdvice(new ResultInterceptor());
+
+ $this->assertEquals("adviced adviced X", $weaver->getProxy()->getX());
+ $this->assertEquals("adviced adviced Y", $weaver->getProxy()->getY());
+
+ $advisor->addAdvice(new ResultInterceptor());
+
+ $this->assertEquals("adviced adviced adviced X", $weaver->getProxy()->getX());
+ $this->assertEquals("adviced adviced adviced Y", $weaver->getProxy()->getY());
+ }
+
+ public function testTargetMethodExistance()
+ {
+ $weaver = $this->weaver;
+
+ $advisor = new Sabel_Aspect_Advisor_RegexMatcherPointcut();
+ $advisor->setClassMatchPattern("/.+/U");
+ $advisor->setMethodMatchPattern("/get+/");
+
+ $beforeAdvice = new Sabel_Tests_Aspect_SimpleBeforeAdvice();
+ $advisor->addAdvice(new Sabel_Tests_Aspect_SimpleAfterReturningAdvice());
+ $advisor->addAdvice(new Sabel_Aspect_Interceptor_SimpleTrace());
+ $advisor->addAdvice($beforeAdvice);
+
+ $weaver->addAdvisor($advisor);
+
+ $target = $weaver->getProxy();
+
+ try {
+ $target->invalidMethod();
+ $this->assertTrue(false);
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testSimpleBeforeAdvice()
+ {
+ $weaver = $this->weaver;
+
+ $advisor = new Sabel_Aspect_Advisor_RegexMatcherPointcut();
+ $advisor->setClassMatchPattern("/.+/U");
+ $advisor->setMethodMatchPattern("/get+/");
+
+ $beforeAdvice = new Sabel_Tests_Aspect_SimpleBeforeAdvice();
+ $advisor->addAdvice(new Sabel_Tests_Aspect_SimpleAfterReturningAdvice());
+ $advisor->addAdvice(new Sabel_Aspect_Interceptor_SimpleTrace());
+ $advisor->addAdvice($beforeAdvice);
+
+ $weaver->addAdvisor($advisor);
+
+ $target = $weaver->getProxy();
+
+ $target->getX();
+ $target->getY();
+
+ $this->assertEquals(array("getX", "getY"), $beforeAdvice->getCalledMethods());
+ }
+
+ public function testSimpleAfterAdvice()
+ {
+ $weaver = $this->weaver;
+
+ $advisor = new Sabel_Aspect_Advisor_RegexMatcherPointcut();
+ $advisor->setClassMatchPattern("/.+/U");
+ $advisor->setMethodMatchPattern("/get+/");
+
+ $advice = new Sabel_Tests_Aspect_SimpleAfterReturningAdvice();
+ $advisor->addAdvice($advice);
+
+ $weaver->addAdvisor($advisor);
+
+ $target = $weaver->getProxy();
+
+ $target->getX();
+ $target->getY();
+
+ $this->assertEquals(array("X", "Y"), $advice->getResults());
+ }
+
+ public function testSimpleThrowsAdvice()
+ {
+ $weaver = $this->weaver;
+
+ $advisor = new Sabel_Aspect_Advisor_RegexMatcherPointcut();
+ $advisor->setClassMatchPattern("/.+/U");
+ $advisor->setMethodMatchPattern("/willThrowException/");
+
+ $advice = new Sabel_Tests_Aspect_SimpleThrowsAdvice();
+ $advisor->addAdvice($advice);
+
+ $weaver->addAdvisor($advisor);
+
+ $target = $weaver->getProxy();
+
+ $this->assertEquals("", $advice->getThrowsMessage());
+
+ try {
+ $target->willThrowException();
+ } catch (Exception $e) {
+ $this->assertEquals("throws", $advice->getThrowsMessage());
+ }
+ }
+
+ public function testSimpleThrowsAndReturnAdvice()
+ {
+ $weaver = $this->weaver;
+
+ $advisor = new Sabel_Aspect_Advisor_RegexMatcherPointcut();
+ $advisor->setClassMatchPattern("/.+/U");
+ $advisor->setMethodMatchPattern("/.+/");
+
+ $throwsAdvice = new Sabel_Tests_Aspect_SimpleThrowsAdvice();
+ $beforeAdvice = new Sabel_Tests_Aspect_SimpleBeforeAdvice();
+ $returningAdvice = new Sabel_Tests_Aspect_SimpleAfterReturningAdvice();
+
+ $advisor->addAdvice($throwsAdvice);
+ $advisor->addAdvice($returningAdvice);
+ $advisor->addAdvice($beforeAdvice);
+
+ $weaver->addAdvisor($advisor);
+
+ $target = $weaver->getProxy();
+
+ $this->assertEquals("", $throwsAdvice->getThrowsMessage());
+
+ try {
+ $target->willThrowException();
+ } catch (Exception $e) {
+ $this->assertEquals("throws", $throwsAdvice->getThrowsMessage());
+ $this->assertEquals(array(), $returningAdvice->getResults());
+ $this->assertEquals(array("willThrowException"), $beforeAdvice->getCalledMethods());
+ }
+ }
+
+ public function testAdvices()
+ {
+ $advices = new Sabel_Aspect_Advices();
+ $advices->addAdvice(new Sabel_Aspect_Interceptor_SimpleTrace());
+ $advices->addAdvice(new Sabel_Tests_Aspect_SimpleBeforeAdvice());
+ $advices->addAdvice(new Sabel_Tests_Aspect_SimpleAfterReturningAdvice());
+
+ foreach ($advices->toArray() as $advice) {
+ $this->assertTrue($advice instanceof Sabel_Aspect_Advice);
+ }
+ }
+
+ public function testPlainObjectAdvice()
+ {
+ $weaver = $this->weaver;
+
+ $advisor = new Sabel_Aspect_Advisor_RegexMatcherPointcut();
+ $advisor->setClassMatchPattern("/.+/");
+ $advisor->setMethodMatchPattern("/get+/");
+
+ $throwAdvisor = new Sabel_Aspect_Advisor_RegexMatcherPointcut();
+ $throwAdvisor->setClassMatchPattern("/.+/");
+ $throwAdvisor->setMethodMatchPattern("/will+/");
+
+ $poAdvice = new Sabel_Tests_Aspect_PlainObject_Advice();
+ $plainObjectInterceptor = new Sabel_Aspect_Interceptor_PlainObjectAdvice($poAdvice);
+ $plainObjectInterceptor->setBeforeAdviceMethod("before");
+ $plainObjectInterceptor->setAfterAdviceMethod("after");
+ $plainObjectInterceptor->setAroundAdviceMethod("around");
+ $plainObjectInterceptor->setThrowsAdviceMethod("throws");
+
+ $advisor->addAdvice($plainObjectInterceptor);
+ $throwAdvisor->addAdvice($plainObjectInterceptor);
+
+ $weaver->addAdvisor($advisor);
+ $weaver->addAdvisor($throwAdvisor);
+
+ $target = $weaver->getProxy();
+
+ $target->getX();
+
+ $this->assertEquals("getX", $poAdvice->before);
+ $this->assertEquals("X", $poAdvice->after);
+
+ $target->willThrowException();
+ $this->assertEquals("throws", $poAdvice->throws);
+ }
+
+ public function testPlainObjectPreventBefore()
+ {
+ $weaver = $this->weaver;
+
+ $advisor = new Sabel_Aspect_Advisor_RegexMatcherPointcut();
+ $advisor->setClassMatchPattern("/.+/");
+ $advisor->setMethodMatchPattern("/get+/");
+
+ $poAdvice = new Sabel_Tests_Aspect_PlainObject_PreventBeforeAdvice();
+ $plainObjectInterceptor = new Sabel_Aspect_Interceptor_PlainObjectAdvice($poAdvice);
+ $plainObjectInterceptor->setBeforeAdviceMethod("before");
+
+ $advisor->addAdvice($plainObjectInterceptor);
+
+ $weaver->addAdvisor($advisor);
+
+ $target = $weaver->getProxy();
+
+ $result = $target->getX();
+
+ $this->assertEquals("Y", $result);
+ }
+
+ public function testAnnotationPlainObjectAdvice()
+ {
+ $weaver = new Sabel_Aspect_Weaver();
+ $weaver->build("Sabel_Tests_Aspect_TargetClass",
+ "Sabel_Tests_Aspect_PlainObject_Advice");
+
+ $advice = $weaver->getAdvice();
+
+ $target = $weaver->getProxy();
+
+ $target->getX();
+ $this->assertEquals("getX", $advice->before);
+
+ $target->setX("x");
+ $this->assertEquals("setX", $advice->before);
+ }
+
+ public function testGetClassName()
+ {
+ $weaver = $this->weaver;
+
+ $weaver->setTarget("Sabel_Tests_Aspect_TargetClass");
+ $target = $weaver->getProxy();
+
+ $this->assertEquals($target->__getClassName(), "Sabel_Tests_Aspect_TargetClass");
+ }
+
+ public function testTargetClass()
+ {
+ $weaver = $this->weaver;
+
+ $weaver->setTarget("Sabel_Tests_Aspect_TargetClass");
+ $target = $weaver->getProxy();
+
+ $this->assertEquals(get_class($target), "Sabel_Aspect_Proxy");
+ }
+}
+
+/**
+ * @classMatch Sabel+
+ *
+ * @advisor Sabel_Aspect_Advisor_RegexMatcherPointcut
+ * @interceptor Sabel_Aspect_Interceptor_PlainObjectAdvice
+ */
+class Sabel_Tests_Aspect_PlainObject_Advice
+{
+ public
+ $before,
+ $after,
+ $throws = "";
+
+ /**
+ * @before get+
+ */
+ public function before($method, $arguments, $target)
+ {
+ $this->before = $method->getName();
+ }
+
+ /**
+ * @before set+
+ */
+ public function beforeSet($method, $arguments, $target)
+ {
+ $this->before = $method->getName();
+ }
+
+ public function after($method, $arguments, $target, $result)
+ {
+ $this->after = $result;
+ }
+
+ public function around($invocation)
+ {
+ $result = $invocation->proceed();
+ return $result;
+ }
+
+ public function throws($method, $arguments, $target, $exception)
+ {
+ $this->throws = $exception->getMessage();
+ }
+}
+
+class Sabel_Tests_Aspect_PlainObject_PreventBeforeAdvice
+{
+ public function before($method, $arguments, $target)
+ {
+ return "Y";
+ }
+}
\ No newline at end of file
diff --git a/Test/Aspect/SimpleUsage.php b/Test/Aspect/SimpleUsage.php
new file mode 100755
index 0000000..1899aa3
--- /dev/null
+++ b/Test/Aspect/SimpleUsage.php
@@ -0,0 +1,66 @@
+
+ */
+class Test_Aspect_SimpleUsage extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Aspect_SimpleUsage");
+ }
+
+ public function setUp()
+ {
+ }
+
+ public function testUsageConfig()
+ {
+ $updatable = load("Sabel_Test_Aspect_SimpleUsage_Person", new Sabel_Test_Aspect_ConfigSimple());
+
+ $this->assertTrue($updatable instanceof Sabel_Aspect_Proxy);
+ $updatable->updateState();
+ }
+}
+
+class Sabel_Test_Aspect_ConfigSimple extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->aspect("Sabel_Test_Aspect_Updatable")->advice("Sabel_Test_Aspect_UpdatableAdvice");
+ }
+}
+
+interface Sabel_Test_Aspect_Updatable
+{
+ public function updateState();
+}
+
+class Sabel_Test_Aspect_SimpleUsage_Person implements Sabel_Test_Aspect_Updatable
+{
+ public function updateState()
+ {
+ // update state
+ }
+}
+
+class Sabel_Test_Aspect_UpdatableAdvice
+{
+ /**
+ * @before update.+
+ */
+ public function before($method, $arguments, $target)
+ {
+ // before
+ }
+
+ /**
+ * @around update.+
+ */
+ public function around($invocation)
+ {
+ // around
+ }
+}
\ No newline at end of file
diff --git a/Test/Aspect/Tests.php b/Test/Aspect/Tests.php
new file mode 100755
index 0000000..98cf1c1
--- /dev/null
+++ b/Test/Aspect/Tests.php
@@ -0,0 +1,41 @@
+
+ */
+class Test_Aspect_Tests
+{
+ public static function main()
+ {
+ PHPUnit_TextUI_TestRunner::run(self::suite());
+ }
+
+ public static function suite()
+ {
+ $suite = new PHPUnit_Framework_TestSuite();
+
+ $suite->addTest(Test_Aspect_Proxy::suite());
+ $suite->addTest(Test_Aspect_Pointcuts::suite());
+ $suite->addTest(Test_Aspect_Matcher::suite());
+ $suite->addTest(Test_Aspect_Introduction::suite());
+ $suite->addTest(Test_Aspect_SimpleUsage::suite());
+
+ // @todo remove this before commit.
+ $suite->addTest(Test_Aspect_Exp::suite());
+
+ return $suite;
+ }
+}
\ No newline at end of file
diff --git a/Test/Aspect/classes/All.php b/Test/Aspect/classes/All.php
new file mode 100755
index 0000000..c076b1f
--- /dev/null
+++ b/Test/Aspect/classes/All.php
@@ -0,0 +1,130 @@
+getName() === "Sabel_Tests_Aspect_TargetClass");
+ }
+}
+
+class MyMethodMatcher extends Sabel_Aspect_Matcher_StaticMethod
+{
+ public function matches($method, $class)
+ {
+ return ($method === "setX");
+ }
+}
+
+class MyStaticMethodMatcherPointcutAdvisor extends Sabel_Aspect_Advisor_StaticMethodMatcherPointcut
+{
+ public function __construct()
+ {
+ defineClass("MyClassMatcher", '
+ class %s implements Sabel_Aspect_Matcher_Class
+ {
+ public function matches($class)
+ {
+ return true;
+ }
+ }
+ ');
+
+ $this->setClassMatcher(new MyClassMatcher());
+ }
+
+ public function matches($method, $class)
+ {
+ return preg_match("/get+/", $method);
+ }
+}
+
+class MyRegexMethodMatcherPointcutAdvisor extends Sabel_Aspect_Advisor_StaticMethodMatcherPointcut
+{
+ private $pattern;
+
+ public function __construct()
+ {
+ defineClass("MyClassMatcher", '
+ class %s implements Sabel_Aspect_Matcher_Class
+ {
+ public function matches($class)
+ {
+ return true;
+ }
+ }
+ ');
+
+ $this->setClassMatcher(new MyClassMatcher());
+ }
+
+ public function setPattern($pattern)
+ {
+ $this->pattern = $pattern;
+ }
+
+ public function matches($method, $class)
+ {
+ return preg_match($this->pattern, $method);
+ }
+}
+
+function defineClass($className, $class)
+{
+ if (!class_exists($className)) {
+ eval(sprintf($class, $className));
+ }
+}
diff --git a/Test/Aspect/classes/Interceptors.php b/Test/Aspect/classes/Interceptors.php
new file mode 100755
index 0000000..45d36a9
--- /dev/null
+++ b/Test/Aspect/classes/Interceptors.php
@@ -0,0 +1,46 @@
+calledMethods[] = $method->getName();
+ }
+
+ public function getCalledMethods()
+ {
+ return $this->calledMethods;
+ }
+}
+
+class Sabel_Tests_Aspect_SimpleAfterReturningAdvice implements Sabel_Aspect_Advice_MethodAfterReturning
+{
+ private $results = array();
+
+ public function after($method, $arguments, $target, $returnValue)
+ {
+ $this->results[] = $returnValue;
+ }
+
+ public function getResults()
+ {
+ return $this->results;
+ }
+}
+
+class Sabel_Tests_Aspect_SimpleThrowsAdvice implements Sabel_Aspect_Advice_MethodThrows
+{
+ private $throwsMessage = "";
+
+ public function throws($method, $arguments, $target, $exception)
+ {
+ $this->throwsMessage = $exception->getMessage();
+ }
+
+ public function getThrowsMessage()
+ {
+ return $this->throwsMessage;
+ }
+}
\ No newline at end of file
diff --git a/Test/Bus/Runner.php b/Test/Bus/Runner.php
new file mode 100755
index 0000000..bc9ac82
--- /dev/null
+++ b/Test/Bus/Runner.php
@@ -0,0 +1,184 @@
+
+ */
+class Test_Bus_Runner extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Bus_Runner");
+ }
+
+ public function testEmptyNameProcessor()
+ {
+ try {
+ $bus = Sabel_Bus::create();
+ $bus->addProcessor(new HogeProcessor(""));
+ } catch (Sabel_Exception_InvalidArgument $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testProcessorList()
+ {
+ $bus = Sabel_Bus::create();
+ $bus->addProcessor(new HogeProcessor("hoge"));
+ $bus->addProcessor(new FugaProcessor("fuga"));
+ $bus->addProcessor(new FooProcessor("foo"));
+
+ $ins = $bus->getProcessor("hoge");
+ $this->assertTrue($ins instanceof HogeProcessor);
+ $this->assertNull($bus->getProcessor("test"));
+
+ $list = $bus->getProcessorList();
+ $this->assertTrue($list->has("hoge"));
+ $this->assertTrue($list->has("fuga"));
+ $this->assertTrue($list->has("foo"));
+ $this->assertFalse($list->has("bar"));
+ }
+
+ public function testConfigs()
+ {
+ $bus = Sabel_Bus::create();
+ eval ("class TemporaryConfig implements Sabel_Config
+ {
+ public function configure() {}
+ }");
+
+ $bus->setConfig("tmp", new TemporaryConfig());
+ $this->assertTrue($bus->getConfig("tmp") instanceof Sabel_Config);
+ $this->assertNull($bus->getConfig("hoge"));
+ }
+
+ public function testBusInit()
+ {
+ $bus = Sabel_Bus::create(array("null" => null,
+ "int" => 10,
+ "string" => "test",
+ "bool" => false));
+
+ $this->assertEquals(null, $bus->get("null"));
+ $this->assertEquals(10, $bus->get("int"));
+ $this->assertEquals("test", $bus->get("string"));
+ $this->assertEquals(false, $bus->get("bool"));
+ }
+
+ public function testRun()
+ {
+ $bus = Sabel_Bus::create();
+ $bus->run(new TestBusConfig());
+
+ $this->assertEquals("10", $bus->get("a"));
+ $this->assertEquals("20", $bus->get("b"));
+ $this->assertEquals(null, $bus->get("c"));
+ }
+
+ public function testAttatchExecuteBeforeEvent()
+ {
+ $bus = Sabel_Bus::create();
+ $bus->attachExecuteBeforeEvent("foo", new TestEvent(), "beforeMethod");
+ $bus->run(new TestBusConfig());
+
+ $this->assertEquals("before: fuga_result", $bus->get("beforeResult"));
+ }
+
+ public function testAttatchExecuteAfterEvent()
+ {
+ $bus = Sabel_Bus::create();
+ $bus->attachExecuteAfterEvent("hoge", new TestEvent(), "afterMethod");
+ $bus->run(new TestBusConfig());
+
+ $this->assertEquals("after: hoge_result", $bus->get("afterResult"));
+ }
+
+ public function testAttatchExecuteAfterEvent2()
+ {
+ $bus = Sabel_Bus::create();
+ $bus->attachExecuteAfterEvent("hoge", new TestEvent(), "afterMethod");
+ $bus->attachExecuteAfterEvent("hoge", new TestEvent2(), "afterMethod");
+ $bus->run(new TestBusConfig());
+
+ $this->assertEquals("after: hoge_result", $bus->get("afterResult"));
+ $this->assertEquals("after: hoge_result", $bus->get("afterResult2"));
+ }
+
+ public function testHas()
+ {
+ $bus = Sabel_Bus::create();
+ $bus->set("a", "10");
+ $bus->set("b", "20");
+ $bus->set("c", "30");
+
+ $this->assertTrue($bus->has("a"));
+ $this->assertFalse($bus->has("d"));
+
+ $this->assertTrue($bus->has(array("a", "b", "c")));
+ $this->assertFalse($bus->has(array("a", "d", "c")));
+ }
+}
+
+class TestEvent
+{
+ public function beforeMethod($bus)
+ {
+ $bus->set("beforeResult", "before: " . $bus->get("result"));
+ }
+
+ public function afterMethod($bus)
+ {
+ $bus->set("afterResult", "after: " . $bus->get("result"));
+ }
+}
+
+class TestEvent2
+{
+ public function afterMethod($bus)
+ {
+ $bus->set("afterResult2", "after: " . $bus->get("result"));
+ }
+}
+
+class TestBusConfig extends Sabel_Bus_Config
+{
+ protected $processors = array("hoge" => "HogeProcessor",
+ "fuga" => "FugaProcessor",
+ "foo" => "FooProcessor");
+}
+
+class HogeProcessor extends Sabel_Bus_Processor
+{
+ public function execute(Sabel_Bus $bus)
+ {
+ $bus->set("a", "10");
+ $bus->set("result", "hoge_result");
+ }
+}
+
+class FugaProcessor extends Sabel_Bus_Processor
+{
+ public function execute(Sabel_Bus $bus)
+ {
+ $bus->set("b", "20");
+ $bus->set("result", "fuga_result");
+ }
+}
+
+class FooProcessor extends Sabel_Bus_Processor
+{
+ public function execute(Sabel_Bus $bus)
+ {
+ $this->a = $bus->get("a");
+ $this->b = $bus->get("b");
+
+ if ($this->a !== "10") throw new Exception("test error");
+ if ($this->b !== "20") throw new Exception("test error");
+
+ $bus->set("result", "foo_result");
+ }
+}
diff --git a/Test/Bus/Tests.php b/Test/Bus/Tests.php
new file mode 100755
index 0000000..aa010cc
--- /dev/null
+++ b/Test/Bus/Tests.php
@@ -0,0 +1,14 @@
+addTest(Test_Bus_Runner::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/Cache/Apc.php b/Test/Cache/Apc.php
new file mode 100755
index 0000000..8bf5679
--- /dev/null
+++ b/Test/Cache/Apc.php
@@ -0,0 +1,23 @@
+
+ */
+class Test_Cache_Apc extends Test_Cache_Test
+{
+ public static function suite()
+ {
+ if (ini_get("apc.enable_cli") === "1") {
+ return self::createSuite("Test_Cache_Apc");
+ } else {
+ throw new Exception("must enable 'apc.enable_cli' in your php.ini");
+ }
+ }
+
+ public function setUp()
+ {
+ $this->cache = Sabel_Cache_Apc::create();
+ $this->cache->delete("hoge");
+ }
+}
diff --git a/Test/Cache/File.php b/Test/Cache/File.php
new file mode 100755
index 0000000..613f045
--- /dev/null
+++ b/Test/Cache/File.php
@@ -0,0 +1,23 @@
+
+ */
+class Test_Cache_File extends Test_Cache_Test
+{
+ public static function suite()
+ {
+ $dir = SABEL_BASE . DIRECTORY_SEPARATOR . "Test" . DIRECTORY_SEPARATOR
+ . "data" . DIRECTORY_SEPARATOR . "application" . DIRECTORY_SEPARATOR . "cache";
+
+ define("CACHE_DIR_PATH", $dir);
+ return self::createSuite("Test_Cache_File");
+ }
+
+ public function setUp()
+ {
+ $this->cache = Sabel_Cache_File::create();
+ $this->cache->delete("hoge");
+ }
+}
diff --git a/Test/Cache/Memcache.php b/Test/Cache/Memcache.php
new file mode 100755
index 0000000..6253a77
--- /dev/null
+++ b/Test/Cache/Memcache.php
@@ -0,0 +1,19 @@
+
+ */
+class Test_Cache_Memcache extends Test_Cache_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Cache_Memcache");
+ }
+
+ public function setUp()
+ {
+ $this->cache = Sabel_Cache_Memcache::create();
+ $this->cache->delete("hoge");
+ }
+}
diff --git a/Test/Cache/Null.php b/Test/Cache/Null.php
new file mode 100755
index 0000000..2c3155b
--- /dev/null
+++ b/Test/Cache/Null.php
@@ -0,0 +1,39 @@
+
+ */
+class Test_Cache_Null extends SabelTestCase
+{
+ protected $cache = null;
+
+ public static function suite()
+ {
+ return self::createSuite("Test_Cache_Null");
+ }
+
+ public function setUp()
+ {
+ $this->cache = Sabel_Cache_Null::create();
+ }
+
+ public function testRead()
+ {
+ $this->assertNull($this->cache->read("hoge"));
+ }
+
+ public function testWrite()
+ {
+ $this->cache->write("hoge", "value");
+ $this->assertNull($this->cache->read("hoge"));
+ }
+
+ public function testDelete()
+ {
+ $this->cache->delete("hoge");
+ $this->assertNull($this->cache->read("hoge"));
+ }
+}
diff --git a/Test/Cache/Test.php b/Test/Cache/Test.php
new file mode 100755
index 0000000..d2bd8b1
--- /dev/null
+++ b/Test/Cache/Test.php
@@ -0,0 +1,56 @@
+
+ */
+class Test_Cache_Test extends SabelTestCase
+{
+ protected $cache = null;
+
+ public function testRead()
+ {
+ $this->assertNull($this->cache->read("hoge"));
+ }
+
+ public function testWrite()
+ {
+ $this->cache->write("hoge", "value");
+ $this->assertEquals("value", $this->cache->read("hoge"));
+ }
+
+ public function testDelete()
+ {
+ $this->cache->write("hoge", "value");
+ $this->assertEquals("value", $this->cache->read("hoge"));
+ $this->cache->delete("hoge");
+ $this->assertNull($this->cache->read("hoge"));
+ }
+
+ public function testArray()
+ {
+ $this->cache->write("hoge", array("k1" => "value", "k2" => 10, "k3" => true));
+ $array = $this->cache->read("hoge");
+ $this->assertTrue(is_array($array));
+ $this->assertEquals("value", $array["k1"]);
+ $this->assertEquals(10, $array["k2"]);
+ $this->assertEquals(true, $array["k3"]);
+ }
+
+ public function testObject()
+ {
+ $obj = new stdClass();
+ $obj->k1 = "value";
+ $obj->k2 = 10;
+ $obj->k3 = true;
+
+ $this->cache->write("hoge", $obj);
+ $object = $this->cache->read("hoge");
+ $this->assertTrue(is_object($object));
+ $this->assertEquals("value", $object->k1);
+ $this->assertEquals(10, $object->k2);
+ $this->assertEquals(true, $object->k3);
+ }
+}
diff --git a/Test/Cache/Tests.php b/Test/Cache/Tests.php
new file mode 100755
index 0000000..b6f30d6
--- /dev/null
+++ b/Test/Cache/Tests.php
@@ -0,0 +1,37 @@
+
+ */
+class Test_Cache_Tests
+{
+ public static function main()
+ {
+ PHPUnit_TextUI_TestRunner::run(self::suite());
+ }
+
+ public static function suite()
+ {
+ $suite = new PHPUnit_Framework_TestSuite();
+
+ //if (extension_loaded("apc")) {
+ // $suite->addTest(Test_Cache_Apc::suite());
+ //}
+
+ if (extension_loaded("memcache")) {
+ $suite->addTest(Test_Cache_Memcache::suite());
+ }
+
+ $suite->addTest(Test_Cache_File::suite());
+ $suite->addTest(Test_Cache_Null::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/Console.php b/Test/Console.php
new file mode 100755
index 0000000..14a83ab
--- /dev/null
+++ b/Test/Console.php
@@ -0,0 +1,89 @@
+
+ */
+class Test_Console extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Console");
+ }
+
+ public function testMessage()
+ {
+ $_SERVER["IS_WINDOWS"] = true;
+
+ ob_start();
+ Sabel_Console::success("success");
+ $result = ob_get_clean();
+ $this->assertEquals("[SUCCESS] success", rtrim($result));
+
+ ob_start();
+ Sabel_Console::warning("warning");
+ $result = ob_get_clean();
+ $this->assertEquals("[WARNING] warning", rtrim($result));
+
+ ob_start();
+ Sabel_Console::error("failure");
+ $result = ob_get_clean();
+ $this->assertEquals("[FAILURE] failure", rtrim($result));
+
+ ob_start();
+ Sabel_Console::message("message");
+ $result = ob_get_clean();
+ $this->assertEquals("[MESSAGE] message", rtrim($result));
+ }
+
+ public function testHasOption()
+ {
+ $args = array("rm", "-r");
+ $this->assertTrue(Sabel_Console::hasOption("r", $args));
+
+ $args = array("rm", "-r", "-f");
+ $this->assertTrue(Sabel_Console::hasOption("r", $args));
+ $this->assertTrue(Sabel_Console::hasOption("f", $args));
+
+ $args = array("rm", "-rf");
+ $this->assertTrue(Sabel_Console::hasOption("r", $args));
+ $this->assertTrue(Sabel_Console::hasOption("f", $args));
+ }
+
+ public function testHasOption2()
+ {
+ $args = array("cmd", "--foo");
+ $this->assertTrue(Sabel_Console::hasOption("foo", $args));
+
+ $args = array("cmd", "--foo=bar");
+ $this->assertTrue(Sabel_Console::hasOption("foo", $args));
+ }
+
+ public function testGetOption()
+ {
+ $args = array("cmd", "-d", "/var/tmp", "-f", "/tmp/test.txt");
+ $this->assertTrue(Sabel_Console::hasOption("d", $args));
+ $this->assertTrue(Sabel_Console::hasOption("f", $args));
+
+ $args = array("cmd", "-d", "/var/tmp", "-f", "/tmp/test.txt");
+ $this->assertEquals("/var/tmp", Sabel_Console::getOption("d", $args));
+ $this->assertEquals(array("cmd", "-f", "/tmp/test.txt"), $args);
+
+ $args = array("cmd", "-d", "/var/tmp", "-f", "/tmp/test.txt");
+ $this->assertEquals("/tmp/test.txt", Sabel_Console::getOption("f", $args));
+ $this->assertEquals(array("cmd", "-d", "/var/tmp"), $args);
+ }
+
+ public function testGetOption2()
+ {
+ $args = array("cmd", "--dir=/var/tmp", "--file=/tmp/test.txt");
+ $this->assertEquals("/var/tmp", Sabel_Console::getOption("dir", $args));
+ $this->assertEquals(array("cmd", "--file=/tmp/test.txt"), $args);
+
+ $args = array("cmd", "--dir=/var/tmp", "--file=/tmp/test.txt");
+ $this->assertEquals("/tmp/test.txt", Sabel_Console::getOption("file", $args));
+ $this->assertEquals(array("cmd", "--dir=/var/tmp"), $args);
+ }
+}
diff --git a/Test/Container.php b/Test/Container.php
new file mode 100755
index 0000000..6a03194
--- /dev/null
+++ b/Test/Container.php
@@ -0,0 +1,566 @@
+
+ */
+class Test_Container extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Container");
+ }
+
+ /**
+ * setUp
+ *
+ * @access public
+ * @return void
+ */
+ public function setUp()
+ {
+ }
+
+ /**
+ * @test
+ */
+ public function createContainer()
+ {
+ $container = Sabel_Container::create(new Sabel_CTest_Config());
+ }
+
+ /**
+ * @test
+ */
+ public function createContainerWithInvalidConfiguration()
+ {
+ try {
+ $container = Sabel_Container::create(new StdClass());
+ } catch (Sabel_Exception_InvalidArgument $e) {
+ $this->assertTrue(true);
+ return;
+ }
+
+ $this->fail();
+ }
+
+ /**
+ * @test
+ */
+ public function instanciateUndefinedClass()
+ {
+ $container = Sabel_Container::create(new Sabel_CTest_Config());
+
+ try {
+ $controller = $container->newInstance("Sabel_CTest_WillNotFoundClass");
+ } catch (Sabel_Container_Exception_UndefinedClass $une) {
+ $this->assertTrue(true);
+ return;
+ } catch (Exception $e) {
+ echo $e->getMessage();
+ $this->fail();
+ }
+
+ $this->fail();
+ }
+
+ /**
+ * @test
+ */
+ public function injectionWithoutSetterConfig()
+ {
+ }
+
+ public function injectionNestedDependency()
+ {
+ }
+
+ /**
+ * @test
+ */
+ public function injectionWithConstructor()
+ {
+ $container = Sabel_Container::create(new Sabel_CTest_Config_Construct());
+ $controller = $container->newInstance("Sabel_CTest_Controller_WithConstruct");
+ $this->assertTrue($controller->model instanceof Sabel_CTest_Model);
+ }
+
+ /**
+ * @test
+ */
+ public function SetterInjection()
+ {
+ $container = Sabel_Container::create(new Sabel_CTest_Config());
+ $controller = $container->newInstance("Sabel_CTest_Controller");
+ $this->assertTrue($controller->model instanceof Sabel_CTest_Model);
+ }
+
+ /**
+ * @test
+ */
+ public function constructorWithBind()
+ {
+ $container = Sabel_Container::create(new Sabel_CTest_Config_Construct());
+ $controller = $container->newInstance("Sabel_CTest_Controller_WithConstruct");
+ $this->assertTrue($controller->model instanceof Sabel_CTest_Model);
+ }
+
+ /**
+ * simple injection
+ *
+ * @test
+ */
+ public function injection()
+ {
+ $injector = Sabel_Container::create(new Config());
+ $person = $injector->newInstance("Person");
+
+ $this->assertEquals(10, $person->calc());
+ }
+
+ public function testConstructorInjection()
+ {
+ $injector = Sabel_Container::create(new ConstructConfig());
+ $car = $injector->newInstance("Car");
+ $this->assertTrue(is_object($car->getEngine()));
+ }
+
+ public function testStrLiteralConstructorInjection()
+ {
+ $injector = Sabel_Container::create(new StrLiteralConstructConfig());
+ $car = $injector->newInstance("Car");
+ $this->assertEquals("this is engine", $car->getEngine());
+ }
+
+ public function testNumLiteralConstructorInjection()
+ {
+ $injector = Sabel_Container::create(new NumLiteralConstructConfig());
+ $car = $injector->newInstance("Car");
+ $this->assertEquals(123, $car->getEngine());
+ }
+
+ public function testBoolLiteralConstructorInjection()
+ {
+ $injector = Sabel_Container::create(new BoolLiteralConstructConfig());
+ $car = $injector->newInstance("Car");
+ $this->assertTrue($car->getEngine());
+ }
+
+ public function testWrongInjectionConfig()
+ {
+ $injector = Sabel_Container::create(new WrongClassNameConfig());
+
+ try {
+ $person = $injector->newInstance("Person");
+ $this->fail();
+ } catch (Exception $e) {
+ $this->assertEquals("WrongCalculator does't exist", $e->getMessage());
+ }
+ }
+
+ public function testMultipleConstructerInjection()
+ {
+ $injector = Sabel_Container::create(new MultipleConstructConfig());
+
+ $oil = new EngineOil("normal");
+ $engine = new MultiEngine($oil);
+ $car = new MultiCar($engine, "multiple");
+
+ $injCar = $injector->newInstance("MultiCar");
+
+ $this->assertEquals($car, $injCar);
+ }
+
+ public function testSpecificSetter()
+ {
+ $injector = Sabel_Container::create(new SpecificSetterConfig());
+ $instance = $injector->newInstance("SpecificSetter");
+
+ $engineOil = new EngineOil("specific");
+ $specific = new SpecificSetter();
+ $specific->setSpecificSetter($engineOil);
+
+ $this->assertEquals($instance, $specific);
+ }
+}
+
+/** test classes **/
+
+interface Sabel_CTest_Controller_Interface
+{
+}
+class Sabel_CTest_Controller implements Sabel_CTest_Controller_Interface
+{
+ public $model = null;
+
+ public function setModel(Sabel_CTest_Model $model)
+ {
+ $this->model = $model;
+ }
+}
+class Sabel_CTest_Controller_WithConstruct
+{
+ public $model = null;
+
+ public function __construct(Sabel_CTest_Model $model)
+ {
+ $this->model = $model;
+ }
+}
+interface Sabel_CTest_Model
+{
+ public function getData();
+}
+interface Sabel_CTest_Result
+{
+}
+class Sabel_CTest_Result_Implement implements Sabel_CTest_Result
+{
+}
+class Sabel_CTest_Model_Implement implements Sabel_CTest_Model
+{
+ // @inject Sabel_CTest_Model
+ public function getData()
+ {
+ }
+}
+class Person
+{
+ protected $age = null;
+ protected $calc = null;
+
+ public function __construct(Age $age)
+ {
+ $this->age = $age;
+ }
+
+ public function howOldAreYou()
+ {
+ return $this->age->getAge();
+ }
+
+ public function setCalculator(Calculator $c)
+ {
+ $this->calc = $c;
+ }
+
+ public function getCalculator()
+ {
+ if (!is_object($this->calc)) {
+ throw new Exception(var_export($this->calc, 1));
+ }
+
+ return $this->calc;
+ }
+
+ public function calc()
+ {
+ return $this->calc->calc($this);
+ }
+}
+class Integer
+{
+ private $self = 0;
+
+ public function set($num)
+ {
+ $this->self = $num;
+ }
+
+ public function add($num)
+ {
+ return $this->self + $num;
+ }
+}
+interface Calculator
+{
+ public function calc($obj);
+}
+class FrastrationCalculator implements Calculator
+{
+ private $int = null;
+
+ public function __construct(Integer $int)
+ {
+ $this->int = $int;
+ }
+
+ public function calc($obj)
+ {
+ $this->int->set(5);
+ $this->int->add(5);
+ return 10;
+ }
+}
+class Age
+{
+ protected $age = 15;
+
+ public function __construct()
+ {
+ }
+
+ public function getAge()
+ {
+ return $this->age;
+ }
+
+ public function setAge($age = 0)
+ {
+ $this->age = $age;
+ }
+}
+class SpecificSetter
+{
+ private $oil = null;
+ public function setSpecificSetter(Oil $oil)
+ {
+ $this->oil = $oil;
+ }
+}
+class MultiCar
+{
+ private $engine = null;
+ private $shaft = null;
+
+ public function __construct($engine, $shaft)
+ {
+ $this->engine = $engine;
+ $this->shaft = $shaft;
+ }
+}
+class MultiEngine
+{
+ private $oil = null;
+
+ public function __construct($oil)
+ {
+ $this->oil = $oil;
+ }
+}
+interface Oil
+{
+}
+class EngineOil implements Oil
+{
+ private $type = "";
+ public function __construct($type)
+ {
+ $this->type = $type;
+ }
+}
+class Car
+{
+ private $engine = null;
+
+ public function __construct($engine)
+ {
+ $this->engine = $engine;
+ }
+
+ public function getEngine()
+ {
+ return $this->engine;
+ }
+}
+class Engine
+{
+ public function run(){}
+}
+
+class AspectConfig extends Sabel_Container_Injection
+{
+ private $trace = null;
+
+ public function __construct($trace)
+ {
+ $this->trace = $trace;
+ }
+
+ public function configure()
+ {
+ $this->aspect("AspectTarget")->apply($this->trace)->to("run");
+ }
+
+ public function getTrace()
+ {
+ return $this->trace;
+ }
+}
+
+class MultipleAspectConfig extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $trace = new Trace();
+ $this->aspect("AspectTarget")->apply($trace)->to("run");
+ $this->aspect("AspectTarget")->apply($trace)->to("run");
+ }
+}
+
+class AspectTarget
+{
+ public function run($parameter)
+ {
+ return $parameter;
+ }
+
+ public function runrun($parameter)
+ {
+ return $parameter;
+ }
+}
+
+class BaseAspect
+{
+ public function after($joinpoint){}
+ public function before($joinpoint){}
+}
+
+class Trace extends BaseAspect
+{
+ private $argument = "";
+
+ public function after($joinpoint)
+ {
+ $this->argument = $joinpoint->getArgument(0);
+ }
+
+ public function getArgument()
+ {
+ return $this->argument;
+ }
+}
+
+class Config extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->bind("Calculator")->to("FrastrationCalculator");
+ }
+}
+class WrongClassNameConfig extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->bind("Calculator")->to("WrongCalculator");
+ }
+}
+class AspectToMethod extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->aspect("AspectTarget")->apply("Trace")->to("run");
+ }
+}
+class AspectToEveryMethods extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->aspect("AspectTarget")->apply("Trace")->toEveryMethods();
+ }
+}
+class AspectToMethodRegex extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->aspect("AspectTarget")->apply("Trace")->toMethodRegex("run.+");
+ }
+}
+class ConstructConfig extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->construct("Car")->with("Engine");
+ }
+}
+class StrLiteralConstructConfig extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->construct("Car")->with("this is engine");
+ }
+}
+class NumLiteralConstructConfig extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->construct("Car")->with(123);
+ }
+}
+class BoolLiteralConstructConfig extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->construct("Car")->with(true);
+ }
+}
+class SpecificSetterConfig extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->construct("EngineOil")->with("specific");
+ $this->bind("Oil")->to("EngineOil")->setter("setSpecificSetter");
+ }
+}
+class MultipleConstructConfig extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->construct("MultiCar")->with("MultiEngine")
+ ->with("multiple");
+
+ $this->construct("MultiEngine")->with("Oil");
+ $this->construct("EngineOil")->with("normal");
+
+ $this->bind("Oil")->to("EngineOil");
+ }
+}
+class Sabel_CTest_Config extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->bind("Sabel_CTest_Model")
+ ->to("Sabel_CTest_Model_Implement")->setter("setModel");
+ }
+}
+class Sabel_CTest_Config_WithoutSetter extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->bind("Sabel_CTest_Model")
+ ->to("Sabel_CTest_Model_Implement");
+ }
+}
+class Sabel_CTest_Config_Construct extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->construct("Sabel_CTest_Controller_WithConstruct")->with("Sabel_CTest_Model_Implement");
+ }
+}
+class Sabel_CTest_Config_ConstructWithBind extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->bind("Sabel_CTest_Model")->to("Sabel_CTest_Model_Implement");
+ $this->construct("Sabel_CTest_Controller_WithConstruct")->with("Sabel_CTest_Model");
+ }
+}
+class Sabel_CTest_Config_ConstructWithInvalidImplement extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->construct("Sabel_CTest_Controller")->with("Sabel_CTest_Model");
+ }
+}
+class Sabel_CTest_Config_ConstructWithInterface extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->construct("Sabel_CTest_Controller")->with("Sabel_CTest_Model");
+ }
+}
diff --git a/Test/Controller/Page.php b/Test/Controller/Page.php
new file mode 100755
index 0000000..dea2080
--- /dev/null
+++ b/Test/Controller/Page.php
@@ -0,0 +1,158 @@
+
+ */
+class Test_Controller_Page extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Controller_Page");
+ }
+
+ public function testSetInvalidAction()
+ {
+ $c = $this->createController();
+ try {
+ $c->setAction(10000);
+ } catch (Sabel_Exception_InvalidArgument $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testIndexAction()
+ {
+ $c = $this->createController();
+ $c->initialize();
+ $c->setAction("index");
+ $c->execute();
+
+ $this->assertTrue($c->isExecuted());
+ $this->assertEquals("index", $c->getAction());
+ $this->assertEquals("index", $c->getAttribute("actionResult"));
+ }
+
+ public function testFugaAction()
+ {
+ $c = $this->createController();
+ $c->initialize();
+ $c->execute("fuga", array("10", "20", "30"));
+
+ $this->assertEquals("10 20 30", $c->getAttribute("actionResult"));
+ }
+
+ public function testReservedAction()
+ {
+ $c = $this->createController();
+ $c->setAction("getRequest");
+ $c->execute();
+
+ $this->assertFalse($c->isExecuted());
+ $this->assertEquals(Sabel_Response::NOT_FOUND, $c->getResponse()->getStatus()->getCode());
+ }
+
+ public function testHiddenAction()
+ {
+ $c = $this->createController();
+ $c->setAction("hiddenAction");
+ $c->execute();
+
+ $this->assertFalse($c->isExecuted());
+ $this->assertEquals(Sabel_Response::NOT_FOUND, $c->getResponse()->getStatus()->getCode());
+ }
+
+ public function testProtectedAction()
+ {
+ $c = $this->createController();
+ $c->setAction("hoge");
+ $c->execute();
+
+ $this->assertFalse($c->isExecuted());
+ }
+
+ public function testAttributesAndResponses()
+ {
+ $c = $this->createController();
+ $c->setAttribute("a", "10");
+ $c->setAttribute("b", "20");
+ $c->assign("c", "30");
+
+ $this->assertEquals("10", $c->getAttribute("a"));
+ $this->assertEquals("20", $c->getAttribute("b"));
+ $this->assertEquals(null, $c->getAttribute("c"));
+ $this->assertEquals("30", $c->getResponse()->getResponse("c"));
+ }
+
+ public function testAttributes()
+ {
+ $c = $this->createController();
+ $c->setAttributes(array("a" => "10", "b" => "20"));
+ $this->assertEquals("10", $c->getAttribute("a"));
+ $this->assertEquals("20", $c->getAttribute("b"));
+ $this->assertEquals(null, $c->getAttribute("c"));
+
+ $expected = array("a" => "10", "b" => "20");
+ $this->assertEquals($expected, $c->getAttributes());
+ }
+
+ public function testIsAttributeSet()
+ {
+ $c = $this->createController();
+ $c->setAttribute("a", "10");
+ $this->assertTrue($c->isAttributeSet("a"));
+ $c->setAttribute("b", null);
+ $this->assertFalse($c->isAttributeSet("b"));
+ }
+
+ public function testHasAttribute()
+ {
+ $c = $this->createController();
+ $c->setAttribute("a", "10");
+ $this->assertTrue($c->hasAttribute("a"));
+ $c->setAttribute("b", null);
+ $this->assertTrue($c->hasAttribute("b"));
+ }
+
+ public function testMagickMethods()
+ {
+ $c = $this->createController();
+ $c->a = "10";
+ $c->b = "20";
+ $this->assertEquals("10", $c->a);
+ $this->assertEquals("20", $c->b);
+ $this->assertEquals(null, $c->c);
+ $this->assertEquals("10", $c->getAttribute("a"));
+ $this->assertEquals("20", $c->getAttribute("b"));
+ }
+
+ protected function createController()
+ {
+ $c = new TestController;
+ $c->setResponse(new Sabel_Response_Object());
+
+ return $c;
+ }
+}
+
+class TestController extends Sabel_Controller_Page
+{
+ protected $hidden = array("hiddenAction");
+
+ public function index()
+ {
+ $this->actionResult = "index";
+ }
+
+ public function fuga($a, $b, $c)
+ {
+ $this->actionResult = "$a $b $c";
+ }
+
+ public function hiddenAction() {}
+ protected function hoge() {}
+}
diff --git a/Test/Controller/Tests.php b/Test/Controller/Tests.php
new file mode 100755
index 0000000..77dd9b2
--- /dev/null
+++ b/Test/Controller/Tests.php
@@ -0,0 +1,14 @@
+addTest(Test_Controller_Page::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/Cookie/InMemory.php b/Test/Cookie/InMemory.php
new file mode 100755
index 0000000..4482d5b
--- /dev/null
+++ b/Test/Cookie/InMemory.php
@@ -0,0 +1,77 @@
+
+ */
+class Test_Cookie_InMemory extends SabelTestCase
+{
+ private $cookie = null;
+
+ public static function suite()
+ {
+ return self::createSuite("Test_Cookie_InMemory");
+ }
+
+ public function setUp()
+ {
+ $this->cookie = Sabel_Cookie_InMemory::create();
+ }
+
+ public function testSimple()
+ {
+ $this->cookie->set("foo", "10");
+ $this->assertEquals("10", $this->cookie->get("foo"));
+ $this->assertEquals(null, $this->cookie->get("bar"));
+ }
+
+ public function testPath()
+ {
+ $this->setUri("/");
+ $this->cookie->set("bar", "20", array("path" => "/bar"));
+ $this->assertEquals(null, $this->cookie->get("bar"));
+
+ # request: http://localhost/foo
+ $this->setUri("/foo");
+ $this->assertEquals(null, $this->cookie->get("bar"));
+
+ # request: http://localhost/bar
+ $this->setUri("/bar");
+ $this->assertEquals("20", $this->cookie->get("bar"));
+
+ # request: http://localhost/bar/baz
+ $this->setUri("/bar/baz");
+ $this->assertEquals("20", $this->cookie->get("bar"));
+ }
+
+ public function testExpire()
+ {
+ $this->setUri("/");
+ $this->cookie->set("hoge", "30");
+ $this->assertEquals("30", $this->cookie->get("hoge"));
+
+ $this->cookie->set("hoge", "30", array("expire" => time() - 3600));
+ $this->assertEquals(null, $this->cookie->get("hoge"));
+ }
+
+ public function testDelete()
+ {
+ $this->setUri("/");
+ $this->assertNotNull($this->cookie->get("foo"));
+
+ $this->cookie->delete("foo");
+ $this->assertNull($this->cookie->get("foo"));
+ }
+
+ protected function setUri($uri)
+ {
+ $bus = Sabel_Context::getContext()->getBus();
+ if (is_object($bus) && ($request = $bus->get("request"))) {
+ $request->setUri($uri);
+ }
+
+ $_SERVER["REQUEST_URI"] = $uri;
+ }
+}
diff --git a/Test/Cookie/Tests.php b/Test/Cookie/Tests.php
new file mode 100755
index 0000000..7c8b8e0
--- /dev/null
+++ b/Test/Cookie/Tests.php
@@ -0,0 +1,14 @@
+addTest(Test_Cookie_InMemory::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/DB/Config.php b/Test/DB/Config.php
new file mode 100755
index 0000000..2446fb8
--- /dev/null
+++ b/Test/DB/Config.php
@@ -0,0 +1,115 @@
+
+ */
+class Test_DB_Config extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Config");
+ }
+
+ public function testInitialize()
+ {
+ Sabel_Db_Config::initialize(new TestDatabaseConfig());
+ $config = Sabel_Db_Config::get("configtest");
+ $this->assertEquals("localhost", $config["host"]);
+ $this->assertEquals("mydb", $config["database"]);
+ }
+
+ public function testDefaultSchemaName()
+ {
+ $params = array("package" => "sabel.db.mysql",
+ "database" => "mydb");
+
+ Sabel_Db_Config::add("configtest", $params);
+ $this->assertEquals("mydb", Sabel_Db_Config::getSchemaName("configtest"));
+
+ $params = array("package" => "sabel.db.pgsql",
+ "database" => "mydb");
+
+ Sabel_Db_Config::add("configtest", $params);
+ $this->assertEquals("public", Sabel_Db_Config::getSchemaName("configtest"));
+
+ $params = array("package" => "sabel.db.pdo.pgsql",
+ "database" => "mydb");
+
+ Sabel_Db_Config::add("configtest", $params);
+ $this->assertEquals("public", Sabel_Db_Config::getSchemaName("configtest"));
+
+ $params = array("package" => "sabel.db.oci",
+ "database" => "mydb", "user" => "webuser");
+
+ Sabel_Db_Config::add("configtest", $params);
+ $this->assertEquals("WEBUSER", Sabel_Db_Config::getSchemaName("configtest"));
+
+ $params = array("package" => "sabel.db.pdo.oci",
+ "database" => "mydb", "user" => "webuser");
+
+ Sabel_Db_Config::add("configtest", $params);
+ $this->assertEquals("WEBUSER", Sabel_Db_Config::getSchemaName("configtest"));
+ }
+
+ public function testSchemaNameSet()
+ {
+ $params = array("package" => "sabel.db.mysql",
+ "database" => "mydb", "schema" => "hoge");
+
+ Sabel_Db_Config::add("configtest", $params);
+ $this->assertEquals("hoge", Sabel_Db_Config::getSchemaName("configtest"));
+
+ $params = array("package" => "sabel.db.pgsql",
+ "database" => "mydb", "schema" => "hoge");
+
+ Sabel_Db_Config::add("configtest", $params);
+ $this->assertEquals("hoge", Sabel_Db_Config::getSchemaName("configtest"));
+
+ $params = array("package" => "sabel.db.oci",
+ "database" => "mydb", "schema" => "HOGE");
+
+ Sabel_Db_Config::add("configtest", $params);
+ $this->assertEquals("HOGE", Sabel_Db_Config::getSchemaName("configtest"));
+ }
+
+ public function testSchemaNameOfCustomPackage()
+ {
+ $params = array("package" => "my.db.org",
+ "database" => "mydb", "schema" => "hoge");
+
+ Sabel_Db_Config::add("configtest", $params);
+ $this->assertEquals("hoge", Sabel_Db_Config::getSchemaName("configtest"));
+
+ $params = array("package" => "my.db.org",
+ "database" => "mydb");
+
+ Sabel_Db_Config::add("configtest", $params);
+
+ try {
+ Sabel_Db_Config::getSchemaName("configtest");
+ } catch (Sabel_Db_Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+}
+
+class TestDatabaseConfig implements Sabel_Config
+{
+ public function configure()
+ {
+ $params = array("configtest" => array(
+ "package" => "sabel.db.mysql",
+ "host" => "localhost",
+ "database" => "mydb",
+ "user" => "root",
+ "password" => "")
+ );
+
+ return $params;
+ }
+}
diff --git a/Test/DB/Ibase.php b/Test/DB/Ibase.php
new file mode 100755
index 0000000..7e7b20e
--- /dev/null
+++ b/Test/DB/Ibase.php
@@ -0,0 +1,50 @@
+
+ */
+class Test_DB_Ibase extends Test_DB_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Ibase");
+ }
+
+ public function testConnectionRefused()
+ {
+ $params = array("package" => "sabel.db.ibase",
+ "host" => "localhost",
+ "user" => "hogehoge",
+ "password" => "fugafuga",
+ "database" => "/home/firebird/sdb_test.fdb");
+
+ Sabel_Db_Config::add("conrefused", $params);
+ $driver = new Sabel_Db_Ibase_Driver("conrefused");
+
+ try {
+ $c = error_reporting(0);
+ $resource = Sabel_Db_Connection::connect($driver);
+ error_reporting($c);
+ } catch (Sabel_Db_Exception_Connection $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getIbaseConfig());
+ Test_DB_Test::$db = "IBASE";
+ }
+
+ public function testDefinedValue()
+ {
+ $this->assertEquals(8, IBASE_COMMITTED);
+ $this->assertEquals(32, IBASE_REC_NO_VERSION);
+ $this->assertEquals(40, IBASE_COMMITTED|IBASE_REC_NO_VERSION);
+ }
+}
diff --git a/Test/DB/Mssql.php b/Test/DB/Mssql.php
new file mode 100755
index 0000000..ab8bd97
--- /dev/null
+++ b/Test/DB/Mssql.php
@@ -0,0 +1,21 @@
+
+ */
+class Test_DB_Mssql extends Test_DB_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Mssql");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getMssqlConfig());
+ Test_DB_Test::$db = "MSSQL";
+ }
+}
diff --git a/Test/DB/Mysql.php b/Test/DB/Mysql.php
new file mode 100755
index 0000000..2a973e6
--- /dev/null
+++ b/Test/DB/Mysql.php
@@ -0,0 +1,43 @@
+
+ */
+class Test_DB_Mysql extends Test_DB_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Mysql");
+ }
+
+ public function testConnectionRefused()
+ {
+ $params = array("package" => "sabel.db.mysql",
+ "host" => "127.0.0.1",
+ "user" => "hogehoge",
+ "password" => "fugafuga",
+ "database" => "sdb_test");
+
+ Sabel_Db_Config::add("conrefused", $params);
+ $driver = new Sabel_Db_Mysql_Driver("conrefused");
+
+ try {
+ $c = error_reporting(0);
+ $resource = Sabel_Db_Connection::connect($driver);
+ error_reporting($c);
+ } catch (Sabel_Db_Exception_Connection $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getMysqlConfig());
+ Test_DB_Test::$db = "MYSQL";
+ }
+}
diff --git a/Test/DB/Mysqli.php b/Test/DB/Mysqli.php
new file mode 100755
index 0000000..0e20ab6
--- /dev/null
+++ b/Test/DB/Mysqli.php
@@ -0,0 +1,43 @@
+
+ */
+class Test_DB_Mysqli extends Test_DB_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Mysqli");
+ }
+
+ public function testConnectionRefused()
+ {
+ $params = array("package" => "sabel.db.mysqli",
+ "host" => "127.0.0.1",
+ "user" => "hogehoge",
+ "password" => "fugafuga",
+ "database" => "sdb_test");
+
+ Sabel_Db_Config::add("conrefused", $params);
+ $driver = new Sabel_Db_Mysqli_Driver("conrefused");
+
+ try {
+ $c = error_reporting(0);
+ $resource = Sabel_Db_Connection::connect($driver);
+ error_reporting($c);
+ } catch (Sabel_Db_Exception_Connection $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getMysqliConfig());
+ Test_DB_Test::$db = "MYSQL";
+ }
+}
diff --git a/Test/DB/Oci.php b/Test/DB/Oci.php
new file mode 100755
index 0000000..d21d8b9
--- /dev/null
+++ b/Test/DB/Oci.php
@@ -0,0 +1,44 @@
+
+ */
+class Test_DB_Oci extends Test_DB_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Oci");
+ }
+
+ public function testConnectionRefused()
+ {
+ $params = array("package" => "sabel.db.oci",
+ "host" => "127.0.0.1",
+ "user" => "hogehoge",
+ "password" => "fugafuga",
+ "database" => "XE",
+ "charset" => "UTF-8");
+
+ Sabel_Db_Config::add("conrefused", $params);
+ $driver = new Sabel_Db_Oci_Driver("conrefused");
+
+ try {
+ $c = error_reporting(0);
+ $resource = Sabel_Db_Connection::connect($driver);
+ error_reporting($c);
+ } catch (Sabel_Db_Exception_Connection $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getOciConfig());
+ Test_DB_Test::$db = "ORACLE";
+ }
+}
diff --git a/Test/DB/PdoMysql.php b/Test/DB/PdoMysql.php
new file mode 100755
index 0000000..a73dcae
--- /dev/null
+++ b/Test/DB/PdoMysql.php
@@ -0,0 +1,15 @@
+ "sabel.db.pdo.oci",
+ "host" => "127.0.0.1",
+ "user" => "develop",
+ "password" => "develop",
+ "database" => "xe");
+
+ public static function main()
+ {
+ require_once "PHPUnit/TextUI/TestRunner.php";
+
+ $suite = new PHPUnit_Framework_TestSuite("Test_DB_PdoOci");
+ $result = PHPUnit_TextUI_TestRunner::run($suite);
+ }
+
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_PdoOci");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", self::$params1);
+ Test_DB_Test::$db = "PDO_ORACLE";
+ }
+}
diff --git a/Test/DB/PdoPgsql.php b/Test/DB/PdoPgsql.php
new file mode 100755
index 0000000..c140c38
--- /dev/null
+++ b/Test/DB/PdoPgsql.php
@@ -0,0 +1,15 @@
+
+ */
+class Test_DB_Pgsql extends Test_DB_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Pgsql");
+ }
+
+ public function testConnectionRefused()
+ {
+ return;
+ $params = array("package" => "sabel.db.pgsql",
+ "host" => "localhost",
+ "user" => "hogehoge",
+ "password" => "fugafuga",
+ "database" => "sdb_test");
+
+ Sabel_Db_Config::add("conrefused", $params);
+ $driver = new Sabel_Db_Pgsql_Driver("conrefused");
+
+ try {
+ $c = error_reporting(0);
+ $resource = Sabel_Db_Connection::connect($driver);
+ error_reporting($c);
+ } catch (Sabel_Db_Exception_Connection $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getPgsqlConfig());
+ Test_DB_Test::$db = "PGSQL";
+ }
+}
diff --git a/Test/DB/SQLite.php b/Test/DB/SQLite.php
new file mode 100755
index 0000000..f4c41ba
--- /dev/null
+++ b/Test/DB/SQLite.php
@@ -0,0 +1,15 @@
+
+ */
+class Test_DB_SchemaColumn extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_SchemaColumn");
+ }
+
+ public function testIntegerCast()
+ {
+ $ts = MODEL("TestSchema");
+
+ $ts->intcol = "0";
+ $this->assertEquals(0, $ts->intcol);
+
+ $ts->intcol = "10";
+ $this->assertEquals(10, $ts->intcol);
+
+ $ts->intcol = "100.000";
+ $this->assertEquals(100, $ts->intcol);
+
+ $ts->intcol = "100.000.000";
+ $this->assertEquals("100.000.000", $ts->intcol);
+
+ $ts->intcol = 10000000000;
+ $this->assertEquals(10000000000, $ts->intcol);
+ $this->assertTrue(is_float($ts->intcol));
+
+ $ts->intcol = 1000000000.0;
+ $this->assertEquals(1000000000, $ts->intcol);
+ $this->assertTrue(is_int($ts->intcol));
+
+ $ts->intcol = false;
+ $this->assertEquals(false, $ts->intcol);
+
+ $ts->intcol = true;
+ $this->assertEquals(true, $ts->intcol);
+ }
+
+ public function testSmallIntegerCast()
+ {
+ $ts = MODEL("TestSchema");
+
+ $ts->sintcol = "0";
+ $this->assertEquals(0, $ts->sintcol);
+
+ $ts->sintcol = "10";
+ $this->assertEquals(10, $ts->sintcol);
+
+ $ts->sintcol = "100.000";
+ $this->assertEquals(100, $ts->sintcol);
+
+ $ts->sintcol = "100.000.000";
+ $this->assertEquals("100.000.000", $ts->sintcol);
+
+ $ts->sintcol = 10000000000;
+ $this->assertEquals(10000000000, $ts->sintcol);
+ $this->assertTrue(is_float($ts->sintcol));
+
+ $ts->sintcol = 10000.0;
+ $this->assertEquals(10000, $ts->sintcol);
+ $this->assertTrue(is_int($ts->sintcol));
+
+ $ts->sintcol = false;
+ $this->assertEquals(false, $ts->sintcol);
+
+ $ts->sintcol = true;
+ $this->assertEquals(true, $ts->sintcol);
+ }
+
+ public function testBooleanCast()
+ {
+ $ts = MODEL("TestSchema");
+
+ $ts->boolcol = 1;
+ $this->assertTrue($ts->boolcol);
+
+ $ts->boolcol = "1";
+ $this->assertTrue($ts->boolcol);
+
+ $ts->boolcol = "t";
+ $this->assertTrue($ts->boolcol);
+
+ $ts->boolcol = "true";
+ $this->assertTrue($ts->boolcol);
+
+ $ts->boolcol = 0;
+ $this->assertFalse($ts->boolcol);
+
+ $ts->boolcol = "0";
+ $this->assertFalse($ts->boolcol);
+
+ $ts->boolcol = "f";
+ $this->assertFalse($ts->boolcol);
+
+ $ts->boolcol = "false";
+ $this->assertFalse($ts->boolcol);
+
+ $ts->boolcol = 10;
+ $this->assertEquals(10, $ts->boolcol);
+
+ $ts->boolcol = "abc";
+ $this->assertEquals("abc", $ts->boolcol);
+ }
+
+ public function testFloatCast()
+ {
+ $ts = MODEL("TestSchema");
+
+ $ts->floatcol = 1;
+ $this->assertTrue(is_float($ts->floatcol));
+ $this->assertEquals(1, $ts->floatcol);
+
+ $ts->floatcol = "1";
+ $this->assertTrue(is_float($ts->floatcol));
+ $this->assertEquals(1, $ts->floatcol);
+
+ $ts->floatcol = "0.123";
+ $this->assertTrue(is_float($ts->floatcol));
+ $this->assertEquals(0.123, $ts->floatcol);
+
+ $ts->floatcol = "0.123.456";
+ $this->assertFalse(is_float($ts->floatcol));
+ $this->assertEquals("0.123.456", $ts->floatcol);
+
+ $ts->floatcol = true;
+ $this->assertFalse(is_float($ts->floatcol));
+ $this->assertEquals(true, $ts->floatcol);
+ }
+}
+
+class Schema_TestSchema
+{
+ public static function get()
+ {
+ $cols = array();
+
+ $cols['intcol'] = array('type' => Sabel_Db_Type::INT,
+ 'max' => PHP_INT_MAX,
+ 'min' => -PHP_INT_MAX - 1,
+ 'increment' => false,
+ 'nullable' => true,
+ 'primary' => false,
+ 'default' => null);
+
+ $cols['sintcol'] = array('type' => Sabel_Db_Type::SMALLINT,
+ 'max' => 32767,
+ 'min' => -32768,
+ 'increment' => false,
+ 'nullable' => true,
+ 'primary' => false,
+ 'default' => null);
+
+ $cols['boolcol'] = array('type' => Sabel_Db_Type::BOOL,
+ 'increment' => false,
+ 'nullable' => true,
+ 'primary' => false,
+ 'default' => null);
+
+ $cols['floatcol'] = array('type' => Sabel_Db_Type::FLOAT,
+ 'min' => -3.4028235E+38,
+ 'max' => 3.4028235E+38,
+ 'increment' => false,
+ 'nullable' => true,
+ 'primary' => false,
+ 'default' => null);
+
+ return $cols;
+ }
+
+ public function getProperty()
+ {
+ $property = array();
+
+ $property["tableEngine"] = null;
+ $property["uniques"] = null;
+ $property["fkeys"] = null;
+
+ return $property;
+ }
+}
diff --git a/Test/DB/Statement/Ibase.php b/Test/DB/Statement/Ibase.php
new file mode 100755
index 0000000..6896ad1
--- /dev/null
+++ b/Test/DB/Statement/Ibase.php
@@ -0,0 +1,52 @@
+
+ */
+class Test_DB_Statement_Ibase extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Statement_Ibase");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getIbaseConfig());
+ }
+
+ public function testQuoteIdentifier()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $this->assertEquals('"FOO"', $stmt->quoteIdentifier("foo"));
+ $this->assertEquals('"BAR"', $stmt->quoteIdentifier("bar"));
+ }
+
+ public function testBuildSelectQuery()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $expected = 'SELECT "ID", "NAME" FROM "STUDENT"';
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testBuildSelectWhereQuery()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $stmt->where('WHERE "ID" = 1');
+ $expected = 'SELECT "ID", "NAME" FROM "STUDENT" WHERE "ID" = 1';
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testClose()
+ {
+ Sabel_Db_Metadata::clear();
+ Sabel_Db_Connection::closeAll();
+ }
+}
diff --git a/Test/DB/Statement/Mysql.php b/Test/DB/Statement/Mysql.php
new file mode 100755
index 0000000..363a1a5
--- /dev/null
+++ b/Test/DB/Statement/Mysql.php
@@ -0,0 +1,78 @@
+
+ */
+class Test_DB_Statement_Mysql extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Statement_Mysql");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getMysqlConfig());
+ }
+
+ public function testQuoteIdentifier()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $this->assertEquals("`foo`", $stmt->quoteIdentifier("foo"));
+ $this->assertEquals("`bar`", $stmt->quoteIdentifier("bar"));
+ }
+
+ public function testBuildSelectQuery()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $expected = "SELECT `id`, `name` FROM `student`";
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testBuildSelectWhereQuery()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $stmt->where("WHERE `id` = 1");
+ $expected = "SELECT `id`, `name` FROM `student` WHERE `id` = 1";
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testBuildSelectOrderByQuery()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $stmt->constraints(array("order" => array("id" => array("mode" => "DESC", "nulls" => "LAST"))));
+ $expected = "SELECT `id`, `name` FROM `student` ORDER BY `id` IS NULL, `id` DESC";
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testBuildSelectOrderByQuery2()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $stmt->constraints(array("order" => array("id" => array("mode" => "DESC", "nulls" => "LAST"), "name" => array("mode" => "ASC", "nulls" => "LAST"))));
+ $expected = "SELECT `id`, `name` FROM `student` ORDER BY `id` IS NULL, `id` DESC, `name` IS NULL, `name` ASC";
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testEscapeString()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $this->assertEquals(array("'a\'b\\\\z'"), $stmt->escape(array("a'b\z")));
+ }
+
+ public function testClose()
+ {
+ Sabel_Db_Metadata::clear();
+ Sabel_Db_Connection::closeAll();
+ }
+}
diff --git a/Test/DB/Statement/Oci.php b/Test/DB/Statement/Oci.php
new file mode 100755
index 0000000..54f3029
--- /dev/null
+++ b/Test/DB/Statement/Oci.php
@@ -0,0 +1,72 @@
+
+ */
+class Test_DB_Statement_Oci extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Statement_Oci");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getOciConfig());
+ }
+
+ public function testQuoteIdentifier()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $this->assertEquals('"FOO"', $stmt->quoteIdentifier("foo"));
+ $this->assertEquals('"BAR"', $stmt->quoteIdentifier("bar"));
+ }
+
+ public function testBuildSelectQuery()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $expected = 'SELECT "ID", "NAME" FROM "STUDENT"';
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testBuildSelectWhereQuery()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $stmt->where('WHERE "ID" = 1');
+ $expected = 'SELECT "ID", "NAME" FROM "STUDENT" WHERE "ID" = 1';
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testBuildSelectOrderByQuery()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $stmt->constraints(array("order" => array("id" => "DESC")));
+ $expected = 'SELECT "ID", "NAME" FROM "STUDENT" ORDER BY "ID" DESC';
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testBuildSelectOrderByQuery2()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $stmt->constraints(array("order" => array("id" => "DESC", "name" => "ASC")));
+ $expected = 'SELECT "ID", "NAME" FROM "STUDENT" ORDER BY "ID" DESC, "NAME" ASC';
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testClose()
+ {
+ Sabel_Db_Metadata::clear();
+ Sabel_Db_Connection::closeAll();
+ }
+}
diff --git a/Test/DB/Statement/Pgsql.php b/Test/DB/Statement/Pgsql.php
new file mode 100755
index 0000000..8b7ceff
--- /dev/null
+++ b/Test/DB/Statement/Pgsql.php
@@ -0,0 +1,78 @@
+
+ */
+class Test_DB_Statement_Pgsql extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Statement_Pgsql");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getPgsqlConfig());
+ }
+
+ public function testQuoteIdentifier()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $this->assertEquals('"foo"', $stmt->quoteIdentifier("foo"));
+ $this->assertEquals('"bar"', $stmt->quoteIdentifier("bar"));
+ }
+
+ public function testBuildSelectQuery()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $expected = 'SELECT "id", "name" FROM "student"';
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testBuildSelectWhereQuery()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $stmt->where('WHERE "id" = 1');
+ $expected = 'SELECT "id", "name" FROM "student" WHERE "id" = 1';
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testBuildSelectOrderByQuery()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $stmt->constraints(array("order" => array("id" => "DESC")));
+ $expected = 'SELECT "id", "name" FROM "student" ORDER BY "id" DESC';
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testBuildSelectOrderByQuery2()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $stmt->type(Sabel_Db_Statement::SELECT);
+ $stmt->setMetadata(Sabel_Db_Metadata::getTableInfo("student"));
+ $stmt->constraints(array("order" => array("id" => "DESC", "name" => "ASC")));
+ $expected = 'SELECT "id", "name" FROM "student" ORDER BY "id" DESC, "name" ASC';
+ $this->assertEquals($expected, $stmt->getQuery());
+ }
+
+ public function testEscapeString()
+ {
+ $stmt = Sabel_Db::createStatement("default");
+ $this->assertEquals(array("'a''b\\\\z'"), $stmt->escape(array("a'b\z")));
+ }
+
+ public function testClose()
+ {
+ Sabel_Db_Metadata::clear();
+ Sabel_Db_Connection::closeAll();
+ }
+}
diff --git a/Test/DB/Statement/Tests.php b/Test/DB/Statement/Tests.php
new file mode 100755
index 0000000..cdbad0b
--- /dev/null
+++ b/Test/DB/Statement/Tests.php
@@ -0,0 +1,43 @@
+
+ */
+class Test_DB_Statement_Tests
+{
+ public static function main()
+ {
+ PHPUnit_TextUI_TestRunner::run(self::suite());
+ }
+
+ public static function suite()
+ {
+ $suite = new PHPUnit_Framework_TestSuite();
+
+ if (extension_loaded("mysql")) {
+ $suite->addTest(Test_DB_Statement_Mysql::suite());
+ }
+
+ if (extension_loaded("pgsql")) {
+ $suite->addTest(Test_DB_Statement_Pgsql::suite());
+ }
+
+ if (extension_loaded("oci8")) {
+ $suite->addTest(Test_DB_Statement_Oci::suite());
+ }
+
+ if (extension_loaded("interbase")) {
+ $suite->addTest(Test_DB_Statement_Ibase::suite());
+ }
+
+ return $suite;
+ }
+}
diff --git a/Test/DB/Storage/Ibase.php b/Test/DB/Storage/Ibase.php
new file mode 100755
index 0000000..bc8e1bf
--- /dev/null
+++ b/Test/DB/Storage/Ibase.php
@@ -0,0 +1,19 @@
+
+ */
+class Test_DB_Storage_Ibase extends Test_DB_Storage_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Storage_Ibase");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getIbaseConfig());
+ MODEL("SblKvs")->delete();
+ }
+}
diff --git a/Test/DB/Storage/Mysql.php b/Test/DB/Storage/Mysql.php
new file mode 100755
index 0000000..6b34584
--- /dev/null
+++ b/Test/DB/Storage/Mysql.php
@@ -0,0 +1,19 @@
+
+ */
+class Test_DB_Storage_Mysql extends Test_DB_Storage_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Storage_Mysql");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getMysqlConfig());
+ MODEL("SblKvs")->delete();
+ }
+}
diff --git a/Test/DB/Storage/Mysqli.php b/Test/DB/Storage/Mysqli.php
new file mode 100755
index 0000000..9cf3c6f
--- /dev/null
+++ b/Test/DB/Storage/Mysqli.php
@@ -0,0 +1,19 @@
+
+ */
+class Test_DB_Storage_Mysqli extends Test_DB_Storage_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Storage_Mysqli");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getMysqliConfig());
+ MODEL("SblKvs")->delete();
+ }
+}
diff --git a/Test/DB/Storage/Oci.php b/Test/DB/Storage/Oci.php
new file mode 100755
index 0000000..3599f24
--- /dev/null
+++ b/Test/DB/Storage/Oci.php
@@ -0,0 +1,19 @@
+
+ */
+class Test_DB_Storage_Oci extends Test_DB_Storage_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Storage_Oci");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getOciConfig());
+ MODEL("Sblkvs")->delete();
+ }
+}
diff --git a/Test/DB/Storage/PdoMysql.php b/Test/DB/Storage/PdoMysql.php
new file mode 100755
index 0000000..ccb843a
--- /dev/null
+++ b/Test/DB/Storage/PdoMysql.php
@@ -0,0 +1,19 @@
+
+ */
+class Test_DB_Storage_PdoMysql extends Test_DB_Storage_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Storage_PdoMysql");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getPdoMysqlConfig());
+ MODEL("SblKvs")->delete();
+ }
+}
diff --git a/Test/DB/Storage/PdoOci.php b/Test/DB/Storage/PdoOci.php
new file mode 100755
index 0000000..4505478
--- /dev/null
+++ b/Test/DB/Storage/PdoOci.php
@@ -0,0 +1,19 @@
+
+ */
+class Test_DB_Storage_PdoOci extends Test_DB_Storage_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Storage_PdoOci");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getPdoOciConfig());
+ MODEL("SblKvs")->delete();
+ }
+}
diff --git a/Test/DB/Storage/PdoPgsql.php b/Test/DB/Storage/PdoPgsql.php
new file mode 100755
index 0000000..fb1c4db
--- /dev/null
+++ b/Test/DB/Storage/PdoPgsql.php
@@ -0,0 +1,19 @@
+
+ */
+class Test_DB_Storage_PdoPgsql extends Test_DB_Storage_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Storage_PdoPgsql");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getPdoPgsqlConfig());
+ MODEL("SblKvs")->delete();
+ }
+}
diff --git a/Test/DB/Storage/PdoSqlite.php b/Test/DB/Storage/PdoSqlite.php
new file mode 100755
index 0000000..b1b016e
--- /dev/null
+++ b/Test/DB/Storage/PdoSqlite.php
@@ -0,0 +1,19 @@
+
+ */
+class Test_DB_Storage_PdoSqlite extends Test_DB_Storage_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Storage_PdoSqlite");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getPdoSqliteConfig());
+ MODEL("SblKvs")->delete();
+ }
+}
diff --git a/Test/DB/Storage/Pgsql.php b/Test/DB/Storage/Pgsql.php
new file mode 100755
index 0000000..194eea5
--- /dev/null
+++ b/Test/DB/Storage/Pgsql.php
@@ -0,0 +1,19 @@
+
+ */
+class Test_DB_Storage_Pgsql extends Test_DB_Storage_Test
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_DB_Storage_Pgsql");
+ }
+
+ public function testInit()
+ {
+ Sabel_Db_Config::add("default", Test_DB_TestConfig::getPgsqlConfig());
+ MODEL("SblKvs")->delete();
+ }
+}
diff --git a/Test/DB/Storage/Test.php b/Test/DB/Storage/Test.php
new file mode 100755
index 0000000..0326f20
--- /dev/null
+++ b/Test/DB/Storage/Test.php
@@ -0,0 +1,58 @@
+
+ */
+class Test_DB_Storage_Test extends SabelTestCase
+{
+ public function testStore()
+ {
+ $obj = new SblStorageTestObj();
+ $obj->hoge = array("int" => 10, "bool" => true);
+
+ $stdClass = new stdClass();
+ $stdClass->int = 20;
+ $stdClass->bool = false;
+ $obj->fuga = $stdClass;
+
+ $storage = Sabel_Kvs_Database::create();
+ $storage->write("hashkey", $obj, 60);
+ }
+
+ public function testFetch()
+ {
+ $storage = Sabel_Kvs_Database::create();
+ $obj = $storage->read("hashkey");
+
+ $hoge = $obj->hoge;
+ $fuga = $obj->fuga;
+
+ $this->assertEquals(10, $hoge["int"]);
+ $this->assertEquals(true, $hoge["bool"]);
+ $this->assertEquals(20, $fuga->int);
+ $this->assertEquals(false, $fuga->bool);
+ }
+
+ public function testClose()
+ {
+ Sabel_Db_Metadata::clear();
+ Sabel_Db_Connection::closeAll();
+ }
+}
+
+class SblStorageTestObj extends Sabel_Object
+{
+ const FOO = "FOO";
+
+ private $foo = null;
+ protected $bar = 0;
+ public $baz = false;
+
+ public $hoge = null;
+ public $fuga = null;
+
+ private function foo() {}
+ protected function bar() {}
+ public function baz() {}
+}
diff --git a/Test/DB/Storage/Tests.php b/Test/DB/Storage/Tests.php
new file mode 100755
index 0000000..9769b98
--- /dev/null
+++ b/Test/DB/Storage/Tests.php
@@ -0,0 +1,69 @@
+
+ */
+class Test_DB_Storage_Tests
+{
+ public static function main()
+ {
+ PHPUnit_TextUI_TestRunner::run(self::suite());
+ }
+
+ public static function suite()
+ {
+ $suite = new PHPUnit_Framework_TestSuite();
+
+ if (extension_loaded("mysql")) {
+ $suite->addTest(Test_DB_Storage_Mysql::suite());
+ }
+
+ if (extension_loaded("mysqli")) {
+ $suite->addTest(Test_DB_Storage_Mysqli::suite());
+ }
+
+ if (extension_loaded("pgsql")) {
+ $suite->addTest(Test_DB_Storage_Pgsql::suite());
+ }
+
+ if (extension_loaded("oci8")) {
+ $suite->addTest(Test_DB_Storage_Oci::suite());
+ }
+
+ if (extension_loaded("interbase")) {
+ $suite->addTest(Test_DB_Storage_Ibase::suite());
+ }
+
+ if (extension_loaded("pdo_sqlite")) {
+ $suite->addTest(Test_DB_Storage_PdoSqlite::suite());
+ }
+
+ if (extension_loaded("pdo_mysql")) {
+ $suite->addTest(Test_DB_Storage_PdoMysql::suite());
+ }
+
+ if (extension_loaded("pdo_pgsql")) {
+ $suite->addTest(Test_DB_Storage_PdoPgsql::suite());
+ }
+
+ if (extension_loaded("pdo_oci")) {
+ $suite->addTest(Test_DB_Storage_PdoOci::suite());
+ }
+
+ return $suite;
+ }
+}
diff --git a/Test/DB/Test.php b/Test/DB/Test.php
new file mode 100755
index 0000000..b2a14b2
--- /dev/null
+++ b/Test/DB/Test.php
@@ -0,0 +1,614 @@
+
+ */
+class Test_DB_Test extends SabelTestCase
+{
+ public static $db = "";
+ public static $tables = array("schema_test", "grandchildren", "children",
+ "parents", "grandparents", "student_course", "student", "course");
+
+ protected static $lastStId = null;
+
+ public function testClean()
+ {
+ $tables = self::$tables;
+ $driver = Sabel_Db::createDriver("default");
+
+ foreach ($tables as $table) {
+ $driver->execute("DELETE FROM $table");
+ }
+ }
+
+ public function testInsert()
+ {
+ $st = MODEL("SchemaTest");
+ $generatedId = $st->insert(array("email" => "test1@example.com", "bl" => true));
+ $this->assertTrue(is_numeric($generatedId));
+ }
+
+ public function testInsertBySave()
+ {
+ $st = MODEL("SchemaTest");
+ $st->email = "test2@example.com";
+ $st->bl = false;
+
+ $st->save();
+
+ // default values.
+ $this->assertEquals("default name", $st->name);
+ $this->assertEquals("90000000000", $st->bint);
+ $this->assertEquals(30000, $st->sint);
+ $this->assertEquals(10.234, $st->ft);
+ $this->assertEquals(10.23456, $st->dbl);
+
+ self::$lastStId = $st->id;
+ }
+
+ public function testSelectOne()
+ {
+ $st = MODEL("SchemaTest");
+ $st = $st->selectOne(self::$lastStId);
+
+ $this->assertTrue($st->isSelected());
+ $this->assertEquals("test2@example.com", $st->email);
+ $this->assertEquals(false, $st->bl);
+ }
+
+ public function testInitSelect()
+ {
+ $st = MODEL("SchemaTest", self::$lastStId);
+ $this->assertTrue($st->isSelected());
+ $this->assertEquals("test2@example.com", $st->email);
+ $this->assertEquals(false, $st->bl);
+ }
+
+ public function testUpdate()
+ {
+ $st = MODEL("SchemaTest");
+ $st->setCondition(self::$lastStId);
+ $st->update(array("bl" => true));
+
+ $st = MODEL("SchemaTest", self::$lastStId);
+ $this->assertEquals("test2@example.com", $st->email);
+ $this->assertEquals(true, $st->bl);
+ }
+
+ public function testUpdateBySave()
+ {
+ $st = MODEL("SchemaTest", self::$lastStId);
+ $st->email = "test2@updated.com";
+ $st->bl = false;
+ $affectedRows = $st->save(); # update
+
+ $this->assertEquals(1, $affectedRows);
+
+ $st = MODEL("SchemaTest", self::$lastStId);
+ $this->assertEquals("test2@updated.com", $st->email);
+ $this->assertEquals(false, $st->bl);
+ }
+
+ public function testCount()
+ {
+ $st = MODEL("SchemaTest");
+ $this->assertEquals(2, $st->getCount());
+
+ $st->setCondition(self::$lastStId);
+ $this->assertEquals(1, $st->getCount());
+
+ $this->assertEquals(1, $st->getCount("email", "test2@updated.com"));
+ }
+
+ public function testDelete()
+ {
+ $st = MODEL("SchemaTest");
+ $st->setCondition(self::$lastStId);
+ $st->delete();
+
+ $this->assertEquals(1, $st->getCount());
+ $affectedRows = $st->delete("email", "test1@example.com");
+ $this->assertEquals(1, $affectedRows);
+ $this->assertEquals(0, $st->getCount());
+ $this->insertTestData();
+ }
+
+ public function testJoin1()
+ {
+ $this->insertJoinTableData();
+ $join = new Sabel_Db_Join("Grandchildren");
+ $join->setOrderBy("Grandchildren.id");
+ $results = $join->add("Children")->select();
+
+ $this->assertEquals(2, count($results));
+ $this->assertEquals("grandchildren1", $results[0]->value);
+ $this->assertEquals("grandchildren2", $results[1]->value);
+ $this->assertEquals("children2", $results[0]->Children->value);
+ $this->assertEquals("children1", $results[1]->Children->value);
+ }
+
+ public function testJoin2()
+ {
+ $join = new Sabel_Db_Join("Grandchildren");
+ $join->setOrderBy("Grandchildren.id");
+ $chilren = new Sabel_Db_Join_Relation("Children");
+ $results = $join->add($chilren->add("Parents"))->select();
+
+ $this->assertEquals(2, count($results));
+ $this->assertEquals("children2", $results[0]->Children->value);
+ $this->assertEquals("children1", $results[1]->Children->value);
+ $this->assertEquals("parents1", $results[0]->Children->Parents->value);
+ $this->assertEquals("parents2", $results[1]->Children->Parents->value);
+ }
+
+ public function testJoin3()
+ {
+ $join = new Sabel_Db_Join("Grandchildren");
+ $join->setOrderBy("Grandchildren.id");
+ $children = new Sabel_Db_Join_Relation("Children");
+ $parents = new Sabel_Db_Join_Relation("Parents");
+ $results = $join->add($children->add($parents->add("Grandparents")))->select();
+
+ $this->assertEquals(2, count($results));
+ $this->assertEquals("children2", $results[0]->Children->value);
+ $this->assertEquals("children1", $results[1]->Children->value);
+ $this->assertEquals("parents1", $results[0]->Children->Parents->value);
+ $this->assertEquals("parents2", $results[1]->Children->Parents->value);
+ $this->assertEquals("grandparents2", $results[0]->Children->Parents->Grandparents->value);
+ $this->assertEquals("grandparents1", $results[1]->Children->Parents->Grandparents->value);
+ }
+
+ public function testJoinCondition()
+ {
+ $join = new Sabel_Db_Join("Grandchildren");
+ $join->setCondition("Grandparents.value", "grandparents2");
+ $children = new Sabel_Db_Join_Relation("Children");
+ $parents = new Sabel_Db_Join_Relation("Parents");
+
+ $results = $join->add($children->add($parents->add("Grandparents")))->select();
+ $this->assertEquals(1, count($results));
+ $this->assertEquals("children2", $results[0]->Children->value);
+ $this->assertEquals("parents1", $results[0]->Children->Parents->value);
+ $this->assertEquals("grandparents2", $results[0]->Children->Parents->Grandparents->value);
+ }
+
+ public function testEqualCondition()
+ {
+ $st = MODEL("SchemaTest");
+ $results = $st->select("sint", 200);
+ $this->assertEquals(2, count($results));
+
+ $results = $st->select(Condition::create(EQUAL, "sint", 200));
+ $this->assertEquals(2, count($results));
+
+ $st->setCondition("sint", 100);
+ $st->setCondition("name", "name2");
+ $results = $st->select();
+ $this->assertEquals(1, count($results));
+ $this->assertEquals("test2@example.com", $results[0]->email);
+
+ $st->setCondition("sint", 100);
+ $st->setCondition("name", "name5");
+ $this->assertEquals(0, count($st->select()));
+ }
+
+ public function testBetweenCondition()
+ {
+ $st = MODEL("SchemaTest");
+ $between = array("2008-01-06", "2008-01-10");
+ $results = $st->select(Condition::create(BETWEEN, "dt", $between));
+ $this->assertEquals(5, count($results));
+
+ $st->setCondition("bl", true);
+ $st->setCondition(Condition::create(BETWEEN, "dt", $between));
+ $this->assertEquals(4, count($st->select()));
+ }
+
+ public function testGreaterCondition()
+ {
+ $st = MODEL("SchemaTest");
+ $results = $st->select(Condition::create(GREATER_THAN, "sint", 300));
+ $this->assertEquals(4, count($results));
+
+ $results = $st->select(Condition::create(GREATER_EQUAL, "sint", 300));
+ $this->assertEquals(6, count($results));
+
+ $st->setCondition(Condition::create(GREATER_EQUAL, "sint", 300));
+ $st->setCondition("bl", false);
+ $this->assertEquals(1, count($st->select()));
+ }
+
+ public function testLessCondition()
+ {
+ $st = MODEL("SchemaTest");
+ $results = $st->select(Condition::create(LESS_THAN, "sint", 300));
+ $this->assertEquals(4, count($results));
+
+ $results = $st->select(Condition::create(LESS_EQUAL, "sint", 300));
+ $this->assertEquals(6, count($results));
+
+ $st->setCondition(Condition::create(LESS_EQUAL, "sint", 300));
+ $st->setCondition("bl", false);
+ $this->assertEquals(3, count($st->select()));
+ }
+
+ public function testIsNullCondition()
+ {
+ $st = MODEL("SchemaTest");
+ $results = $st->select(Condition::create(ISNULL, "txt"));
+ $this->assertEquals(8, count($results));
+
+ $this->assertEquals(4, count($st->select("bl", false)));
+
+ $st->setCondition(Condition::create(ISNULL, "txt"));
+ $st->setCondition("bl", false);
+ $this->assertEquals(3, count($st->select()));
+ }
+
+ public function testIsNotNullCondition()
+ {
+ $st = MODEL("SchemaTest");
+ $results = $st->select(Condition::create(ISNOTNULL, "txt"));
+ $this->assertEquals(2, count($results));
+ }
+
+ public function testLikeCondition()
+ {
+ $st = MODEL("SchemaTest");
+ $results = $st->select(Condition::create(LIKE, "name", "name"));
+ $this->assertEquals(10, count($results));
+
+ $like = Condition::create(LIKE, "name", "0")->type(LIKE_ENDS_WITH);
+ $this->assertEquals(1, count($st->select($like)));
+
+ $like = Condition::create(LIKE, "name", "a")->type(LIKE_ENDS_WITH);
+ $this->assertEquals(0, count($st->select($like)));
+ }
+
+ public function testInCondition()
+ {
+ // Segfault...
+ if (self::$db === "PDO_ORACLE") return;
+
+ $st = MODEL("SchemaTest");
+ $st->setCondition(Condition::create(IN, "name", array("name1", "name3", "name5")));
+ $st->setOrderBy("id");
+ $results = $st->select();
+
+ $this->assertEquals(3, count($results));
+ $this->assertEquals("name1", $results[0]->name);
+ $this->assertEquals("name3", $results[1]->name);
+ $this->assertEquals("name5", $results[2]->name);
+ }
+
+ public function testOrCondition()
+ {
+ $st = MODEL("SchemaTest");
+ $or = new Sabel_Db_Condition_Or();
+ $or->add(Condition::create(EQUAL, "sint", 200));
+ $or->add(Condition::create(EQUAL, "email", "test9@example.com"));
+ $st->setOrderBy("id");
+ $results = $st->select($or);
+
+ $this->assertEquals(3, count($results));
+ $this->assertEquals("test3@example.com", $results[0]->email);
+ $this->assertEquals("test4@example.com", $results[1]->email);
+ $this->assertEquals("test9@example.com", $results[2]->email);
+ }
+
+ public function testOrCondition2()
+ {
+ $st = MODEL("SchemaTest");
+ $or = new Sabel_Db_Condition_Or();
+ $or->add(Condition::create(EQUAL, "sint", 100));
+ $or->add(Condition::create(BETWEEN, "dt", array("2008-01-07", "2008-01-09")));
+ $st->setOrderBy("id");
+ $results = $st->select($or);
+
+ $this->assertEquals(5, count($results));
+ $this->assertEquals("test1@example.com", $results[0]->email);
+ $this->assertEquals("test2@example.com", $results[1]->email);
+ $this->assertEquals("test7@example.com", $results[2]->email);
+ $this->assertEquals("test8@example.com", $results[3]->email);
+ $this->assertEquals("test9@example.com", $results[4]->email);
+ }
+
+ public function testOrAndOrCondition()
+ {
+ $st = MODEL("SchemaTest");
+ $or1 = new Sabel_Db_Condition_Or();
+ $or1->add(Condition::create(EQUAL, "sint", 100));
+ $or1->add(Condition::create(EQUAL, "sint", 300));
+ $or2 = new Sabel_Db_Condition_Or();
+ $or2->add(Condition::create(EQUAL, "sint", 300));
+ $or2->add(Condition::create(EQUAL, "sint", 500));
+ $st->setOrderBy("id");
+
+ $st->setCondition($or1);
+ $st->setCondition($or2);
+
+ $results = $st->select();
+
+ $this->assertEquals(2, count($results));
+ $this->assertEquals("test5@example.com", $results[0]->email);
+ $this->assertEquals("test6@example.com", $results[1]->email);
+ }
+
+ public function testOrderBy()
+ {
+ if (self::$db === "MSSQL") return; // @todo SQL Server 2008
+
+ $st = MODEL("SchemaTest");
+ $results = $st->setOrderBy("dt")->select();
+ $this->assertEquals("2008-01-01", $results[0]->dt);
+ $this->assertEquals("2008-01-10", $results[9]->dt);
+
+ $results = $st->setOrderBy("dt", "desc")->select();
+ $this->assertEquals("2008-01-10", $results[0]->dt);
+ $this->assertEquals("2008-01-01", $results[9]->dt);
+
+ $results = $st->setOrderBy("SchemaTest.dt", "desc")->select();
+ $this->assertEquals("2008-01-10", $results[0]->dt);
+ $this->assertEquals("2008-01-01", $results[9]->dt);
+ }
+
+ public function testLimitation()
+ {
+ $st = MODEL("SchemaTest");
+ $st->setLimit(2)->setOrderBy("id", "desc");
+ $results = $st->select();
+ $this->assertEquals(2, count($results));
+ $this->assertEquals("name10", $results[0]->name);
+ $this->assertEquals("name_", $results[1]->name);
+
+ $st->setLimit(2)->setOffset(2)->setOrderBy("id", "desc");
+ $results = $st->select();
+ $this->assertEquals(2, count($results));
+ $this->assertEquals("name8", $results[0]->name);
+ $this->assertEquals("name7", $results[1]->name);
+
+ $st->setLimit(2)->setOffset(4)->setOrderBy("id", "desc");
+ $results = $st->select();
+ $this->assertEquals(2, count($results));
+ $this->assertEquals("name6", $results[0]->name);
+ $this->assertEquals("name5", $results[1]->name);
+ }
+
+ public function testModelCondition()
+ {
+ $child = MODEL("Children")->selectOne(1);
+ $gChildren = MODEL("Grandchildren")->select($child);
+ $this->assertEquals(1, count($gChildren));
+
+ $gc = $gChildren[0];
+ $this->assertEquals(2, $gc->id);
+ $this->assertEquals(1, $gc->children_id);
+ $this->assertEquals("grandchildren2", $gc->value);
+ }
+
+ public function testRollback()
+ {
+ Sabel_Db_Transaction::activate();
+
+ $gp = MODEL("Grandparents");
+ $gp->insert(array("id" => 3, "value" => "grandparents3"));
+ $gp->insert(array("id" => 4, "value" => "grandparents4"));
+
+ Sabel_Db_Transaction::rollback();
+ $this->assertEquals(2, $gp->getCount());
+ }
+
+ public function testCommit()
+ {
+ Sabel_Db_Transaction::activate(Sabel_Db_Transaction::SERIALIZABLE);
+
+ $gp = MODEL("Grandparents");
+ $gp->insert(array("id" => 3, "value" => "grandparents3"));
+ $gp->insert(array("id" => 4, "value" => "grandparents4"));
+
+ Sabel_Db_Transaction::commit();
+ $this->assertEquals(4, $gp->getCount());
+ }
+
+ public function testBridge()
+ {
+ $join = new Sabel_Db_Join("StudentCourse");
+ $join->setOrderBy("StudentCourse.student_id")->setOrderBy("StudentCourse.course_id");
+ $r = $join->add("Student")->add("Course")->select();
+
+ $this->assertEquals(7, count($r));
+ $this->assertEquals("yamada", $r[0]->Student->name);
+ $this->assertEquals("Mathematics", $r[0]->Course->name);
+ $this->assertEquals("yamada", $r[1]->Student->name);
+ $this->assertEquals("Physics", $r[1]->Course->name);
+ $this->assertEquals("tanaka", $r[2]->Student->name);
+ $this->assertEquals("Mathematics", $r[2]->Course->name);
+ $this->assertEquals("tanaka", $r[3]->Student->name);
+ $this->assertEquals("Science", $r[3]->Course->name);
+ $this->assertEquals("suzuki", $r[4]->Student->name);
+ $this->assertEquals("Mathematics", $r[4]->Course->name);
+ $this->assertEquals("suzuki", $r[5]->Student->name);
+ $this->assertEquals("Physics", $r[5]->Course->name);
+ $this->assertEquals("suzuki", $r[6]->Student->name);
+ $this->assertEquals("Science", $r[6]->Course->name);
+ }
+
+ public function testBridgeWithCondition()
+ {
+ $join = new Sabel_Db_Join("StudentCourse");
+ $join->setOrderBy("StudentCourse.student_id")->setOrderBy("StudentCourse.course_id");
+ $join->setCondition("Student.id", 1);
+ $r = $join->add("Student")->add("Course")->select();
+
+ $this->assertEquals(2, count($r));
+ $this->assertEquals("yamada", $r[0]->Student->name);
+ $this->assertEquals("Mathematics", $r[0]->Course->name);
+ $this->assertEquals("yamada", $r[1]->Student->name);
+ $this->assertEquals("Physics", $r[1]->Course->name);
+ }
+
+ public function testBridgeCount()
+ {
+ $join = new Sabel_Db_Join("StudentCourse");
+ $join->setCondition("Student.id", 3);
+ $this->assertEquals(3, $join->add("Student")->add("Course")->getCount());
+ }
+
+ public function testBinaryData()
+ {
+ $image = file_get_contents(SABEL_BASE . DS . "Test" . DS . "data" . DS . "php.gif");
+ $st = MODEL("SchemaTest");
+ $st->email = "test@example.com";
+ $st->idata = $image;
+ $st->save();
+
+ if (self::$db === "IBASE") return;
+ $s = MODEL("SchemaTest", $st->id);
+ $this->assertEquals(0, strcmp($image, $s->idata));
+ }
+
+ /**
+ * information(schema) of table
+ */
+ public function testTableInfo()
+ {
+ $schema = MODEL("SchemaTest")->getMetadata();
+ $this->assertEquals("schema_test", $schema->getTableName());
+ $this->assertEquals("id", $schema->getPrimaryKey());
+ $this->assertEquals("id", $schema->getSequenceColumn());
+
+ $this->assertTrue($schema->id->isInt(true));
+ $this->assertTrue($schema->id->primary);
+ $this->assertTrue($schema->id->increment);
+ $this->assertFalse($schema->name->primary);
+ $this->assertFalse($schema->name->increment);
+
+ $this->assertTrue($schema->bint->isBigint());
+ $this->assertTrue($schema->sint->isSmallint());
+ $this->assertTrue($schema->bint->isInt());
+ $this->assertTrue($schema->sint->isInt());
+ $this->assertFalse($schema->bint->isInt(true)); // strict mode
+ $this->assertFalse($schema->sint->isInt(true)); // strict mode
+
+ $this->assertTrue($schema->name->isString());
+ $this->assertEquals(128, $schema->name->max);
+ $this->assertTrue($schema->email->isString());
+ $this->assertEquals(255, $schema->email->max);
+
+ $this->assertTrue($schema->bl->isBool());
+ $this->assertEquals(false, $schema->bl->default);
+ $this->assertTrue($schema->ft->isFloat());
+ $this->assertEquals(10.234, $schema->ft->default);
+ $this->assertTrue($schema->dbl->isDouble());
+ $this->assertEquals(10.23456, $schema->dbl->default);
+ $this->assertTrue($schema->txt->isText());
+
+ if (self::$db !== "MSSQL") { // @todo SQL-Server 2008
+ $this->assertTrue($schema->dt->isDate());
+ }
+
+ $uniques = $schema->getUniques();
+ $this->assertTrue(is_array($uniques));
+ $this->assertEquals(1, count($uniques));
+ $this->assertEquals(1, count($uniques[0]));
+ $this->assertEquals("email", $uniques[0][0]);
+ $this->assertTrue($schema->isUnique("email"));
+ }
+
+ public function testForeignKeyInfo()
+ {
+ if (self::$db === "SQLITE") return;
+
+ $schema = MODEL("Children")->getMetadata();
+ $fkey = $schema->getForeignKey();
+ $this->assertFalse($fkey->has("foo"));
+ $this->assertTrue($fkey->has("parents_id"));
+
+ $this->assertEquals("parents", $fkey->parents_id->table);
+ $this->assertEquals("id", $fkey->parents_id->column);
+ }
+
+ // @todo more tests
+
+ public function testClear()
+ {
+ Sabel_Db_Metadata::clear();
+ Sabel_Db_Connection::closeAll();
+ }
+
+ protected function insertTestData()
+ {
+ $data = array();
+ $data[] = array("name" => "name1", "email" => "test1@example.com", "sint" => 100, "bl" => false, "ft" => 1.234, "dt" => "2008-01-01");
+ $data[] = array("name" => "name2", "email" => "test2@example.com", "sint" => 100, "bl" => false, "ft" => 2.234, "dt" => "2008-01-02");
+ $data[] = array("name" => "name3", "email" => "test3@example.com", "sint" => 200, "bl" => true, "ft" => 3.234, "dt" => "2008-01-03");
+ $data[] = array("name" => "name4", "email" => "test4@example.com", "sint" => 200, "bl" => false, "ft" => 4.234, "txt" => "body", "dt" => "2008-01-04");
+ $data[] = array("name" => "name5", "email" => "test5@example.com", "sint" => 300, "bl" => true, "ft" => 5.234, "dt" => "2008-01-05");
+ $data[] = array("name" => "name6", "email" => "test6@example.com", "sint" => 300, "bl" => true, "ft" => 6.234, "txt" => "body", "dt" => "2008-01-06");
+ $data[] = array("name" => "name7", "email" => "test7@example.com", "sint" => 400, "bl" => false, "ft" => 7.234, "dt" => "2008-01-07");
+ $data[] = array("name" => "name8", "email" => "test8@example.com", "sint" => 400, "bl" => true, "ft" => 8.234, "dt" => "2008-01-08");
+ $data[] = array("name" => "name_", "email" => "test9@example.com", "sint" => 500, "bl" => true, "ft" => 9.234, "dt" => "2008-01-09");
+ $data[] = array("name" => "name10", "email" => "test10@example.com", "sint" => 500, "bl" => true, "ft" => 10.234, "dt" => "2008-01-10");
+
+ $st = MODEL("SchemaTest");
+ foreach ($data as $values) $st->insert($values);
+
+ $data = array();
+ $data[] = array("id" => 1, "name" => "yamada");
+ $data[] = array("id" => 2, "name" => "tanaka");
+ $data[] = array("id" => 3, "name" => "suzuki");
+
+ $student = MODEL("Student");
+ foreach ($data as $values) $student->insert($values);
+
+ $data = array();
+ $data[] = array("id" => 1, "name" => "Mathematics");
+ $data[] = array("id" => 2, "name" => "Physics");
+ $data[] = array("id" => 3, "name" => "Science");
+
+ $course = MODEL("Course");
+ foreach ($data as $values) $course->insert($values);
+
+ $data = array();
+ $data[] = array("student_id" => 1, "course_id" => 1,"val" => "val1");
+ $data[] = array("student_id" => 1, "course_id" => 2,"val" => "val2");
+ $data[] = array("student_id" => 2, "course_id" => 1,"val" => "val3");
+ $data[] = array("student_id" => 2, "course_id" => 3,"val" => "val4");
+ $data[] = array("student_id" => 3, "course_id" => 1,"val" => "val5");
+ $data[] = array("student_id" => 3, "course_id" => 2,"val" => "val6");
+ $data[] = array("student_id" => 3, "course_id" => 3,"val" => "val7");
+
+ $sc = MODEL("StudentCourse");
+ foreach ($data as $values) $sc->insert($values);
+ }
+
+ protected function insertJoinTableData()
+ {
+ $data = array();
+ $data[] = array("id" => 1, "value" => "grandparents1");
+ $data[] = array("id" => 2, "value" => "grandparents2");
+ $gp = MODEL("Grandparents");
+ foreach ($data as $values) $gp->insert($values);
+
+ $data = array();
+ $data[] = array("id" => 1, "grandparents_id" => 2, "value" => "parents1");
+ $data[] = array("id" => 2, "grandparents_id" => 1, "value" => "parents2");
+ $p = MODEL("Parents");
+ foreach ($data as $values) $p->insert($values);
+
+ $data = array();
+ $data[] = array("id" => 1, "parents_id" => 2, "value" => "children1");
+ $data[] = array("id" => 2, "parents_id" => 1, "value" => "children2");
+ $c = MODEL("Children");
+ foreach ($data as $values) $c->insert($values);
+
+ $data = array();
+ $data[] = array("id" => 1, "children_id" => 2, "value" => "grandchildren1");
+ $data[] = array("id" => 2, "children_id" => 1, "value" => "grandchildren2");
+ $gc = MODEL("Grandchildren");
+ foreach ($data as $values) $gc->insert($values);
+ }
+}
diff --git a/Test/DB/TestConfig.php b/Test/DB/TestConfig.php
new file mode 100755
index 0000000..7a6db14
--- /dev/null
+++ b/Test/DB/TestConfig.php
@@ -0,0 +1,93 @@
+ "sabel.db.mysql",
+ "host" => "127.0.0.1",
+ "user" => "root",
+ "password" => "",
+ "database" => "sdb_test");
+ }
+
+ public static function getMysqliConfig()
+ {
+ return array("package" => "sabel.db.mysqli",
+ "host" => "127.0.0.1",
+ "user" => "root",
+ "password" => "",
+ "database" => "sdb_test");
+ }
+
+ public static function getPgsqlConfig()
+ {
+ return array("package" => "sabel.db.pgsql",
+ "host" => "127.0.0.1",
+ "user" => "pgsql",
+ "password" => "pgsql",
+ "database" => "sdb_test");
+ }
+
+ public static function getIbaseConfig()
+ {
+ return array("package" => "sabel.db.ibase",
+ "host" => "localhost",
+ "user" => "develop",
+ "password" => "develop",
+ "database" => "/home/firebird/sdb_test.fdb");
+ }
+
+ public static function getOciConfig()
+ {
+ return array("package" => "sabel.db.oci",
+ "host" => "127.0.0.1",
+ "user" => "develop",
+ "password" => "develop",
+ "database" => "XE",
+ "charset" => "UTF8");
+ }
+
+ public static function getMssqlConfig()
+ {
+ return array("package" => "sabel.db.mssql",
+ "host" => ".\\SQLEXPRESS",
+ "user" => "develop",
+ "password" => "develop",
+ "database" => "sdb_test");
+ }
+
+ public static function getPdoMysqlConfig()
+ {
+ return array("package" => "sabel.db.pdo.mysql",
+ "host" => "127.0.0.1",
+ "user" => "root",
+ "password" => "",
+ "database" => "sdb_test");
+ }
+
+ public static function getPdoPgsqlConfig()
+ {
+ return array("package" => "sabel.db.pdo.pgsql",
+ "host" => "127.0.0.1",
+ "user" => "pgsql",
+ "password" => "pgsql",
+ "database" => "sdb_test");
+ }
+
+ public static function getPdoSqliteConfig()
+ {
+ return array("package" => "sabel.db.pdo.sqlite",
+ "database" => SABEL_BASE . "/Test/data/sdb_test.sq3");
+ }
+
+ public static function getPdoOciConfig()
+ {
+ return array("package" => "sabel.db.pdo.oci",
+ "host" => "127.0.0.1",
+ "user" => "develop",
+ "password" => "develop",
+ "database" => "XE",
+ "charset" => "UTF8");
+ }
+}
diff --git a/Test/DB/Tests.php b/Test/DB/Tests.php
new file mode 100755
index 0000000..98b9740
--- /dev/null
+++ b/Test/DB/Tests.php
@@ -0,0 +1,99 @@
+
+ */
+class Test_DB_Tests
+{
+ public static function main()
+ {
+ PHPUnit_TextUI_TestRunner::run(self::suite());
+ }
+
+ public static function suite()
+ {
+ $suite = new PHPUnit_Framework_TestSuite();
+
+ if (extension_loaded("mysql")) {
+ $suite->addTest(Test_DB_Mysql::suite());
+ }
+
+ if (extension_loaded("mysqli")) {
+ $suite->addTest(Test_DB_Mysqli::suite());
+ }
+
+ if (extension_loaded("pgsql")) {
+ $suite->addTest(Test_DB_Pgsql::suite());
+ }
+
+ if (extension_loaded("pdo_mysql")) {
+ $suite->addTest(Test_DB_PdoMysql::suite());
+ }
+
+ if (extension_loaded("pdo_pgsql")) {
+ $suite->addTest(Test_DB_PdoPgsql::suite());
+ }
+
+ if (extension_loaded("interbase")) {
+ $suite->addTest(Test_DB_Ibase::suite());
+ }
+
+ //if (extension_loaded("mssql")) {
+ // $suite->addTest(Test_DB_Mssql::suite());
+ //}
+
+ if (extension_loaded("oci8")) {
+ $suite->addTest(Test_DB_Oci::suite());
+ }
+
+ if (extension_loaded("pdo_sqlite")) {
+ $suite->addTest(Test_DB_SQLite::suite());
+ }
+
+ if (extension_loaded("pdo_oci")) {
+ $suite->addTest(Test_DB_PdoOci::suite());
+ }
+
+ $suite->addTest(Test_DB_SchemaColumn::suite());
+ $suite->addTest(Test_DB_Config::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/DB/migration/10_Grandchildren_create.php b/Test/DB/migration/10_Grandchildren_create.php
new file mode 100755
index 0000000..e985c19
--- /dev/null
+++ b/Test/DB/migration/10_Grandchildren_create.php
@@ -0,0 +1,7 @@
+column("id")->type(_INT)->primary(true);
+$create->column("children_id")->type(_INT)->nullable(false);
+$create->column("value")->type(_STRING)->nullable(false);
+$create->fkey("children_id");
+$create->options("engine", "InnoDB");
diff --git a/Test/DB/migration/1_SchemaTest_create.php b/Test/DB/migration/1_SchemaTest_create.php
new file mode 100755
index 0000000..63040db
--- /dev/null
+++ b/Test/DB/migration/1_SchemaTest_create.php
@@ -0,0 +1,14 @@
+column("id")->type(_INT)->primary(true)->increment(true);
+$create->column("name")->type(_STRING)->length(128)->nullable(false)->value("default name");
+$create->column("email")->type(_STRING)->nullable(false);
+$create->column("bint")->type(_BIGINT)->value("90000000000");
+$create->column("sint")->type(_SMALLINT)->value(30000);
+$create->column("txt")->type(_TEXT);
+$create->column("bl")->type(_BOOL);
+$create->column("ft")->type(_FLOAT)->value(10.234);
+$create->column("dbl")->type(_DOUBLE)->value(10.23456);
+$create->column("dt")->type(_DATE);
+$create->column("idata")->type(_BINARY);
+$create->unique("email");
diff --git a/Test/DB/migration/2_Student_create.php b/Test/DB/migration/2_Student_create.php
new file mode 100755
index 0000000..f57c4fe
--- /dev/null
+++ b/Test/DB/migration/2_Student_create.php
@@ -0,0 +1,9 @@
+column("id")->type(_INT)
+ ->primary(true);
+
+$create->column("name")->type(_STRING)
+ ->nullable(false);
+
+$create->options("engine", "InnoDB");
diff --git a/Test/DB/migration/3_Course_create.php b/Test/DB/migration/3_Course_create.php
new file mode 100755
index 0000000..f57c4fe
--- /dev/null
+++ b/Test/DB/migration/3_Course_create.php
@@ -0,0 +1,9 @@
+column("id")->type(_INT)
+ ->primary(true);
+
+$create->column("name")->type(_STRING)
+ ->nullable(false);
+
+$create->options("engine", "InnoDB");
diff --git a/Test/DB/migration/4_StudentCourse_create.php b/Test/DB/migration/4_StudentCourse_create.php
new file mode 100755
index 0000000..966784d
--- /dev/null
+++ b/Test/DB/migration/4_StudentCourse_create.php
@@ -0,0 +1,13 @@
+column("student_id")->type(_INT)
+ ->nullable(false);
+
+$create->column("course_id")->type(_INT)
+ ->nullable(false);
+
+$create->column("val")->type(_STRING)
+ ->nullable(false);
+
+$create->primary(array("student_id", "course_id"));
+$create->options("engine", "InnoDB");
diff --git a/Test/DB/migration/5_Session_create.php b/Test/DB/migration/5_Session_create.php
new file mode 100755
index 0000000..58c3fe8
--- /dev/null
+++ b/Test/DB/migration/5_Session_create.php
@@ -0,0 +1,10 @@
+column("sid")->type(_STRING)
+ ->length(64)
+ ->primary(true);
+
+$create->column("sdata")->type(_TEXT);
+
+$create->column("timeout")->type(_INT)
+ ->value(0);
diff --git a/Test/DB/migration/6_SblStorage_create.php b/Test/DB/migration/6_SblStorage_create.php
new file mode 100755
index 0000000..db365ab
--- /dev/null
+++ b/Test/DB/migration/6_SblStorage_create.php
@@ -0,0 +1,10 @@
+column("id")->type(_STRING)
+ ->length(64) # client_id(32) + key(32)
+ ->primary(true);
+
+$create->column("data")->type(_TEXT);
+
+$create->column("timeout")->type(_INT)
+ ->value(0);
diff --git a/Test/DB/migration/7_Grandparents_create.php b/Test/DB/migration/7_Grandparents_create.php
new file mode 100755
index 0000000..171b486
--- /dev/null
+++ b/Test/DB/migration/7_Grandparents_create.php
@@ -0,0 +1,5 @@
+column("id")->type(_INT)->primary(true);
+$create->column("value")->type(_STRING)->nullable(false);
+$create->options("engine", "InnoDB");
diff --git a/Test/DB/migration/8_Parents_create.php b/Test/DB/migration/8_Parents_create.php
new file mode 100755
index 0000000..3409388
--- /dev/null
+++ b/Test/DB/migration/8_Parents_create.php
@@ -0,0 +1,7 @@
+column("id")->type(_INT)->primary(true);
+$create->column("grandparents_id")->type(_INT)->nullable(false);
+$create->column("value")->type(_STRING)->nullable(false);
+$create->fkey("grandparents_id");
+$create->options("engine", "InnoDB");
diff --git a/Test/DB/migration/9_Children_create.php b/Test/DB/migration/9_Children_create.php
new file mode 100755
index 0000000..1522bda
--- /dev/null
+++ b/Test/DB/migration/9_Children_create.php
@@ -0,0 +1,7 @@
+column("id")->type(_INT)->primary(true);
+$create->column("parents_id")->type(_INT)->nullable(false);
+$create->column("value")->type(_STRING)->nullable(false);
+$create->fkey("parents_id");
+$create->options("engine", "InnoDB");
diff --git a/Test/DB/migtests/exec.php b/Test/DB/migtests/exec.php
new file mode 100755
index 0000000..3d77d0b
--- /dev/null
+++ b/Test/DB/migtests/exec.php
@@ -0,0 +1,53 @@
+ array(
+ "package" => "sabel.db.pdo.sqlite",
+ "database" => "/home/ebine/test.sq3"),
+ "mysql" => array(
+ "package" => "sabel.db.mysql",
+ "host" => "127.0.0.1",
+ "database" => "sdb_test",
+ "port" => "3306",
+ "user" => "root",
+ "password" => ""),
+ "pgsql" => array(
+ "package" => "sabel.db.pgsql",
+ "host" => "127.0.0.1",
+ "database" => "sdb_test",
+ "user" => "pgsql",
+ "password" => "pgsql"),
+ "oci" => array(
+ "package" => "sabel.db.oci",
+ "host" => "127.0.0.1",
+ "database" => "XE",
+ "schema" => "DEVELOP",
+ "user" => "DEVELOP",
+ "password" => "DEVELOP")
+ );
+
+foreach ($configs as $key => $param) {
+ Sabel_Db_Config::add($key, $param);
+}
+
+$args = $_SERVER["argv"];
+$path = $args[1];
+$conName = $args[2];
+$type = $args[3];
+
+$schema = Sabel_Db::createMetadata($conName);
+$stmt = Sabel_Db::createStatement($conName);
+
+Sabel_Db_Migration_Manager::setSchema($schema);
+Sabel_Db_Migration_Manager::setStatement($stmt);
+Sabel_Db_Migration_Manager::setDirectory(RUN_BASE . "/migration/tmp");
+Sabel_Db_Migration_Manager::setApplyMode($type);
+
+$dirs = explode(".", Sabel_Db_Config::getPackage($conName));
+$className = implode("_", array_map("ucfirst", $dirs)) . "_Migration";
+$mig = new $className();
+$mig->execute($path);
diff --git a/Test/DB/migtests/files/1_Hoge_create.php b/Test/DB/migtests/files/1_Hoge_create.php
new file mode 100755
index 0000000..67c439d
--- /dev/null
+++ b/Test/DB/migtests/files/1_Hoge_create.php
@@ -0,0 +1,20 @@
+column("id")->type(_INT)
+ ->primary(true)
+ ->increment(true);
+
+$create->column("name")->type(_STRING)
+ ->length(128)
+ ->value("default name");
+
+$create->column("test")->type(_STRING)
+ ->nullable(true);
+
+$create->column("body")->type(_TEXT)
+ ->nullable(false);
+
+$create->column("bool")->type(_BOOL)
+ ->value(false);
+
+$create->options("engine", "InnoDB");
diff --git a/Test/DB/migtests/files/2_Hoge_addColumn.php b/Test/DB/migtests/files/2_Hoge_addColumn.php
new file mode 100755
index 0000000..228712f
--- /dev/null
+++ b/Test/DB/migtests/files/2_Hoge_addColumn.php
@@ -0,0 +1,15 @@
+column("ft")->type(_FLOAT)
+ ->value(1.333);
+
+$add->column("dbl")->type(_DOUBLE)
+ ->nullable(false)
+ ->value(1.23456);
+
+$add->column("sint")->type(_SMALLINT)
+ ->value(30000);
+
+$add->column("bint")->type(_BIGINT)
+ ->value(400000000);
+
diff --git a/Test/DB/migtests/files/3_Hoge_dropColumn.php b/Test/DB/migtests/files/3_Hoge_dropColumn.php
new file mode 100755
index 0000000..8c3cce7
--- /dev/null
+++ b/Test/DB/migtests/files/3_Hoge_dropColumn.php
@@ -0,0 +1,5 @@
+column("dbl");
+$drop->column("sint");
+
diff --git a/Test/DB/migtests/files/4_Hoge_changeColumn.php b/Test/DB/migtests/files/4_Hoge_changeColumn.php
new file mode 100755
index 0000000..4914bfa
--- /dev/null
+++ b/Test/DB/migtests/files/4_Hoge_changeColumn.php
@@ -0,0 +1,5 @@
+column("name")->value(_NULL);
+$change->column("test")->nullable(false);
+$change->column("ft")->type(_DOUBLE)->value(1.33333);
diff --git a/Test/DB/migtests/files/5_Huga_create.php b/Test/DB/migtests/files/5_Huga_create.php
new file mode 100755
index 0000000..55759ad
--- /dev/null
+++ b/Test/DB/migtests/files/5_Huga_create.php
@@ -0,0 +1,11 @@
+column("id")->type(_INT)
+ ->primary(true);
+
+$create->column("email")->type(_STRING)
+ ->length(255)
+ ->nullable(false);
+
+$create->unique("email");
+$create->options("engine", "InnoDB");
diff --git a/Test/DB/migtests/files/6_Foo_create.php b/Test/DB/migtests/files/6_Foo_create.php
new file mode 100755
index 0000000..4cae9f0
--- /dev/null
+++ b/Test/DB/migtests/files/6_Foo_create.php
@@ -0,0 +1,6 @@
+column("id")->type(_INT)
+ ->primary(true);
+
+$create->options("engine", "InnoDB");
diff --git a/Test/DB/migtests/files/7_Hoge_drop.php b/Test/DB/migtests/files/7_Hoge_drop.php
new file mode 100755
index 0000000..e69de29
diff --git a/Test/DB/migtests/files/8_Bar_create.php b/Test/DB/migtests/files/8_Bar_create.php
new file mode 100755
index 0000000..ade53e3
--- /dev/null
+++ b/Test/DB/migtests/files/8_Bar_create.php
@@ -0,0 +1,15 @@
+column("id")->type(_INT)
+ ->primary(true);
+
+$create->column("huga_id")->type(_INT)
+ ->nullable(false);
+
+$create->column("foo_id")->type(_INT)
+ ->nullable(false);
+
+$create->fkey("huga_id");
+$create->fkey("foo_id")->onDelete("CASCADE");
+$create->options("engine", "InnoDB");
+
diff --git a/Test/DB/migtests/run.php b/Test/DB/migtests/run.php
new file mode 100755
index 0000000..467252e
--- /dev/null
+++ b/Test/DB/migtests/run.php
@@ -0,0 +1,6 @@
+ array(
+ "package" => "sabel.db.pdo.sqlite",
+ "database" => "/home/ebine/test.sq3"),
+ "mysql" => array(
+ "package" => "sabel.db.mysql",
+ "host" => "127.0.0.1",
+ "database" => "sdb_test",
+ "port" => "3306",
+ "user" => "root",
+ "password" => ""),
+ "pgsql" => array(
+ "package" => "sabel.db.pgsql",
+ "host" => "127.0.0.1",
+ "database" => "sdb_test",
+ "user" => "pgsql",
+ "password" => "pgsql"),
+ "oci" => array(
+ "package" => "sabel.db.oci",
+ "host" => "127.0.0.1",
+ "database" => "XE",
+ "schema" => "DEVELOP",
+ "user" => "DEVELOP",
+ "password" => "DEVELOP")
+ );
+
+foreach ($configs as $key => $param) {
+ Sabel_Db_Config::add($key, $param);
+}
+
+echo "[ " . CONNAME . " ]\n";
+
+$path = RUN_BASE . "/migration/tmp/1_Hoge_create.php";
+system("php exec.php $path " . CONNAME . " upgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$hoge = $accessor->getTable("hoge");
+$id = $hoge->id;
+$name = $hoge->name;
+$test = $hoge->test;
+$body = $hoge->body;
+$bool = $hoge->bool;
+
+isTrue($id->isInt(true));
+isTrue($id->primary);
+isTrue($id->increment);
+isNull($id->default);
+isFalse($id->nullable);
+equals($id->max, PHP_INT_MAX);
+
+isTrue($name->isString());
+isFalse($name->primary);
+isFalse($name->increment);
+equals($name->default, "default name");
+isTrue($name->nullable);
+equals($name->max, 128);
+
+isTrue($test->isString());
+isFalse($test->primary);
+isFalse($test->increment);
+isNull($test->default);
+isTrue($test->nullable);
+equals($test->max, 255);
+
+isTrue($body->isText());
+isFalse($body->primary);
+isFalse($body->increment);
+isNull($body->default);
+isFalse($body->nullable);
+
+isTrue($bool->isBool());
+isFalse($bool->primary);
+isFalse($bool->increment);
+isFalse($bool->default);
+isTrue($bool->nullable);
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/2_Hoge_addColumn.php";
+system("php exec.php $path " . CONNAME . " upgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$hoge = $accessor->getTable("hoge");
+$id = $hoge->id;
+$name = $hoge->name;
+$test = $hoge->test;
+$body = $hoge->body;
+$ft = $hoge->ft;
+$dbl = $hoge->dbl;
+$sint = $hoge->sint;
+$bint = $hoge->bint;
+$bool = $hoge->bool;
+
+isTrue($id->isInt(true));
+isTrue($id->primary);
+isTrue($id->increment);
+isNull($id->default);
+isFalse($id->nullable);
+equals($id->max, PHP_INT_MAX);
+
+isTrue($name->isString());
+isFalse($name->primary);
+isFalse($name->increment);
+equals($name->default, "default name");
+isTrue($name->nullable);
+equals($name->max, 128);
+
+isTrue($test->isString());
+isFalse($test->primary);
+isFalse($test->increment);
+isNull($test->default);
+isTrue($test->nullable);
+equals($test->max, 255);
+
+isTrue($body->isText());
+isFalse($body->primary);
+isFalse($body->increment);
+isNull($body->default);
+isFalse($body->nullable);
+
+isTrue($ft->isFloat());
+isFalse($ft->primary);
+isFalse($ft->increment);
+equals($ft->default, 1.333);
+isTrue($ft->nullable);
+
+isTrue($dbl->isDouble());
+isFalse($dbl->primary);
+isFalse($dbl->increment);
+equals($dbl->default, 1.23456);
+isFalse($dbl->nullable);
+
+isTrue($sint->isSmallint());
+isFalse($sint->primary);
+isFalse($sint->increment);
+equals($sint->default, 30000);
+isTrue($sint->nullable);
+
+isTrue($bint->isBigint());
+isFalse($bint->primary);
+isFalse($bint->increment);
+equals($bint->default, "400000000");
+isTrue($bint->nullable);
+
+isTrue($bool->isBool());
+isFalse($bool->primary);
+isFalse($bool->increment);
+isFalse($bool->default);
+isTrue($bool->nullable);
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/3_Hoge_dropColumn.php";
+system("php exec.php $path " . CONNAME . " upgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$hoge = $accessor->getTable("hoge");
+$id = $hoge->id;
+$name = $hoge->name;
+$test = $hoge->test;
+$body = $hoge->body;
+$bint = $hoge->bint;
+$bool = $hoge->bool;
+
+isTrue($id->isInt(true));
+isTrue($id->primary);
+isTrue($id->increment);
+isNull($id->default);
+isFalse($id->nullable);
+equals($id->max, PHP_INT_MAX);
+
+isTrue($name->isString());
+isFalse($name->primary);
+isFalse($name->increment);
+equals($name->default, "default name");
+isTrue($name->nullable);
+equals($name->max, 128);
+
+isTrue($test->isString());
+isFalse($test->primary);
+isFalse($test->increment);
+isNull($test->default);
+isTrue($test->nullable);
+equals($test->max, 255);
+
+isTrue($body->isText());
+isFalse($body->primary);
+isFalse($body->increment);
+isNull($body->default);
+isFalse($body->nullable);
+
+isTrue($ft->isFloat());
+isFalse($ft->primary);
+isFalse($ft->increment);
+equals($ft->default, 1.333);
+isTrue($ft->nullable);
+
+isTrue($bint->isBigint());
+isFalse($bint->primary);
+isFalse($bint->increment);
+equals($bint->default, "400000000");
+isTrue($bint->nullable);
+
+isTrue($bool->isBool());
+isFalse($bool->primary);
+isFalse($bool->increment);
+isFalse($bool->default);
+isTrue($bool->nullable);
+
+isNull($hoge->dbl);
+isNull($hoge->sint);
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/4_Hoge_changeColumn.php";
+system("php exec.php $path " . CONNAME . " upgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$hoge = $accessor->getTable("hoge");
+$id = $hoge->id;
+$name = $hoge->name;
+$test = $hoge->test;
+$body = $hoge->body;
+$ft = $hoge->ft;
+$bint = $hoge->bint;
+$bool = $hoge->bool;
+
+isTrue($id->isInt(true));
+isTrue($id->primary);
+isTrue($id->increment);
+isNull($id->default);
+isFalse($id->nullable);
+equals($id->max, PHP_INT_MAX);
+
+isTrue($name->isString());
+isFalse($name->primary);
+isFalse($name->increment);
+isNull($name->default);
+isTrue($name->nullable);
+equals($name->max, 128);
+
+isTrue($test->isString());
+isFalse($test->primary);
+isFalse($test->increment);
+isNull($test->default);
+isFalse($test->nullable);
+equals($test->max, 255);
+
+isTrue($body->isText());
+isFalse($body->primary);
+isFalse($body->increment);
+isNull($body->default);
+isFalse($body->nullable);
+
+isTrue($ft->isDouble());
+isFalse($ft->primary);
+isFalse($ft->increment);
+equals($ft->default, 1.33333);
+isTrue($ft->nullable);
+
+isTrue($bint->isBigint());
+isFalse($bint->primary);
+isFalse($bint->increment);
+equals($bint->default, "400000000");
+isTrue($bint->nullable);
+
+isTrue($bool->isBool());
+isFalse($bool->primary);
+isFalse($bool->increment);
+isFalse($bool->default);
+isTrue($bool->nullable);
+
+isNull($hoge->dbl);
+isNull($hoge->sint);
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/5_Huga_create.php";
+system("php exec.php $path " . CONNAME . " upgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$huga = $accessor->getTable("huga");
+$id = $huga->id;
+$email = $huga->email;
+
+isTrue($id->isInt(true));
+isTrue($id->primary);
+isFalse($id->increment);
+isNull($id->default);
+isFalse($id->nullable);
+equals($id->max, PHP_INT_MAX);
+
+isTrue($email->isString());
+isFalse($email->primary);
+isFalse($email->increment);
+isNull($email->default);
+isFalse($email->nullable);
+equals($email->max, 255);
+isTrue($huga->isUnique("email"));
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/6_Foo_create.php";
+system("php exec.php $path " . CONNAME . " upgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$foo = $accessor->getTable("foo");
+$id = $foo->id;
+
+isTrue($id->isInt(true));
+isTrue($id->primary);
+isFalse($id->increment);
+isNull($id->default);
+isFalse($id->nullable);
+equals($id->max, PHP_INT_MAX);
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/7_Hoge_drop.php";
+system("php exec.php $path " . CONNAME . " upgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$tables = $accessor->getTableList();
+isFalse(in_array("hoge", $tables));
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/8_Bar_create.php";
+system("php exec.php $path " . CONNAME . " upgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$bar = $accessor->getTable("bar");
+$id = $bar->id;
+$huga_id = $bar->huga_id;
+$foo_id = $bar->foo_id;
+
+isTrue($id->isInt(true));
+isTrue($id->primary);
+isFalse($id->increment);
+isNull($id->default);
+isFalse($id->nullable);
+equals($id->max, PHP_INT_MAX);
+
+isTrue($huga_id->isInt(true));
+isFalse($huga_id->primary);
+isFalse($huga_id->increment);
+isNull($huga_id->default);
+isFalse($huga_id->nullable);
+equals($huga_id->max, PHP_INT_MAX);
+
+isTrue($foo_id->isInt(true));
+isFalse($foo_id->primary);
+isFalse($foo_id->increment);
+isNull($foo_id->default);
+isFalse($foo_id->nullable);
+equals($foo_id->max, PHP_INT_MAX);
+
+if (CONNAME !== "sqlite") {
+ $fkey = $bar->getForeignKey();
+ isTrue($fkey->has("huga_id"));
+ isTrue($fkey->has("foo_id"));
+ $hugaId = $fkey->huga_id;
+ $fooId = $fkey->foo_id;
+ equals($hugaId->table, "huga");
+ equals($fooId->table, "foo");
+ equals($hugaId->column, "id");
+ equals($fooId->column, "id");
+ equals($hugaId->onDelete, "NO ACTION");
+ equals($fooId->onDelete, "CASCADE");
+ equals($hugaId->onUpdate, "NO ACTION");
+ equals($fooId->onUpdate, "NO ACTION");
+}
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/8_Bar_create.php";
+system("php exec.php $path " . CONNAME . " downgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$tables = $accessor->getTableList();
+isFalse(in_array("bar", $tables));
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/7_Hoge_drop.php";
+system("php exec.php $path " . CONNAME . " downgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$hoge = $accessor->getTable("hoge");
+$id = $hoge->id;
+$name = $hoge->name;
+$test = $hoge->test;
+$body = $hoge->body;
+$ft = $hoge->ft;
+$bint = $hoge->bint;
+$bool = $hoge->bool;
+
+isTrue($id->isInt(true));
+isTrue($id->primary);
+isTrue($id->increment);
+isNull($id->default);
+isFalse($id->nullable);
+equals($id->max, PHP_INT_MAX);
+
+isTrue($name->isString());
+isFalse($name->primary);
+isFalse($name->increment);
+isNull($name->default);
+isTrue($name->nullable);
+equals($name->max, 128);
+
+isTrue($test->isString());
+isFalse($test->primary);
+isFalse($test->increment);
+isNull($test->default);
+isFalse($test->nullable);
+equals($test->max, 255);
+
+isTrue($body->isText());
+isFalse($body->primary);
+isFalse($body->increment);
+isNull($body->default);
+isFalse($body->nullable);
+
+isTrue($ft->isDouble());
+isFalse($ft->primary);
+isFalse($ft->increment);
+equals($ft->default, 1.33333);
+isTrue($ft->nullable);
+
+isTrue($bint->isBigint());
+isFalse($bint->primary);
+isFalse($bint->increment);
+equals($bint->default, "400000000");
+isTrue($bint->nullable);
+
+isTrue($bool->isBool());
+isFalse($bool->primary);
+isFalse($bool->increment);
+isFalse($bool->default);
+isTrue($bool->nullable);
+
+isNull($hoge->dbl);
+isNull($hoge->sint);
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/6_Foo_create.php";
+system("php exec.php $path " . CONNAME . " downgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$tables = $accessor->getTableList();
+isFalse(in_array("foo", $tables));
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/5_Huga_create.php";
+system("php exec.php $path " . CONNAME . " downgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$tables = $accessor->getTableList();
+isFalse(in_array("huga", $tables));
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/4_Hoge_changeColumn.php";
+system("php exec.php $path " . CONNAME . " downgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$hoge = $accessor->getTable("hoge");
+$id = $hoge->id;
+$name = $hoge->name;
+$test = $hoge->test;
+$body = $hoge->body;
+$ft = $hoge->ft;
+$bint = $hoge->bint;
+$bool = $hoge->bool;
+
+isTrue($id->isInt(true));
+isTrue($id->primary);
+isTrue($id->increment);
+isNull($id->default);
+isFalse($id->nullable);
+equals($id->max, PHP_INT_MAX);
+
+isTrue($name->isString());
+isFalse($name->primary);
+isFalse($name->increment);
+equals($name->default, "default name");
+isTrue($name->nullable);
+equals($name->max, 128);
+
+isTrue($test->isString());
+isFalse($test->primary);
+isFalse($test->increment);
+isNull($test->default);
+isTrue($test->nullable);
+equals($test->max, 255);
+
+isTrue($body->isText());
+isFalse($body->primary);
+isFalse($body->increment);
+isNull($body->default);
+isFalse($body->nullable);
+
+isTrue($ft->isFloat());
+isFalse($ft->primary);
+isFalse($ft->increment);
+equals($ft->default, 1.333);
+isTrue($ft->nullable);
+
+isTrue($bint->isBigint());
+isFalse($bint->primary);
+isFalse($bint->increment);
+equals($bint->default, "400000000");
+isTrue($bint->nullable);
+
+isTrue($bool->isBool());
+isFalse($bool->primary);
+isFalse($bool->increment);
+isFalse($bool->default);
+isTrue($bool->nullable);
+
+isNull($hoge->dbl);
+isNull($hoge->sint);
+
+$tables = $accessor->getTableList();
+isFalse(in_array("huga", $tables));
+isFalse(in_array("foo", $tables));
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/3_Hoge_dropColumn.php";
+system("php exec.php $path " . CONNAME . " downgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$hoge = $accessor->getTable("hoge");
+$id = $hoge->id;
+$name = $hoge->name;
+$test = $hoge->test;
+$body = $hoge->body;
+$ft = $hoge->ft;
+$dbl = $hoge->dbl;
+$sint = $hoge->sint;
+$bint = $hoge->bint;
+$bool = $hoge->bool;
+
+isTrue($id->isInt(true));
+isTrue($id->primary);
+isTrue($id->increment);
+isNull($id->default);
+isFalse($id->nullable);
+equals($id->max, PHP_INT_MAX);
+
+isTrue($name->isString());
+isFalse($name->primary);
+isFalse($name->increment);
+equals($name->default, "default name");
+isTrue($name->nullable);
+equals($name->max, 128);
+
+isTrue($test->isString());
+isFalse($test->primary);
+isFalse($test->increment);
+isNull($test->default);
+isTrue($test->nullable);
+equals($test->max, 255);
+
+isTrue($body->isText());
+isFalse($body->primary);
+isFalse($body->increment);
+isNull($body->default);
+isFalse($body->nullable);
+
+isTrue($ft->isFloat());
+isFalse($ft->primary);
+isFalse($ft->increment);
+equals($ft->default, 1.333);
+isTrue($ft->nullable);
+
+isTrue($dbl->isDouble());
+isFalse($dbl->primary);
+isFalse($dbl->increment);
+equals($dbl->default, 1.23456);
+isFalse($dbl->nullable);
+
+isTrue($sint->isSmallint());
+isFalse($sint->primary);
+isFalse($sint->increment);
+equals($sint->default, 30000);
+isTrue($sint->nullable);
+
+isTrue($bint->isBigint());
+isFalse($bint->primary);
+isFalse($bint->increment);
+equals($bint->default, "400000000");
+isTrue($bint->nullable);
+
+isTrue($bool->isBool());
+isFalse($bool->primary);
+isFalse($bool->increment);
+isFalse($bool->default);
+isTrue($bool->nullable);
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/2_Hoge_addColumn.php";
+system("php exec.php $path " . CONNAME . " downgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$hoge = $accessor->getTable("hoge");
+$id = $hoge->id;
+$name = $hoge->name;
+$test = $hoge->test;
+$body = $hoge->body;
+$bool = $hoge->bool;
+
+isTrue($id->isInt(true));
+isTrue($id->primary);
+isTrue($id->increment);
+isNull($id->default);
+isFalse($id->nullable);
+equals($id->max, PHP_INT_MAX);
+
+isTrue($name->isString());
+isFalse($name->primary);
+isFalse($name->increment);
+equals($name->default, "default name");
+isTrue($name->nullable);
+equals($name->max, 128);
+
+isTrue($test->isString());
+isFalse($test->primary);
+isFalse($test->increment);
+isNull($test->default);
+isTrue($test->nullable);
+equals($test->max, 255);
+
+isTrue($body->isText());
+isFalse($body->primary);
+isFalse($body->increment);
+isNull($body->default);
+isFalse($body->nullable);
+
+isTrue($bool->isBool());
+isFalse($bool->primary);
+isFalse($bool->increment);
+isFalse($bool->default);
+isTrue($bool->nullable);
+
+isNull($hoge->ft);
+isNull($hoge->dbl);
+isNull($hoge->sint);
+isNull($hoge->bint);
+
+Sabel_Db_Connection::closeAll();
+
+$path = RUN_BASE . "/migration/tmp/1_Hoge_create.php";
+system("php exec.php $path " . CONNAME . " downgrade");
+
+$accessor = Sabel_Db::createMetadata(CONNAME);
+$tables = $accessor->getTableList();
+isFalse(in_array("hoge", $tables));
+
+Sabel_Db_Connection::closeAll();
+
+echo "\n";
diff --git a/Test/Exception.php b/Test/Exception.php
new file mode 100755
index 0000000..fe4c1ae
--- /dev/null
+++ b/Test/Exception.php
@@ -0,0 +1,60 @@
+
+ */
+class Test_Exception extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Exception");
+ }
+
+ public function testInvalidArgument()
+ {
+ try {
+ $this->throwInvalidArgumentException();
+ } catch (Sabel_Exception_InvalidArgument $e) {
+ $this->assertTrue($e instanceof InvalidArgumentException); // SPL
+ $this->assertEquals("invalid argument.", $e->getMessage());
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testRuntime()
+ {
+ try {
+ $this->throwRuntimeException();
+ } catch (Sabel_Exception_Runtime $e) {
+ $message = <<writeSyslog($message);
+ $this->assertEquals(3, count($messageLines));
+ $this->assertEquals("throw", $messageLines[0]);
+ $this->assertEquals("runtime", $messageLines[1]);
+ $this->assertEquals("exception", $messageLines[2]);
+ return;
+ }
+
+ $this->fail();
+ }
+
+ protected function throwInvalidArgumentException()
+ {
+ throw new Sabel_Exception_InvalidArgument("invalid argument.");
+ }
+
+ protected function throwRuntimeException()
+ {
+ throw new Sabel_Exception_Runtime("");
+ }
+}
diff --git a/Test/Experimental.php b/Test/Experimental.php
new file mode 100755
index 0000000..71e4d1e
--- /dev/null
+++ b/Test/Experimental.php
@@ -0,0 +1,23 @@
+
+ */
+class Test_Experimental extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Experimental");
+ }
+
+ public function setUp()
+ {
+ }
+
+ public function testExperimental()
+ {
+ }
+}
diff --git a/Test/I18n/Gettext.php b/Test/I18n/Gettext.php
new file mode 100755
index 0000000..c0a9a90
--- /dev/null
+++ b/Test/I18n/Gettext.php
@@ -0,0 +1,51 @@
+
+ */
+class Test_I18n_Gettext extends SabelTestCase
+{
+ public static function suite()
+ {
+ ini_set("mbstring.internal_encoding", "UTF-8");
+ define("LOCALE_DIR_PATH", dirname(__FILE__) . DIRECTORY_SEPARATOR . "locale");
+ return self::createSuite("Test_I18n_Gettext");
+ }
+
+ public function testI18n()
+ {
+ $gettext = Sabel_I18n_Gettext::getInstance();
+ $gettext->setMessagesFileName("messages.php");
+ $gettext->init("ja,en-us;q=0.7,en;q=0.3");
+
+ $this->assertTrue($gettext->isInitialized());
+
+ $this->assertEquals("名前", __("name"));
+ $this->assertEquals("住所", __("address"));
+ }
+
+ public function testMessagesFileName()
+ {
+ $this->assertEquals("名前", __("name"));
+ $this->assertEquals("住所", __("address"));
+
+ Sabel_I18n_Gettext::getInstance()->setMessagesFileName("hiragana.php");
+ $this->assertEquals("なまえ", __("name"));
+ $this->assertEquals("じゅうしょ", __("address"));
+ }
+
+ public function testCodeSet()
+ {
+ $gettext = Sabel_I18n_Gettext::getInstance();
+ $gettext->setCodeSet("EUC-JP");
+
+ $this->assertEquals(mb_convert_encoding("なまえ", "EUC-JP", "UTF-8"), __("name"));
+ $this->assertEquals(mb_convert_encoding("じゅうしょ", "EUC-JP", "UTF-8"), __("address"));
+ }
+}
+
+function __($msgid)
+{
+ return Sabel_I18n_Sabel_Gettext::_($msgid);
+}
diff --git a/Test/I18n/locale/ja/hiragana.php b/Test/I18n/locale/ja/hiragana.php
new file mode 100755
index 0000000..72dbe45
--- /dev/null
+++ b/Test/I18n/locale/ja/hiragana.php
@@ -0,0 +1,6 @@
+ "なまえ",
+ "address" => "じゅうしょ",
+);
diff --git a/Test/I18n/locale/ja/messages.php b/Test/I18n/locale/ja/messages.php
new file mode 100755
index 0000000..31336de
--- /dev/null
+++ b/Test/I18n/locale/ja/messages.php
@@ -0,0 +1,6 @@
+ "名前",
+ "address" => "住所",
+);
diff --git a/Test/Mail/MimeDecode.php b/Test/Mail/MimeDecode.php
new file mode 100755
index 0000000..18666e7
--- /dev/null
+++ b/Test/Mail/MimeDecode.php
@@ -0,0 +1,375 @@
+
+ */
+class Test_Mail_MimeDecode extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Mail_MimeDecode");
+ }
+
+ /**
+ * @test
+ */
+ public function plain()
+ {
+ $decoded = $this->decode("plain");
+
+ $this->assertEquals("text/plain", $decoded->content->getType());
+ $this->assertEquals("ISO-2022-JP", $decoded->content->getCharset());
+
+ $this->assertEquals('"from1" ', $decoded->getHeader("From"));
+ $this->assertEquals('from1', $decoded->getFromName());
+ $this->assertEquals('from1@example.com', $decoded->getFromAddr());
+
+ $this->assertEquals('to1@example.com', $decoded->getToAddr());
+
+ $this->assertEquals("件名", $decoded->getHeader("Subject"));
+ $this->assertNotEquals(false, strpos($decoded->body->getContent(), "本文"));
+
+ $this->assertNull($decoded->html);
+ $this->assertEquals(array(), $decoded->attachments);
+ }
+
+ /**
+ * @test
+ */
+ public function html()
+ {
+ $decoded = $this->decode("html");
+
+ $this->assertEquals("text/html", $decoded->content->getType());
+ $this->assertEquals("ISO-2022-JP", $decoded->content->getCharset());
+
+ $this->assertEquals('"from2" ', $decoded->getHeader("From"));
+ $this->assertEquals('from2', $decoded->getFromName());
+ $this->assertEquals('from2@example.com', $decoded->getFromAddr());
+
+ $this->assertEquals('to2@example.com', $decoded->getToAddr());
+
+ $this->assertEquals("件名", $decoded->getHeader("Subject"));
+ $this->assertNotEquals(false, stripos($decoded->html->getContent(), ""));
+
+ $this->assertNull($decoded->body);
+ $this->assertEquals(array(), $decoded->html->getImages());
+ $this->assertEquals(array(), $decoded->attachments);
+ }
+
+ /**
+ * @test
+ */
+ public function plain_html()
+ {
+ $decoded = $this->decode("plain_html");
+
+ $this->assertEquals("multipart/alternative", $decoded->content->getType());
+
+ $this->assertEquals('"from3" ', $decoded->getHeader("From"));
+ $this->assertEquals('from3', $decoded->getFromName());
+ $this->assertEquals('from3@example.com', $decoded->getFromAddr());
+
+ $this->assertEquals('to3@example.com', $decoded->getToAddr());
+
+ $this->assertEquals("件名", $decoded->getHeader("Subject"));
+ $this->assertNotEquals(false, strpos($decoded->body->getContent(), "本文"));
+ $this->assertNotEquals(false, stripos($decoded->html->getContent(), ""));
+
+ $this->assertEquals(array(), $decoded->html->getImages());
+ $this->assertEquals(array(), $decoded->attachments);
+ }
+
+ /**
+ * @test
+ */
+ public function htmlimg()
+ {
+ $decoded = $this->decode("htmlimg");
+
+ $this->assertEquals("multipart/related", $decoded->content->getType());
+
+ $this->assertEquals('"from4" ', $decoded->getHeader("From"));
+ $this->assertEquals('from4', $decoded->getFromName());
+ $this->assertEquals('from4@example.com', $decoded->getFromAddr());
+
+ $this->assertEquals('to4@example.com', $decoded->getToAddr());
+
+ $this->assertEquals("件名", $decoded->getHeader("Subject"));
+ $this->assertNotEquals(false, stripos($decoded->html->getContent(), ""));
+
+ $images = $decoded->html->getImages();
+ $this->assertEquals(2, count($images));
+ $this->assertEquals("image/png", $images[0]["mimetype"]);
+ $this->assertEquals("image/png", $images[1]["mimetype"]);
+ $this->assertTrue($images[0]["data"] !== $images[1]["data"]);
+
+ $this->assertNull($decoded->body);
+ $this->assertEquals(array(), $decoded->attachments);
+ }
+
+ /**
+ * @test
+ */
+ public function plain_attachment()
+ {
+ $decoded = $this->decode("plain_attachment");
+
+ $this->assertEquals("multipart/mixed", $decoded->content->getType());
+
+ $this->assertEquals('"from5" ', $decoded->getHeader("From"));
+ $this->assertEquals('from5', $decoded->getFromName());
+ $this->assertEquals('from5@example.com', $decoded->getFromAddr());
+
+ $this->assertEquals('to5@example.com', $decoded->getToAddr());
+
+ $this->assertEquals("件名", $decoded->getHeader("Subject"));
+ $this->assertNotEquals(false, strpos($decoded->body->getContent(), "本文"));
+
+ $this->assertNull($decoded->html);
+
+ $attachments = $decoded->attachments;
+ $this->assertEquals(2, count($attachments));
+ $this->assertEquals("image/png", $attachments[0]->getType());
+ $this->assertEquals("image/png", $attachments[1]->getType());
+ $this->assertTrue($attachments[0]->getContent() !== $attachments[1]->getContent());
+ }
+
+ /**
+ * @test
+ */
+ public function plain_html_attachment()
+ {
+ $decoded = $this->decode("plain_html_attachment");
+
+ $this->assertEquals("multipart/mixed", $decoded->content->getType());
+
+ $this->assertEquals('"from6" ', $decoded->getHeader("From"));
+ $this->assertEquals('from6', $decoded->getFromName());
+ $this->assertEquals('from6@example.com', $decoded->getFromAddr());
+
+ $this->assertEquals('to6@example.com', $decoded->getToAddr());
+
+ $this->assertEquals("件名", $decoded->getHeader("Subject"));
+ $this->assertNotEquals(false, strpos($decoded->body->getContent(), "本文"));
+ $this->assertNotEquals(false, stripos($decoded->html->getContent(), ""));
+ $this->assertEquals(array(), $decoded->html->getImages());
+
+ $attachments = $decoded->attachments;
+ $this->assertEquals(2, count($attachments));
+ $this->assertEquals("image/png", $attachments[0]->getType());
+ $this->assertEquals("image/png", $attachments[1]->getType());
+ $this->assertTrue($attachments[0]->getContent() !== $attachments[1]->getContent());
+ }
+
+ /**
+ * @test
+ */
+ public function html_attachment()
+ {
+ $decoded = $this->decode("html_attachment");
+
+ $this->assertEquals("multipart/mixed", $decoded->content->getType());
+
+ $this->assertEquals('"from7" ', $decoded->getHeader("From"));
+ $this->assertEquals('from7', $decoded->getFromName());
+ $this->assertEquals('from7@example.com', $decoded->getFromAddr());
+
+ $this->assertEquals('to7@example.com', $decoded->getToAddr());
+
+ $this->assertEquals("件名", $decoded->getHeader("Subject"));
+ $this->assertNotEquals(false, stripos($decoded->html->getContent(), ""));
+
+ $this->assertNull($decoded->body);
+
+ $attachments = $decoded->attachments;
+ $this->assertEquals(2, count($attachments));
+ $this->assertEquals("image/png", $attachments[0]->getType());
+ $this->assertEquals("image/png", $attachments[1]->getType());
+ $this->assertTrue($attachments[0]->getContent() !== $attachments[1]->getContent());
+ }
+
+ /**
+ * @test
+ */
+ public function htmlimg_attachment()
+ {
+ $decoded = $this->decode("htmlimg_attachment");
+
+ $this->assertEquals("multipart/mixed", $decoded->content->getType());
+
+ $this->assertEquals('"from8" ', $decoded->getHeader("From"));
+ $this->assertEquals('from8', $decoded->getFromName());
+ $this->assertEquals('from8@example.com', $decoded->getFromAddr());
+
+ $this->assertEquals('to8@example.com', $decoded->getToAddr());
+
+ $this->assertEquals("件名", $decoded->getHeader("Subject"));
+ $this->assertNotEquals(false, stripos($decoded->html->getContent(), ""));
+
+ $images = $decoded->html->getImages();
+ $this->assertEquals(2, count($images));
+ $this->assertEquals("image/png", $images[0]["mimetype"]);
+ $this->assertEquals("image/png", $images[1]["mimetype"]);
+ $this->assertTrue($images[0]["data"] !== $images[1]["data"]);
+
+ $this->assertNull($decoded->body);
+
+ $attachments = $decoded->attachments;
+ $this->assertEquals(2, count($attachments));
+ $this->assertEquals("image/png", $attachments[0]->getType());
+ $this->assertEquals("image/png", $attachments[1]->getType());
+ $this->assertTrue($attachments[0]->getContent() !== $attachments[1]->getContent());
+ }
+
+ /**
+ * @test
+ */
+ public function plain_htmlimg() // thunderbird
+ {
+ $decoded = $this->decode("plain_htmlimg");
+
+ $this->assertEquals("multipart/alternative", $decoded->content->getType());
+
+ $this->assertEquals('"from9" ', $decoded->getHeader("From"));
+ $this->assertEquals('from9', $decoded->getFromName());
+ $this->assertEquals('from9@example.com', $decoded->getFromAddr());
+
+ $this->assertEquals('to9@example.com', $decoded->getToAddr());
+
+ $this->assertEquals("件名", $decoded->getHeader("Subject"));
+ $this->assertNotEquals(false, strpos($decoded->body->getContent(), "本文"));
+ $this->assertNotEquals(false, stripos($decoded->html->getContent(), ""));
+
+ $images = $decoded->html->getImages();
+ $this->assertEquals(2, count($images));
+ $this->assertEquals("image/png", $images[0]["mimetype"]);
+ $this->assertEquals("image/png", $images[1]["mimetype"]);
+ $this->assertTrue($images[0]["data"] !== $images[1]["data"]);
+
+ $this->assertEquals(array(), $decoded->attachments);
+ }
+
+ /**
+ * @test
+ */
+ public function plain_htmlimg2() // outlook express
+ {
+ $decoded = $this->decode("plain_htmlimg2");
+
+ $this->assertEquals("multipart/related", $decoded->content->getType());
+
+ $this->assertEquals('"from10" ', $decoded->getHeader("From"));
+ $this->assertEquals('from10', $decoded->getFromName());
+ $this->assertEquals('from10@example.com', $decoded->getFromAddr());
+
+ $this->assertEquals('to10@example.com', $decoded->getToAddr());
+
+ $this->assertEquals("件名", $decoded->getHeader("Subject"));
+ $this->assertNotEquals(false, strpos($decoded->body->getContent(), "本文"));
+ $this->assertNotEquals(false, stripos($decoded->html->getContent(), ""));
+
+ $images = $decoded->html->getImages();
+ $this->assertEquals(2, count($images));
+ $this->assertEquals("image/png", $images[0]["mimetype"]);
+ $this->assertEquals("image/png", $images[1]["mimetype"]);
+ $this->assertTrue($images[0]["data"] !== $images[1]["data"]);
+
+ $this->assertEquals(array(), $decoded->attachments);
+ }
+
+ /**
+ * @test
+ */
+ public function plain_htmlimg_attachment() // thunderbird
+ {
+ $decoded = $this->decode("plain_htmlimg_attachment");
+
+ $this->assertEquals("multipart/mixed", $decoded->content->getType());
+
+ $this->assertEquals('"from11" ', $decoded->getHeader("From"));
+ $this->assertEquals('from11', $decoded->getFromName());
+ $this->assertEquals('from11@example.com', $decoded->getFromAddr());
+
+ $this->assertEquals('to11@example.com', $decoded->getToAddr());
+
+ $this->assertEquals("件名", $decoded->getHeader("Subject"));
+ $this->assertNotEquals(false, strpos($decoded->body->getContent(), "本文"));
+ $this->assertNotEquals(false, stripos($decoded->html->getContent(), ""));
+
+ $images = $decoded->html->getImages();
+ $this->assertEquals(2, count($images));
+ $this->assertEquals("image/png", $images[0]["mimetype"]);
+ $this->assertEquals("image/png", $images[1]["mimetype"]);
+ $this->assertTrue($images[0]["data"] !== $images[1]["data"]);
+
+ $attachments = $decoded->attachments;
+ $this->assertEquals(2, count($attachments));
+ $this->assertEquals("image/png", $attachments[0]->getType());
+ $this->assertEquals("image/png", $attachments[1]->getType());
+ $this->assertTrue($attachments[0]->getContent() !== $attachments[1]->getContent());
+ }
+
+ /**
+ * @test
+ */
+ public function plain_htmlimg_attachment2() // outlook express
+ {
+ $decoded = $this->decode("plain_htmlimg_attachment2");
+
+ $this->assertEquals("multipart/mixed", $decoded->content->getType());
+
+ $this->assertEquals('"from12" ', $decoded->getHeader("From"));
+ $this->assertEquals('from12', $decoded->getFromName());
+ $this->assertEquals('from12@example.com', $decoded->getFromAddr());
+
+ $this->assertEquals('to12@example.com', $decoded->getToAddr());
+
+ $this->assertEquals("件名", $decoded->getHeader("Subject"));
+ $this->assertNotEquals(false, strpos($decoded->body->getContent(), "本文"));
+ $this->assertNotEquals(false, stripos($decoded->html->getContent(), ""));
+
+ $images = $decoded->html->getImages();
+ $this->assertEquals(2, count($images));
+ $this->assertEquals("image/png", $images[0]["mimetype"]);
+ $this->assertEquals("image/png", $images[1]["mimetype"]);
+ $this->assertTrue($images[0]["data"] !== $images[1]["data"]);
+
+ $attachments = $decoded->attachments;
+ $this->assertEquals(2, count($attachments));
+ $this->assertEquals("image/png", $attachments[0]->getType());
+ $this->assertEquals("image/png", $attachments[1]->getType());
+ $this->assertTrue($attachments[0]->getContent() !== $attachments[1]->getContent());
+ }
+
+ /**
+ * @test
+ */
+ public function jpattachment() // thunderbird (RFC2231)
+ {
+ $decoded = $this->decode("jpattachment");
+
+ $attachment = $decoded->attachments[0];
+ $this->assertEquals("日本語日本語日本語日本語日本語日本語.png", $attachment->getName());
+ }
+
+ /**
+ * @test
+ */
+ public function jpattachment2() // outlook express (mime encoding)
+ {
+ $decoded = $this->decode("jpattachment2");
+
+ $attachment = $decoded->attachments[0];
+ $this->assertEquals("日本語日本語日本語日本語日本語日本語.png", $attachment->getName());
+ }
+
+ protected function decode($name)
+ {
+ $decoder = new Sabel_Mail_MimeDecode();
+ return $decoder->decode(file_get_contents(dirname(__FILE__) . DS . "mails" . DS . $name . ".eml"));
+ }
+}
diff --git a/Test/Mail/Tests.php b/Test/Mail/Tests.php
new file mode 100755
index 0000000..acbf45f
--- /dev/null
+++ b/Test/Mail/Tests.php
@@ -0,0 +1,14 @@
+addTest(Test_Mail_MimeDecode::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/Mail/mails/html.eml b/Test/Mail/mails/html.eml
new file mode 100755
index 0000000..7d7178d
--- /dev/null
+++ b/Test/Mail/mails/html.eml
@@ -0,0 +1,20 @@
+Return-Path:
+X-Original-To: to2@example.com
+Delivered-To: to2@example.com
+Message-ID: <484F2B3B.9000304@sabel.jp>
+Date: Wed, 11 Jun 2008 10:32:43 +0900
+From: "from2"
+MIME-Version: 1.0
+To: to2@example.com
+Subject: =?ISO-2022-JP?B?GyRCN29MPhsoQg==?=
+Content-Type: text/html; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+
+
+
+
+
+$BK\J8(B
+
+
diff --git a/Test/Mail/mails/html_attachment.eml b/Test/Mail/mails/html_attachment.eml
new file mode 100755
index 0000000..4b5e941
--- /dev/null
+++ b/Test/Mail/mails/html_attachment.eml
@@ -0,0 +1,108 @@
+Return-Path:
+X-Original-To: to7@example.com
+Delivered-To: to7@example.com
+Message-ID: <484F2DE9.6040208@sabel.jp>
+Date: Wed, 11 Jun 2008 10:44:09 +0900
+From: "from7"
+MIME-Version: 1.0
+To: to7@example.com
+Subject: =?ISO-2022-JP?B?GyRCN29MPhsoQg==?=
+Content-Type: multipart/mixed;
+ boundary="------------090708090103020604030603"
+
+This is a multi-part message in MIME format.
+--------------090708090103020604030603
+Content-Type: text/html; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+
+
+
+
+
+$BK\J8(B
+
+
+
+--------------090708090103020604030603
+Content-Type: image/png;
+ name="comment_rss_32.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline;
+ filename="comment_rss_32.png"
+
+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
+AAAASUVORK5CYII=
+--------------090708090103020604030603
+Content-Type: image/png;
+ name="check_all_32.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline;
+ filename="check_all_32.png"
+
+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
+--------------090708090103020604030603--
diff --git a/Test/Mail/mails/htmlimg.eml b/Test/Mail/mails/htmlimg.eml
new file mode 100755
index 0000000..8a20dba
--- /dev/null
+++ b/Test/Mail/mails/htmlimg.eml
@@ -0,0 +1,114 @@
+Return-Path:
+X-Original-To: to4@example.com
+Delivered-To: to4@example.com
+Message-ID: <484F2C54.3000704@sabel.jp>
+Date: Wed, 11 Jun 2008 10:37:24 +0900
+From: "from4"
+MIME-Version: 1.0
+To: to4@example.com
+Subject: =?ISO-2022-JP?B?GyRCN29MPhsoQg==?=
+Content-Type: multipart/related;
+ boundary="------------020704060607050404030000"
+
+This is a multi-part message in MIME format.
+--------------020704060607050404030000
+Content-Type: text/html; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+
+
+
+
+
+$BK\J8(B
+
+
+
+
+
+--------------020704060607050404030000
+Content-Type: image/png;
+ name="comment_rss_32.png"
+Content-Transfer-Encoding: base64
+Content-ID:
+Content-Disposition: inline;
+ filename="comment_rss_32.png"
+
+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
+AAAASUVORK5CYII=
+--------------020704060607050404030000
+Content-Type: image/png;
+ name="check_all_32.png"
+Content-Transfer-Encoding: base64
+Content-ID:
+Content-Disposition: inline;
+ filename="check_all_32.png"
+
+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
+--------------020704060607050404030000--
diff --git a/Test/Mail/mails/htmlimg_attachment.eml b/Test/Mail/mails/htmlimg_attachment.eml
new file mode 100755
index 0000000..7b3bbb1
--- /dev/null
+++ b/Test/Mail/mails/htmlimg_attachment.eml
@@ -0,0 +1,202 @@
+Return-Path:
+X-Original-To: to8@example.com
+Delivered-To: to8@example.com
+Message-ID: <484F2EA3.6070303@sabel.jp>
+Date: Wed, 11 Jun 2008 10:47:15 +0900
+From: "from8"
+MIME-Version: 1.0
+To: to8@example.com
+Subject: =?ISO-2022-JP?B?GyRCN29MPhsoQg==?=
+Content-Type: multipart/mixed;
+ boundary="------------020908030104060509010004"
+
+This is a multi-part message in MIME format.
+--------------020908030104060509010004
+Content-Type: multipart/related;
+ boundary="------------080902090709030608060706"
+
+
+--------------080902090709030608060706
+Content-Type: text/html; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+
+
+
+
+
+$BK\J8(B
+
+
+
+
+
+--------------080902090709030608060706
+Content-Type: image/png;
+ name="comment_rss_32.png"
+Content-Transfer-Encoding: base64
+Content-ID:
+Content-Disposition: inline;
+ filename="comment_rss_32.png"
+
+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
+AAAASUVORK5CYII=
+--------------080902090709030608060706
+Content-Type: image/png;
+ name="check_all_32.png"
+Content-Transfer-Encoding: base64
+Content-ID:
+Content-Disposition: inline;
+ filename="check_all_32.png"
+
+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
+--------------080902090709030608060706--
+
+--------------020908030104060509010004
+Content-Type: image/png;
+ name="comment_rss_32.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline;
+ filename="comment_rss_32.png"
+
+iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9i
+ZSBJbWFnZVJlYWR5ccllPAAABjdJREFUeNrEV1tsFFUY/mZmZ3d72d5rW2yrLRRoI5iIEpOS
+NKg8eCEYiYmJIZFLovGBxEA0PkiMPigkPphQE0rA+KAQn4yi8crVlFBKQQoFWopt0/tl2+5u
+9zI7M8f/zJy9DN1VKzGc7J+zZ86c83/n+7//nDMSYwz3s7gSf65+VNpC1QGy9f+zzw6yt9e+
+O3PGAYCZ7PhDjzxQ6Vu+CVJuPZiSA0jULSnUq9h1mjHrmSz6JWH/UOIzCPceW//n+fbj1Kpy
+AmCoLGjYDGPiJ7DIV4DsIXOL2ksYvOJ/ok21lHhHFWC5ZQcieSqQu2obWHt75aIQmCaBgAxz
+7rpwYsDzwgg9S2iEamaAhfrA9BBYeABs7grMhTsCBJlk2GA4OxkKi0xA8a22fC0CQCGgiYNU
+czQaTcQsViUmCRCSRbdEE0hcuEXrwJa9BDk6BXPsO5jT56hfp3E0gcyZUDOGhRkxy1eiyA4G
+NBuADUYTTiX6kSXjLCVptp56y6HU7YC0eh/gqaJxYbKYcKTz0DoM9DwjAyZ3qgVgGqlOvf8w
+poOwxOZSffCWNsBbVA/F7UsuThKLcfnqwJr2IXqzFXLgUiosFEqLDUkWDEQtXxk1YMYCdghE
+iV54A/7phIJk63XN9CKndhNKVm1FWePLNhAm8Cgqcpp2I3TjEFz+s9S2nUuJsJA+mB7NwoBB
+DMSCXGeOUlOcfIOMwiKZJMATWLjwMwauH0JZ837kV61LsUE6yW98HbMXhuGN9dqC5kCESJke
+sXxl1IARDVoh+FvTKa5xDblKDCWxi4j8uhn+nq9T2rA0o8D36B5ENBeFdYE0HSaL2KunRWZl
+wIwtOBjwbemw+yh9zLke6GNnEB/+yUpHGJq1IjePcSetmFKzeO0OES5AzSmFa+Wb0K99AMUV
+JxYIuOKmSMw7GHCKMBp2AFDKbGqtrK55Dp41e6EHhxDr+hDaraNc0nZoKMZS1x6Ei1cit2aD
+YAMofLgFg911xFSvBYBvXkyac4gwFQJrUVEH3RkPD18t8loOQ33qG5osx2YuHif2woid3UUD
+446ULWh6FQuhEPXbZkRmHXPLjhBocYuBhOmTlxAcIRu9sghI/orNUJ+hDYi57fd1k8LUj/C1
+NiEHe+8oqmvBbMhlOWccRGQ+swgNnoZ8kjQG/MfW42or2cF16DxQh9GLXzhA+Oo2IrLiLRh6
+aky069O0DYscUPoplRughWn1UQIQDVi+MjNgijrNlpfZVu0ahHF6OwZP7HaAqN74HmaMiuT7
+8dk70CYTjNlZUVDzBALzYTsEnIWMaWg9VB0hgJIPb+NrUJe1WG2uWPeNg/B3tqUmUHOgNmyD
+pqXGxYd/d4arcg0CQQJAIgfXi5FFhIyO1/QQlGz9AUXPHkXpK6cgrdqZfK6df592zJSSCuua
+4Q+lxun+fgcAT+EyhEJ0BlgiN7KIkFJDUnIdDLjK1yRfjOQ1YiYoxBkYR3yyO9nnLa7FQiQ1
+jlPtyBxvITFE+uI0UVJnPguIFlnNc+wD87+8A9/T+xHxD2HwVCtyY+TAY/dNtD2G21MELG63
+c1Qkx4YuHcHQuSMYmUu7jEh2v0wXmywbEW956NZSACMcsJ4FLrZh4Ewbxqmp0m5UXOLcH+pL
+nKmZ3ldEQIsq7r4RFVg+sm3FI1ODkw9WNNCFw+VOCYislh94dD/QZsehTQ3BW7UcanHVkm+j
+fI6Jvn7LVyYGdnX3BD7v7rlceffAiiIT9dUu5Dc1U5hy6YR1o+f0eUzOyYgbS8YxTrYryUri
+u0ByXiYdjS9fVAeeXF9eW1Czgu6mebhx8iQGxszbUR3NO0/Ep5ZCQuoSzJwMpDlWRHZYID7e
+6KpRZdTmllZa1Pf++C36R8zpyQW2Ze9v+jzfGpbg3BQnGFsUAuGQt73CVAsIY8/nexk85TUY
+7erAtZvz0auTbPsnHQZXavm/dM4d83yJCtMTIFwZVk9fJCgW+nOpkrSpwGsiNDqM9tO3zM4x
+tq/1ssEvatVLWDl3yDeHWQHEyAQgQZGeOujpwHHj8b4xGX3f/4GBefYZOT/3Hz7HEtTr4j/L
+JkJZxJSz4BGMSLi3woTzGN9Q7YslneIJvxmyQE6ze3V+N7sJwyIA96v8JcAAbf9pxPL6PpQA
+AAAASUVORK5CYII=
+--------------020908030104060509010004
+Content-Type: image/png;
+ name="check_all_32.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline;
+ filename="check_all_32.png"
+
+iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9i
+ZSBJbWFnZVJlYWR5ccllPAAABvpJREFUeNrEVwlsFFUY/mZmj570YhdaCrSlLQQoJNxVQNSA
+SBAkgAmYgBeIEUgIGEUTFEwwBhETY0hARWwwgkdiYhGEIJcK5aYItbZlW3YLpV1Kt8teszPP
+f449BnYL0Rin+/e9efPmfd///f87hmOM4f+8uAiBi+/lVVKxmazyP8b8nWz1iLVupYQp0spk
+9mVBf19p7vB5GrP0YsCSd//hpOADoTJPrVreuvRtZeu1tF1ULTEQkGWU5hA4Aw/Zdw3s9nmA
+TyGzxkrBqt9biCEZb6aShuD4+0udUgQu4IKC4WzeWxxpjylAkWA+F+RQF1jghgbCiwRKxocJ
+g0pZq2sm6RYhoQzFJVcg1E3OWcH7HIhPu5gCEoPsdZL3LhU8dbaLXoj0ZCpDFvaA+W/QYB1E
+tgWy+wSY2KUrEiEj9CCDVcOQWAICMoXT06R5wTO14Bink+C0nzmLHO1F9+VglLNC/2fAuhsg
+tR2E3HlGG4RX1FCIJFZDwZDlXokIEJSkeyuFYqARAaJeaPHiwGnUMstgyiiF3FWLsGMXqXOb
+GiNETPeGQtKwIhdvCIHyULc43fQfF3evWeRPqfPZFTBXbACfM5pC5ScLakaqMIaoaeMnCQGL
+Az66PhZLjmKbkT8KppRs5JZOQ++hc2HJ7KerES0o/FaYS5dQ4mYi7Nqv5YQgEz+TVo8qkCwJ
+4wiM6B8nGxMREE9C8prQfeIwnIffQXrxVBQ/ug5ptmH6iqb3JTbmkgU0mzyQbhxTXWZxSXq3
+AoYQKOwSGYhxCgmSLoTRJz2EclsQWbf24coXU+A4tF6V+e6wWMtfpIWsN+WEj4xCIgYAJSTJ
+CEhx8e/ZSCkxhDQ+iOIcP8Taj3B5z7PU5ovlhx42a8UqyGGR1LhDBHxU+tQxFCzuIKeaIQQs
+HJO993ItHrK/HbLnKkTnLwi17IfoOhKhrEqTa5HhbduLK7sXYdjCb8glProemTKLYB4wG6H6
+r8AoPzhSUMFIrICMhLOAT7XB1GccUke/jqw5h5A19zgEe2WcGqKqRlrbPjTuX6fPFi6qhqVk
+DvWTIAe9mkkaVpJpGLNklzm/Ejnzj6iEov3DkkoicOYDeFrPES4XJSLQzGkdMBFvW/7E+0Id
+2jnWQxKSPBHreWcRkDlxI9LHvGF4J88aRnP1m4a1o6arBptM9bho68KBHCc25TF0mkgCBSMc
+Nw2lu6T/abnGTbBmIHfQRAycshK2odMNPDInb0SorRaBhuqYQq0/o6u5BlkDx6H6ZjWeOjkT
+T+fb8HC/sXB3nEVNXx/aBhNyCDbq3p50Glb01WxIthcZ1/ehbvsM1H23huQOGUk8toX+W6Lv
+ZZBLHef2oKq5CrOPzcSCfoUYbCvDzZsX4Qv4MJx2dtcQIhDEK2TJl+JoMpJaaTTowGzAd+JD
+1FYtUfu3B9qxoXYDPnMfgLVsjuGdHTd246VfF2FxWRkKcwrR4jwLj9eDIHFXTFZ8CEJQCCRd
+ilOKHkHOjC1qvXPvKgQcR5CXSvXLVbhy9gl87D+Omls74b7jw7m0aXhLf3crbXSfpLbihYrh
+SLFYUN94hsIrRset95AzlwX8UShtNZwJd8+zsnGDYx0LX3PAnD1ArYu3W+DcVKQdqUizTX0L
+ERxpRnlBPi7Vncap1iDmdFD8aaiv7WYsfKgCYQpVk+MyORabc010dJhwHZh0wYxllSKXZDvW
+k5IJMMfVI892UZDtjxcg4O9GzfkaAhJRmkHtXiA3MxWLJ4+Eu7MDjY4GQ6600C49qQ1Y4efQ
+RKsy/Al2Q4utBMG2JvW+ducyjHh+u3Zi3rEEebojN++IFIIrtLd4DADD7FZMHj8Kjc3NaHE5
+Dc+udwIjm4HlJg797CVokFsSEIibBeoDmloHVhRo4ciidovWviDAYeYFD/rn0wxIj4cJ4ujJ
+c/De8RnA228BUyk8y2lN6B1GbDPyJTiUmnrZaeJbEHDWIYP0Hxx3Kme6AuVkn4Y5LHUw5NOW
+bbHG+viDRvDbbuBJIvAqT+DKZLUXqRiMNScMQcPVUydLi8aMh6VPCX0SFFIS2hMuhLOVZLzW
+iLV/nUUe5aZw78kLXvJ6XuogrBk7FDl+D0JuJ0xZdhAGYfFXIwSis2DbNNNkgcNmIjvmQT9x
+fkxj+CFXRmax8QwaIPAJLRxm+XhkycZ3KNdPUwRWv9xHOmogwHHqEEriU04jV1nk1CWup8O+
+ck3HSqEAC6z6CUpsJ3Pge1zA53DBrZ/WlKWnWxFOEUfphvnaOTeeQGRzUkBpyYESXeG+BDQS
+7/L5eE4JqHwNVWjCFtSjPfZRoR4egvrkC2EW5Hs+TrmYhnyccQ8UCzsRHYmlSqKTf9vwG9ru
+Po1rizrZdMgJv47/8ef1VO5fvf+3AAMAoXePQvnneewAAAAASUVORK5CYII=
+--------------020908030104060509010004--
diff --git a/Test/Mail/mails/jpattachment.eml b/Test/Mail/mails/jpattachment.eml
new file mode 100755
index 0000000..a2ec3b6
--- /dev/null
+++ b/Test/Mail/mails/jpattachment.eml
@@ -0,0 +1,61 @@
+Return-Path:
+X-Original-To: to@example.com
+Delivered-To: to@example.com
+Message-ID: <484F31D8.50807@sabel.jp>
+Date: Wed, 11 Jun 2008 11:00:56 +0900
+From: "jpattach1"
+MIME-Version: 1.0
+To: to@example.com
+Subject: =?ISO-2022-JP?B?GyRCN29MPhsoQg==?=
+Content-Type: multipart/mixed;
+ boundary="------------060205050506040602050305"
+
+This is a multi-part message in MIME format.
+--------------060205050506040602050305
+Content-Type: text/plain; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+$BK\J8(B
+
+--------------060205050506040602050305
+Content-Type: image/png;
+ name="=?ISO-2022-JP?B?GyRCRnxLXDhsRnxLXDhsRnxLXDhsRnxLXDhsRnxLXDhsRnxLXDhsGyhCLg==?==?ISO-2022-JP?B?cG5n?="
+Content-Transfer-Encoding: base64
+Content-Disposition: inline;
+ filename*0*=ISO-2022-JP''%1B%24%42%46%7C%4B%5C%38%6C%46%7C%4B%5C%38%6C%46;
+ filename*1*=%7C%4B%5C%38%6C%46%7C%4B%5C%38%6C%46%7C%4B%5C%38%6C%46%7C%4B;
+ filename*2*=%5C%38%6C%1B%28%42%2E%70%6E%67
+
+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
+AAAASUVORK5CYII=
+--------------060205050506040602050305--
diff --git a/Test/Mail/mails/jpattachment2.eml b/Test/Mail/mails/jpattachment2.eml
new file mode 100755
index 0000000..9cb0bff
--- /dev/null
+++ b/Test/Mail/mails/jpattachment2.eml
@@ -0,0 +1,85 @@
+Return-Path:
+X-Original-To: to@example.com
+Delivered-To: to@example.com
+Message-ID: <001801c8cb67$ec49f900$c401000a@YOURC6937191ED>
+From: "jpattach2"
+To: to@example.com
+Subject: =?iso-2022-jp?B?GyRCN29MPhsoQg==?=
+Date: Wed, 11 Jun 2008 11:02:05 +0900
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="----=_NextPart_000_000B_01C8CBB2.95B84D80"
+
+This is a multi-part message in MIME format.
+
+------=_NextPart_000_000B_01C8CBB2.95B84D80
+Content-Type: multipart/alternative;
+ boundary="----=_NextPart_001_000C_01C8CBB2.95B84D80"
+
+
+------=_NextPart_001_000C_01C8CBB2.95B84D80
+Content-Type: text/plain;
+ charset="iso-2022-jp"
+Content-Transfer-Encoding: 7bit
+
+$BK\J8(B
+------=_NextPart_001_000C_01C8CBB2.95B84D80
+Content-Type: text/html;
+ charset="iso-2022-jp"
+Content-Transfer-Encoding: quoted-printable
+
+
+
+
+
+
+
+
+=1B$BK\J8=1B(B
+
+------=_NextPart_001_000C_01C8CBB2.95B84D80--
+
+------=_NextPart_000_000B_01C8CBB2.95B84D80
+Content-Type: image/png;
+ name="=?iso-2022-jp?B?GyRCRnxLXDhsRnxLXDhsRnxLXDhsRnxLXDhsRnxLXDhsRnxLXDhsGyhC?=
+ =?iso-2022-jp?B?LnBuZw==?="
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename="=?iso-2022-jp?B?GyRCRnxLXDhsRnxLXDhsRnxLXDhsRnxLXDhsRnxLXDhsRnxLXDhsGyhC?=
+ =?iso-2022-jp?B?LnBuZw==?="
+
+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
+
+------=_NextPart_000_000B_01C8CBB2.95B84D80--
+
diff --git a/Test/Mail/mails/plain.eml b/Test/Mail/mails/plain.eml
new file mode 100755
index 0000000..28111c1
--- /dev/null
+++ b/Test/Mail/mails/plain.eml
@@ -0,0 +1,13 @@
+Return-Path:
+X-Original-To: to1@example.com
+Delivered-To: to1@example.com
+Message-ID: <484F2A46.1050504@sabel.jp>
+Date: Wed, 11 Jun 2008 10:28:38 +0900
+From: "from1"
+MIME-Version: 1.0
+To: to1@example.com
+Subject: =?ISO-2022-JP?B?GyRCN29MPhsoQg==?=
+Content-Type: text/plain; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+$BK\J8(B
diff --git a/Test/Mail/mails/plain_attachment.eml b/Test/Mail/mails/plain_attachment.eml
new file mode 100755
index 0000000..f07c1fb
--- /dev/null
+++ b/Test/Mail/mails/plain_attachment.eml
@@ -0,0 +1,101 @@
+Return-Path:
+X-Original-To: to5@example.com
+Delivered-To: to5@example.com
+Message-ID: <484F2D49.2030501@sabel.jp>
+Date: Wed, 11 Jun 2008 10:41:29 +0900
+From: "from5"
+MIME-Version: 1.0
+To: to5@example.com
+Subject: =?ISO-2022-JP?B?GyRCN29MPhsoQg==?=
+Content-Type: multipart/mixed;
+ boundary="------------060004050807000209060302"
+
+This is a multi-part message in MIME format.
+--------------060004050807000209060302
+Content-Type: text/plain; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+$BK\J8(B
+
+--------------060004050807000209060302
+Content-Type: image/png;
+ name="comment_rss_32.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline;
+ filename="comment_rss_32.png"
+
+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
+AAAASUVORK5CYII=
+--------------060004050807000209060302
+Content-Type: image/png;
+ name="check_all_32.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline;
+ filename="check_all_32.png"
+
+iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9i
+ZSBJbWFnZVJlYWR5ccllPAAABvpJREFUeNrEVwlsFFUY/mZmj570YhdaCrSlLQQoJNxVQNSA
+SBAkgAmYgBeIEUgIGEUTFEwwBhETY0hARWwwgkdiYhGEIJcK5aYItbZlW3YLpV1Kt8teszPP
+f449BnYL0Rin+/e9efPmfd///f87hmOM4f+8uAiBi+/lVVKxmazyP8b8nWz1iLVupYQp0spk
+9mVBf19p7vB5GrP0YsCSd//hpOADoTJPrVreuvRtZeu1tF1ULTEQkGWU5hA4Aw/Zdw3s9nmA
+TyGzxkrBqt9biCEZb6aShuD4+0udUgQu4IKC4WzeWxxpjylAkWA+F+RQF1jghgbCiwRKxocJ
+g0pZq2sm6RYhoQzFJVcg1E3OWcH7HIhPu5gCEoPsdZL3LhU8dbaLXoj0ZCpDFvaA+W/QYB1E
+tgWy+wSY2KUrEiEj9CCDVcOQWAICMoXT06R5wTO14Bink+C0nzmLHO1F9+VglLNC/2fAuhsg
+tR2E3HlGG4RX1FCIJFZDwZDlXokIEJSkeyuFYqARAaJeaPHiwGnUMstgyiiF3FWLsGMXqXOb
+GiNETPeGQtKwIhdvCIHyULc43fQfF3evWeRPqfPZFTBXbACfM5pC5ScLakaqMIaoaeMnCQGL
+Az66PhZLjmKbkT8KppRs5JZOQ++hc2HJ7KerES0o/FaYS5dQ4mYi7Nqv5YQgEz+TVo8qkCwJ
+4wiM6B8nGxMREE9C8prQfeIwnIffQXrxVBQ/ug5ptmH6iqb3JTbmkgU0mzyQbhxTXWZxSXq3
+AoYQKOwSGYhxCgmSLoTRJz2EclsQWbf24coXU+A4tF6V+e6wWMtfpIWsN+WEj4xCIgYAJSTJ
+CEhx8e/ZSCkxhDQ+iOIcP8Taj3B5z7PU5ovlhx42a8UqyGGR1LhDBHxU+tQxFCzuIKeaIQQs
+HJO993ItHrK/HbLnKkTnLwi17IfoOhKhrEqTa5HhbduLK7sXYdjCb8glProemTKLYB4wG6H6
+r8AoPzhSUMFIrICMhLOAT7XB1GccUke/jqw5h5A19zgEe2WcGqKqRlrbPjTuX6fPFi6qhqVk
+DvWTIAe9mkkaVpJpGLNklzm/Ejnzj6iEov3DkkoicOYDeFrPES4XJSLQzGkdMBFvW/7E+0Id
+2jnWQxKSPBHreWcRkDlxI9LHvGF4J88aRnP1m4a1o6arBptM9bho68KBHCc25TF0mkgCBSMc
+Nw2lu6T/abnGTbBmIHfQRAycshK2odMNPDInb0SorRaBhuqYQq0/o6u5BlkDx6H6ZjWeOjkT
+T+fb8HC/sXB3nEVNXx/aBhNyCDbq3p50Glb01WxIthcZ1/ehbvsM1H23huQOGUk8toX+W6Lv
+ZZBLHef2oKq5CrOPzcSCfoUYbCvDzZsX4Qv4MJx2dtcQIhDEK2TJl+JoMpJaaTTowGzAd+JD
+1FYtUfu3B9qxoXYDPnMfgLVsjuGdHTd246VfF2FxWRkKcwrR4jwLj9eDIHFXTFZ8CEJQCCRd
+ilOKHkHOjC1qvXPvKgQcR5CXSvXLVbhy9gl87D+Omls74b7jw7m0aXhLf3crbXSfpLbihYrh
+SLFYUN94hsIrRset95AzlwX8UShtNZwJd8+zsnGDYx0LX3PAnD1ArYu3W+DcVKQdqUizTX0L
+ERxpRnlBPi7Vncap1iDmdFD8aaiv7WYsfKgCYQpVk+MyORabc010dJhwHZh0wYxllSKXZDvW
+k5IJMMfVI892UZDtjxcg4O9GzfkaAhJRmkHtXiA3MxWLJ4+Eu7MDjY4GQ6600C49qQ1Y4efQ
+RKsy/Al2Q4utBMG2JvW+ducyjHh+u3Zi3rEEebojN++IFIIrtLd4DADD7FZMHj8Kjc3NaHE5
+Dc+udwIjm4HlJg797CVokFsSEIibBeoDmloHVhRo4ciidovWviDAYeYFD/rn0wxIj4cJ4ujJ
+c/De8RnA228BUyk8y2lN6B1GbDPyJTiUmnrZaeJbEHDWIYP0Hxx3Kme6AuVkn4Y5LHUw5NOW
+bbHG+viDRvDbbuBJIvAqT+DKZLUXqRiMNScMQcPVUydLi8aMh6VPCX0SFFIS2hMuhLOVZLzW
+iLV/nUUe5aZw78kLXvJ6XuogrBk7FDl+D0JuJ0xZdhAGYfFXIwSis2DbNNNkgcNmIjvmQT9x
+fkxj+CFXRmax8QwaIPAJLRxm+XhkycZ3KNdPUwRWv9xHOmogwHHqEEriU04jV1nk1CWup8O+
+ck3HSqEAC6z6CUpsJ3Pge1zA53DBrZ/WlKWnWxFOEUfphvnaOTeeQGRzUkBpyYESXeG+BDQS
+7/L5eE4JqHwNVWjCFtSjPfZRoR4egvrkC2EW5Hs+TrmYhnyccQ8UCzsRHYmlSqKTf9vwG9ru
+Po1rizrZdMgJv47/8ef1VO5fvf+3AAMAoXePQvnneewAAAAASUVORK5CYII=
+--------------060004050807000209060302--
diff --git a/Test/Mail/mails/plain_html.eml b/Test/Mail/mails/plain_html.eml
new file mode 100755
index 0000000..4e1c9f6
--- /dev/null
+++ b/Test/Mail/mails/plain_html.eml
@@ -0,0 +1,33 @@
+Return-Path:
+X-Original-To: to3@example.com
+Delivered-To: to3@example.com
+Message-ID: <484F2B24.5000108@sabel.jp>
+Date: Wed, 11 Jun 2008 10:32:20 +0900
+From: "from3"
+MIME-Version: 1.0
+To: to3@example.com
+Subject: =?ISO-2022-JP?B?GyRCN29MPhsoQg==?=
+Content-Type: multipart/alternative;
+ boundary="------------090105030302000205020906"
+
+This is a multi-part message in MIME format.
+--------------090105030302000205020906
+Content-Type: text/plain; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+*$BK\J8(B*
+
+--------------090105030302000205020906
+Content-Type: text/html; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+
+
+
+
+
+$BK\J8(B
+
+
+
+--------------090105030302000205020906--
diff --git a/Test/Mail/mails/plain_html_attachment.eml b/Test/Mail/mails/plain_html_attachment.eml
new file mode 100755
index 0000000..8dc907b
--- /dev/null
+++ b/Test/Mail/mails/plain_html_attachment.eml
@@ -0,0 +1,121 @@
+Return-Path:
+X-Original-To: to6@example.com
+Delivered-To: to6@example.com
+Message-ID: <484F2E2F.4080707@sabel.jp>
+Date: Wed, 11 Jun 2008 10:45:19 +0900
+From: "from6"
+MIME-Version: 1.0
+To: to6@example.com
+Subject: =?ISO-2022-JP?B?GyRCN29MPhsoQg==?=
+Content-Type: multipart/mixed;
+ boundary="------------050700040003030904080909"
+
+This is a multi-part message in MIME format.
+--------------050700040003030904080909
+Content-Type: multipart/alternative;
+ boundary="------------090708010308090509060302"
+
+
+--------------090708010308090509060302
+Content-Type: text/plain; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+*$BK\J8(B*
+
+--------------090708010308090509060302
+Content-Type: text/html; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+
+
+
+
+
+$BK\J8(B
+
+
+
+--------------090708010308090509060302--
+
+--------------050700040003030904080909
+Content-Type: image/png;
+ name="comment_rss_32.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline;
+ filename="comment_rss_32.png"
+
+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
+AAAASUVORK5CYII=
+--------------050700040003030904080909
+Content-Type: image/png;
+ name="check_all_32.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline;
+ filename="check_all_32.png"
+
+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
+--------------050700040003030904080909--
diff --git a/Test/Mail/mails/plain_htmlimg.eml b/Test/Mail/mails/plain_htmlimg.eml
new file mode 100755
index 0000000..50d789c
--- /dev/null
+++ b/Test/Mail/mails/plain_htmlimg.eml
@@ -0,0 +1,129 @@
+Return-Path:
+X-Original-To: to9@example.com
+Delivered-To: to9@example.com
+Message-ID: <484F2D00.4040309@sabel.jp>
+Date: Wed, 11 Jun 2008 10:40:16 +0900
+From: "from9"
+MIME-Version: 1.0
+To: to9@example.com
+Subject: =?ISO-2022-JP?B?GyRCN29MPhsoQg==?=
+Content-Type: multipart/alternative;
+ boundary="------------070605040107080804030704"
+
+This is a multi-part message in MIME format.
+--------------070605040107080804030704
+Content-Type: text/plain; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+$BK\J8(B
+
+
+
+--------------070605040107080804030704
+Content-Type: multipart/related;
+ boundary="------------050006090903040902020905"
+
+
+--------------050006090903040902020905
+Content-Type: text/html; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+
+
+
+
+
+$BK\J8(B
+
+
+
+
+
+--------------050006090903040902020905
+Content-Type: image/png;
+ name="comment_rss_32.png"
+Content-Transfer-Encoding: base64
+Content-ID:
+Content-Disposition: inline;
+ filename="comment_rss_32.png"
+
+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
+AAAASUVORK5CYII=
+--------------050006090903040902020905
+Content-Type: image/png;
+ name="check_all_32.png"
+Content-Transfer-Encoding: base64
+Content-ID:
+Content-Disposition: inline;
+ filename="check_all_32.png"
+
+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
+--------------050006090903040902020905--
+
+--------------070605040107080804030704--
diff --git a/Test/Mail/mails/plain_htmlimg2.eml b/Test/Mail/mails/plain_htmlimg2.eml
new file mode 100755
index 0000000..032fa44
--- /dev/null
+++ b/Test/Mail/mails/plain_htmlimg2.eml
@@ -0,0 +1,132 @@
+Return-Path:
+X-Original-To: to10@example.com
+Delivered-To: to10@example.com
+Message-ID: <001801c8cb65$9646fff0$c401000a@YOURC6937191ED>
+From: "from10"
+To:
+Subject: =?iso-2022-jp?B?GyRCN29MPhsoQg==?=
+Date: Wed, 11 Jun 2008 10:50:55 +0900
+MIME-Version: 1.0
+Content-Type: multipart/related;
+ type="multipart/alternative";
+ boundary="----=_NextPart_000_0013_01C8CBB1.06146930"
+
+This is a multi-part message in MIME format.
+
+------=_NextPart_000_0013_01C8CBB1.06146930
+Content-Type: multipart/alternative;
+ boundary="----=_NextPart_001_0014_01C8CBB1.06146930"
+
+
+------=_NextPart_001_0014_01C8CBB1.06146930
+Content-Type: text/plain;
+ charset="iso-2022-jp"
+Content-Transfer-Encoding: 7bit
+
+$BK\J8(B
+
+
+------=_NextPart_001_0014_01C8CBB1.06146930
+Content-Type: text/html;
+ charset="iso-2022-jp"
+Content-Transfer-Encoding: quoted-printable
+
+
+
+
+
+
+
+
+=1B$BK\J8=1B(B
+
+
+
+------=_NextPart_001_0014_01C8CBB1.06146930--
+
+------=_NextPart_000_0013_01C8CBB1.06146930
+Content-Type: image/png;
+ name="comment_rss_32.png"
+Content-Transfer-Encoding: base64
+Content-ID: <001101c8cb65$962cc130$c401000a@YOURC6937191ED>
+
+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
+
+------=_NextPart_000_0013_01C8CBB1.06146930
+Content-Type: image/png;
+ name="check_all_32.png"
+Content-Transfer-Encoding: base64
+Content-ID: <001201c8cb65$962cc130$c401000a@YOURC6937191ED>
+
+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
+
+------=_NextPart_000_0013_01C8CBB1.06146930--
+
diff --git a/Test/Mail/mails/plain_htmlimg_attachment.eml b/Test/Mail/mails/plain_htmlimg_attachment.eml
new file mode 100755
index 0000000..23a6582
--- /dev/null
+++ b/Test/Mail/mails/plain_htmlimg_attachment.eml
@@ -0,0 +1,217 @@
+Return-Path:
+X-Original-To: to11@example.com
+Delivered-To: to11@example.com
+Message-ID: <484F3128.8090706@sabel.jp>
+Date: Wed, 11 Jun 2008 10:58:00 +0900
+From: "from11"
+MIME-Version: 1.0
+To: to11@example.com
+Subject: =?ISO-2022-JP?B?GyRCN29MPhsoQg==?=
+Content-Type: multipart/mixed;
+ boundary="------------030903050804010805090909"
+
+This is a multi-part message in MIME format.
+--------------030903050804010805090909
+Content-Type: multipart/alternative;
+ boundary="------------090801000708050000010400"
+
+
+--------------090801000708050000010400
+Content-Type: text/plain; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+$BK\J8(B
+
+
+
+--------------090801000708050000010400
+Content-Type: multipart/related;
+ boundary="------------000401030302040404070905"
+
+
+--------------000401030302040404070905
+Content-Type: text/html; charset=ISO-2022-JP
+Content-Transfer-Encoding: 7bit
+
+
+
+
+
+
+$BK\J8(B
+
+
+
+
+
+--------------000401030302040404070905
+Content-Type: image/png;
+ name="comment_rss_32.png"
+Content-Transfer-Encoding: base64
+Content-ID:
+Content-Disposition: inline;
+ filename="comment_rss_32.png"
+
+iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAGXRFWHRTb2Z0d2FyZQBBZG9i
+ZSBJbWFnZVJlYWR5ccllPAAABjdJREFUeNrEV1tsFFUY/mZmZ3d72d5rW2yrLRRoI5iIEpOS
+NKg8eCEYiYmJIZFLovGBxEA0PkiMPigkPphQE0rA+KAQn4yi8crVlFBKQQoFWopt0/tl2+5u
+9zI7M8f/zJy9DN1VKzGc7J+zZ86c83/n+7//nDMSYwz3s7gSf65+VNpC1QGy9f+zzw6yt9e+
+O3PGAYCZ7PhDjzxQ6Vu+CVJuPZiSA0jULSnUq9h1mjHrmSz6JWH/UOIzCPceW//n+fbj1Kpy
+AmCoLGjYDGPiJ7DIV4DsIXOL2ksYvOJ/ok21lHhHFWC5ZQcieSqQu2obWHt75aIQmCaBgAxz
+7rpwYsDzwgg9S2iEamaAhfrA9BBYeABs7grMhTsCBJlk2GA4OxkKi0xA8a22fC0CQCGgiYNU
+czQaTcQsViUmCRCSRbdEE0hcuEXrwJa9BDk6BXPsO5jT56hfp3E0gcyZUDOGhRkxy1eiyA4G
+NBuADUYTTiX6kSXjLCVptp56y6HU7YC0eh/gqaJxYbKYcKTz0DoM9DwjAyZ3qgVgGqlOvf8w
+poOwxOZSffCWNsBbVA/F7UsuThKLcfnqwJr2IXqzFXLgUiosFEqLDUkWDEQtXxk1YMYCdghE
+iV54A/7phIJk63XN9CKndhNKVm1FWePLNhAm8Cgqcpp2I3TjEFz+s9S2nUuJsJA+mB7NwoBB
+DMSCXGeOUlOcfIOMwiKZJMATWLjwMwauH0JZ837kV61LsUE6yW98HbMXhuGN9dqC5kCESJke
+sXxl1IARDVoh+FvTKa5xDblKDCWxi4j8uhn+nq9T2rA0o8D36B5ENBeFdYE0HSaL2KunRWZl
+wIwtOBjwbemw+yh9zLke6GNnEB/+yUpHGJq1IjePcSetmFKzeO0OES5AzSmFa+Wb0K99AMUV
+JxYIuOKmSMw7GHCKMBp2AFDKbGqtrK55Dp41e6EHhxDr+hDaraNc0nZoKMZS1x6Ei1cit2aD
+YAMofLgFg911xFSvBYBvXkyac4gwFQJrUVEH3RkPD18t8loOQ33qG5osx2YuHif2woid3UUD
+446ULWh6FQuhEPXbZkRmHXPLjhBocYuBhOmTlxAcIRu9sghI/orNUJ+hDYi57fd1k8LUj/C1
+NiEHe+8oqmvBbMhlOWccRGQ+swgNnoZ8kjQG/MfW42or2cF16DxQh9GLXzhA+Oo2IrLiLRh6
+aky069O0DYscUPoplRughWn1UQIQDVi+MjNgijrNlpfZVu0ahHF6OwZP7HaAqN74HmaMiuT7
+8dk70CYTjNlZUVDzBALzYTsEnIWMaWg9VB0hgJIPb+NrUJe1WG2uWPeNg/B3tqUmUHOgNmyD
+pqXGxYd/d4arcg0CQQJAIgfXi5FFhIyO1/QQlGz9AUXPHkXpK6cgrdqZfK6df592zJSSCuua
+4Q+lxun+fgcAT+EyhEJ0BlgiN7KIkFJDUnIdDLjK1yRfjOQ1YiYoxBkYR3yyO9nnLa7FQiQ1
+jlPtyBxvITFE+uI0UVJnPguIFlnNc+wD87+8A9/T+xHxD2HwVCtyY+TAY/dNtD2G21MELG63
+c1Qkx4YuHcHQuSMYmUu7jEh2v0wXmywbEW956NZSACMcsJ4FLrZh4Ewbxqmp0m5UXOLcH+pL
+nKmZ3ldEQIsq7r4RFVg+sm3FI1ODkw9WNNCFw+VOCYislh94dD/QZsehTQ3BW7UcanHVkm+j
+fI6Jvn7LVyYGdnX3BD7v7rlceffAiiIT9dUu5Dc1U5hy6YR1o+f0eUzOyYgbS8YxTrYryUri
+u0ByXiYdjS9fVAeeXF9eW1Czgu6mebhx8iQGxszbUR3NO0/Ep5ZCQuoSzJwMpDlWRHZYID7e
+6KpRZdTmllZa1Pf++C36R8zpyQW2Ze9v+jzfGpbg3BQnGFsUAuGQt73CVAsIY8/nexk85TUY
+7erAtZvz0auTbPsnHQZXavm/dM4d83yJCtMTIFwZVk9fJCgW+nOpkrSpwGsiNDqM9tO3zM4x
+tq/1ssEvatVLWDl3yDeHWQHEyAQgQZGeOujpwHHj8b4xGX3f/4GBefYZOT/3Hz7HEtTr4j/L
+JkJZxJSz4BGMSLi3woTzGN9Q7YslneIJvxmyQE6ze3V+N7sJwyIA96v8JcAAbf9pxPL6PpQA
+AAAASUVORK5CYII=
+--------------000401030302040404070905
+Content-Type: image/png;
+ name="check_all_32.png"
+Content-Transfer-Encoding: base64
+Content-ID:
+Content-Disposition: inline;
+ filename="check_all_32.png"
+
+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
+--------------000401030302040404070905--
+
+--------------090801000708050000010400--
+
+--------------030903050804010805090909
+Content-Type: image/png;
+ name="comment_rss_32.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline;
+ filename="comment_rss_32.png"
+
+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
+AAAASUVORK5CYII=
+--------------030903050804010805090909
+Content-Type: image/png;
+ name="check_all_32.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: inline;
+ filename="check_all_32.png"
+
+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
+--------------030903050804010805090909--
diff --git a/Test/Mail/mails/plain_htmlimg_attachment2.eml b/Test/Mail/mails/plain_htmlimg_attachment2.eml
new file mode 100755
index 0000000..fc302cd
--- /dev/null
+++ b/Test/Mail/mails/plain_htmlimg_attachment2.eml
@@ -0,0 +1,218 @@
+Return-Path:
+X-Original-To: to12@example.com
+Delivered-To: to12@example.com
+Message-ID: <002401c8cb65$dfa33650$c401000a@YOURC6937191ED>
+From: "from12"
+To:
+Subject: =?iso-2022-jp?B?GyRCN29MPhsoQg==?=
+Date: Wed, 11 Jun 2008 10:52:55 +0900
+MIME-Version: 1.0
+Content-Type: multipart/mixed;
+ boundary="----=_NextPart_000_001E_01C8CBB1.4DB07900"
+
+This is a multi-part message in MIME format.
+
+------=_NextPart_000_001E_01C8CBB1.4DB07900
+Content-Type: multipart/related;
+ type="multipart/alternative";
+ boundary="----=_NextPart_001_001F_01C8CBB1.4DB07900"
+
+
+------=_NextPart_001_001F_01C8CBB1.4DB07900
+Content-Type: multipart/alternative;
+ boundary="----=_NextPart_002_0020_01C8CBB1.4DB07900"
+
+
+------=_NextPart_002_0020_01C8CBB1.4DB07900
+Content-Type: text/plain;
+ charset="iso-2022-jp"
+Content-Transfer-Encoding: 7bit
+
+$BK\J8(B
+
+
+------=_NextPart_002_0020_01C8CBB1.4DB07900
+Content-Type: text/html;
+ charset="iso-2022-jp"
+Content-Transfer-Encoding: quoted-printable
+
+
+
+
+
+
+
+
+=1B$BK\J8=1B(B
+
+
+
+------=_NextPart_002_0020_01C8CBB1.4DB07900--
+
+------=_NextPart_001_001F_01C8CBB1.4DB07900
+Content-Type: image/png;
+ name="comment_rss_32.png"
+Content-Transfer-Encoding: base64
+Content-ID: <001c01c8cb65$ddc66000$c401000a@YOURC6937191ED>
+
+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
+
+------=_NextPart_001_001F_01C8CBB1.4DB07900
+Content-Type: image/png;
+ name="check_all_32.png"
+Content-Transfer-Encoding: base64
+Content-ID: <001d01c8cb65$ddc8d100$c401000a@YOURC6937191ED>
+
+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
+
+------=_NextPart_001_001F_01C8CBB1.4DB07900--
+
+------=_NextPart_000_001E_01C8CBB1.4DB07900
+Content-Type: image/png;
+ name="comment_rss_32.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename="comment_rss_32.png"
+
+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
+
+------=_NextPart_000_001E_01C8CBB1.4DB07900
+Content-Type: image/png;
+ name="check_all_32.png"
+Content-Transfer-Encoding: base64
+Content-Disposition: attachment;
+ filename="check_all_32.png"
+
+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
+
+------=_NextPart_000_001E_01C8CBB1.4DB07900--
+
diff --git a/Test/Map/Destination.php b/Test/Map/Destination.php
new file mode 100755
index 0000000..16a6322
--- /dev/null
+++ b/Test/Map/Destination.php
@@ -0,0 +1,92 @@
+
+ */
+class Test_Map_Destination extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Map_Destination");
+ }
+
+ public function testDestination()
+ {
+ $destination = $this->createDestination();
+ $this->assertEquals("module", $destination->getModule());
+ $this->assertEquals("controller", $destination->getController());
+ $this->assertEquals("action", $destination->getAction());
+ }
+
+ public function testModule()
+ {
+ $destination = $this->createDestination();
+ $this->assertTrue($destination->hasModule());
+ $destination->setModule("admin");
+ $this->assertEquals("admin", $destination->getModule());
+ }
+
+ public function testController()
+ {
+ $destination = $this->createDestination();
+ $this->assertTrue($destination->hasController());
+ $destination->setController("main");
+ $this->assertEquals("main", $destination->getController());
+ }
+
+ public function testAction()
+ {
+ $destination = $this->createDestination();
+ $this->assertTrue($destination->hasAction());
+ $destination->setAction("index");
+ $this->assertEquals("index", $destination->getAction());
+ }
+
+ public function testToArray()
+ {
+ list ($m, $c, $a) = $this->createDestination()->toArray();
+ $this->assertEquals("module", $m);
+ $this->assertEquals("controller", $c);
+ $this->assertEquals("action", $a);
+ }
+
+ public function testInvalidModuleSet()
+ {
+ try {
+ $this->createDestination()->setModule(1);
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testInvalidControllerSet()
+ {
+ try {
+ $this->createDestination()->setController(false);
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testInvalidActionSet()
+ {
+ try {
+ $this->createDestination()->setAction(new stdClass());
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ private function createDestination()
+ {
+ $param = array("module" => "module", "controller" => "controller", "action" => "action");
+ return new Sabel_Map_Destination($param);
+ }
+}
diff --git a/Test/Map/Match.php b/Test/Map/Match.php
new file mode 100755
index 0000000..fc8be9f
--- /dev/null
+++ b/Test/Map/Match.php
@@ -0,0 +1,369 @@
+
+ */
+class Test_Map_Match extends SabelTestCase
+{
+ private $config = null;
+ private $request = null;
+
+ public static function suite()
+ {
+ return self::createSuite("Test_Map_Match");
+ }
+
+ public function setUp()
+ {
+ $this->config = new ConfigMap();
+ }
+
+ public function tearDown()
+ {
+ $this->config->clearRoutes();
+ }
+
+ public function testSimple()
+ {
+ $this->route("default")
+ ->uri(":controller/:action")
+ ->module("index");
+
+ $c = $this->routing("test/test");
+ $this->assertEquals("default", $c->getName());
+ }
+
+ public function testNoMatch()
+ {
+ $this->route("default")
+ ->uri(":controller/:action")
+ ->module("index");
+
+ try {
+ $c = $this->routing("test");
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testFailMismatchUriAndElement()
+ {
+ $this->route("default")
+ ->uri(":controller/:action")
+ ->module("index");
+
+ try {
+ $this->routing("test/test/test");
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testFailWithDefault()
+ {
+ $this->route("default")
+ ->uri(":controller/:action")
+ ->module("index");
+
+ try {
+ $this->routing("test");
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testMatchWithDefault()
+ {
+ $this->route("default")
+ ->uri(":controller/:action")
+ ->module("index")
+ ->defaults(array(":controller" => "index", ":action" => "index"));
+
+ $candidate = $this->routing("test");
+ $destination = $candidate->getDestination();
+
+ $this->assertEquals("default", $candidate->getName());
+ $this->assertEquals("index", $destination->getAction());
+ }
+
+ public function testMatchWithDefaultPriority()
+ {
+ $this->route("default")
+ ->uri(":controller/:action")
+ ->module("index")
+ ->defaults(array(":action" => "index"));
+
+ $candidate = $this->routing("test/test");
+ $destination = $candidate->getDestination();
+
+ $this->assertEquals("default", $candidate->getName());
+ $this->assertEquals("test", $destination->getAction());
+ }
+
+ public function testMatchWithParameter()
+ {
+ $this->route("default")
+ ->uri(":controller/:action/:param")
+ ->module("index")
+ ->requirements(array(":param" => "[0-9]+"));
+
+ $candidate = $this->routing("test/test/1000");
+
+ $this->assertEquals("default", $candidate->getName());
+ $this->assertEquals("1000", $this->request->fetchParameterValue("param"));
+ }
+
+ public function testMismatchWithParameter()
+ {
+ $this->route("default")
+ ->uri(":controller/:action/:param")
+ ->module("index")
+ ->requirements(array(":param" => "[0-9]+"));
+
+ try {
+ $candidate = $this->routing("test/test/test");
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testMismatchWithMultipleParameter()
+ {
+ $this->route("default")
+ ->uri(":controller/:action/:param/:param2")
+ ->module("index")
+ ->requirements(array(":param" => "[0-9]+", ":param2" => "[a-z]+"));
+
+ try {
+ $candidate = $this->routing("test/test/1000/1000");
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testMatchhWithMultipleParameterWithDefaultsWithRequirements()
+ {
+ $this->route("default")
+ ->uri(":controller/:action/:param/:param2")
+ ->module("index")
+ ->requirements(array(":param" => "0-9]+", ":param2" => "[a-z]+"))
+ ->defaults(array(":param" => "100", ":param2" => "abc"));
+
+ $candidate = $this->routing("test/test");
+ $this->assertEquals("100" ,$this->request->fetchParameterValue("param"));
+ $this->assertEquals("abc" ,$this->request->fetchParameterValue("param2"));
+ }
+
+ public function testMisMatchhWithMultipleParameterWithRequirements()
+ {
+ $this->route("default")
+ ->uri(":controller/:action/:param/:param2")
+ ->module("index")
+ ->requirements(array(":param" => "[0-9]{1}", ":param2" => "[a-z]+"));
+
+ try {
+ $candidate = $this->routing("test/test/100/abc");
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail("matched");
+ }
+
+ public function testMultipleRoutePriority()
+ {
+ $this->route("article")
+ ->uri(":controller/:action/:year/:month/:day")
+ ->module("index")
+ ->requirements(array(":year" => "[1-3][0-9]{3}",
+ ":month" => "[0-2][0-9]",
+ ":day" => "[0-3][0-9]"));
+
+ $this->route("default")
+ ->uri(":controller/:action")
+ ->module("index");
+
+ $candidate = $this->routing("blog/article/2008/01/20");
+ $this->assertEquals("article", $candidate->getName());
+
+ $candidate = $this->routing("blog/article");
+ $this->assertEquals("default", $candidate->getName());
+
+ try {
+ $candidate = $this->routing("blog/article/9999/99/99");
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testMultipleRoutePriorityWithDefault()
+ {
+ $this->route("article")
+ ->uri(":controller/:action/:year/:month/:day")
+ ->module("index")
+ ->requirements(array(":year" => "[1-3][0-9]{3}",
+ ":month" => "[0-2][0-9]",
+ ":day" => "[0-3][0-9]"))
+ ->defaults(array(":day" => "01"));
+
+ $this->route("default")
+ ->uri(":controller/:action")
+ ->module("index");
+
+ $candidate = $this->routing("blog/article/2008/01");
+
+ $this->assertEquals("article", $candidate->getName());
+ $this->assertEquals("2008", $this->request->fetchParameterValue("year"));
+ $this->assertEquals("01", $this->request->fetchParameterValue("month"));
+ $this->assertEquals("01", $this->request->fetchParameterValue("day"));
+
+ $candidate = $this->routing("blog/article");
+ $this->assertEquals("default", $candidate->getName());
+
+ try {
+ $candidate = $this->routing("blog/article/9999/99/99");
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testMultipleRoutePriorityWithAllDefault()
+ {
+ $this->route("article")
+ ->uri(":controller/:action/:year/:month/:day")
+ ->module("index")
+ ->requirements(array(":year" => "[1-3][0-9]{3}",
+ ":month" => "[0-2][0-9]",
+ ":day" => "[0-3][0-9]"))
+ ->defaults(array(":year" => "2008", ":month" => "01", ":day" => null));
+
+ $this->route("default")
+ ->uri(":controller/:action")
+ ->module("index")
+ ->defaults(array(":action" => "test"));
+
+ $candidate = $this->routing("blog/article");
+
+ $this->assertEquals("article", $candidate->getName());
+ $this->assertEquals("2008", $this->request->fetchParameterValue("year"));
+ $this->assertEquals("01", $this->request->fetchParameterValue("month"));
+ $this->assertEquals(null, $this->request->fetchParameterValue("day"));
+
+ $candidate = $this->routing("test");
+ $this->assertEquals("default", $candidate->getName());
+
+ try {
+ $candidate = $this->routing("blog/article/9999/99/99");
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testWithConstant()
+ {
+ $this->route("admin")
+ ->uri("admin/:controller/:action")
+ ->module("admin");
+
+ $this->route("manage")
+ ->uri("manage/:controller/:action")
+ ->module("manage");
+
+ $candidate = $this->routing("admin/test/test");
+ $this->assertEquals("admin", $candidate->getName());
+
+ $candidate = $this->routing("manage/test/test");
+ $this->assertEquals("manage", $candidate->getName());
+ }
+
+ public function testWithConstantWithDefaults()
+ {
+ $this->route("admin")
+ ->uri("admin/:controller/:action/:param")
+ ->module("admin")
+ ->defaults(array(":param" => "param"));
+
+ $this->route("manage")
+ ->uri("manage/:controller/:action")
+ ->module("manage");
+
+ $candidate = $this->routing("admin/test/test");
+ $destination = $candidate->getDestination();
+ $this->assertEquals("admin", $candidate->getName());
+ $this->assertEquals("test", $destination->getController());
+ $this->assertEquals("test", $destination->getAction());
+ $this->assertEquals("param", $this->request->fetchParameterValue("param"));
+
+ $candidate = $this->routing("manage/test/test");
+ $this->assertEquals("manage", $candidate->getName());
+ }
+
+ public function testMatchAll()
+ {
+ $this->route("default")
+ ->uri(":controller/:action")
+ ->module("admin");
+
+ $this->route("matchall")
+ ->uri("*")
+ ->module("module")
+ ->controller("controller")
+ ->action("action");
+
+ $candidate = $this->routing("hoge/fuga/foo/bar/baz");
+ $this->assertEquals("matchall", $candidate->getName());
+
+ $destination = $candidate->getDestination();
+ $this->assertEquals("module", $destination->getModule());
+ $this->assertEquals("controller", $destination->getController());
+ $this->assertEquals("action", $destination->getAction());
+ }
+
+ protected function route($name)
+ {
+ return $this->config->route($name);
+ }
+
+ protected function request($uri)
+ {
+ return $request = new Sabel_Request_Object($uri);
+ }
+
+ protected function routing($uri)
+ {
+ $this->config->configure();
+
+ $this->request = $request = $this->request($uri);
+ if (!$candidate = $this->config->getValidCandidate($request->getUri())) {
+ throw new Sabel_Exception_Runtime("map not match.");
+ }
+
+ $request->setParameterValues($candidate->getUriParameters());
+
+ return $candidate;
+ }
+}
+
+class ConfigMap extends Sabel_Map_Configurator
+{
+ public function configure() {}
+}
diff --git a/Test/Map/Tests.php b/Test/Map/Tests.php
new file mode 100755
index 0000000..7e20847
--- /dev/null
+++ b/Test/Map/Tests.php
@@ -0,0 +1,16 @@
+addTest(Test_Map_Match::suite());
+ $suite->addTest(Test_Map_Destination::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/Object.php b/Test/Object.php
new file mode 100755
index 0000000..52018d1
--- /dev/null
+++ b/Test/Object.php
@@ -0,0 +1,63 @@
+
+ */
+class Test_Object extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Object");
+ }
+
+ public function testHasMethod()
+ {
+ $obj = new TestObject();
+ $this->assertTrue($obj->hasMethod("hoge"));
+ $this->assertFalse($obj->hasMethod("fuga"));
+ }
+
+ public function testGetName()
+ {
+ $obj = new TestObject();
+ $this->assertEquals("TestObject", $obj->getName());
+ $this->assertNotEquals("_TestObject", $obj->getName());
+ }
+
+ public function testHashCode()
+ {
+ $obj = new TestObject();
+ $hc = "ca3a664fbcf20ac679384e974cf3d052ffd64695";
+ $this->assertEquals($hc, $obj->hashCode());
+ $this->assertEquals($hc, $obj->__toString());
+ }
+
+ public function testEquals()
+ {
+ $obj1 = new TestObject();
+ $obj2 = new TestObject();
+
+ $this->assertTrue($obj1->equals($obj2));
+ $obj1->attr = 20;
+ $this->assertFalse($obj1->equals($obj2));
+ }
+
+ public function testReflection()
+ {
+ $obj = new TestObject();
+ $reflection = $obj->getReflection();
+ $this->assertTrue($reflection instanceof ReflectionClass);
+ $this->assertTrue($reflection instanceof Sabel_Reflection_Class);
+ $this->assertEquals("TestObject", $reflection->getName());
+ $this->assertTrue($reflection->hasMethod("hoge"));
+ }
+}
+
+class TestObject extends Sabel_Object
+{
+ public $attr = 10;
+ public function hoge() {}
+}
diff --git a/Test/Preference.php b/Test/Preference.php
new file mode 100755
index 0000000..2efe8e1
--- /dev/null
+++ b/Test/Preference.php
@@ -0,0 +1,29 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Test_Preference extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Preference");
+ }
+
+ /**
+ * set up
+ *
+ * @access public
+ * @return void
+ */
+ public function setUp()
+ {
+ }
+}
\ No newline at end of file
diff --git a/Test/Preference/Base.php b/Test/Preference/Base.php
new file mode 100755
index 0000000..d0b25fc
--- /dev/null
+++ b/Test/Preference/Base.php
@@ -0,0 +1,172 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Test_Preference_Base extends SabelTestCase
+{
+ protected $pref = null;
+
+ public function testGetInt()
+ {
+ $this->assertEquals(1, $this->pref->getInt("test", 1));
+
+ $this->assertEquals(2, $this->pref->getInt("test", 2));
+
+ $this->assertEquals(2, $this->pref->getInt("test"));
+ }
+
+ public function testGetIntWithString()
+ {
+ $this->assertEquals(1, $this->pref->getInt("test", "1"));
+
+ $this->pref->setInt("test2", "2");
+ $this->assertEquals(2, $this->pref->getInt("test2"));
+ }
+
+ public function testBoolean()
+ {
+ $this->pref->setBoolean("bool1", 1);
+ $this->assertTrue($this->pref->getBoolean("bool1"));
+
+ $this->pref->setBoolean("bool2", 0);
+ $this->assertFalse($this->pref->getBoolean("bool2"));
+
+ $this->assertTrue($this->pref->getBoolean("bool3", true));
+ $this->assertTrue($this->pref->getBoolean("bool4", "true"));
+ $this->assertTrue($this->pref->getBoolean("bool5", "t"));
+ $this->assertTrue($this->pref->getBoolean("bool6", 1.0));
+ $this->assertTrue($this->pref->getBoolean("bool7", "false"));
+ $this->assertTrue($this->pref->getBoolean("bool8", "f"));
+
+ $this->assertFalse($this->pref->getBoolean("bool9", false));
+ $this->assertFalse($this->pref->getBoolean("bool10", 0.0));
+ }
+
+ public function testFloat()
+ {
+ $this->pref->setFloat("float", 1.0);
+ $this->assertEquals(1.0, $this->pref->getFloat("float"));
+
+ $this->assertEquals(1.1, $this->pref->getFloat("float2", 1.1));
+ $this->assertEquals(1.1, $this->pref->getFloat("float2"));
+ }
+
+ public function testGetAll()
+ {
+ $this->pref->setInt("test", 1);
+ $this->pref->setString("test1", "str");
+ $this->pref->setBoolean("test2", false);
+ $this->pref->setFloat("test3", 1.2);
+
+ $result = $this->pref->getAll();
+
+ $this->assertTrue(is_int($result["test"]));
+ $this->assertTrue(is_string($result["test1"]));
+ $this->assertTrue(is_bool($result["test2"]));
+ $this->assertTrue(is_float($result["test3"]));
+
+ $this->assertEquals(1, $result["test"]);
+ $this->assertEquals("str", $result["test1"]);
+ $this->assertEquals(false, $result["test2"]);
+ $this->assertEquals(1.2, $result["test3"]);
+ }
+
+ public function testArrayType()
+ {
+ $obj = new StdClass();
+ $obj->a = "a";
+ $obj->b = 0;
+ $obj->c = 1.1;
+ $obj->d = false;
+ $obj->obj = new StdClass();
+ $obj->array = array(0, 1, 2, array(1, 2, 3));
+
+ $assertValue = array("test", 0, 1, 1.0, false, true,
+ array("key" => "value", 0 => 1), array(0, 1, 2),
+ new StdClass(), $obj);
+
+ $this->pref->setArray("test", $assertValue);
+ $this->assertEquals($assertValue, $this->pref->getArray("test"));
+ }
+
+ public function testObjectType()
+ {
+ $obj = new TestForObjectType();
+
+ $this->pref->setObject("test", $obj);
+
+ $this->assertEquals($obj, $this->pref->getObject("test"));
+ }
+
+ public function testObjectTypeWithDefault()
+ {
+ $obj = new TestForObjectType();
+
+ $obj2 = $this->pref->getObject("test", $obj);
+
+ $this->assertEquals($obj, $this->pref->getObject("test"));
+ $this->assertEquals($obj, $obj2);
+ }
+
+ public function testDelete()
+ {
+ $this->pref->setInt("test", 1);
+
+ $this->pref->delete("test");
+
+ try {
+ $this->pref->getInt("test");
+ } catch (Sabel_Exception_Runtime $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testContains()
+ {
+ $this->pref->setInt("test", 1);
+
+ $this->assertTrue($this->pref->contains("test"));
+ $this->assertFalse($this->pref->contains("test1"));
+ }
+
+ public function testGetUndefinedKeyWithNotDefault()
+ {
+ try {
+ $this->pref->getInt("undefined_key");
+ } catch (Sabel_Exception_Runtime $e) {
+ // exception occured this test pass ok
+ return;
+ }
+
+ $this->fail();
+ }
+}
+
+class TestForObjectType
+{
+ private $name = "string";
+ private $age = 11;
+ private $height = 192.1;
+
+ private $composite;
+
+ public function __construct()
+ {
+ $this->composite = new StdClass();
+ }
+
+ public function getName()
+ {
+ return $this->name;
+ }
+}
diff --git a/Test/Preference/Database.php b/Test/Preference/Database.php
new file mode 100755
index 0000000..7ae457a
--- /dev/null
+++ b/Test/Preference/Database.php
@@ -0,0 +1,129 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Test_Preference_Database extends Test_Preference_Base
+{
+ public function __construct()
+ {
+ Sabel_Db_Config::initialize(new Config_Database());
+ }
+
+ public static function suite()
+ {
+ return self::createSuite("Test_Preference_Database");
+ }
+
+ /**
+ * set up
+ *
+ * @access public
+ * @return void
+ */
+ public function setUp()
+ {
+ $this->pref = Sabel_Preference::create(new __Preference_Database_Config());
+ }
+
+ public function tearDown()
+ {
+ MODEL("SblPreference")->prepareStatement(Sabel_Db_Statement::DELETE)->execute();
+ }
+
+ public function testUseNamespace()
+ {
+ $pref = Sabel_Preference::create(new __Preference_Database_Config_Namespace());
+ $pref->setInt("test", 1);
+
+ $this->assertEquals(1, MODEL("SblPreference")->getCount("namespace", "myapp"));
+ }
+
+ public function testUseCustomModelClass()
+ {
+ $pref = Sabel_Preference::create(new __Preference_Database_Config_CustomModel());
+ $pref->setInt("test", 1);
+ }
+}
+
+class __Preference_Database_Config implements Sabel_Config
+{
+ public function configure()
+ {
+ return array("backend" => "Sabel_Preference_Database");
+ }
+}
+
+class __Preference_Database_Config_Namespace implements Sabel_Config
+{
+ public function configure()
+ {
+ return array("backend" => "Sabel_Preference_Database",
+ "namespace" => "myapp");
+ }
+}
+
+class __Preference_Database_Config_CustomModel implements Sabel_Config
+{
+ public function configure()
+ {
+ return array("backend" => "Sabel_Preference_Database",
+ "model" => "__Preference_CustomModel");
+ }
+}
+
+class __Preference_CustomModel extends Sabel_Db_Model
+{
+ protected $tableName = "sbl_preference";
+}
+
+class Config_Database implements Sabel_Config
+{
+ public function configure()
+ {
+ switch (ENVIRONMENT) {
+ case PRODUCTION:
+ $params = array("default" => array(
+ "package" => "sabel.db.*",
+ "host" => "localhost",
+ "database" => "dbname",
+ "user" => "user",
+ "password" => "password")
+ );
+ break;
+
+ case TEST:
+ $params = array("default" => array(
+ "package" => "sabel.db.mysql",
+ "host" => "localhost",
+ "database" => "sabel",
+ "user" => "root",
+ "password" => "")
+ );
+ break;
+
+ case DEVELOPMENT:
+ $params = array("default" => array(
+ "package" => "sabel.db.mysql",
+ "host" => "localhost",
+ "database" => "sabel",
+ "user" => "root",
+ "password" => "")
+ );
+ break;
+ }
+
+ return $params;
+ }
+}
\ No newline at end of file
diff --git a/Test/Preference/Memcache.php b/Test/Preference/Memcache.php
new file mode 100755
index 0000000..b24bc6f
--- /dev/null
+++ b/Test/Preference/Memcache.php
@@ -0,0 +1,45 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Test_Preference_Memcache extends Test_Preference_Base
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Preference_Memcache");
+ }
+
+ /**
+ * set up
+ *
+ * @access public
+ * @return void
+ */
+ public function setUp()
+ {
+ $this->pref = Sabel_Preference::create(new __Preference_Memcache_Config());
+ }
+
+ public function tearDown()
+ {
+ $memcache = new Memcache();
+ $memcache->addServer("localhost", 11211);
+ $memcache->flush();
+ }
+}
+
+class __Preference_Memcache_Config implements Sabel_Config
+{
+ public function configure()
+ {
+ return array("backend" => "Sabel_Preference_Memcache");
+ }
+}
diff --git a/Test/Preference/Tests.php b/Test/Preference/Tests.php
new file mode 100755
index 0000000..4002b22
--- /dev/null
+++ b/Test/Preference/Tests.php
@@ -0,0 +1,31 @@
+
+ */
+class Test_Preference_Tests
+{
+ public static function main()
+ {
+ PHPUnit_TextUI_TestRunner::run(self::suite());
+ }
+
+ public static function suite()
+ {
+ $suite = new PHPUnit_Framework_TestSuite();
+
+ $suite->addTest(Test_Preference_Xml::suite());
+ $suite->addTest(Test_Preference_Database::suite());
+ $suite->addTest(Test_Preference_Memcache::suite());
+
+ return $suite;
+ }
+}
\ No newline at end of file
diff --git a/Test/Preference/Xml.php b/Test/Preference/Xml.php
new file mode 100755
index 0000000..f9b7ba2
--- /dev/null
+++ b/Test/Preference/Xml.php
@@ -0,0 +1,89 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+
+class Test_Preference_Xml extends Test_Preference_Base
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Preference_Xml");
+ }
+
+ /**
+ * set up
+ *
+ * @access public
+ * @return void
+ */
+ public function setUp()
+ {
+ $files = array("/tmp/data/preferences/default.xml",
+ "/tmp/data/preferences/test.xml");
+
+ foreach ($files as $file) {
+ if (is_readable($file)) {
+ unlink($file);
+ }
+ }
+
+ $this->pref = Sabel_Preference::create();
+ }
+
+ public function testNotConfigurationFileSpecified()
+ {
+ $preference = Sabel_Preference::create();
+ $this->assertTrue(is_readable("/tmp/data/preferences/default.xml"));
+ }
+
+ public function testNotSpecifyFile()
+ {
+ $preference = Sabel_Preference::create(new __PrefConfigNotSpecifyFile());
+ $this->assertTrue(is_readable("/tmp/data/preferences/default.xml"));
+ }
+
+ public function testConfig()
+ {
+ $preference = Sabel_Preference::create(new __PrefConfig());
+ $this->assertTrue(is_readable("/tmp/data/preferences/specified.xml"));
+ }
+
+ public function testConfigNoDot()
+ {
+ $preference = Sabel_Preference::create(new __PrefConfigNoDot());
+ $this->assertTrue(is_readable("/tmp/data/preferences/test.xml"));
+ }
+}
+
+class __PrefConfigNotSpecifyFile implements Sabel_Config
+{
+ public function configure()
+ {
+ return array("backend" => "Sabel_Preference_Xml");
+ }
+}
+
+class __PrefConfig implements Sabel_Config
+{
+ public function configure()
+ {
+ return array("backend" => "Sabel_Preference_Xml", "file" => "specified.xml");
+ }
+}
+
+class __PrefConfigNoDot implements Sabel_Config
+{
+ public function configure()
+ {
+ return array("backend" => "Sabel_Preference_Xml",
+ "file" => "test");
+ }
+}
diff --git a/Test/Processor/Abstract.php b/Test/Processor/Abstract.php
new file mode 100755
index 0000000..7089d15
--- /dev/null
+++ b/Test/Processor/Abstract.php
@@ -0,0 +1,21 @@
+
+ */
+class Test_Processor_Abstract extends SabelTestCase
+{
+ protected $bus = null;
+
+ public function setUp()
+ {
+ $this->bus = new Sabel_Bus();
+ $this->bus->set("session", Sabel_Session_InMemory::create());
+ $this->bus->setConfig("map", new TestMapConfig());
+ $this->bus->setConfig("addon", new TestAddonConfig());
+ }
+}
diff --git a/Test/Processor/Addon.php b/Test/Processor/Addon.php
new file mode 100755
index 0000000..24c6ada
--- /dev/null
+++ b/Test/Processor/Addon.php
@@ -0,0 +1,43 @@
+
+ */
+class Test_Processor_Addon extends Test_Processor_Abstract
+{
+ public static function suite()
+ {
+ Sabel::fileUsing(PROCESSORS_DIR . DS . "Addon.php", true);
+ return self::createSuite("Test_Processor_Addon");
+ }
+
+ public function testProcess()
+ {
+ $bus = $this->bus;
+
+ $processor = new Processor_Addon("addon");
+ $processor->execute($bus);
+
+ $this->assertEquals(1, $bus->get("hogeAddon"));
+ $this->assertEquals(2, $bus->get("fugaAddon"));
+ }
+}
+
+class Hoge_Addon extends Sabel_Object
+{
+ public function execute($bus)
+ {
+ $bus->set("hogeAddon", 1);
+ }
+}
+
+class Fuga_Addon extends Sabel_Object
+{
+ public function execute($bus)
+ {
+ $bus->set("fugaAddon", 2);
+ }
+}
diff --git a/Test/Processor/Controller.php b/Test/Processor/Controller.php
new file mode 100755
index 0000000..baddcc5
--- /dev/null
+++ b/Test/Processor/Controller.php
@@ -0,0 +1,54 @@
+
+ */
+class Test_Processor_Controller extends Test_Processor_Abstract
+{
+ public static function suite()
+ {
+ Sabel::fileUsing(PROCESSORS_DIR . DS . "Controller.php", true);
+ return self::createSuite("Test_Processor_Controller");
+ }
+
+ public function testHogeController()
+ {
+ $bus = $this->bus;
+ $bus->set("response", new Sabel_Response_Object());
+ $bus->set("destination", $this->getDestination("Hoge"));
+
+ $processor = new Processor_Controller("controller");
+ $processor->execute($bus);
+
+ $controller = $bus->get("controller");
+ $this->assertTrue($controller instanceof Test_Controllers_Hoge);
+ $this->assertTrue($bus->get("response") instanceof Sabel_Response);
+ $this->assertTrue($controller->getSession() instanceof Sabel_Session_Abstract);
+ }
+
+ public function testFugaController()
+ {
+ $bus = $this->bus;
+ $bus->set("response", new Sabel_Response_Object());
+ $bus->set("destination", $this->getDestination("Fuga"));
+
+ $processor = new Processor_Controller("controller");
+ $processor->execute($bus);
+
+ $this->assertTrue($bus->get("controller") instanceof Test_Controllers_Fuga);
+ }
+
+ protected function getDestination($name)
+ {
+ return new Sabel_Map_Destination(array("module" => "Test",
+ "controller" => $name,
+ "action" => "index"));
+ }
+}
+
+class Test_Controllers_Hoge extends Sabel_Controller_Page {}
+class Test_Controllers_Fuga extends Sabel_Controller_Page {}
diff --git a/Test/Processor/Flow.php b/Test/Processor/Flow.php
new file mode 100755
index 0000000..653a23b
--- /dev/null
+++ b/Test/Processor/Flow.php
@@ -0,0 +1,73 @@
+
+ * @copyright 2002-2006 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Test_Processor_Flow extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Processor_Flow");
+ }
+
+ private $bus = null;
+
+ public function setUp()
+ {
+ $bus = new Sabel_Bus();
+
+ $request = new Sabel_Request_Object("index/index");
+ $storage = new Sabel_Storage_InMemory();
+ $controller = new StandardFlow();
+ $destination = new Sabel_Destination("index", "index", "top");
+
+ $controller->setup($request, $destination, $storage);
+
+ $bus->set("request", $request);
+ $bus->set("storage", $storage);
+ $bus->set("controller", $controller);
+ $bus->set("destination", $destination);
+
+ $this->bus = $bus;
+ }
+
+ public function testStandardFlow()
+ {
+ $processor = new Processor_Flow("flow");
+ $processor->setBus($this->bus);
+ $processor->execute($this->bus);
+ }
+
+ public function testFailRequired()
+ {
+ $bus = new Sabel_Bus();
+ $processor = new Processor_Flow("flow");
+ $processor->setBus($this->bus);
+
+ try {
+ $processor->execute($bus);
+ $this->fail();
+ } catch (Sabel_Exception_Runtime $e) {
+ $this->assertTrue(true);
+ }
+ }
+}
+
+class Config_Factory extends Sabel_Container_Injection
+{
+ public function configure()
+ {
+ $this->bind("Sabel_Response")->to("Sabel_Response_Web");
+ $this->bind("Sabel_Locale")->to("Sabel_Locale_Null");
+ }
+}
diff --git a/Test/Processor/Request.php b/Test/Processor/Request.php
new file mode 100755
index 0000000..9c1d6f3
--- /dev/null
+++ b/Test/Processor/Request.php
@@ -0,0 +1,44 @@
+
+ */
+class Test_Processor_Request extends Test_Processor_Abstract
+{
+ public static function suite()
+ {
+ Sabel::fileUsing(PROCESSORS_DIR . DS . "Request.php", true);
+ return self::createSuite("Test_Processor_Request");
+ }
+
+ public function testProcess()
+ {
+ $bus = $this->bus;
+
+ $this->assertFalse($bus->get("session")->isStarted());
+
+ $processor = new Processor_Request("request");
+ $processor->execute($bus);
+
+ $this->assertTrue($bus->get("request") instanceof Sabel_Request);
+ $this->assertNull($bus->get("request")->fetchPostValue("hoge"));
+ $this->assertFalse($bus->get("session")->isStarted());
+ }
+
+ public function testSetRequestObject()
+ {
+ $bus = $this->bus;
+ $request = new Sabel_Request_Object("");
+ $request->setPostValue("hoge", "1");
+ $bus->set("request", $request);
+
+ $processor = new Processor_Request("request");
+ $processor->execute($bus);
+
+ $this->assertEquals("1", $bus->get("request")->fetchPostValue("hoge"));
+ }
+}
diff --git a/Test/Processor/Response.php b/Test/Processor/Response.php
new file mode 100755
index 0000000..8971557
--- /dev/null
+++ b/Test/Processor/Response.php
@@ -0,0 +1,43 @@
+
+ */
+class Test_Processor_Response extends Test_Processor_Abstract
+{
+ public static function suite()
+ {
+ Sabel::fileUsing(PROCESSORS_DIR . DS . "Response.php", true);
+ return self::createSuite("Test_Processor_Response");
+ }
+
+ public function testProcess()
+ {
+ $bus = $this->bus;
+ $response = new Sabel_Response_Object();
+ $response->setResponse("a", "1");
+ $response->setResponse("b", "2");
+
+ $controller = new ResponseTestController($response);
+ $controller->setAttribute("b", "3");
+ $controller->setAttribute("c", "4");
+
+ $bus->set("controller", $controller);
+ $bus->set("response", $response);
+
+ $processor = new Processor_Response("response");
+ $processor->afterAction($bus);
+
+ $responses = $bus->get("response")->getResponses();
+ $this->assertEquals("1", $responses["a"]);
+ $this->assertEquals("3", $responses["b"]);
+ $this->assertEquals("4", $responses["c"]);
+ $this->assertFalse(isset($responses["d"]));
+ }
+}
+
+class ResponseTestController extends Sabel_Controller_Page {}
diff --git a/Test/Processor/Router.php b/Test/Processor/Router.php
new file mode 100755
index 0000000..0729afb
--- /dev/null
+++ b/Test/Processor/Router.php
@@ -0,0 +1,44 @@
+
+ */
+class Test_Processor_Router extends Test_Processor_Abstract
+{
+ public static function suite()
+ {
+ Sabel::fileUsing(PROCESSORS_DIR . DS . "Router.php", true);
+ return self::createSuite("Test_Processor_Router");
+ }
+
+ public function testCreateDefaultCandidate()
+ {
+ $bus = $this->bus;
+ $bus->set("request", new Sabel_Request_Object("index/test"));
+
+ $processor = new Processor_Router("router");
+ $processor->execute($bus);
+
+ $candidate = Sabel_Context::getContext()->getCandidate();
+ $this->assertTrue($candidate instanceof Sabel_Map_Candidate);
+ $this->assertEquals("default", $candidate->getName());
+ }
+
+ public function testCreateDevelopmentCandidate()
+ {
+ $bus = $this->bus;
+ $bus->set("request", new Sabel_Request_Object("devel/main/index/db"));
+
+ $processor = new Processor_Router("router");
+ $processor->execute($bus);
+
+ $candidate = Sabel_Context::getContext()->getCandidate();
+ $this->assertTrue($candidate instanceof Sabel_Map_Candidate);
+ $this->assertEquals("devel", $candidate->getName());
+ $this->assertEquals("db", $bus->get("request")->fetchParameterValue("param"));
+ }
+}
diff --git a/Test/Processor/Tests.php b/Test/Processor/Tests.php
new file mode 100755
index 0000000..9a7e81e
--- /dev/null
+++ b/Test/Processor/Tests.php
@@ -0,0 +1,56 @@
+addTest(Test_Processor_Request::suite());
+ $suite->addTest(Test_Processor_Router::suite());
+ $suite->addTest(Test_Processor_Addon::suite());
+ $suite->addTest(Test_Processor_Controller::suite());
+ $suite->addTest(Test_Processor_Response::suite());
+
+ return $suite;
+ }
+}
+
+class TestMapConfig extends Sabel_Map_Configurator
+{
+ public function configure()
+ {
+ $this->route("devel")
+ ->uri("devel/:controller/:action/:param")
+ ->module("devel")
+ ->defaults(array(":controller" => "main",
+ ":action" => "index",
+ ":param" => null));
+
+ $this->route("default")
+ ->uri(":controller/:action")
+ ->module("index")
+ ->defaults(array(":controller" => "index",
+ ":action" => "index"));
+ }
+}
+
+class TestAddonConfig implements Sabel_Config
+{
+ public function configure()
+ {
+ $addons = array();
+ $addons[] = "hoge";
+ $addons[] = "fuga";
+
+ return $addons;
+ }
+}
diff --git a/Test/Processor/classes/Index.php b/Test/Processor/classes/Index.php
new file mode 100755
index 0000000..3b447e6
--- /dev/null
+++ b/Test/Processor/classes/Index.php
@@ -0,0 +1,13 @@
+
+ */
+class Test_Reflection extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Reflection");
+ }
+
+ public function testReflection()
+ {
+ $o = new Vircle();
+ $reflection = $o->getReflection();
+
+ $this->assertTrue($reflection->hasAnnotation("hoge"));
+ $this->assertTrue($reflection->hasAnnotation("class"));
+
+ $annotation = $reflection->getAnnotation("class");
+ $this->assertEquals("value", $annotation[0][0]);
+ }
+
+ public function testReflectionMethod()
+ {
+ $o = new Vircle();
+ $reflection = $o->getReflection()->getMethod("fooMethod");
+
+ $this->assertTrue($reflection->hasAnnotation("fuga"));
+ $this->assertTrue($reflection->hasAnnotation("method"));
+
+ $annotation = $reflection->getAnnotation("method");
+ $this->assertEquals("value", $annotation[0][0]);
+ }
+
+ public function testMethodAnnotation()
+ {
+ $o = new Vircle();
+ $annotation = $o->getReflection()->getMethodAnnotation("fooMethod", "method");
+ $this->assertEquals("value", $annotation[0][0]);
+ }
+
+ public function testGetMethods()
+ {
+ $o = new Vircle();
+ $methods = $o->getReflection()->getMethods();
+ $annotations = $methods["fooMethod"]->getAnnotations();
+ $this->assertEquals("value", $annotations["method"][0][0]);
+ }
+
+ public function testProperty()
+ {
+ $o = new Vircle();
+ $hoge = $o->getReflection()->getProperty("hoge");
+ $this->assertTrue($hoge->hasAnnotation("var"));
+ $annotation = $hoge->getAnnotation("var");
+ $this->assertEquals("string", $annotation[0][0]);
+ }
+
+ public function testGetProperties()
+ {
+ $o = new Vircle();
+ $props = $o->getReflection()->getProperties();
+ $annotations = $props["fuga"]->getAnnotations();
+ $this->assertEquals("array", $annotations["var"][0][0]);
+ }
+}
+
+/**
+ * @hoge
+ * @class value
+ */
+class Vircle extends Sabel_Object
+{
+ /**
+ * @var string
+ */
+ protected $hoge = "";
+
+ /**
+ * @var array
+ */
+ protected $fuga = array();
+
+ /**
+ * @fuga
+ * @method value
+ */
+ public function fooMethod()
+ {
+
+ }
+}
diff --git a/Test/Request/Object.php b/Test/Request/Object.php
new file mode 100755
index 0000000..22f4f81
--- /dev/null
+++ b/Test/Request/Object.php
@@ -0,0 +1,176 @@
+
+ * @author Ebine Yutaka
+ */
+class Test_Request_Object extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Request_Object");
+ }
+
+ public function testUri()
+ {
+ $path = "foo/bar";
+ $request = new Sabel_Request_Object("");
+ $request->get($path);
+ $this->assertEquals($path, $request->getUri());
+ }
+
+ public function testGetValue()
+ {
+ $request = new Sabel_Request_Object("");
+ $request->get("index/index");
+ $request->value("a", "1")->value("b", "2");
+ $this->assertEquals("1", $request->fetchGetValue("a"));
+ $this->assertEquals("2", $request->fetchGetValue("b"));
+ $this->assertEquals(null, $request->fetchPostValue("a"));
+ $this->assertEquals(null, $request->fetchPostValue("b"));
+ }
+
+ public function testPostValue()
+ {
+ $request = new Sabel_Request_Object("");
+ $request->post("index/index");
+ $request->value("a", "1")->value("b", "2");
+ $this->assertEquals(null, $request->fetchGetValue("a"));
+ $this->assertEquals(null, $request->fetchGetValue("b"));
+ $this->assertEquals("1", $request->fetchPostValue("a"));
+ $this->assertEquals("2", $request->fetchPostValue("b"));
+ }
+
+ public function testGetValues()
+ {
+ $request = new Sabel_Request_Object("");
+ $request->get("index/index")->values(array("a" => "1", "b" => "2"));
+ $this->assertEquals(array("a" => "1", "b" => "2"), $request->fetchGetValues());
+ $this->assertEquals(array(), $request->fetchPostValues());
+ }
+
+ public function testPostValues()
+ {
+ $request = new Sabel_Request_Object("");
+ $request->post("index/index")->values(array("a" => "1", "b" => "2"));
+ $this->assertEquals(array(), $request->fetchGetValues());
+ $this->assertEquals(array("a" => "1", "b" => "2"), $request->fetchPostValues());
+ }
+
+ public function testHasValueWithMethod()
+ {
+ $request = new Sabel_Request_Object("");
+ $request->get("index/index")->values(array("a" => "1", "b" => "2"));
+ $this->assertTrue($request->hasValueWithMethod("a"));
+
+ $request->post("index/index");
+ $this->assertFalse($request->hasValueWithMethod("a"));
+ }
+
+ public function testGetValueWithMethod()
+ {
+ $request = new Sabel_Request_Object("");
+ $request->get("index/index")->values(array("a" => "1", "b" => "2"));
+ $this->assertEquals("2", $request->getValueWithMethod("b"));
+
+ $request->post("index/index");
+ $this->assertNull($request->getValueWithMethod("b"));
+ }
+
+ public function testSetValue()
+ {
+ $request = new Sabel_Request_Object("");
+ $this->assertEquals(null, $request->fetchGetValue("a"));
+ $request->setGetValue("a", "1");
+ $this->assertEquals("1", $request->fetchGetValue("a"));
+ }
+
+ public function testSetValues()
+ {
+ $request = new Sabel_Request_Object("");
+ $this->assertEquals(null, $request->fetchGetValue("a"));
+ $request->setGetValues(array("a" => "1"));
+ $this->assertEquals("1", $request->fetchGetValue("a"));
+ }
+
+ public function testHasGetValue()
+ {
+ $request = new Sabel_Request_Object("");
+ $this->assertFalse($request->hasGetValue("a"));
+ $request->setGetValues(array("a" => "1", "b" => ""));
+ $this->assertTrue($request->hasGetValue("a"));
+ $this->assertFalse($request->hasGetValue("b"));
+ }
+
+ public function testIsGetSet()
+ {
+ $request = new Sabel_Request_Object("");
+ $this->assertFalse($request->hasGetValue("a"));
+ $request->setGetValues(array("a" => "1", "b" => ""));
+
+ $this->assertTrue($request->hasGetValue("a"));
+ $this->assertFalse($request->hasGetValue("b"));
+
+ $this->assertTrue($request->isGetSet("a"));
+ $this->assertTrue($request->isGetSet("b"));
+ }
+
+ public function testHasPostValue()
+ {
+ $request = new Sabel_Request_Object("");
+ $request->post("");
+
+ $this->assertFalse($request->hasPostValue("a"));
+ $request->setPostValues(array("a" => "1", "b" => ""));
+ $this->assertTrue($request->hasPostValue("a"));
+ $this->assertFalse($request->hasPostValue("b"));
+ }
+
+ public function testIsPostSet()
+ {
+ $request = new Sabel_Request_Object("");
+ $request->post("");
+
+ $this->assertFalse($request->hasPostValue("a"));
+ $request->setPostValues(array("a" => "1", "b" => ""));
+
+ $this->assertTrue($request->hasPostValue("a"));
+ $this->assertFalse($request->hasPostValue("b"));
+
+ $this->assertTrue($request->isPostSet("a"));
+ $this->assertTrue($request->isPostSet("b"));
+ }
+
+ public function testFind()
+ {
+ $request = new Sabel_Request_Object("");
+ $request->setGetValue("a", "10");
+ $request->setPostValue("b", "20");
+ $request->setParameterValue("c", "30");
+
+ $this->assertEquals("10", $request->find("a"));
+ $this->assertEquals("20", $request->find("b"));
+ $this->assertEquals("30", $request->find("c"));
+ }
+
+ public function testFindDuplicateValues()
+ {
+ $request = new Sabel_Request_Object("");
+ $request->setGetValue("a", "10");
+ $request->setPostValue("b", "20");
+ $request->setParameterValue("b", "30");
+
+ $this->assertEquals("10", $request->find("a"));
+
+ try {
+ $this->assertEquals("20", $request->find("b"));
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+}
diff --git a/Test/Request/Tests.php b/Test/Request/Tests.php
new file mode 100755
index 0000000..dafe5f5
--- /dev/null
+++ b/Test/Request/Tests.php
@@ -0,0 +1,14 @@
+addTest(Test_Request_Object::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/Response/Header.php b/Test/Response/Header.php
new file mode 100755
index 0000000..580cc24
--- /dev/null
+++ b/Test/Response/Header.php
@@ -0,0 +1,40 @@
+
+ */
+class Test_Response_Header extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Response_Header");
+ }
+
+ public function testOutputHeader()
+ {
+ $response = new Sabel_Response_Object();
+ $response->setHeader("Content-Type", "text/html; charset=UTF-8");
+ $response->setHeader("Content-Length", "4096");
+
+ $headers = $response->outputHeader();
+ $this->assertEquals("Content-Type: text/html; charset=UTF-8", $headers[1]);
+ $this->assertEquals("Content-Length: 4096", $headers[2]);
+ }
+
+ public function testOutputStatus()
+ {
+ $response = new Sabel_Response_Object();
+ $response->getStatus()->setCode(Sabel_Response::FORBIDDEN);
+ $headers = $response->outputHeader();
+ $this->assertEquals("HTTP/1.0 403 Forbidden", $headers[0]);
+
+ $response = new Sabel_Response_Object();
+ $response->getStatus()->setCode(Sabel_Response::NOT_MODIFIED);
+ $headers = $response->outputHeader();
+ $this->assertEquals("HTTP/1.0 304 Not Modified", $headers[0]);
+ }
+}
diff --git a/Test/Response/Object.php b/Test/Response/Object.php
new file mode 100755
index 0000000..a72a82f
--- /dev/null
+++ b/Test/Response/Object.php
@@ -0,0 +1,92 @@
+
+ */
+class Test_Response_Object extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Response_Object");
+ }
+
+ public function testResponseValue()
+ {
+ $response = new Sabel_Response_Object();
+ $response->setResponse("a", "10");
+ $response->setResponse("b", "20");
+
+ $this->assertEquals("10", $response->getResponse("a"));
+ $this->assertEquals("20", $response->getResponse("b"));
+ $this->assertEquals(null, $response->getResponse("c"));
+ }
+
+ public function testResponseValues()
+ {
+ $response = new Sabel_Response_Object();
+ $response->setResponses(array("a" => "10", "b" => "20"));
+
+ $this->assertEquals("10", $response->getResponse("a"));
+ $this->assertEquals("20", $response->getResponse("b"));
+ $this->assertEquals(null, $response->getResponse("c"));
+
+ $expected = array("a" => "10", "b" => "20");
+ $this->assertEquals($expected, $response->getResponses());
+ }
+
+ public function testResponseHeader()
+ {
+ $response = new Sabel_Response_Object();
+ $response->setHeader("Content-Type", "image/gif");
+ $response->setHeader("Content-Length", "4096");
+ $this->assertEquals("image/gif", $response->getHeader("Content-Type"));
+ $this->assertEquals("4096", $response->getHeader("Content-Length"));
+ $this->assertEquals(array("Content-Type" => "image/gif", "Content-Length" => "4096"), $response->getHeaders());
+ $this->assertEquals(null, $response->getHeader("Foo-Bar"));
+ }
+
+ public function testExpiredCacheHeaders()
+ {
+ $response = new Sabel_Response_Object();
+ $response->expiredCache("300000000");
+ $headers = $response->getHeaders();
+ $this->assertTrue(isset($headers["Expires"]));
+ $this->assertTrue(isset($headers["Last-Modified"]));
+ $this->assertTrue(isset($headers["Cache-Control"]));
+ $this->assertTrue(isset($headers["Pragma"]));
+ }
+
+ public function testStatus()
+ {
+ $response = new Sabel_Response_Object();
+ $status = $response->getStatus();
+
+ $this->assertTrue($status->isSuccess());
+
+ $status->setCode(Sabel_Response::NOT_FOUND);
+ $this->assertTrue($status->isClientError());
+
+ $status->setCode(Sabel_Response::INTERNAL_SERVER_ERROR);
+ $this->assertTrue($status->isServerError());
+ }
+
+ public function testIsFailure()
+ {
+ $response = new Sabel_Response_Object();
+ $status = $response->getStatus();
+
+ $this->assertFalse($status->isFailure());
+
+ $status->setCode(Sabel_Response::NOT_FOUND);
+ $this->assertTrue($status->isFailure());
+
+ $status->setCode(Sabel_Response::INTERNAL_SERVER_ERROR);
+ $this->assertTrue($status->isFailure());
+
+ $status->setCode(Sabel_Response::FORBIDDEN);
+ $this->assertTrue($status->isFailure());
+ }
+}
diff --git a/Test/Response/Redirector.php b/Test/Response/Redirector.php
new file mode 100755
index 0000000..0d0053a
--- /dev/null
+++ b/Test/Response/Redirector.php
@@ -0,0 +1,80 @@
+
+ */
+class Test_Response_Redirector extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Response_Redirector");
+ }
+
+ public function setUp()
+ {
+ $config = new TestConfigMap();
+ $config->route("default")
+ ->uri(":controller/:action")
+ ->module("index");
+
+ $this->routing($config);
+ }
+
+ public function testIsRedirected()
+ {
+ $redirector = new Sabel_Response_Redirector();
+ $this->assertFalse($redirector->isRedirected());
+ }
+
+ public function testRedirect()
+ {
+ $redirector = new Sabel_Response_Redirector();
+ $redirector->to("a: test");
+ $this->assertTrue($redirector->isRedirected());
+ $this->assertEquals("/index/test", $redirector->getUri());
+ }
+
+ public function testRedirectByUrl()
+ {
+ $redirector = new Sabel_Response_Redirector();
+ $redirector->url("index/test");
+ $this->assertTrue($redirector->isRedirected());
+ $this->assertEquals("index/test", $redirector->getUrl());
+ }
+
+ public function testRedirectWithParameters()
+ {
+ $redirector = new Sabel_Response_Redirector();
+ $redirector->to("a: test", array("page" => "1"));
+ $this->assertTrue($redirector->isRedirected());
+ $this->assertTrue($redirector->hasParameters());
+ $this->assertEquals("/index/test?page=1", $redirector->getUri());
+ }
+
+ public function testUriParameter()
+ {
+ $redirector = new Sabel_Response_Redirector();
+ $redirector->to("n: default");
+ $this->assertTrue($redirector->isRedirected());
+ //$this->assertEquals("index/index", $redirector->getUri());
+ $this->assertEquals("/", $redirector->getUri());
+ }
+
+ protected function routing($config)
+ {
+ $request = new Sabel_Request_Object("index/index");
+
+ $config->configure();
+ $candidate = $config->getValidCandidate($request->getUri());
+ Sabel_Context::getContext()->setCandidate($candidate);
+ }
+}
+
+class TestConfigMap extends Sabel_Map_Configurator
+{
+ public function configure() {}
+}
diff --git a/Test/Response/Tests.php b/Test/Response/Tests.php
new file mode 100755
index 0000000..ccda559
--- /dev/null
+++ b/Test/Response/Tests.php
@@ -0,0 +1,18 @@
+addTest(Test_Response_Object::suite());
+ $suite->addTest(Test_Response_Redirector::suite());
+ $suite->addTest(Test_Response_Header::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/SabelTestCase.php b/Test/SabelTestCase.php
new file mode 100755
index 0000000..ccfc2c1
--- /dev/null
+++ b/Test/SabelTestCase.php
@@ -0,0 +1,9 @@
+
+ */
+class Test_Session_Database extends SabelTestCase
+{
+ private static $sid = "";
+
+ private $session = null;
+ private $sessionId = "";
+
+ public static function suite()
+ {
+ if (self::initTable()) {
+ self::$sid = md5hash();
+ ini_set("session.use_cookies", "0");
+ return self::createSuite("Test_Session_Database");
+ } else {
+ return self::createSuite("");
+ }
+ }
+
+ public function setUp()
+ {
+ $_SERVER["REQUEST_METHOD"] = "GET";
+ $this->session = Sabel_Session_Database::create();
+ $this->session->setTableName("session");
+ $_GET[session_name()] = self::$sid;
+ $this->session->start();
+ }
+
+ public function testEmpty()
+ {
+ $this->assertTrue($this->session->isStarted());
+ $this->assertEquals(self::$sid, $this->session->getId());
+ $this->assertNull($this->session->read("a"));
+ $this->assertNull($this->session->read("b"));
+
+ $this->session->write("a", "10");
+ $this->session->shutdown();
+ }
+
+ public function testWrite()
+ {
+ $this->assertEquals("10", $this->session->read("a"));
+ $this->assertNull($this->session->read("b"));
+
+ $this->session->write("b", "20");
+ $this->session->delete("a");
+ $this->session->shutdown();
+ }
+
+ public function testDelete()
+ {
+ $this->assertNull($this->session->read("a"));
+ $this->assertEquals("20", $this->session->read("b"));
+ $this->session->shutdown();
+ }
+
+ public function testRegeneratedId()
+ {
+ $this->session->regenerateId();
+ $newId = $this->session->getId();
+ $this->assertNotEquals(self::$sid, $newId);
+
+ $this->assertEquals("20", $this->session->read("b"));
+
+ self::$sid = $newId;
+ $this->session->shutdown();
+ }
+
+ public function testDestroy()
+ {
+ $this->assertEquals("20", $this->session->read("b"));
+ $this->session->destroy();
+
+ $this->assertNull($this->session->read("b"));
+ $this->session->shutdown();
+ }
+
+ private static function initTable()
+ {
+ if (extension_loaded("mysql")) {
+ $params = array("package" => "sabel.db.mysql",
+ "host" => "127.0.0.1",
+ "user" => "root",
+ "password" => "",
+ "database" => "sdb_test");
+ } elseif (extension_loaded("pgsql")) {
+ $params = array("package" => "sabel.db.pgsql",
+ "host" => "127.0.0.1",
+ "user" => "root",
+ "password" => "",
+ "database" => "sdb_test");
+ } elseif (extension_loaded("pdo_sqlite")) {
+ $params = array("package" => "sabel.db.pdo.sqlite",
+ "database" => SABEL_BASE . "/Test/data/sdb_test.sq3");
+ } else {
+ Sabel_Console::message("skipped 'Test_Session_Database'.");
+ return false;
+ }
+
+ Sabel_Db_Config::add("default", $params);
+ Sabel_Db::createDriver("default")->execute("DELETE FROM session");
+ return true;
+ }
+}
diff --git a/Test/Session/Tests.php b/Test/Session/Tests.php
new file mode 100755
index 0000000..64bb1ab
--- /dev/null
+++ b/Test/Session/Tests.php
@@ -0,0 +1,24 @@
+
+ */
+class Test_Session_Tests
+{
+ public static function main()
+ {
+ PHPUnit_TextUI_TestRunner::run(self::suite());
+ }
+
+ public static function suite()
+ {
+ $suite = new PHPUnit_Framework_TestSuite();
+
+ //$suite->addTest(Test_Session_Database::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/Util/FileSystem.php b/Test/Util/FileSystem.php
new file mode 100755
index 0000000..7ebf006
--- /dev/null
+++ b/Test/Util/FileSystem.php
@@ -0,0 +1,290 @@
+
+ */
+class Test_Util_FileSystem extends SabelTestCase
+{
+ protected $basedir = "";
+
+ public static function suite()
+ {
+ return self::createSuite("Test_Util_FileSystem");
+ }
+
+ public function setUp()
+ {
+ $this->basedir = SABEL_BASE . DS . "Test" . DS . "data" . DS . "application" . DS . "data";
+ }
+
+ public function testMakedir()
+ {
+ $fs = new Sabel_Util_FileSystem($this->basedir);
+
+ $dir = $fs->mkdir("test");
+ $this->assertEquals($this->basedir . DS . "test", $dir->pwd());
+
+ $this->assertTrue($fs->isDir("test"));
+ $this->assertTrue($fs->isDir($this->basedir . DS . "test"));
+ $this->assertTrue(is_dir($this->basedir . DS . "test"));
+ }
+
+ public function testRecursiveMakedir()
+ {
+ $path = "hoge" . DS . "fuga" . DS . "foo";
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+
+ $dir = $fs->mkdir($path);
+ $this->assertEquals($this->basedir . DS . "test" . DS . $path, $dir->pwd());
+
+ $this->assertTrue($fs->isDir($path));
+ $this->assertTrue($fs->isDir($this->basedir . DS . "test" . DS . $path));
+ $this->assertTrue(is_dir($this->basedir . DS . "test" . DS . $path));
+ }
+
+ public function testChangeDirectory()
+ {
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $this->assertTrue($fs->isDir("hoge"));
+ $this->assertFalse($fs->isDir("fuga"));
+ $this->assertFalse($fs->isDir("foo"));
+
+ $fs->cd("hoge");
+ $this->assertFalse($fs->isDir("hoge"));
+ $this->assertTrue($fs->isDir("fuga"));
+ $this->assertFalse($fs->isDir("foo"));
+
+ $fs->cd("fuga");
+ $this->assertFalse($fs->isDir("hoge"));
+ $this->assertFalse($fs->isDir("fuga"));
+ $this->assertTrue($fs->isDir("foo"));
+ }
+
+ public function testPwd()
+ {
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $this->assertEquals($this->basedir . DS . "test", $fs->pwd());
+
+ $fs->cd("hoge");
+ $this->assertEquals($this->basedir . DS . "test" . DS . "hoge", $fs->pwd());
+
+ $fs->cd("fuga");
+ $this->assertEquals($this->basedir . DS . "test" . DS . "hoge" . DS . "fuga", $fs->pwd());
+ }
+
+ public function testRmdir()
+ {
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $fs->cd("hoge" . DS . "fuga");
+
+ $this->assertTrue($fs->isDir("foo"));
+ $fs->rmdir("foo");
+ $this->assertFalse($fs->isDir("foo"));
+ }
+
+ public function testRecursiveRmdir()
+ {
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $this->assertTrue($fs->isDir("hoge"));
+ $this->assertTrue($fs->isDir("hoge" . DS . "fuga"));
+
+ $fs->rmdir("hoge");
+
+ $this->assertFalse($fs->isDir("hoge"));
+ $this->assertFalse($fs->isDir("hoge" . DS . "fuga"));
+ }
+
+ public function testMkfile()
+ {
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $fs->mkfile("hoge.txt");
+ $this->assertTrue($fs->isFile("hoge.txt"));
+ $this->assertTrue($fs->isFile($this->basedir . DS . "test" . DS . "hoge.txt"));
+ $this->assertTrue(is_file($this->basedir . DS . "test" . DS . "hoge.txt"));
+ }
+
+ public function testRecursiveMakeFile()
+ {
+ $file = "hoge" . DS . "fuga" . DS . "foo.txt";
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $fs->mkfile($file);
+ $this->assertTrue($fs->isFile($file));
+ $this->assertTrue($fs->isFile($this->basedir . DS . "test" . DS . $file));
+ $this->assertTrue(is_file($this->basedir . DS . "test" . DS . $file));
+ }
+
+ public function testFilePermission()
+ {
+ // win
+ if (DIRECTORY_SEPARATOR === "\\") return;
+
+ $file = "hoge" . DS . "fuga" . DS . "foo.txt";
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $file = $fs->getFile($file);
+ $this->assertEquals(0755, $file->getPermission());
+ $file->chmod(0777);
+ $this->assertEquals(0777, $file->getPermission());
+ }
+
+ public function testFilePermission2()
+ {
+ // win
+ if (DIRECTORY_SEPARATOR === "\\") return;
+
+ chmod($this->basedir . DS . "readable.txt", 0444);
+ chmod($this->basedir . DS . "writable.txt", 0222);
+ chmod($this->basedir . DS . "executable.txt", 0111);
+
+ $fs = new Sabel_Util_FileSystem($this->basedir);
+
+ $readable = $fs->getFile("readable.txt");
+ $this->assertTrue($readable->isReadable());
+ $this->assertFalse($readable->isWritable());
+ $this->assertFalse($readable->isExecutable());
+
+ $writable = $fs->getFile("writable.txt");
+ $this->assertFalse($writable->isReadable());
+ $this->assertTrue($writable->isWritable());
+ $this->assertFalse($writable->isExecutable());
+
+ $executable = $fs->getFile("executable.txt");
+ $this->assertFalse($executable->isReadable());
+ $this->assertFalse($executable->isWritable());
+ $this->assertTrue($executable->isExecutable());
+
+ chmod($this->basedir . DS . "readable.txt", 0777);
+ chmod($this->basedir . DS . "writable.txt", 0777);
+ chmod($this->basedir . DS . "executable.txt", 0777);
+ }
+
+ public function testDirectoryPermission()
+ {
+ // win
+ if (DIRECTORY_SEPARATOR === "\\") return;
+
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $dir = $fs->getDirectory("hoge" . DS . "fuga");
+ $this->assertEquals(0755, $dir->getPermission());
+ $dir->chmod(0777);
+ $this->assertEquals(0777, $dir->getPermission());
+ }
+
+ public function testFileSize()
+ {
+ $file = "hoge" . DS . "fuga" . DS . "foo.txt";
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $file = $fs->getFile($file);
+ $currentSize = $file->getSize();
+ $file->write("abcdefg")->save();
+ $this->assertTrue($file->getSize() > $currentSize);
+ }
+
+ public function testFileContents()
+ {
+ $file = "hoge" . DS . "fuga" . DS . "foo.txt";
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $file = $fs->getFile($file);
+ $this->assertEquals("abcdefg", $file->getContents());
+
+ $file->open();
+ $file->write("hijklmn")->save();
+ $this->assertEquals("abcdefg" . PHP_EOL . "hijklmn", $file->getContents());
+ }
+
+ public function testFileContentsAsArray()
+ {
+ $file = "hoge" . DS . "fuga" . DS . "foo.txt";
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $lines = $fs->getFile($file)->getContentsAsArray();
+
+ $this->assertEquals(2, count($lines));
+ $this->assertEquals("abcdefg", $lines[0]);
+ $this->assertEquals("hijklmn", $lines[1]);
+ $this->assertFalse(isset($lines[2]));
+ }
+
+ public function testClearContents()
+ {
+ $file = "hoge" . DS . "fuga" . DS . "foo.txt";
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $file = $fs->getFile($file);
+ $file->clearContents();
+ $this->assertEquals("", $file->getContents());
+ }
+
+ public function testFileCopy()
+ {
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test" . DS . "hoge" . DS . "fuga");
+ $file = $fs->getFile("foo.txt");
+ $file->copyTo(".." . DS . "test" . DS . "foo2.txt");
+
+ $fs->cd(".." . DS . "test");
+ $this->assertTrue($fs->isFile("foo2.txt"));
+
+ $file->copyTo(".." . DS . "test" . DS . "foo.txt");
+ $this->assertTrue($fs->isFile("foo.txt"));
+ }
+
+ public function testFileMove()
+ {
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $file = $fs->getFile("hoge" . DS . "test" . DS . "foo2.txt");
+ $moved = $file->moveTo(".." . DS . "test2" . DS . "foo2.txt");
+
+ $fs->cd(".." . DS . "test" . DS . "hoge");
+ $this->assertTrue($fs->isFile("test2" . DS . "foo2.txt"));
+ $this->assertFalse($fs->isFile("hoge" . DS . "foo2.txt"));
+
+ $moved->moveTo("foo3.txt");
+ $this->assertTrue($fs->isFile("test2" . DS . "foo3.txt"));
+ $this->assertFalse($fs->isFile("test2" . DS . "foo2.txt"));
+ }
+
+ public function testRemoveFile()
+ {
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $fs->isFile("hoge.txt");
+ $fs->getFile("hoge.txt")->remove();
+ $this->assertFalse($fs->isFile("hoge.txt"));
+ $this->assertFalse($fs->isFile($this->basedir . DS . "test" . DS . "hoge.txt"));
+ $this->assertFalse(is_file($this->basedir . DS . "test" . DS . "hoge.txt"));
+ }
+
+ public function testRemoveDir()
+ {
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test" . DS . "hoge");
+ $this->assertTrue(in_array("test2", $fs->ls(), true));
+ $fs->cd("test2");
+ $fs->rmdir();
+
+ $fs->cd($this->basedir . DS . "test" . DS . "hoge");
+ $this->assertFalse(in_array("test2", $fs->ls(), true));
+ }
+
+ public function testDirectoryCopy()
+ {
+ $fs = new Sabel_Util_FileSystem($this->basedir . DS . "test");
+ $this->assertFalse($fs->isDir("moved"));
+
+ $fs->copyTo(".." . DS . "moved");
+ $fs->cd("..");
+ $this->assertTrue($fs->isDir("moved"));
+ $fs->cd("moved");
+ $this->assertTrue($fs->isDir("hoge"));
+ $fs->cd("hoge");
+ $this->assertTrue($fs->isDir("fuga"));
+ $this->assertTrue($fs->isDir("test"));
+ $fs->cd("fuga");
+ $this->assertTrue($fs->isFile("foo.txt"));
+ $fs->cd(".." . DS . "test");
+ $this->assertTrue($fs->isFile("foo.txt"));
+ }
+
+ public function testCleanup()
+ {
+ $fs = new Sabel_Util_FileSystem($this->basedir);
+ $fs->rmdir("test");
+ $fs->rmdir("moved");
+ }
+}
diff --git a/Test/Util/HashList.php b/Test/Util/HashList.php
new file mode 100755
index 0000000..4a4a6e5
--- /dev/null
+++ b/Test/Util/HashList.php
@@ -0,0 +1,342 @@
+
+ */
+class Test_Util_HashList extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Util_HashList");
+ }
+
+ public function testAdd()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $this->assertEquals(3, $list->count());
+
+ try {
+ $list->add("a", "hoge");
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testRemove()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $this->assertEquals(3, $list->count());
+
+ $removed = $list->remove("b");
+ $this->assertEquals("2", $removed);
+
+ $this->assertEquals(2, $list->count());
+ $this->assertEquals("1", $list->get("a"));
+ $this->assertNull($list->get("b"));
+ $this->assertEquals("3", $list->get("c"));
+
+ $this->assertEquals("1", $list->next());
+ $this->assertEquals("3", $list->next());
+ }
+
+ public function testReplace()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $list->replace("b", "d", "4");
+
+ $this->assertEquals(3, $list->count());
+
+ $this->assertEquals("1", $list->get("a"));
+ $this->assertNull($list->get("b"));
+ $this->assertEquals("3", $list->get("c"));
+ $this->assertEquals("4", $list->get("d"));
+
+ try {
+ $list->replace("z", "e", "5");
+ } catch (Sabel_Exception_Runtime $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testInsertPrevious()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $list->insertPrevious("c", "d", "4");
+ $this->assertEquals(4, $list->count());
+
+ $this->assertEquals("1", $list->next());
+ $this->assertEquals("2", $list->next());
+ $this->assertEquals("4", $list->next());
+ $this->assertEquals("3", $list->next());
+ }
+
+ public function testInsertNext()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $list->insertNext("a", "d", "4");
+ $this->assertEquals(4, $list->count());
+
+ $this->assertEquals("1", $list->next());
+ $this->assertEquals("4", $list->next());
+ $this->assertEquals("2", $list->next());
+ $this->assertEquals("3", $list->next());
+ }
+
+ public function testToArray()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $list->insertNext("a", "d", "4");
+ $list->insertPrevious("c", "e", "5");
+
+ $array = $list->toArray();
+ $this->assertEquals("1", $array["a"]);
+ $this->assertEquals("2", $array["b"]);
+ $this->assertEquals("3", $array["c"]);
+ $this->assertEquals("4", $array["d"]);
+ $this->assertEquals("5", $array["e"]);
+ }
+
+ public function testFirst()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $this->assertEquals("1", $list->next());
+ $this->assertEquals("2", $list->next());
+ $this->assertEquals("3", $list->next());
+ $this->assertNull($list->next());
+
+ $list->first();
+
+ $this->assertEquals("1", $list->next());
+ $this->assertEquals("2", $list->next());
+ $this->assertEquals("3", $list->next());
+ $this->assertNull($list->next());
+ }
+
+ public function testLast()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $list->last();
+
+ $this->assertEquals("3", $list->previous());
+ }
+
+ public function testPrevious()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $list->last();
+
+ $this->assertEquals("3", $list->previous());
+ $this->assertEquals("2", $list->previous());
+ $this->assertEquals("1", $list->previous());
+ }
+
+ public function testCursor()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $this->assertEquals("1", $list->next());
+ $this->assertEquals("2", $list->next());
+ $this->assertEquals("3", $list->next());
+ $this->assertEquals("2", $list->previous());
+ $this->assertEquals("1", $list->previous());
+ $this->assertEquals("2", $list->next());
+ $this->assertEquals("1", $list->previous());
+ $this->assertEquals("2", $list->next());
+ $this->assertEquals("3", $list->next());
+ }
+
+ public function testDynamicInsert1()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $this->assertEquals("1", $list->next());
+ $this->assertEquals("2", $list->next());
+ $list->insertNext("b", "d", "4");
+ $this->assertEquals("4", $list->next());
+ $this->assertEquals("3", $list->next());
+ $this->assertNull($list->next());
+
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $this->assertEquals("1", $list->next());
+ $list->insertNext("c", "d", "4");
+
+ $this->assertEquals("2", $list->next());
+ $this->assertEquals("3", $list->next());
+ $this->assertEquals("4", $list->next());
+ $this->assertNull($list->next());
+ }
+
+ public function testDynamicInsert2()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $this->assertEquals("1", $list->next());
+ $this->assertEquals("2", $list->next());
+ $list->insertPrevious("b", "d", "4");
+ $this->assertEquals("3", $list->next());
+ $this->assertNull($list->next());
+
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $this->assertEquals("1", $list->next());
+ $list->insertPrevious("c", "d", "4");
+
+ $this->assertEquals("2", $list->next());
+ $this->assertEquals("4", $list->next());
+ $this->assertEquals("3", $list->next());
+ $this->assertNull($list->next());
+ }
+
+ public function testDynamicInsert3()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $list->last();
+ $this->assertEquals("3", $list->previous());
+ $list->insertPrevious("c", "d", "4");
+ $this->assertEquals("4", $list->previous());
+ $this->assertEquals("2", $list->previous());
+ $this->assertEquals("1", $list->previous());
+
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $list->last();
+ $this->assertEquals("3", $list->previous());
+ $list->insertPrevious("a", "d", "4");
+ $this->assertEquals("2", $list->previous());
+ $this->assertEquals("1", $list->previous());
+ $this->assertEquals("4", $list->previous());
+ }
+
+ public function testDynamicInsertAndCursor()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+
+ $this->assertEquals("1", $list->next());
+ $list->insertNext("b", "d", "4"); // 1 2 4 3
+ $this->assertEquals("2", $list->next());
+ $this->assertEquals("4", $list->next());
+ $list->insertPrevious("b", "e", "5"); // 1 5 2 4 3
+ $this->assertEquals("3", $list->next());
+ $this->assertEquals("4", $list->previous());
+ $list->insertPrevious("c", "f", "6"); // 1 5 2 4 6 3
+ $this->assertEquals("6", $list->next());
+ $this->assertEquals("3", $list->next());
+ $this->assertNull($list->next());
+
+ $list->last();
+ $list->insertNext("c", "g", "7"); // 1 5 2 4 6 3 7
+
+ $this->assertEquals("7", $list->previous());
+ $this->assertEquals("3", $list->previous());
+ $this->assertEquals("6", $list->previous());
+ $this->assertEquals("4", $list->previous());
+ $this->assertEquals("2", $list->previous());
+ $this->assertEquals("5", $list->previous());
+ $this->assertEquals("1", $list->previous());
+ $this->assertNull($list->previous());
+ }
+
+ public function testDynamicRemoveAndCursor()
+ {
+ $list = new Sabel_Util_HashList();
+ $list->add("a", "1");
+ $list->add("b", "2");
+ $list->add("c", "3");
+ $list->add("d", "4");
+ $list->add("e", "5");
+ $list->add("f", "6");
+
+ $this->assertEquals("1", $list->next());
+ $this->assertEquals("2", $list->next());
+ $list->remove("d"); // 1 2 3 5 6
+ $this->assertEquals("3", $list->next());
+ $this->assertEquals("5", $list->next());
+ $list->remove("e"); // 1 2 3 6
+ $this->assertEquals("6", $list->next());
+ $this->assertEquals("3", $list->previous());
+ $list->remove("f"); // 1 2 3
+ $this->assertEquals("2", $list->previous());
+ $list->insertPrevious("a", "g", "0"); // 0 1 2 3
+ $list->remove("a"); // 0 2 3
+ $this->assertEquals("0", $list->previous());
+ $this->assertNull($list->previous());
+
+ $list->last();
+ $list->add("h", "8"); // 0 2 3 8
+
+ $this->assertEquals("8", $list->previous());
+ $this->assertEquals("3", $list->previous());
+ $this->assertEquals("2", $list->previous());
+ $this->assertEquals("0", $list->previous());
+ $this->assertNull($list->previous());
+ }
+}
diff --git a/Test/Util/List.php b/Test/Util/List.php
new file mode 100755
index 0000000..62f1172
--- /dev/null
+++ b/Test/Util/List.php
@@ -0,0 +1,106 @@
+
+ */
+class Test_Util_LinkedList extends SabelTestCase
+{
+ private $list = null;
+
+ public static function suite()
+ {
+ return self::createSuite("Test_Util_LinkedList");
+ }
+
+ public function setUp()
+ {
+ $first = new StdClass();
+ $first->name = "first";
+
+ $second = new StdClass();
+ $second->name = "second";
+
+ $third = new StdClass();
+ $third->name = "third";
+
+ $this->list = new Sabel_Util_LinkedList("first", $first);
+
+ $this->list->insertNext("second", $second)
+ ->insertNext("third", $third);
+ }
+
+ public function testInsertPreviousAndNext()
+ {
+ $list = new Sabel_Util_LinkedList("test", new StdClass());
+
+ for ($i=0; $i < 299; $i++) {
+ $list->insertNext("test{$i}", new StdClass());
+ }
+ $this->assertEquals(300, $list->size());
+
+ for ($i=0; $i<300; $i++) {
+ $list->insertPrevious("test{$i}", new StdClass());
+ }
+ $this->assertEquals(600, $list->size());
+ }
+
+ public function testFindByName()
+ {
+ $list = new Sabel_Util_LinkedList("test", new StdClass());
+
+ $target = new StdClass();
+ $target->value = "ebine";
+ $next = $list->insertNext("target", $target);
+ $next->insertNext("test2", new StdClass());
+
+ $obj = $list->find("target");
+
+ $this->assertTrue(($obj instanceof Sabel_Util_LinkedList));
+ $this->assertTrue(is_object($obj));
+ $this->assertEquals("ebine", $obj->current->value);
+ $this->assertEquals("test", $obj->getFirst()->name);
+ $this->assertEquals("ebine", $obj->getFirst()->next->current->value);
+ $this->assertEquals("test2", $obj->getFirst()->next->next->name);
+ }
+
+ public function testUnlink()
+ {
+ $list = $this->list;
+ $list->getFirst()->next()->unlink();
+ $this->assertEquals("first", $list->getFirst()->name);
+ $this->assertEquals("third", $list->getFirst()->next()->name);
+ }
+
+ public function testUnlinkWithFind()
+ {
+ $list = $this->list;
+ $list->find("second")->unlink();
+ $this->assertEquals("first", $list->getFirst()->name);
+ $this->assertEquals("third", $list->getFirst()->next()->name);
+ }
+
+ public function testUnlinkAndInsert()
+ {
+ $list = $this->list;
+
+ $list->find("second")->unlink();
+ $list->getFirst()->insertNext("second", new StdClass());
+ $this->assertEquals("second", $list->getFirst()->next()->name);
+ }
+
+ public function testUnlinkLast()
+ {
+ $this->list->getLast()->unlink();
+ $this->assertEquals("second", $this->list->getLast()->name);
+ }
+
+ public function testInsertNextPreviousPointer()
+ {
+ $list = $this->list;
+ $list->find("third")->insertNext("force", new StdClass());
+ $this->assertEquals("third", $list->getLast()->previous->name);
+ }
+}
diff --git a/Test/Util/Map.php b/Test/Util/Map.php
new file mode 100755
index 0000000..da5c5cd
--- /dev/null
+++ b/Test/Util/Map.php
@@ -0,0 +1,379 @@
+
+ */
+class Test_Util_Map extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Util_Map");
+ }
+
+ public function testIsEmpty()
+ {
+ $map = new UtilMap();
+ $this->assertTrue($map->isEmpty());
+ }
+
+ public function testCount()
+ {
+ $map = new UtilMap();
+ $this->assertEquals($map->count(), 0);
+
+ $map->set(array("test1" => "hoge", "test2" => "fuga", "test3" => "foo"));
+ $this->assertEquals($map->count(), 3);
+ }
+
+ public function testForeach()
+ {
+ $data = array("test1" => "hoge", "test2" => "fuga");
+ $map = new UtilMap($data);
+
+ $through = false;
+ foreach ($map as $key => $value) {
+ if ($through) {
+ $this->assertEquals($key, "test2");
+ $this->assertTrue($value->equals("fuga"));
+ } else {
+ $this->assertEquals($key, "test1");
+ $this->assertTrue($value->equals("hoge"));
+ }
+ $through = true;
+ }
+
+ $this->assertTrue($through);
+ }
+
+ public function testIterator()
+ {
+ $data = array("test1" => "hoge", "test2" => "fuga");
+ $map = new UtilMap($data);
+
+ $through = false;
+ while ($map->valid()) {
+ if ($through) {
+ $this->assertTrue($map->current()->equals("fuga"));
+ } else {
+ $this->assertTrue($map->current()->equals("hoge"));
+ }
+ $map->next();
+ $through = true;
+ }
+
+ $this->assertTrue($through);
+ }
+
+ public function testIterator2()
+ {
+ $data = array("test1" => "hoge", "test2" => "fuga");
+ $map = new UtilMap($data);
+
+ $through = false;
+ while ($map->hasMoreElements()) {
+ if ($through) {
+ $this->assertTrue($map->nextElement()->equals("fuga"));
+ } else {
+ $this->assertTrue($map->nextElement()->equals("hoge"));
+ }
+ $through = true;
+ }
+
+ $this->assertTrue($through);
+ }
+
+ public function testValues()
+ {
+ $data = array("test1" => "hoge", "test2" => "fuga");
+ $map = new UtilMap($data);
+ $values = $map->values();
+ $this->assertEquals(count($values), 2);
+ $this->assertTrue($values[0]->equals("hoge"));
+ $this->assertTrue($values[1]->equals("fuga"));
+ }
+
+ public function testKeys()
+ {
+ $data = array("test1" => "hoge", "test2" => "fuga");
+ $map = new UtilMap($data);
+ $keys = $map->keys();
+ $this->assertEquals(count($keys), 2);
+ $this->assertEquals($keys[0], "test1");
+ $this->assertEquals($keys[1], "test2");
+ }
+
+ public function testImplode()
+ {
+ $data = array("test1" => "hoge", "test2" => "fuga");
+ $map = new UtilMap($data);
+ $this->assertTrue($map->implode()->equals("hoge, fuga"));
+ $this->assertTrue($map->implode(".")->equals("hoge.fuga"));
+ }
+
+ public function testPut()
+ {
+ $map = new UtilMap();
+ $map->put("test1", "hoge")
+ ->put("test2", "fuga");
+
+ $this->assertEquals($map->count(), 2);
+ $this->assertTrue($map->get("test1")->equals("hoge"));
+ $this->assertTrue($map->get("test2")->equals("fuga"));
+ $this->assertNull($map->get("test3"));
+ }
+
+ public function testPush()
+ {
+ $fuga = new String("fuga");
+ $um = new UtilMap(array("test" => "foo"));
+
+ $map = new UtilMap();
+ $map->push("hoge")->push($fuga)->push($um);
+ $this->assertEquals($map->count(), 3);
+
+ $this->assertTrue($map->get(0)->equals("hoge"));
+ $this->assertTrue($map->get(1)->equals("fuga"));
+ $this->assertTrue($map->get(1)->equals($fuga));
+ $this->assertTrue($map->get(2)->equals($um));
+ $this->assertNull($map->get(3));
+ }
+
+ public function testRemove()
+ {
+ $map = new UtilMap();
+ $map->put("test", "hoge")
+ ->put(new UtilMap(array(1)), "fuga");
+
+ $this->assertEquals(2, $map->count());
+
+ $map->remove("test");
+ $this->assertEquals(1, $map->count());
+ $this->assertNull($map->get("test"));
+
+ $map->remove(new UtilMap());
+ $this->assertEquals(1, $map->count());
+ $map->remove(new UtilMap(array(2)));
+ $this->assertEquals(1, $map->count());
+
+ $this->assertTrue($map->get(new UtilMap(array(1)))->equals("fuga"));
+
+ $map->remove(new UtilMap(array(1)));
+ $this->assertEquals(0, $map->count());
+
+ $this->assertNull($map->get(new UtilMap(array(1))));
+ }
+
+ public function testClear()
+ {
+ $map = new UtilMap();
+ $map->push("hoge")->push("fuga");
+ $this->assertEquals($map->count(), 2);
+
+ $array = $map->clear();
+ $this->assertEquals($map->count(), 0);
+ $this->assertTrue(is_array($array));
+ }
+
+ public function testCannotConvertToString()
+ {
+ $map = new UtilMap();
+
+ try {
+ $map->put(new stdClass(), "hoge");
+ } catch (Sabel_Exception_Runtime $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testGet()
+ {
+ $map = new UtilMap();
+ $map->put("test1", "hoge")
+ ->put("test2", new String("fuga"))
+ ->put(new String("test3"), "foo")
+ ->put(new UtilMap(array(1)), "bar")
+ ->put(new UtilMap(array(1, 2)), new UtilMap(array(3, 4)));
+
+ $this->assertTrue($map->get("test1")->equals("hoge"));
+ $this->assertTrue($map->get("test2")->equals("fuga"));
+ $this->assertTrue($map->get("test3")->equals("foo"));
+ $this->assertTrue($map->get(new String("test3"))->equals("foo"));
+ $this->assertTrue($map->get(new UtilMap(array(1)))->equals("bar"));
+
+ $um = new UtilMap(array(3, 4));
+ $this->assertTrue($map->get(new UtilMap(array(1, 2)))->equals($um));
+
+ $this->assertNull($map->get("test4"));
+ $this->assertNull($map->get(new UtilMap(array(3, 4))));
+ }
+
+ public function testSort()
+ {
+ $fruits = array("lemon", "orange", "banana", "apple");
+ $map = new UtilMap($fruits);
+ $map->sort();
+
+ $this->assertTrue($map->get(0)->equals("apple"));
+ $this->assertTrue($map->get(1)->equals("banana"));
+ $this->assertTrue($map->get(2)->equals("lemon"));
+ $this->assertTrue($map->get(3)->equals("orange"));
+ }
+
+ public function testRsort()
+ {
+ $fruits = array("lemon", "orange", "banana", "apple");
+ $map = new UtilMap($fruits);
+ $map->rsort();
+
+ $this->assertTrue($map->get(0)->equals("orange"));
+ $this->assertTrue($map->get(1)->equals("lemon"));
+ $this->assertTrue($map->get(2)->equals("banana"));
+ $this->assertTrue($map->get(3)->equals("apple"));
+ }
+
+ public function testReverse()
+ {
+ $fruits = array("lemon", "orange", "banana", "apple");
+ $map = new UtilMap($fruits);
+ $map->reverse();
+
+ $this->assertTrue($map->get(0)->equals("apple"));
+ $this->assertTrue($map->get(1)->equals("banana"));
+ $this->assertTrue($map->get(2)->equals("orange"));
+ $this->assertTrue($map->get(3)->equals("lemon"));
+ }
+
+ public function testMerge()
+ {
+ $data = array("test1" => "hoge", "test2" => "fuga");
+ $map = new UtilMap($data);
+ $this->assertEquals($map->count(), 2);
+
+ $data = array("test3" => "foo", "test4" => "bar");
+ $map->merge($data);
+ $this->assertEquals($map->count(), 4);
+
+ $values = $map->values();
+
+ $this->assertTrue($values[0]->equals("hoge"));
+ $this->assertTrue($values[1]->equals("fuga"));
+ $this->assertTrue($values[2]->equals("foo"));
+ $this->assertTrue($values[3]->equals("bar"));
+
+ $data = array("test5" => "biz", "test6" => "buz");
+ $map->merge(new UtilMap($data));
+ $this->assertEquals($map->count(), 6);
+
+ $values = $map->values();
+
+ $this->assertTrue($values[0]->equals("hoge"));
+ $this->assertTrue($values[1]->equals("fuga"));
+ $this->assertTrue($values[2]->equals("foo"));
+ $this->assertTrue($values[3]->equals("bar"));
+ $this->assertTrue($values[4]->equals("biz"));
+ $this->assertTrue($values[5]->equals("buz"));
+ }
+
+ public function testUnique()
+ {
+ $data = array("test1" => "hoge", "test2" => "fuga");
+ $map = new UtilMap($data);
+ $map->put("test4", "foo")
+ ->put("test5", "hoge")
+ ->put("test6", "fuga");
+
+ $this->assertEquals($map->count(), 5);
+ $this->assertEquals($map->unique()->count(), 3);
+ }
+
+ public function testSum()
+ {
+ $data = array("test1" => 2, "test2" => 3, "test3" => 5);
+ $map = new UtilMap($data);
+ $this->assertEquals($map->sum(), 10);
+
+ $data = array("test1" => 2.2, "test2" => 5.3, "test3" => 2.5);
+ $this->assertEquals($map->sum(), 10.0);
+ }
+
+ public function testHas()
+ {
+ $map = new UtilMap();
+ $map->put("test1", "hoge")
+ ->put("test2", null)
+ ->put(new UtilMap(), "fuga");
+
+ $this->assertTrue($map->has("test1"));
+ $this->assertFalse($map->has("test2"));
+ $this->assertTrue($map->exists("test2"));
+ $this->assertTrue($map->has(new UtilMap()));
+
+ $this->assertFalse($map->has("test4"));
+ $this->assertFalse($map->has(new UtilMap(array(1))));
+ }
+
+ public function testPop()
+ {
+ $map = new UtilMap();
+ $map->put("test1", "hoge")
+ ->put("test2", "fuga");
+
+ $this->assertTrue($map->has("test1"));
+ $this->assertTrue($map->has("test2"));
+
+ $this->assertTrue($map->pop()->equals("fuga"));
+ $this->assertTrue($map->has("test1"));
+ $this->assertFalse($map->has("test2"));
+ $this->assertEquals($map->count(), 1);
+ }
+
+ public function testShift()
+ {
+ $map = new UtilMap();
+ $map->put("test1", "hoge")
+ ->put("test2", "fuga");
+
+ $this->assertTrue($map->has("test1"));
+ $this->assertTrue($map->has("test2"));
+
+ $this->assertTrue($map->shift()->equals("hoge"));
+ $this->assertFalse($map->has("test1"));
+ $this->assertTrue($map->has("test2"));
+ $this->assertEquals($map->count(), 1);
+ }
+
+ public function testSearch()
+ {
+ $map = new UtilMap();
+ $map->put("test1", "hoge")
+ ->put("test2", new String("bar"))
+ ->put(new String("test3"), "biz")
+ ->put("test4", new UtilMap(array(1)))
+ ->put("test5", new UtilMap(array("test" => "buz")))
+ ->put(new UtilMap(array(1, 2, 3)), "hello world");
+
+ $this->assertTrue($map->search("hoge")->equals("test1"));
+ $this->assertFalse($map->search("hogehoge"));
+
+ $this->assertTrue($map->search("bar")->equals("test2"));
+ $this->assertTrue($map->search(new String("bar"))->equals("test2"));
+
+ $this->assertTrue($map->search("biz")->equals("test3"));
+ $this->assertTrue($map->search(new String("biz"))->equals("test3"));
+
+ $this->assertTrue($map->search(new UtilMap(array(1)))->equals("test4"));
+ $this->assertTrue($map->search(new UtilMap(array("test" => "buz")))->equals("test5"));
+ $this->assertFalse($map->search(new UtilMap(array(2))));
+ $this->assertFalse($map->search(new UtilMap(array("test" => "abc"))));
+
+ $um = new UtilMap(array(1, 2, 3));
+ $this->assertTrue($map->get($um)->equals("hello world"));
+ }
+}
diff --git a/Test/Util/String.php b/Test/Util/String.php
new file mode 100755
index 0000000..3638327
--- /dev/null
+++ b/Test/Util/String.php
@@ -0,0 +1,408 @@
+
+ */
+class Test_Util_String extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_Util_String");
+ }
+
+ public function testIsEmpty()
+ {
+ $string = new String();
+ $this->assertTrue($string->isEmpty());
+ }
+
+ public function testNotString()
+ {
+ try {
+ $string = new String(10000);
+ } catch (Sabel_Exception_InvalidArgument $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testCharAt()
+ {
+ $string = new String("test");
+ $this->assertEquals("t", $string->charAt(0)->toString());
+ $this->assertEquals("e", $string->charAt(1)->toString());
+ $this->assertEquals("t", $string->charAt(3)->toString());
+ $this->assertEquals("", $string->charAt(4)->toString());
+ $this->assertEquals("", $string->charAt(-1)->toString());
+
+ $string = new String("あいうえお");
+ $this->assertEquals("い", $string->charAt(1)->toString());
+ $this->assertEquals("え", $string->charAt(3)->toString());
+ $this->assertEquals("", $string->charAt(5)->toString());
+ }
+
+ public function testIndexOf()
+ {
+ $string = new String("Hello World");
+ $this->assertEquals(6, $string->indexOf("W"));
+ $this->assertEquals(4, $string->indexOf("o"));
+ $this->assertEquals(7, $string->indexOf("o", 5));
+
+ $string = new String("あいうえお あいうえお");
+ $this->assertEquals(2, $string->indexOf("う"));
+ $this->assertEquals(5, $string->indexOf(" "));
+ $this->assertEquals(8, $string->indexOf("う", 5));
+ }
+
+ public function testLastChar()
+ {
+ $string = new String("Hello World");
+ $this->assertEquals("d", $string->last()->toString());
+
+ $string = new String();
+ $this->assertEquals("", $string->last()->toString());
+
+ $string = new String("あいうえお");
+ $this->assertEquals("お", $string->last()->toString());
+ }
+
+ public function testTrim()
+ {
+ $string = new String(" Hello World ");
+ $this->assertTrue($string->trim()->equals("Hello World"));
+
+ $string = new String("/*/*/Hello World/*/*/");
+ $this->assertTrue($string->trim("/*")->equals("Hello World"));
+
+ $str = <<assertTrue($string->trim()->equals("aiueo"));
+
+ // multibyte
+
+ $string = new String(" あいうえお ");
+ $this->assertTrue($string->trim()->equals("あいうえお"));
+
+ $string = new String("表能申あいうえお申能表");
+ $this->assertTrue($string->trim("表能申")->equals("あいうえお"));
+
+ $str = <<assertTrue($string->trim()->equals("あいうえお"));
+ }
+
+ public function testRtrim()
+ {
+ $string = new String(" Hello World ");
+ $this->assertTrue($string->rtrim()->equals(" Hello World"));
+
+ $string = new String(" あいうえお ");
+ $this->assertTrue($string->rtrim()->equals(" あいうえお"));
+ }
+
+ public function testLtrim()
+ {
+ $string = new String(" Hello World ");
+ $this->assertTrue($string->ltrim()->equals("Hello World "));
+
+ $string = new String(" あいうえお ");
+ $this->assertTrue($string->ltrim()->equals("あいうえお "));
+ }
+
+ public function testToUpperCase()
+ {
+ $string = new String("Hello World");
+ $this->assertTrue($string->toUpperCase()->equals("HELLO WORLD"));
+ }
+
+ public function testToLowerCase()
+ {
+ $string = new String("Hello World");
+ $this->assertTrue($string->toLowerCase()->equals("hello world"));
+ }
+
+ public function testUcFirst()
+ {
+ $string = new String("test");
+ $this->assertTrue($string->ucfirst()->equals("Test"));
+ }
+
+ public function testLcFirst()
+ {
+ $string = new String("ABCDE");
+ $this->assertTrue($string->lcfirst()->equals("aBCDE"));
+ }
+
+ public function testExplode()
+ {
+ $string = new String("hoge:fuga:foo:bar");
+ $array = $string->explode(":");
+ $this->assertEquals("hoge", $array[0]);
+ $this->assertEquals("fuga", $array[1]);
+ $this->assertEquals("foo", $array[2]);
+ $this->assertEquals("bar", $array[3]);
+
+ $string = new String("hoge:fuga:foo:bar");
+ $array = $string->explode(":", 3);
+ $this->assertEquals("hoge", $array[0]);
+ $this->assertEquals("fuga", $array[1]);
+ $this->assertEquals("foo:bar", $array[2]);
+ }
+
+ public function testSplit()
+ {
+ $string = new String("hoge.fuga.foo.bar");
+ $array = $string->split();
+ $this->assertEquals("h", $array[0]);
+ $this->assertEquals("f", $array[5]);
+ $this->assertEquals("f", $array[10]);
+ $this->assertEquals("b", $array[14]);
+
+ $array = $string->split(3);
+ $this->assertEquals("hog", $array[0]);
+ $this->assertEquals("e.f", $array[1]);
+ $this->assertEquals("uga", $array[2]);
+ $this->assertEquals(".fo", $array[3]);
+ $this->assertEquals("o.b", $array[4]);
+ $this->assertEquals("ar", $array[5]);
+
+ // multibyte
+
+ $string = new String("あいうえおかきくけこ");
+ $array = $string->split();
+ $this->assertEquals("あ", $array[0]);
+ $this->assertEquals("う", $array[2]);
+ $this->assertEquals("お", $array[4]);
+ $this->assertEquals("き", $array[6]);
+ $this->assertEquals("け", $array[8]);
+
+ $array = $string->split(3);
+ $this->assertEquals("あいう", $array[0]);
+ $this->assertEquals("えおか", $array[1]);
+ $this->assertEquals("きくけ", $array[2]);
+ $this->assertEquals("こ", $array[3]);
+ }
+
+ public function testReplace()
+ {
+ $string = new String("hoge huga");
+ $this->assertTrue($string->replace("hoge", "foo")->equals("foo huga"));
+ }
+
+ public function testAppend()
+ {
+ $string = new String("hoge");
+ $this->assertTrue($string->append("hoge")->equals("hogehoge"));
+
+ $hoge = new String("hoge");
+ $huga = new String("huga");
+ $this->assertTrue($hoge->append($huga)->equals("hogehuga"));
+ }
+
+ public function testEquals()
+ {
+ $string = new String("hoge");
+ $this->assertTrue($string->equals("hoge"));
+ $this->assertTrue($string->equals("huga", "hoge"));
+ $this->assertFalse($string->equals("huga"));
+ $this->assertFalse($string->equals("huga", "foo"));
+
+ $hoge1 = new String("hoge");
+ $hoge2 = new String("hoge");
+
+ $this->assertTrue($hoge1->equals($hoge2));
+ }
+
+ public function testSha1()
+ {
+ $string = new String("hoge");
+ $this->assertEquals(sha1("hoge"), $string->sha1()->toString());
+ }
+
+ public function testMd5()
+ {
+ $string = new String("hoge");
+ $this->assertEquals(md5("hoge"), $string->md5()->toString());
+ }
+
+ public function testSucc()
+ {
+ $string = new String("a");
+ $this->assertTrue($string->succ()->equals("b"));
+ $this->assertTrue($string->succ()->equals("c"));
+
+ $string = new String("00");
+ $this->assertTrue($string->succ()->equals("01"));
+ $this->assertTrue($string->succ()->equals("02"));
+
+ $string = new String("99");
+ $this->assertTrue($string->succ()->equals("100"));
+ $this->assertTrue($string->succ()->equals("101"));
+
+ $string = new String("y");
+ $this->assertTrue($string->succ()->equals("z"));
+ $this->assertTrue($string->succ()->equals("aa"));
+ $this->assertTrue($string->succ()->equals("ab"));
+
+ $string = new String("Y");
+ $this->assertTrue($string->succ()->equals("Z"));
+ $this->assertTrue($string->succ()->equals("AA"));
+ $this->assertTrue($string->succ()->equals("AB"));
+
+ $string = new String("ay");
+ $this->assertTrue($string->succ()->equals("az"));
+ $this->assertTrue($string->succ()->equals("ba"));
+ $this->assertTrue($string->succ()->equals("bb"));
+
+ $string = new String("aY");
+ $this->assertTrue($string->succ()->equals("aZ"));
+ $this->assertTrue($string->succ()->equals("bA"));
+ $this->assertTrue($string->succ()->equals("bB"));
+
+ $string = new String("0Y");
+ $this->assertTrue($string->succ()->equals("0Z"));
+ $this->assertTrue($string->succ()->equals("1A"));
+ $this->assertTrue($string->succ()->equals("1B"));
+
+ $string = new String("9Y");
+ $this->assertTrue($string->succ()->equals("9Z"));
+ $this->assertTrue($string->succ()->equals("10A"));
+ $this->assertTrue($string->succ()->equals("10B"));
+
+ $string = new String("A998");
+ $this->assertTrue($string->succ()->equals("A999"));
+ $this->assertTrue($string->succ()->equals("B000"));
+ $this->assertTrue($string->succ()->equals("B001"));
+ }
+
+ public function testSubString()
+ {
+ $string = new String("Hello World");
+
+ $str = $string->substring(6);
+ $this->assertTrue($str->equals("World"));
+ $this->assertTrue($string->equals("Hello World"));
+
+ $str = $string->substring(6, 3);
+ $this->assertTrue($str->equals("Wor"));
+
+ $str = $string->substring(1, -1);
+ $this->assertTrue($str->equals("ello Worl"));
+
+ $string = new String("あいうえおかきくけこ");
+
+ $str = $string->substring(5);
+ $this->assertTrue($str->equals("かきくけこ"));
+
+ $str = $string->substring(6, 3);
+ $this->assertTrue($str->equals("きくけ"));
+
+ $str = $string->substring(1, -1);
+ $this->assertTrue($str->equals("いうえおかきくけ"));
+ }
+
+ public function testInsert()
+ {
+ $string = new String("Hello World");
+ $string->insert(6, "PHP ");
+ $this->assertTrue($string->equals("Hello PHP World"));
+
+ $string = new String("Hello World");
+ $string->insert(0, "PHP. ");
+ $this->assertTrue($string->equals("PHP. Hello World"));
+
+ $string = new String("あいうえお さしすせそ");
+ $string->insert(6, "かきくけこ ");
+ $this->assertTrue($string->equals("あいうえお かきくけこ さしすせそ"));
+ }
+
+ public function testPad()
+ {
+ $string = new String("1");
+ $this->assertEquals(" 1", $string->pad(" ", 4)->toString());
+
+ $string = new String("1");
+ $this->assertEquals("1 ", $string->pad(" ", 4, STR_PAD_RIGHT)->toString());
+
+ $string = new String("1");
+ $this->assertEquals("=.=.1", $string->pad("=.", 5)->toString());
+
+ $string = new String("1");
+ $this->assertEquals("1=.=.", $string->pad("=.", 5, STR_PAD_RIGHT)->toString());
+
+ $string = new String("1");
+ $this->assertEquals("=.=1", $string->pad("=.", 4)->toString());
+
+ $string = new String("1");
+ $this->assertEquals("_1_", $string->pad("_", 3, STR_PAD_BOTH)->toString());
+
+ $string = new String("1");
+ $this->assertEquals("_1__", $string->pad("_", 4, STR_PAD_BOTH)->toString());
+
+ $string = new String("1");
+ $this->assertEquals("__1__", $string->pad("_", 5, STR_PAD_BOTH)->toString());
+
+ $string = new String("123");
+ $this->assertEquals("aba123abab", $string->pad("ab", 10, STR_PAD_BOTH)->toString());
+
+ // multibyte
+
+ $string = new String("あ");
+ $this->assertEquals("いいいあ", $string->pad("い", 4)->toString());
+
+ $string = new String("あ");
+ $this->assertEquals("あいいい", $string->pad("い", 4, STR_PAD_RIGHT)->toString());
+
+ $string = new String("あ");
+ $this->assertEquals("いういうあ", $string->pad("いう", 5)->toString());
+
+ $string = new String("あ");
+ $this->assertEquals("あいういう", $string->pad("いう", 5, STR_PAD_RIGHT)->toString());
+
+ $string = new String("あ");
+ $this->assertEquals("いういあ", $string->pad("いう", 4)->toString());
+
+ $string = new String("あ");
+ $this->assertEquals("いあい", $string->pad("い", 3, STR_PAD_BOTH)->toString());
+
+ $string = new String("あ");
+ $this->assertEquals("いあいい", $string->pad("い", 4, STR_PAD_BOTH)->toString());
+
+ $string = new String("あ");
+ $this->assertEquals("いいあいい", $string->pad("い", 5, STR_PAD_BOTH)->toString());
+
+ $string = new String("あいう");
+ $this->assertEquals("えおえあいうえおえお", $string->pad("えお", 10, STR_PAD_BOTH)->toString());
+ }
+
+ public function testClone()
+ {
+ $string = new String("Hello World");
+ $cloned = $string->cloning();
+ $this->assertTrue($string == $cloned);
+ $this->assertFalse($string === $cloned);
+ }
+}
diff --git a/Test/Util/Tests.php b/Test/Util/Tests.php
new file mode 100755
index 0000000..3acecdd
--- /dev/null
+++ b/Test/Util/Tests.php
@@ -0,0 +1,26 @@
+
+ */
+class Test_Util_Tests
+{
+ public static function suite()
+ {
+ $suite = new PHPUnit_Framework_TestSuite();
+ $suite->addTest(Test_Util_String::suite());
+ $suite->addTest(Test_Util_Map::suite());
+ $suite->addTest(Test_Util_LinkedList::suite());
+ $suite->addTest(Test_Util_HashList::suite());
+ $suite->addTest(Test_Util_FileSystem::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/ValueObject.php b/Test/ValueObject.php
new file mode 100755
index 0000000..82bb11e
--- /dev/null
+++ b/Test/ValueObject.php
@@ -0,0 +1,147 @@
+
+ */
+class Test_ValueObject extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_ValueObject");
+ }
+
+ /**
+ * @test
+ */
+ public function simpleTest()
+ {
+ $object = new Sabel_ValueObject();
+ $object->set("a", 10);
+ $object->set("b", "20");
+ $object->set("c", true);
+ $object->set("d", new stdClass());
+ $object->set("e", array("foo", "bar"));
+
+ $this->assertEquals(10, $object->get("a"));
+ $this->assertEquals("20", $object->get("b"));
+ $this->assertEquals(true, $object->get("c"));
+ $this->assertEquals(new stdClass(), $object->get("d"));
+ $this->assertEquals(array("foo", "bar"), $object->get("e"));
+ }
+
+ /**
+ * @test
+ */
+ public function simpleTest2()
+ {
+ $object = new Sabel_ValueObject();
+ $object->a = 10;
+ $object->b = "20";
+ $object->c = true;
+ $object->d = new stdClass();
+ $object->e = array("foo", "bar");
+
+ $this->assertEquals(10, $object->a);
+ $this->assertEquals("20", $object->b);
+ $this->assertEquals(true, $object->c);
+ $this->assertEquals(new stdClass(), $object->d);
+ $this->assertEquals(array("foo", "bar"), $object->e);
+ }
+
+ /**
+ * @test
+ */
+ public function toArray()
+ {
+ $object = new Sabel_ValueObject();
+ $object->a = 10;
+ $object->b = "20";
+ $object->c = true;
+
+ $array = $object->toArray();
+
+ $this->assertEquals(10, $array["a"]);
+ $this->assertEquals("20", $array["b"]);
+ $this->assertEquals(true, $array["c"]);
+ }
+
+ /**
+ * @test
+ */
+ public function fromArray()
+ {
+ $object = Sabel_ValueObject::fromArray(array(
+ "a" => 10,
+ "b" => "20",
+ "c" => true
+ ));
+
+ $this->assertEquals(10, $object->a);
+ $this->assertEquals("20", $object->b);
+ $this->assertEquals(true, $object->c);
+ }
+
+ /**
+ * @test
+ */
+ public function containts()
+ {
+ $object = new Sabel_ValueObject();
+ $object->a = 10;
+ $object->b = null;
+
+ $this->assertTrue($object->has("a"));
+ $this->assertFalse($object->has("b"));
+ $this->assertTrue($object->exists("a"));
+ $this->assertTrue($object->exists("b"));
+ }
+
+ /**
+ * @test
+ */
+ public function remove()
+ {
+ $object = new Sabel_ValueObject();
+ $object->a = 10;
+
+ $this->assertTrue($object->has("a"));
+
+ $object->remove("a");
+
+ $this->assertFalse($object->has("a"));
+ }
+
+ /**
+ * @test
+ */
+ public function merge()
+ {
+ $object = new Sabel_ValueObject();
+ $object->a = 10;
+ $object->b = 20;
+
+ $this->assertEquals(2, count($object->toArray()));
+
+ $object->merge(array("c" => 30, "d" => 40));
+
+ $this->assertEquals(4, count($object->toArray()));
+
+ $object2 = new Sabel_ValueObject();
+ $object2->e = 50;
+ $object2->f = 60;
+
+ $object->merge($object2);
+
+ $this->assertEquals(6, count($object->toArray()));
+
+ $this->assertEquals(10, $object->a);
+ $this->assertEquals(20, $object->b);
+ $this->assertEquals(30, $object->c);
+ $this->assertEquals(40, $object->d);
+ $this->assertEquals(50, $object->e);
+ $this->assertEquals(60, $object->f);
+ }
+}
diff --git a/Test/View/PageViewer.php b/Test/View/PageViewer.php
new file mode 100755
index 0000000..4151e2c
--- /dev/null
+++ b/Test/View/PageViewer.php
@@ -0,0 +1,63 @@
+
+ */
+class Test_View_PageViewer extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_View_PageViewer");
+ }
+
+ public function testStandardUse()
+ {
+ $pager = new Sabel_View_Pager(200, 10);
+ $pager->setPageNumber(10);
+ $pv = new Sabel_View_PageViewer($pager);
+
+ $this->assertTrue($pv->isCurrent());
+ $this->assertFalse($pv->isFirst());
+
+ $this->assertEquals(10, $pv->getCurrent());
+ $this->assertEquals(11, $pv->getNext());
+ $this->assertEquals(9, $pv->getPrevious());
+
+ $num = 5;
+ foreach ($pv as $page) $this->assertEquals($num++, $page->getCurrent());
+ $this->assertEquals(15, $num);
+ $this->assertEquals(10, $pv->getCurrent());
+ $this->assertEquals(11, $pv->getNext());
+ $this->assertEquals(9, $pv->getPrevious());
+
+ $this->assertEquals(20, $pv->getLast());
+ $this->assertEquals(1, $pv->getFirst());
+ }
+
+ public function testStandardUseAndPageNumberFirst()
+ {
+ $pager = new Sabel_View_Pager(200, 10);
+ $pager->setPageNumber(1);
+ $pv = new Sabel_View_PageViewer($pager, 5);
+
+ $this->assertTrue($pv->isFirst());
+ $this->assertTrue($pv->isCurrent());
+ $this->assertFalse($pv->isLast());
+
+ $this->assertEquals(1, $pv->getCurrent());
+ $this->assertEquals(2, $pv->getNext());
+ $this->assertEquals(1, $pv->getPrevious());
+
+ $num = 1;
+ foreach ($pv as $page) $this->assertEquals($num++, $page->getCurrent());
+ $this->assertEquals(6, $num);
+ $this->assertEquals(1, $pv->getCurrent());
+ $this->assertEquals(2, $pv->getNext());
+ $this->assertEquals(1, $pv->getPrevious());
+ }
+
+ // @todo more tests
+}
diff --git a/Test/View/Pager.php b/Test/View/Pager.php
new file mode 100755
index 0000000..5a85502
--- /dev/null
+++ b/Test/View/Pager.php
@@ -0,0 +1,95 @@
+
+ */
+class Test_View_Pager extends SabelTestCase
+{
+ private $pager = null;
+
+ public static function suite()
+ {
+ return self::createSuite("Test_View_Pager");
+ }
+
+ public function testStandardPagerUse()
+ {
+ $pager = new Sabel_View_Pager(100, 10);
+ $pager->setPageNumber(3);
+
+ $this->assertEquals(100, $pager->getNumberOfItem());
+ $this->assertEquals(10, $pager->getLimit());
+ $this->assertEquals(3, $pager->getPageNumber());
+ $this->assertEquals(10, $pager->getTotalPageNumber());
+ $this->assertEquals(20, $pager->getSqlOffset());
+ }
+
+ public function testPageNumberRoundPagerUse()
+ {
+ $pager = new Sabel_View_Pager(300, 70);
+ $pager->setPageNumber(100);
+
+ $this->assertEquals(300, $pager->getNumberOfItem());
+ $this->assertEquals(70, $pager->getLimit());
+ $this->assertEquals(5, $pager->getPageNumber());
+ $this->assertEquals(5, $pager->getTotalPageNumber());
+ $this->assertEquals(280, $pager->getSqlOffset());
+ }
+
+ public function testExceptedPagerUse()
+ {
+ try {
+ $pager = new Sabel_View_Pager(-1, 10);
+ } catch (Sabel_Exception_InvalidArgument $e) {
+ return;
+ }
+
+ $this->fail('set number of item method not thrown.');
+ }
+
+ public function testExceptedPagerUse2()
+ {
+ try {
+ $pager = new Sabel_View_Pager('a', 10);
+ } catch (Sabel_Exception_InvalidArgument $e) {
+ return;
+ }
+
+ $this->fail('set number of item method not thrown.');
+ }
+
+ public function testUnusualPagerUse()
+ {
+ $pager = new Sabel_View_Pager(1, 1);
+ $pager->setPageNumber(10);
+
+ $this->assertEquals(1, $pager->getPageNumber());
+ $this->assertEquals(1, $pager->getTotalPageNumber());
+ $this->assertEquals(0, $pager->getSqlOffset());
+
+ $pager = new Sabel_View_Pager(250, 15);
+ $pager->setPageNumber(10);
+
+ $this->assertEquals(250, $pager->getNumberOfItem());
+ $this->assertEquals(15, $pager->getLimit());
+ $this->assertEquals(10, $pager->getPageNumber());
+ $this->assertEquals(17, $pager->getTotalPageNumber());
+ $this->assertEquals(135, $pager->getSqlOffset());
+ }
+
+ public function testInitializedPagerUse()
+ {
+ $pager = new Sabel_View_Pager(200, 20);
+
+ $pager->setPageNumber(4.3);
+
+ $this->assertEquals(200, $pager->getNumberOfItem());
+ $this->assertEquals(20, $pager->getLimit());
+ $this->assertEquals(4, $pager->getPageNumber());
+ $this->assertEquals(10, $pager->getTotalPageNumber());
+ $this->assertEquals(60, $pager->getSqlOffset());
+ }
+}
diff --git a/Test/View/Renderer.php b/Test/View/Renderer.php
new file mode 100755
index 0000000..dcc6a52
--- /dev/null
+++ b/Test/View/Renderer.php
@@ -0,0 +1,37 @@
+
+ */
+class Test_View_Renderer extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_View_Renderer");
+ }
+
+ public function testRenderingFromString()
+ {
+ $renderer = new Sabel_View_Renderer();
+ $contents = 'name: ';
+ $result = $renderer->rendering($contents, array("name" => "hoge"));
+ $this->assertEquals("name: hoge", $result);
+ }
+
+ public function testRenderingFromFile()
+ {
+ $renderer = new Sabel_View_Renderer();
+ $path = MODULES_DIR_PATH . "/views/test.tpl";
+ $result = $renderer->rendering(null, array("a" => "10", "b" => "20"), $path);
+
+ $expected = <<
+b: 20
+CONTENTS;
+
+ $this->assertEquals($expected, rtrim($result));
+ }
+}
diff --git a/Test/View/Template.php b/Test/View/Template.php
new file mode 100755
index 0000000..0229cce
--- /dev/null
+++ b/Test/View/Template.php
@@ -0,0 +1,114 @@
+
+ */
+abstract class Test_View_Template extends SabelTestCase
+{
+ protected static $view = null;
+
+ public function testValidTemplate()
+ {
+ $template = self::$view->getValidLocation("index");
+ $this->assertTrue($template instanceof Sabel_View_Location);
+ $this->assertEquals(MODULES_DIR_PATH . DS . "index" . DS . VIEW_DIR_NAME . DS . "hoge" . DS . "index" . TPL_SUFFIX, $template->getPath());
+
+ $template = self::$view->getValidLocation("hoge");
+ $this->assertTrue($template instanceof Sabel_View_Location);
+ $this->assertEquals(MODULES_DIR_PATH . DS . "index" . DS . VIEW_DIR_NAME . DS . "hoge" . DS . "hoge" . TPL_SUFFIX, $template->getPath());
+
+ $template = self::$view->getValidLocation("error");
+ $this->assertTrue($template instanceof Sabel_View_Location);
+ $this->assertEquals(MODULES_DIR_PATH . DS . "index" . DS . VIEW_DIR_NAME . DS . "error" . TPL_SUFFIX, $template->getPath());
+ }
+
+ public function testInvalidTemplate()
+ {
+ $template = self::$view->getValidLocation("fuga");
+ $this->assertNull($template);
+
+ $template = self::$view->getValidLocation("abcdef");
+ $this->assertNull($template);
+ }
+
+ public function testSetup2()
+ {
+ $repository = $this->createRepository("fuga");
+
+ $this->assertEquals(3, count($repository->getLocations()));
+ $this->assertTrue($repository->getLocation("controller") instanceof Sabel_View_Location);
+ $this->assertTrue($repository->getLocation("module") instanceof Sabel_View_Location);
+ $this->assertTrue($repository->getLocation("app") instanceof Sabel_View_Location);
+ $this->assertNull($repository->getLocation("fuga"));
+ }
+
+ public function testValidTemplate2()
+ {
+ $template = self::$view->getValidLocation("index");
+ $this->assertTrue($template instanceof Sabel_View_Location);
+ $this->assertEquals(MODULES_DIR_PATH . DS . "index" . DS . VIEW_DIR_NAME . DS . "fuga" . DS . "index" . TPL_SUFFIX, $template->getPath());
+
+ $template = self::$view->getValidLocation("fuga");
+ $this->assertTrue($template instanceof Sabel_View_Location);
+ $this->assertEquals(MODULES_DIR_PATH . DS . "index" . DS . VIEW_DIR_NAME . DS . "fuga" . DS . "fuga" . TPL_SUFFIX, $template->getPath());
+
+ $template = self::$view->getValidLocation("error");
+ $this->assertTrue($template instanceof Sabel_View_Location);
+ $this->assertEquals(MODULES_DIR_PATH . DS . "index" . DS . VIEW_DIR_NAME . DS . "error" . TPL_SUFFIX, $template->getPath());
+ }
+
+ public function testInvalidTemplate2()
+ {
+ $template = self::$view->getValidLocation("hoge");
+ $this->assertNull($template);
+
+ $template = self::$view->getValidLocation("abcdef");
+ $this->assertNull($template);
+ }
+
+ public function testGetContents()
+ {
+ $template = self::$view->getValidLocation("index");
+ $contents = $template->getContents();
+ $this->assertEquals("fuga/index.tpl", rtrim($contents));
+
+ $template = self::$view->getValidLocation("fuga");
+ $contents = $template->getContents();
+ $this->assertEquals("fuga/fuga.tpl", rtrim($contents));
+ }
+
+ public function testIsValid()
+ {
+ $this->assertTrue(self::$view->isValid("controller", "index"));
+ $this->assertTrue(self::$view->isValid("controller", "fuga"));
+
+ $this->assertTrue(self::$view->isValid("module", "error"));
+ $this->assertFalse(self::$view->isValid("module", "fuga"));
+
+ $this->assertTrue(self::$view->isValid("app", "serverError"));
+ $this->assertFalse(self::$view->isValid("app", "error"));
+ $this->assertFalse(self::$view->isValid("app", "index"));
+
+ try {
+ self::$view->isValid("hoge", "index");
+ } catch (Exception $e) {
+ return;
+ }
+
+ $this->fail();
+ }
+
+ public function testCreate()
+ {
+ $time = microtime();
+ self::$view->create("controller", "new", $time);
+ $this->assertEquals($time, trim(self::$view->getContents("new")));
+ }
+
+ public function testDelete()
+ {
+ self::$view->delete("controller", "new");
+ $this->assertNull(self::$view->getValidLocation("new"));
+ }
+}
diff --git a/Test/View/TemplateDb.php b/Test/View/TemplateDb.php
new file mode 100755
index 0000000..312f265
--- /dev/null
+++ b/Test/View/TemplateDb.php
@@ -0,0 +1,100 @@
+
+ */
+class Test_View_TemplateDb extends Test_View_Template
+{
+ public static function suite()
+ {
+ $base = dirname(__FILE__) . DS . "templates";
+ if (!defined("MODULES_DIR_PATH")) define("MODULES_DIR_PATH", $base);
+
+ if (self::initTable()) {
+ return self::createSuite("Test_View_TemplateDb");
+ } else {
+ return self::createSuite("");
+ }
+ }
+
+ public function testSetup()
+ {
+ $repository = $this->createRepository("hoge");
+
+ $this->assertEquals(3, count($repository->getLocations()));
+ $this->assertTrue($repository->getLocation("controller") instanceof Sabel_View_Location);
+ $this->assertTrue($repository->getLocation("module") instanceof Sabel_View_Location);
+ $this->assertTrue($repository->getLocation("app") instanceof Sabel_View_Location);
+ $this->assertNull($repository->getLocation("hoge"));
+ }
+
+ protected function createRepository($controllerName)
+ {
+ $controller = new Sabel_View_Location_Database("index" . DS . VIEW_DIR_NAME . DS . $controllerName . DS);
+ $view = new Sabel_View_Object("controller", $controller);
+
+ $module = new Sabel_View_Location_Database("index" . DS . VIEW_DIR_NAME . DS);
+ $view->addLocation("module", $module);
+
+ $app = new Sabel_View_Location_Database(VIEW_DIR_NAME . DS);
+ $view->addLocation("app", $app);
+
+ return self::$view = $view;
+ }
+
+ private static function initTable()
+ {
+ if (extension_loaded("mysql")) {
+ $params = array("package" => "sabel.db.mysql",
+ "host" => "127.0.0.1",
+ "user" => "root",
+ "password" => "",
+ "database" => "sdb_test");
+ } elseif (extension_loaded("pgsql")) {
+ $params = array("package" => "sabel.db.pgsql",
+ "host" => "127.0.0.1",
+ "user" => "root",
+ "password" => "",
+ "database" => "sdb_test");
+ } elseif (extension_loaded("pdo_sqlite")) {
+ $params = array("package" => "sabel.db.pdo.sqlite",
+ "database" => SABEL_BASE . "/Test/data/sdb_test.sq3");
+ } else {
+ Sabel_Console::message("skipped 'TemplateDb'.");
+ return false;
+ }
+
+ Sabel_Db_Config::add("default", $params);
+ $stmt = Sabel_Db::createStatement();
+ $tblName = $stmt->quoteIdentifier("sbl_template");
+ $nCol = $stmt->quoteIdentifier("name");
+ $nsCol = $stmt->quoteIdentifier("namespace");
+ $cCol = $stmt->quoteIdentifier("contents");
+ $stmt->setQuery("DELETE FROM $tblName")->execute();
+
+ $data = array();
+ $data[0]["path"] = "views" . DS . "serverError" . TPL_SUFFIX;
+ $data[0]["cont"] = "";
+ $data[1]["path"] = "index" . DS . "views" . DS . "error" . TPL_SUFFIX;
+ $data[1]["cont"] = "";
+ $data[2]["path"] = "index" . DS . "views" . DS . "hoge" . DS . "index" . TPL_SUFFIX;
+ $data[2]["cont"] = "hoge/index.tpl";
+ $data[3]["path"] = "index" . DS . "views" . DS . "hoge" . DS . "hoge" . TPL_SUFFIX;
+ $data[3]["cont"] = "hoge/hoge.tpl";
+ $data[4]["path"] = "index" . DS . "views" . DS . "fuga" . DS . "index" . TPL_SUFFIX;
+ $data[4]["cont"] = "fuga/index.tpl";
+ $data[5]["path"] = "index" . DS . "views" . DS . "fuga" . DS . "fuga" . TPL_SUFFIX;
+ $data[5]["cont"] = "fuga/fuga.tpl";
+
+ foreach ($data as $d) {
+ $query = "INSERT INTO {$tblName}({$nCol}, {$nsCol}, {$cCol}) VALUES('{$d["path"]}', '', '{$d["cont"]}')";
+ $stmt->setQuery($query)->execute();
+ }
+
+ return true;
+ }
+}
diff --git a/Test/View/TemplateFile.php b/Test/View/TemplateFile.php
new file mode 100755
index 0000000..a1adf3f
--- /dev/null
+++ b/Test/View/TemplateFile.php
@@ -0,0 +1,40 @@
+
+ */
+class Test_View_TemplateFile extends Test_View_Template
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_View_TemplateFile");
+ }
+
+ public function testSetup()
+ {
+ $repository = $this->createRepository("hoge");
+
+ $this->assertEquals(3, count($repository->getLocations()));
+ $this->assertTrue($repository->getLocation("controller") instanceof Sabel_View_Location);
+ $this->assertTrue($repository->getLocation("module") instanceof Sabel_View_Location);
+ $this->assertTrue($repository->getLocation("app") instanceof Sabel_View_Location);
+ $this->assertNull($repository->getLocation("hoge"));
+ }
+
+ protected function createRepository($controllerName)
+ {
+ $controller = new Sabel_View_Location_File("index" . DS . VIEW_DIR_NAME . DS . $controllerName . DS);
+ $view = new Sabel_View_Object("controller", $controller);
+
+ $module = new Sabel_View_Location_File("index" . DS . VIEW_DIR_NAME . DS);
+ $view->addLocation("module", $module);
+
+ $app = new Sabel_View_Location_File(VIEW_DIR_NAME . DS);
+ $view->addLocation("app", $app);
+
+ return self::$view = $view;
+ }
+}
diff --git a/Test/View/Tests.php b/Test/View/Tests.php
new file mode 100755
index 0000000..bf05421
--- /dev/null
+++ b/Test/View/Tests.php
@@ -0,0 +1,29 @@
+
+ */
+class Test_View_Tests
+{
+ public static function suite()
+ {
+ $suite = new PHPUnit_Framework_TestSuite();
+ $suite->addTest(Test_View_TemplateFile::suite());
+ // $suite->addTest(Test_View_TemplateDb::suite());
+ $suite->addTest(Test_View_Renderer::suite());
+ $suite->addTest(Test_View_Pager::suite());
+ $suite->addTest(Test_View_PageViewer::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/VirtualInheritance.php b/Test/VirtualInheritance.php
new file mode 100755
index 0000000..9a0af67
--- /dev/null
+++ b/Test/VirtualInheritance.php
@@ -0,0 +1,49 @@
+
+ */
+class Test_VirtualInheritance extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_VirtualInheritance");
+ }
+
+ public function testVirtualInheritance()
+ {
+ $inherited = new Sabel_Aspect_VirtualInheritProxy(new InheritedClass());
+ $inherited->inherit("VirtualParentClass")->inherit("VirtualParentClassTwo");
+
+ $this->assertEquals("method", $inherited->method());
+ $this->assertEquals("parentMethod 1", $inherited->parentMethod(1));
+ $this->assertEquals("parentMethodTwo 1 2", $inherited->parentMethodTwo(1, 2));
+ }
+}
+
+class InheritedClass
+{
+ public function method()
+ {
+ return "method";
+ }
+}
+
+class VirtualParentClass
+{
+ public function parentMethod($arg)
+ {
+ return "parentMethod $arg";
+ }
+}
+
+class VirtualParentClassTwo
+{
+ public function parentMethodTwo($arg1, $arg2)
+ {
+ return "parentMethodTwo $arg1 $arg2";
+ }
+}
diff --git a/Test/XML/Test.php b/Test/XML/Test.php
new file mode 100755
index 0000000..343c057
--- /dev/null
+++ b/Test/XML/Test.php
@@ -0,0 +1,640 @@
+
+ */
+class Test_XML_Test extends SabelTestCase
+{
+ public static function suite()
+ {
+ return self::createSuite("Test_XML_Test");
+ }
+
+ /**
+ * @test
+ */
+ public function initialize()
+ {
+ $this->outputUsersXml();
+ }
+
+ public function testDocument()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $test = $this->loadXML($xml, "simple");
+ $this->assertEquals("test", $test->tagName);
+ $this->assertEquals("utf-8", $xml->getEncoding());
+ $this->assertEquals("1.0", $xml->getVersion());
+ }
+
+ public function testAttribute()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $test = $this->loadXML($xml, "simple");
+ $this->assertEquals("foo", $test->getChild("foo")->at("attr"));
+ $this->assertEquals("bar", $test->getChild("bar")->at("attr"));
+ $this->assertEquals("baz", $test->getChild("baz")->at("attr"));
+ }
+
+ public function testAttributes()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $test = $this->loadXML($xml, "simple");
+ $attrs = $test->getChild("foo")->getAttributes();
+
+ $this->assertEquals(true, $attrs->has("a"));
+ $this->assertEquals(true, $attrs->has("b"));
+ $this->assertEquals(true, $attrs->has("c"));
+ $this->assertEquals(false, $attrs->has("d"));
+
+ $this->assertEquals("10", $attrs->get("a"));
+ $this->assertEquals("20", $attrs->get("b"));
+ $this->assertEquals("30", $attrs->get("c"));
+ $this->assertEquals(null, $attrs->get("d"));
+
+ // getter
+
+ $this->assertEquals("10", $attrs->a);
+ $this->assertEquals("20", $attrs->b);
+ $this->assertEquals("30", $attrs->c);
+ $this->assertEquals(null, $attrs->d);
+ }
+
+ public function testNodeValue()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $test = $this->loadXML($xml, "simple");
+ $this->assertEquals("footext", trim($test->getChild("foo")->getValue()));
+ $this->assertEquals("bartext", trim($test->getChild("bar")->getValue()));
+ $this->assertEquals("baztext", trim($test->getChild("baz")->getValue()));
+ }
+
+ public function testElementsCount()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $test = $this->loadXML($xml, "test");
+ $this->assertEquals(2, $test->getRawElement()->getElementsByTagName("foo")->length);
+ $this->assertEquals(1, $test->getChildren("foo")->length);
+ }
+
+ public function testCreateDocument()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $xml->setEncoding("utf-8")->setVersion("1.0");
+
+ $users = $xml->createElement("users");
+
+ $aUser = $users->addChild("user");
+ $aUser->addChild("name", "tanaka");
+ $aUser->addChild("age", "18");
+
+ $aUser = $users->addChild("user");
+ $aUser->addChild("name", "suzuki");
+ $aUser->addChild("age", "25");
+
+ $aUser = $users->addChild("user");
+ $aUser->addChild("name", "satou");
+ $aUser->addChild("age", "40");
+
+ $xml->setDocumentElement($users);
+ $this->saveXML($xml, "users");
+ }
+
+ public function testElements()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $_users = $this->loadXML($xml, "users");
+ $users = $_users->getChildren("user");
+ $this->assertEquals(3, $users->length);
+
+ foreach ($users as $i => $user) {}
+ $this->assertEquals(2, $i);
+
+ $this->assertEquals("tanaka", $users[0]->getChild("name")->getValue());
+ $this->assertEquals("18", $users[0]->getChild("age")->getValue());
+ $this->assertEquals("suzuki", $users[1]->getChild("name")->getValue());
+ $this->assertEquals("25", $users[1]->getChild("age")->getValue());
+ $this->assertEquals("satou", $users[2]->getChild("name")->getValue());
+ $this->assertEquals("40", $users[2]->getChild("age")->getValue());
+ }
+
+ public function testSimpleAccess()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $this->assertEquals("tanaka", $users->user[0]->name[0]->getValue());
+ $this->assertEquals("18", $users->user[0]->age[0]->getValue());
+ $this->assertEquals("suzuki", $users->user[1]->name[0]->getValue());
+ $this->assertEquals("25", $users->user[1]->age[0]->getValue());
+ $this->assertEquals("satou", $users->user[2]->name[0]->getValue());
+ $this->assertEquals("40", $users->user[2]->age[0]->getValue());
+ }
+
+ public function setSetNodeValue()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+ $users->user[0]->age[0]->setNodeValue("20");
+ $this->saveXML($xml, "users");
+
+ $xml = new Sabel_Xml_Document();
+ $users = $this->loadXML($xml, "users");
+ $this->assertEquals("20", $users->user[0]->age[0]->getValue());
+ }
+
+ public function testSetAttribute()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+ $users->user[0]->setAttribute("id", 1);
+ $users->user[1]->setAttribute("id", 2);
+ $users->user[2]->setAttribute("id", 3);
+ $this->saveXML($xml, "users");
+
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+ $this->assertEquals("1", $users->user[0]->getAttribute("id"));
+ $this->assertEquals("2", $users->user[1]->getAttribute("id"));
+ $this->assertEquals("3", $users->user[2]->getAttribute("id"));
+
+ $this->assertEquals("1", $users->user[0]->at("id"));
+ $this->assertEquals("2", $users->user[1]->at("id"));
+ $this->assertEquals("3", $users->user[2]->at("id"));
+ }
+
+ public function testInsertBefore()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $this->assertEquals("tanaka", $users->user[0]->name[0]->getValue());
+ $this->assertEquals("suzuki", $users->user[1]->name[0]->getValue());
+ $this->assertEquals("satou", $users->user[2]->name[0]->getValue());
+
+ $aUser = $xml->createElement("user");
+ $aUser->addChild("name", "yamada");
+ $aUser->addChild("age", "60");
+
+ $users->user[2]->insertPreviousSibling($aUser);
+ $this->saveXML($xml, "users");
+
+ //-------------------------------------
+
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+ $this->assertEquals("tanaka", $users->user[0]->name[0]->getValue());
+ $this->assertEquals("suzuki", $users->user[1]->name[0]->getValue());
+ $this->assertEquals("yamada", $users->user[2]->name[0]->getValue());
+ $this->assertEquals("satou", $users->user[3]->name[0]->getValue());
+ }
+
+ public function testInsertAfter()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $aUser = $xml->createElement("user");
+ $aUser->addChild("name", "koike");
+ $aUser->addChild("age", "80");
+
+ $users->user[3]->insertNextSibling($aUser);
+ $this->saveXML($xml, "users");
+
+ //-------------------------------------
+
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+ $this->assertEquals("tanaka", $users->user[0]->name[0]->getValue());
+ $this->assertEquals("suzuki", $users->user[1]->name[0]->getValue());
+ $this->assertEquals("yamada", $users->user[2]->name[0]->getValue());
+ $this->assertEquals("satou", $users->user[3]->name[0]->getValue());
+ $this->assertEquals("koike", $users->user[4]->name[0]->getValue());
+ }
+
+ public function testGetParent()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $age = $users->user[0]->age[0];
+ $this->assertEquals("age", $age->tagName);
+ $this->assertEquals("user", $age->getParent()->tagName);
+ $this->assertEquals("users", $age->getParent("users")->tagName);
+ }
+
+ public function testGetFirstChild()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $aUser = $users->user[0];
+ $this->assertEquals("name", $aUser->getFirstChild()->tagName);
+ }
+
+ public function testGetLastChild()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $aUser = $users->getLastChild();
+ $this->assertEquals("user", $aUser->tagName);
+ $this->assertEquals("age", $aUser->getLastChild()->tagName);
+ $this->assertEquals("80", $aUser->getLastChild()->getValue());
+ }
+
+ public function testGetNextSibling()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $aUser = $users->getFirstChild();
+ $this->assertEquals("tanaka", $aUser->name[0]->getValue());
+ $aUser = $aUser->getNextSibling();
+ $this->assertEquals("suzuki", $aUser->name[0]->getValue());
+ $aUser = $aUser->getNextSibling();
+ $this->assertEquals("yamada", $aUser->name[0]->getValue());
+ $aUser = $aUser->getNextSibling();
+ $this->assertEquals("satou", $aUser->name[0]->getValue());
+ $aUser = $aUser->getNextSibling();
+ $this->assertEquals("koike", $aUser->name[0]->getValue());
+ $aUser = $aUser->getNextSibling();
+ $this->assertEquals(null, $aUser);
+ }
+
+ public function testGetNextSiblings()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $elems = $users->user[2]->getNextSiblings();
+ $this->assertEquals(2, $elems->length);
+ $this->assertEquals("satou", $elems[0]->name[0]->getValue());
+ $this->assertEquals("koike", $elems[1]->name[0]->getValue());
+ }
+
+ public function testGetPreviousSibling()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $aUser = $users->getLastChild();
+ $this->assertEquals("koike", $aUser->name[0]->getValue());
+ $aUser = $aUser->getPreviousSibling();
+ $this->assertEquals("satou", $aUser->name[0]->getValue());
+ $aUser = $aUser->getPreviousSibling();
+ $this->assertEquals("yamada", $aUser->name[0]->getValue());
+ $aUser = $aUser->getPreviousSibling();
+ $this->assertEquals("suzuki", $aUser->name[0]->getValue());
+ $aUser = $aUser->getPreviousSibling();
+
+ $this->assertEquals("tanaka", $aUser->name[0]->getValue());
+ $aUser = $aUser->getPreviousSibling();
+ $this->assertEquals(null, $aUser);
+ }
+
+ public function testGetPreviousSiblings()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $elems = $users->user[2]->getPreviousSiblings();
+ $this->assertEquals(2, $elems->length);
+ $this->assertEquals("suzuki", $elems[0]->name[0]->getValue());
+ $this->assertEquals("tanaka", $elems[1]->name[0]->getValue());
+
+ $elems->reverse();
+
+ $this->assertEquals("tanaka", $elems[0]->name[0]->getValue());
+ $this->assertEquals("suzuki", $elems[1]->name[0]->getValue());
+ }
+
+ public function testGetSiblings()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $elems = $users->user[2]->getSiblings();
+ $this->assertEquals(4, $elems->length);
+ $this->assertEquals("tanaka", $elems[0]->name[0]->getValue());
+ $this->assertEquals("suzuki", $elems[1]->name[0]->getValue());
+ $this->assertEquals("satou", $elems[2]->name[0]->getValue());
+ $this->assertEquals("koike", $elems[3]->name[0]->getValue());
+ }
+
+ public function testFindFromAttribute()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "find");
+
+ $elems = $users->select("from user where @id = 1");
+ $this->assertEquals(1, $elems->length);
+
+ $elem = $elems[0];
+ $this->assertEquals("1", $elem->at("id"));
+ $this->assertEquals("tanaka", $elem->profile[0]->name[0]->getValue());
+
+ $elems = $users->select("from user.foo.bar where @type = 'b'");
+ $this->assertEquals(1, $elems->length);
+
+ $elem = $elems[0]->getParent("user");
+ $this->assertEquals("2", $elem->at("id"));
+ $this->assertEquals("suzuki", $elem->profile[0]->name[0]->getValue());
+
+ // not equals
+ $elems = $users->select("from user where not @id = 1");
+ $this->assertEquals(4, $elems->length);
+ }
+
+ public function testSelectByIsNull()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "find");
+
+ $elems = $users->select("from user.foo.bar where @type IS NULL");
+ $this->assertEquals(1, $elems->length);
+
+ $elem = $elems[0]->getParent("user");
+ $this->assertEquals("5", $elem->at("id"));
+ $this->assertEquals("koike", $elem->profile[0]->name[0]->getValue());
+
+ $elems = $users->select("from user.foo.bar where @type IS NOT NULL");
+ $this->assertEquals(4, $elems->length);
+
+ $elems = $users->select("from user where test IS NULL");
+ $this->assertEquals(3, $elems->length);
+
+ $elems = $users->select("from user where test IS NOT NULL");
+ $this->assertEquals(2, $elems->length);
+ }
+
+ public function testReturnElement()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "find");
+
+ $elems = $users->select("from user.foo.bar where @type = 'b'");
+ $this->assertEquals("bar", $elems[0]->tagName);
+
+ $elems = $users->select("from user.foo.bar where @type IS NULL");
+ $this->assertEquals("bar", $elems[0]->tagName);
+
+ $elems = $users->select("from user.foo.bar where @type IS NOT NULL");
+ $this->assertEquals("bar", $elems[0]->tagName);
+
+ //-------------------------------------------
+
+ $elems = $users->select("from user where foo.bar@type = 'b'");
+ $this->assertEquals("user", $elems[0]->tagName);
+
+ $elems = $users->select("from user where foo.bar@type IS NULL");
+ $this->assertEquals("user", $elems[0]->tagName);
+
+ $elems = $users->select("from user where foo.bar@type IS NOT NULL");
+ $this->assertEquals("user", $elems[0]->tagName);
+
+ //-------------------------------------------
+
+ $aUser = $users->user[0];
+ $elems = $aUser->select("from . where @id = 2");
+ $this->assertEquals("2", $elems[0]->at("id"));
+ $this->assertEquals("suzuki", $elems[0]->profile[0]->name[0]->getValue());
+
+ //-------------------------------------------
+
+ $elems = $users->select("from user.foo.bar.baz where value() = 'test456'");
+ $this->assertEquals("baz", $elems[0]->tagName);
+ $this->assertEquals("test456", $elems[0]->getValue());
+ $this->assertEquals("2", $elems[0]->getParent("user")->at("id"));
+ }
+
+ public function testLike()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "find");
+
+ $elems = $users->select("from user where foo.bar.baz like 'test%'");
+ $this->assertEquals(2, $elems->length);
+ $this->assertEquals("tanaka", $elems[0]->profile[0]->name[0]->getValue());
+ $this->assertEquals("suzuki", $elems[1]->profile[0]->name[0]->getValue());
+
+ $elems = $users->select("from user where foo.bar.baz like '%456%'");
+ $this->assertEquals(3, $elems->length);
+ $this->assertEquals("suzuki", $elems[0]->profile[0]->name[0]->getValue());
+ $this->assertEquals("satou", $elems[1]->profile[0]->name[0]->getValue());
+ $this->assertEquals("koike", $elems[2]->profile[0]->name[0]->getValue());
+
+ //-------------------------------------------------------
+
+ $elems = $users->select("from user where not foo.bar.baz like 'test%'");
+ $this->assertEquals(3, $elems->length);
+ $this->assertEquals("yamada", $elems[0]->profile[0]->name[0]->getValue());
+ $this->assertEquals("satou", $elems[1]->profile[0]->name[0]->getValue());
+ $this->assertEquals("koike", $elems[2]->profile[0]->name[0]->getValue());
+
+ $elems = $users->select("from user where not foo.bar.baz like '%456%'");
+ $this->assertEquals(2, $elems->length);
+ $this->assertEquals("tanaka", $elems[0]->profile[0]->name[0]->getValue());
+ $this->assertEquals("yamada", $elems[1]->profile[0]->name[0]->getValue());
+ }
+
+ public function testAnd()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "find");
+
+ $elems = $users->select("from user where foo.bar.baz LIKE '%456%' AND test IS NOT NULL");
+ $this->assertEquals(1, $elems->length);
+ $this->assertEquals("koike", $elems[0]->profile[0]->name[0]->getValue());
+ }
+
+ public function testOr()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "find");
+
+ $elems = $users->select("from user where profile.age >= 60 OR profile.age <= 20");
+ $this->assertEquals(3, $elems->length);
+ $this->assertEquals("tanaka", $elems[0]->profile[0]->name[0]->getValue());
+ $this->assertEquals("1", $elems[0]->at("id"));
+ $this->assertEquals("yamada", $elems[1]->profile[0]->name[0]->getValue());
+ $this->assertEquals("3", $elems[1]->at("id"));
+ $this->assertEquals("koike", $elems[2]->profile[0]->name[0]->getValue());
+ $this->assertEquals("5", $elems[2]->at("id"));
+ }
+
+ public function testSwap()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $tanaka = $users->user[0];
+ $satou = $users->user[3];
+ $this->assertEquals("tanaka", $tanaka->name[0]->getValue());
+ $this->assertEquals("satou", $satou->name[0]->getValue());
+
+ $tanaka->swap($satou);
+
+ $tanaka = $users->user[3];
+ $satou = $users->user[0];
+ $this->assertEquals("tanaka", $tanaka->name[0]->getValue());
+ $this->assertEquals("satou", $satou->name[0]->getValue());
+
+ $suzuki = $users->user[1];
+ $koike = $users->user[4];
+ $this->assertEquals("suzuki", $suzuki->name[0]->getValue());
+ $this->assertEquals("koike", $koike->name[0]->getValue());
+
+ $koike->swap($suzuki);
+
+ $suzuki = $users->user[4];
+ $koike = $users->user[1];
+ $this->assertEquals("suzuki", $suzuki->name[0]->getValue());
+ $this->assertEquals("koike", $koike->name[0]->getValue());
+
+ $koike = $users->user[1];
+ $yamada = $users->user[2];
+ $this->assertEquals("koike", $koike->name[0]->getValue());
+ $this->assertEquals("yamada", $yamada->name[0]->getValue());
+
+ $koike->swap($yamada);
+
+ $koike = $users->user[2];
+ $yamada = $users->user[1];
+ $this->assertEquals("koike", $koike->name[0]->getValue());
+ $this->assertEquals("yamada", $yamada->name[0]->getValue());
+ }
+
+ public function testRemoveElement()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $this->assertEquals(5, $users->user->length);
+
+ $deleted = $users->user[2]->remove();
+ $this->assertEquals("yamada", $deleted->name[0]->getValue());
+ $this->assertEquals(4, $users->user->length);
+
+ $deleted = $users->user[2]->remove();
+ $this->assertEquals("satou", $deleted->name[0]->getValue());
+ $this->assertEquals(3, $users->user->length);
+
+ $this->saveXML($xml, "users");
+ }
+
+ public function testDelete()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+
+ $aUser = $users->user[1];
+ $this->assertEquals(1, $aUser->age->length);
+ $this->assertEquals("25", $aUser->age[0]->getValue());
+
+ $users->delete("from user.age where value() = 25");
+
+ $this->assertEquals(0, $aUser->age->length);
+
+ //-------------------------------------------------
+
+ $this->assertEquals(3, $users->user->length);
+
+ $users->delete("from user where age = 18");
+
+ $this->assertEquals(2, $users->user->length);
+
+ $this->saveXML($xml, "users");
+ }
+
+ public function testCDATA()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+ $aUser = $users->getFirstChild();
+ $aUser->name[0]->setValue(null);
+
+ $cdata = $xml->createCDATA(" ");
+ $aUser->name[0]->appendChild($cdata);
+ $this->saveXML($xml, "users");
+
+ //--------------------------------------------
+
+ $xml = Sabel_Xml_Document::create();
+ $users = $this->loadXML($xml, "users");
+ $aUser = $users->getFirstChild();
+ $this->assertEquals(" ", $aUser->name[0]->getValue());
+ }
+
+ public function testNamespace()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $nodes = $this->loadXML($xml, "ns");
+
+ $this->assertEquals("defitem4 value", $nodes->defitem3[0]->defitem4[0]->getValue());
+
+ $foo = $nodes->getChild("foo:foo");
+ $this->assertEquals("fooitem2 value", $foo->fooitem1[0]->fooitem2[0]->getValue());
+
+ $defitem = $foo->getChild("defitem", "http://www.example.com/default");
+ $this->assertEquals("defitem2 value", $defitem->defitem2[0]->getValue());
+
+ $baz = $foo->getChild("baz", "http://www.example.com/baz");
+ $this->assertEquals("bazitem3 value", $baz->bazitem1[0]->bazitem2[0]->bazitem3[0]->getValue());
+ }
+
+ public function testGetAllChildren()
+ {
+ $xml = Sabel_Xml_Document::create();
+ $nodes = $this->loadXML($xml, "ns");
+ $foo = $nodes->getChild("foo:foo");
+
+ $children = $foo->getChildren();
+ $this->assertEquals(4, $children->length);
+ $this->assertEquals("foo:fooitem1", $children->item(0)->tagName);
+ $this->assertEquals("defitem", $children->item(1)->tagName);
+ $this->assertEquals("bar:baritem1", $children->item(2)->tagName);
+ $this->assertEquals("baz", $children->item(3)->tagName);
+ }
+
+ protected function getXmlAsString($name)
+ {
+ return file_get_contents(XML_TEST_DIR . DS . "xml" . DS . $name . ".xml");
+ }
+
+ protected function loadXML(Sabel_Xml_Document $xml, $name)
+ {
+ return $xml->loadXML(file_get_contents(XML_TEST_DIR . DS . "xml" . DS . $name . ".xml"));
+ }
+
+ protected function saveXML(Sabel_Xml_Document $xml, $name = "_tmp")
+ {
+ return $xml->saveXML(XML_TEST_DIR . DS . "xml" . DS . $name . ".xml");
+ }
+
+ protected function outputUsersXml()
+ {
+ $xml = <<
+
+
+ tanaka
+ 18
+
+
+ suzuki
+ 25
+
+
+ satou
+ 40
+
+
+XML;
+
+ file_put_contents(XML_TEST_DIR . DS . "xml" . DS . "users.xml", $xml);
+ }
+}
diff --git a/Test/XML/Tests.php b/Test/XML/Tests.php
new file mode 100755
index 0000000..7f6087b
--- /dev/null
+++ b/Test/XML/Tests.php
@@ -0,0 +1,18 @@
+addTest(Test_XML_Element::suite());
+ //$suite->addTest(Test_XML_Elements::suite());
+ $suite->addTest(Test_XML_Test::suite());
+
+ return $suite;
+ }
+}
diff --git a/Test/XML/xml/find.xml b/Test/XML/xml/find.xml
new file mode 100755
index 0000000..3129c50
--- /dev/null
+++ b/Test/XML/xml/find.xml
@@ -0,0 +1,60 @@
+
+
+
+
+ tanaka
+ 18
+
+
+
+ test123
+
+
+
+
+
+
+ suzuki
+ 25
+
+
+
+ test456
+
+
+
+
+
+ yamada
+ 60
+
+
+
+ 123test
+
+
+
+
+
+ satou
+ 40
+
+
+
+ 456test
+
+
+
+
+
+ koike
+ 80
+
+
+
+ 123456
+
+
+
+
+
diff --git a/Test/XML/xml/ns.xml b/Test/XML/xml/ns.xml
new file mode 100755
index 0000000..fd53803
--- /dev/null
+++ b/Test/XML/xml/ns.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ fooitem2 value
+
+
+ defitem2 value
+
+
+ baritem2 value
+
+
+
+
+ bazitem3 value
+
+
+
+
+
+ defitem4 value
+
+
diff --git a/Test/XML/xml/simple.xml b/Test/XML/xml/simple.xml
new file mode 100755
index 0000000..23cbbdb
--- /dev/null
+++ b/Test/XML/xml/simple.xml
@@ -0,0 +1,12 @@
+
+
+
+ footext
+
+
+ bartext
+
+
+ baztext
+
+
diff --git a/Test/XML/xml/test.xml b/Test/XML/xml/test.xml
new file mode 100755
index 0000000..81ea78d
--- /dev/null
+++ b/Test/XML/xml/test.xml
@@ -0,0 +1,7 @@
+
+
+ test
+
+ hoge
+
+
diff --git a/Test/XML/xml/users.xml b/Test/XML/xml/users.xml
new file mode 100755
index 0000000..264f17b
--- /dev/null
+++ b/Test/XML/xml/users.xml
@@ -0,0 +1,10 @@
+
+
+
+ ]]>
+
+
+ koike
+ 80
+
+
diff --git a/Test/data/application/app/helpers/Date.php b/Test/data/application/app/helpers/Date.php
new file mode 100755
index 0000000..148972c
--- /dev/null
+++ b/Test/data/application/app/helpers/Date.php
@@ -0,0 +1,387 @@
+
+ * @copyright 2002-2006 Ebine Yutaka
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Helpers_Date
+{
+ const NORMAL = 0;
+ const ATOM = 1;
+ const RSS = 2;
+ const COOKIE = 3;
+ const ISO = 4;
+ const RFC822 = 5;
+ const RFC850 = 6;
+ const RFC1036 = 7;
+ const RFC1123 = 8;
+ const RFC2822 = 9;
+ const RFC = 10;
+ const W3C = 11;
+
+ private static $formats = array(self::NORMAL => array(
+ "all" => "Y-m-d H:i:s",
+ "date" => "Y-m-d",
+ "time" => "H:i:s"),
+
+ self::ATOM => array(
+ "all" => "c",
+ "date" => "Y-m-d",
+ "time" => "H:i:sP"),
+
+ self::RSS => array(
+ "all" => "r",
+ "date" => "D, d M Y",
+ "time" => "H:i:s O"),
+
+ self::COOKIE => array(
+ "all" => "l, d-M-y H:i:s T",
+ "date" => "l, d-M-y",
+ "time" => "H:i:s T"),
+
+ self::ISO => array(
+ "all" => "Y-m-d\TH:i:sO",
+ "date" => "Y-m-d",
+ "time" => "H:i:sO"),
+
+ self::RFC822 => array(
+ "all" => "D, d M y H:i:s O",
+ "date" => "D, d M y",
+ "time" => "H:i:s O"),
+
+ self::RFC850 => array(
+ "all" => "l, d-M-y H:i:s T",
+ "date" => "l, d-M-y",
+ "time" => "H:i:s T"),
+
+ self::RFC1036 => array(
+ "all" => "D, d M y H:i:s O",
+ "date" => "D, d M y",
+ "time" => "H:i:s O"),
+
+ self::RFC1123 => array(
+ "all" => "r",
+ "date" => "D, d M Y",
+ "time" => "H:i:s O"),
+
+ self::RFC2822 => array(
+ "all" => "r",
+ "date" => "D, d M Y",
+ "time" => "H:i:s O"),
+
+ self::RFC => array(
+ "all" => "r",
+ "date" => "D, d M Y",
+ "time" => "H:i:s O"),
+
+ self::W3C => array(
+ "all" => "c",
+ "date" => "Y-m-d",
+ "time" => "H:i:sP"));
+
+ protected
+ $timestamp = null,
+ $format = self::NORMAL;
+
+ public static function format($date, $format)
+ {
+ return date(self::$formats[$format]["all"], strtotime($date));
+ }
+
+ public function __construct($arg = null)
+ {
+ if ($arg === null) {
+ $this->timestamp = time();
+ } elseif (is_string($arg)) {
+ $this->timestamp = strtotime($arg);
+ } elseif (is_array($arg)) {
+ $y = (isset($arg["y"])) ? $arg["y"] : date("Y");
+ $m = (isset($arg["m"])) ? $arg["m"] : date("m");
+ $d = (isset($arg["d"])) ? $arg["d"] : 1;
+ $h = (isset($arg["h"])) ? $arg["h"] : 0;
+ $i = (isset($arg["i"])) ? $arg["i"] : 0;
+ $s = (isset($arg["s"])) ? $arg["s"] : 0;
+
+ $this->timestamp = mktime($h, $i, $s, $m, $d, $y);
+ } else {
+ throw new Exception("Helpers_Date::__construct() invalid parameter.");
+ }
+ }
+
+ public function __toString()
+ {
+ return $this->getDateTime();
+ }
+
+ public function setFormat($format)
+ {
+ $this->format = $format;
+
+ return $this;
+ }
+
+ public function getDatetime()
+ {
+ return date(self::$formats[$this->format]["all"], $this->timestamp);
+ }
+
+ public function getDate()
+ {
+ return date(self::$formats[$this->format]["date"], $this->timestamp);
+ }
+
+ public function getTime()
+ {
+ return date(self::$formats[$this->format]["time"], $this->timestamp);
+ }
+
+ public function getYear($twoDigits = false)
+ {
+ if ($twoDigits) {
+ return date("y", $this->timestamp);
+ } else {
+ return date("Y", $this->timestamp);
+ }
+ }
+
+ public function getMonth($withLeadingZeros = true)
+ {
+ if ($withLeadingZeros) {
+ return date("m", $this->timestamp);
+ } else {
+ return date("n", $this->timestamp);
+ }
+ }
+
+ public function getTextualMonth($short = true)
+ {
+ if ($short) {
+ return date("M", $this->timestamp);
+ } else {
+ return date("F", $this->timestamp);
+ }
+ }
+
+ public function getDay($withLeadingZeros = true)
+ {
+ if ($withLeadingZeros) {
+ return date("d", $this->timestamp);
+ } else {
+ return date("j", $this->timestamp);
+ }
+ }
+
+ public function getLastDay()
+ {
+ $timestamp = mktime($this->getHour(),
+ $this->getMinute(),
+ $this->getSecond(),
+ $this->getMonth() + 1,
+ 0,
+ $this->getYear());
+
+ return date("d", $timestamp);
+ }
+
+ public function getHour($withLeadingZeros = true)
+ {
+ if ($withLeadingZeros) {
+ return date("H", $this->timestamp);
+ } else {
+ return date("G", $this->timestamp);
+ }
+ }
+
+ public function getHourBy12Format($withLeadingZeros = true)
+ {
+ if ($withLeadingZeros) {
+ return date("h", $this->timestamp);
+ } else {
+ return date("g", $this->timestamp);
+ }
+ }
+
+ public function getMinute()
+ {
+ return date("i", $this->timestamp);
+ }
+
+ public function getSecond()
+ {
+ return date("s", $this->timestamp);
+ }
+
+ public function getMeridiem($lower = true)
+ {
+ return date(($lower) ? "a" : "A", $this->timestamp);
+ }
+
+ public function getTextualWeek($short = true)
+ {
+ if ($short) {
+ return date("D", $this->timestamp);
+ } else {
+ return date("l", $this->timestamp);
+ }
+ }
+
+ public function getWeekNumber()
+ {
+ return date("w", $this->timestamp);
+ }
+
+ public function ymd($sep = "-")
+ {
+ return $this->getYear() . $sep . $this->getMonth() . $sep . $this->getDay();
+ }
+
+ public function his($sep = ":")
+ {
+ return $this->getHour() . $sep . $this->getMinute() . $sep . $this->getSecond();
+ }
+
+ public function incYear($year = 1)
+ {
+ $year = $this->getYear() + $year;
+
+ $this->timestamp = mktime($this->getHour(),
+ $this->getMinute(),
+ $this->getSecond(),
+ $this->getMonth(),
+ $this->getDay(),
+ $year);
+
+ return $year;
+ }
+
+ public function decYear($year = 1)
+ {
+ $year = $this->getYear() - $year;
+
+ $this->timestamp = mktime($this->getHour(),
+ $this->getMinute(),
+ $this->getSecond(),
+ $this->getMonth(),
+ $this->getDay(),
+ $year);
+
+ return $year;
+ }
+
+ public function incMonth($month = 1)
+ {
+ $month = $this->getMonth() + $month;
+
+ $this->timestamp = mktime($this->getHour(),
+ $this->getMinute(),
+ $this->getSecond(),
+ $month,
+ $this->getDay(),
+ $this->getYear());
+
+ return $month;
+ }
+
+ public function decMonth($month = 1)
+ {
+ $month = $this->getMonth() - $month;
+
+ $this->timestamp = mktime($this->getHour(),
+ $this->getMinute(),
+ $this->getSecond(),
+ $month,
+ $this->getDay(),
+ $this->getYear());
+
+ return $month;
+ }
+
+ public function incDay($day = 1)
+ {
+ $this->timestamp += 86400 * $day;
+
+ return $this->getDay();
+ }
+
+ public function decDay($day = 1)
+ {
+ $this->timestamp -= 86400 * $day;
+
+ return $this->getDay();
+ }
+
+ public function incHour($hour = 1)
+ {
+ $this->timestamp += 3600 * $hour;
+
+ return $this->getHour();
+ }
+
+ public function decHour($hour = 1)
+ {
+ $this->timestamp -= 3600 * $hour;
+
+ return $this->getHour();
+ }
+
+ public function incMinute($min = 1)
+ {
+ $this->timestamp += 60 * $min;
+
+ return $this->getMinute();
+ }
+
+ public function decMinute($min = 1)
+ {
+ $this->timestamp -= 60 * $min;
+
+ return $this->getMinute();
+ }
+
+ public function incSecond($second = 1)
+ {
+ $this->timestamp += $second;
+
+ return $this->getSecond();
+ }
+
+ public function decSecond($second = 1)
+ {
+ $this->timestamp -= $second;
+
+ return $this->getSecond();
+ }
+
+ public function y()
+ {
+ return $this->getYear();
+ }
+
+ public function m()
+ {
+ return $this->getMonth();
+ }
+
+ public function d()
+ {
+ return $this->getDay();
+ }
+
+ public function h()
+ {
+ return $this->getHour();
+ }
+
+ public function i()
+ {
+ return $this->getMinute();
+ }
+
+ public function s()
+ {
+ return $this->getSecond();
+ }
+}
diff --git a/Test/data/application/app/helpers/application.php b/Test/data/application/app/helpers/application.php
new file mode 100755
index 0000000..7f465ea
--- /dev/null
+++ b/Test/data/application/app/helpers/application.php
@@ -0,0 +1,49 @@
+%s', uri($uri), $anchor);
+ } else {
+ return sprintf('%s ', uri($uri), $uriQuery, $anchor);
+ }
+}
+
+function ah($param, $anchor, $uriQuery = "")
+{
+ return a($param, h($anchor), $uriQuery);
+}
+
+function linkto($file)
+{
+ if ($bus = Sabel_Context::getContext()->getBus()) {
+ if ($bus->get("NO_VIRTUAL_HOST")) {
+ return dirname($_SERVER["SCRIPT_NAME"]) . "/" . $file;
+ }
+ }
+
+ return "/" . $file;
+}
+
+function h($string, $charset = null)
+{
+ return htmlescape($string, $charset);
+}
+
+function mb_trim($string)
+{
+ $string = new Sabel_Util_String($string);
+ return $string->trim()->toString();
+}
+
+function to_date($date, $format)
+{
+ return Helpers_Date::format($date, constant("Helpers_Date::" . $format));
+}
+
+function __include($uri, $values = array(), $method = Sabel_Request::GET, $withLayout = false)
+{
+ $requester = new Sabel_Request_Internal($method);
+ $requester->values($values)->withLayout($withLayout);
+ return $requester->request($uri)->getResult();
+}
diff --git a/Test/data/application/app/index/controllers/Index.php b/Test/data/application/app/index/controllers/Index.php
new file mode 100755
index 0000000..60cc116
--- /dev/null
+++ b/Test/data/application/app/index/controllers/Index.php
@@ -0,0 +1,15 @@
+hoge = "10";
+ $this->response->setResponse("fuga", "20");
+ }
+
+ public function hoge()
+ {
+ $this->name = "yamada";
+ }
+}
diff --git a/Test/data/application/app/index/controllers/Main.php b/Test/data/application/app/index/controllers/Main.php
new file mode 100755
index 0000000..2950314
--- /dev/null
+++ b/Test/data/application/app/index/controllers/Main.php
@@ -0,0 +1,24 @@
+values(array("pv" => "foo"));
+ $internal->method(Sabel_Request::POST);
+ $internal->request("main/bar", new AppBusConfig());
+ $response = $internal->getResponse();
+ $this->bar = $response->getResponse("bar");
+ }
+
+ public function bar()
+ {
+ $foo = $this->request->fetchPostValue("pv");
+ $this->bar = $foo . " bar";
+ }
+}
diff --git a/Test/data/application/app/index/views/error.tpl b/Test/data/application/app/index/views/error.tpl
new file mode 100755
index 0000000..e69de29
diff --git a/Test/data/application/app/index/views/fuga/fuga.tpl b/Test/data/application/app/index/views/fuga/fuga.tpl
new file mode 100755
index 0000000..9bfae89
--- /dev/null
+++ b/Test/data/application/app/index/views/fuga/fuga.tpl
@@ -0,0 +1 @@
+fuga/fuga.tpl
diff --git a/Test/data/application/app/index/views/fuga/index.tpl b/Test/data/application/app/index/views/fuga/index.tpl
new file mode 100755
index 0000000..ee5052c
--- /dev/null
+++ b/Test/data/application/app/index/views/fuga/index.tpl
@@ -0,0 +1 @@
+fuga/index.tpl
diff --git a/Test/data/application/app/index/views/hoge/hoge.tpl b/Test/data/application/app/index/views/hoge/hoge.tpl
new file mode 100755
index 0000000..a575ef9
--- /dev/null
+++ b/Test/data/application/app/index/views/hoge/hoge.tpl
@@ -0,0 +1 @@
+hoge/hoge.tpl
diff --git a/Test/data/application/app/index/views/hoge/index.tpl b/Test/data/application/app/index/views/hoge/index.tpl
new file mode 100755
index 0000000..4caf5ff
--- /dev/null
+++ b/Test/data/application/app/index/views/hoge/index.tpl
@@ -0,0 +1 @@
+hoge/index.tpl
diff --git a/Test/data/application/app/index/views/index/hoge.tpl b/Test/data/application/app/index/views/index/hoge.tpl
new file mode 100755
index 0000000..557755a
--- /dev/null
+++ b/Test/data/application/app/index/views/index/hoge.tpl
@@ -0,0 +1 @@
+= $name ?>
diff --git a/Test/data/application/app/index/views/layout.tpl b/Test/data/application/app/index/views/layout.tpl
new file mode 100755
index 0000000..675306d
--- /dev/null
+++ b/Test/data/application/app/index/views/layout.tpl
@@ -0,0 +1,5 @@
+
+
+ = $contentForLayout ?>
+
+
diff --git a/Test/data/application/app/manage/controllers/Index.php b/Test/data/application/app/manage/controllers/Index.php
new file mode 100755
index 0000000..2620e37
--- /dev/null
+++ b/Test/data/application/app/manage/controllers/Index.php
@@ -0,0 +1,21 @@
+request->fetchParameterValue("param1");
+
+ if (is_numeric($param1)) {
+ $this->param1 = $param1;
+ $this->param2 = $this->request->fetchParameterValue("param2");
+ } else {
+ $this->redirect->to("c: login, a: prepare");
+ }
+ }
+
+ public function refuse()
+ {
+ throw new Exception("");
+ }
+}
diff --git a/Test/data/application/app/manage/controllers/Login.php b/Test/data/application/app/manage/controllers/Login.php
new file mode 100755
index 0000000..7281ace
--- /dev/null
+++ b/Test/data/application/app/manage/controllers/Login.php
@@ -0,0 +1,8 @@
+param1: = $param1 ?>
+param2: = $param2 ?>
diff --git a/Test/data/application/app/manage/views/layout.tpl b/Test/data/application/app/manage/views/layout.tpl
new file mode 100755
index 0000000..9fd6ea3
--- /dev/null
+++ b/Test/data/application/app/manage/views/layout.tpl
@@ -0,0 +1,6 @@
+
+
+ manage
+ = $contentForLayout ?>
+
+
diff --git a/Test/data/application/app/views/serverError.tpl b/Test/data/application/app/views/serverError.tpl
new file mode 100755
index 0000000..e69de29
diff --git a/Test/data/application/app/views/test.tpl b/Test/data/application/app/views/test.tpl
new file mode 100755
index 0000000..d4bc4b2
--- /dev/null
+++ b/Test/data/application/app/views/test.tpl
@@ -0,0 +1,2 @@
+a:
+b:
diff --git a/Test/data/application/data/executable.txt b/Test/data/application/data/executable.txt
new file mode 100755
index 0000000..e69de29
diff --git a/Test/data/application/data/readable.txt b/Test/data/application/data/readable.txt
new file mode 100755
index 0000000..e69de29
diff --git a/Test/data/application/data/writable.txt b/Test/data/application/data/writable.txt
new file mode 100755
index 0000000..e69de29
diff --git a/Test/data/application/lib/Paginate.php b/Test/data/application/lib/Paginate.php
new file mode 100755
index 0000000..7a0598e
--- /dev/null
+++ b/Test/data/application/lib/Paginate.php
@@ -0,0 +1,136 @@
+
+ * @copyright 2002-2006 Ebine Yutaka
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Paginate extends Sabel_Object
+{
+ protected
+ $model = null,
+ $isJoin = false,
+ $attributes = array(),
+ $method = "select",
+ $parameters = array();
+
+ public function __construct($model)
+ {
+ if (is_string($model)) {
+ $model = MODEL($model);
+ }
+
+ if (is_model($model)) {
+ $model->autoReinit(false);
+ $this->method = "select";
+ } elseif ($model instanceof Sabel_Db_Join) {
+ $model->getModel()->autoReinit(false);
+ $this->method = "join";
+ $this->isJoin = true;
+ } else {
+ throw new Exception("Paginate::__construct() invalid instance.");
+ }
+
+ $this->model = $model;
+ }
+
+ public function __get($key)
+ {
+ if (isset($this->attributes[$key])) {
+ return $this->attributes[$key];
+ } else {
+ return null;
+ }
+ }
+
+ public function getQueryString($page)
+ {
+ unset($this->parameters["page"]);
+ $queryString = array("page={$page}");
+
+ foreach ($this->parameters as $key => $val) {
+ $queryString[] = "{$key}={$val}";
+ }
+
+ return "?" . implode("&", $queryString);
+ }
+
+ public function setCondition($arg1, $arg2 = null)
+ {
+ $this->model->setCondition($arg1, $arg2);
+
+ return $this;
+ }
+
+ public function setOrderBy($orderBy)
+ {
+ $this->model->setOrderBy($orderBy);
+
+ return $this;
+ }
+
+ public function setParameters(array $parameters)
+ {
+ $this->parameters = $parameters;
+ }
+
+ public function addParameter($key, $val)
+ {
+ $this->parameters[$key] = $val;
+ }
+
+ public function setMethod($method)
+ {
+ $this->method = $method;
+
+ return $this;
+ }
+
+ public function build($limit, $page)
+ {
+ if (!is_numeric($page) || $page < 1) {
+ $page = 1;
+ }
+
+ $model = $this->model;
+ $attributes =& $this->attributes;
+
+ if ($this->isJoin) {
+ $count = $model->getCount(null, false);
+ } else {
+ $count = $model->getCount();
+ }
+
+ $attributes["count"] = $count;
+ $attributes["limit"] = $limit;
+ $attributes["page"] = $page;
+
+ $pager = Sabel_View_Pager::create($count, $limit);
+ $pager->setPageNumber($page);
+ $attributes["viewer"] = new Sabel_View_PageViewer($pager);
+
+ if ($count === 0) {
+ $attributes["offset"] = 0;
+ $attributes["results"] = array();
+ } else {
+ $offset = $pager->getSqlOffset();
+
+ if ($this->isJoin) {
+ $model->getModel()->setLimit($limit);
+ $model->getModel()->setOffset($offset);
+ } else {
+ $model->setLimit($limit);
+ $model->setOffset($offset);
+ }
+
+ $attributes["offset"] = $offset;
+ $attributes["results"] = $model->{$this->method}();
+ }
+
+ return $this;
+ }
+}
diff --git a/Test/data/application/lib/db/Model.php b/Test/data/application/lib/db/Model.php
new file mode 100755
index 0000000..ea0d88d
--- /dev/null
+++ b/Test/data/application/lib/db/Model.php
@@ -0,0 +1,6 @@
+ "validateEmailAddress",
+ "model" => "MODEL_NAME",
+ "column" => "COLUMN_NAME");
+
+$passwdValidator = array("function" => "validatePasswords",
+ "model" => "MODEL_NAME",
+ "column" => "COLUMN_NAME",
+ "arguments" => "REINPUT");
+*/
+
+// Sabel_Db_Validate_Config::addValidator($emailValidator);
+// Sabel_Db_Validate_Config::addValidator($passwdValidator);
+
+function checkEmailAddress($email)
+{
+ $regex = '/^[\w.\-_]+@([\w\-_]+\.)+[a-zA-Z]+$/';
+ return (preg_match($regex, $email) !== 0);
+}
+
+function validateEmailAddress($model, $name, $localizedName)
+{
+ if ($model->$name !== null && !checkEmailAddress($model->$name)) {
+ return "invalid mail address format.";
+ }
+}
+
+function validatePasswords($model, $name, $localizedName, $reInput)
+{
+ if ($model->$name !== $model->$reInput) {
+ return "passwords didn't match.";
+ } else {
+ $model->unsetValue($reInput);
+ }
+}
diff --git a/Test/data/application/lib/processor/Addon.php b/Test/data/application/lib/processor/Addon.php
new file mode 100755
index 0000000..3182323
--- /dev/null
+++ b/Test/data/application/lib/processor/Addon.php
@@ -0,0 +1,16 @@
+getConfig("addon");
+ $addons = $config->configure();
+
+ foreach ($addons as $addon) {
+ $className = ucfirst($addon) . "_Addon";
+ $instance = new $className();
+ $instance->execute($bus);
+ }
+ }
+}
diff --git a/Test/data/application/lib/processor/Controller.php b/Test/data/application/lib/processor/Controller.php
new file mode 100755
index 0000000..16d4fb4
--- /dev/null
+++ b/Test/data/application/lib/processor/Controller.php
@@ -0,0 +1,57 @@
+get("destination");
+ if (($controller = $this->createController($destination)) === null) {
+ $controller = $this->createVirtualController();
+ }
+
+ if (($response = $bus->get("response")) !== null) {
+ $controller->setResponse($response);
+ if ($controller instanceof $this->virtualControllerName) {
+ $response->getStatus()->setCode(Sabel_Response::NOT_FOUND);
+ }
+ }
+
+ if (($request = $bus->get("request")) !== null) {
+ $controller->setRequest($request);
+ }
+
+ if (($session = $bus->get("session")) !== null) {
+ $controller->setSession($session);
+ }
+
+ $bus->set("controller", $controller);
+ }
+
+ protected function createController($destination)
+ {
+ list ($module, $controller,) = $destination->toArray();
+ $class = ucfirst($module) . "_Controllers_" . ucfirst($controller);
+
+ if (Sabel::using($class)) {
+ l("create controller '{$class}'");
+ return new $class();
+ } else {
+ l("controller '{$class}' not found", SBL_LOG_WARN);
+ return null;
+ }
+ }
+
+ protected function createVirtualController()
+ {
+ $className = $this->virtualControllerName;
+ if (!class_exists($className, false)) {
+ eval ("class $className extends Sabel_Controller_Page {}");
+ }
+
+ l("create virtual controller '{$className}'");
+
+ return new $className();
+ }
+}
diff --git a/Test/data/application/lib/processor/Executer.php b/Test/data/application/lib/processor/Executer.php
new file mode 100755
index 0000000..1ec30c2
--- /dev/null
+++ b/Test/data/application/lib/processor/Executer.php
@@ -0,0 +1,32 @@
+get("response");
+ $controller = $bus->get("controller");
+
+ if ($response->isFailure() || $response->isRedirected()) return;
+
+ $action = $bus->get("destination")->getAction();
+ $controller->setAction($action);
+
+ try {
+ $controller->initialize();
+
+ if ($response->isSuccess() && !$response->isRedirected()) {
+ $controller->execute();
+ }
+
+ $controller->finalize();
+ } catch (Exception $e) {
+ $response->getStatus()->setCode(Sabel_Response::INTERNAL_SERVER_ERROR);
+ Sabel_Context::getContext()->setException($e);
+ }
+
+ if ($controller->getAttribute("layout") === false) {
+ $bus->set("NO_LAYOUT", true);
+ }
+ }
+}
diff --git a/Test/data/application/lib/processor/Helper.php b/Test/data/application/lib/processor/Helper.php
new file mode 100755
index 0000000..b442802
--- /dev/null
+++ b/Test/data/application/lib/processor/Helper.php
@@ -0,0 +1,25 @@
+get("destination");
+ $moduleName = $destination->getModule();
+ $controllerName = $destination->getController();
+
+ $sharedHelper = "application";
+ $commonHelpers = MODULES_DIR_PATH . DS . HELPERS_DIR_NAME;
+ $moduleHelpers = MODULES_DIR_PATH . DS . $moduleName . DS . HELPERS_DIR_NAME;
+
+ $helpers = array();
+
+ $helpers[] = $commonHelpers . DS . $sharedHelper;
+ $helpers[] = $moduleHelpers . DS . $sharedHelper;
+ $helpers[] = $moduleHelpers . DS . $controllerName;
+
+ foreach ($helpers as $helper) {
+ Sabel::fileUsing($helper . ".php", true);
+ }
+ }
+}
diff --git a/Test/data/application/lib/processor/Initializer.php b/Test/data/application/lib/processor/Initializer.php
new file mode 100755
index 0000000..944edad
--- /dev/null
+++ b/Test/data/application/lib/processor/Initializer.php
@@ -0,0 +1,41 @@
+getConfig("database"));
+ //Sabel::fileUsing(RUN_BASE . DS . LIB_DIR_NAME . DS . "db" . DS . "utility.php", true);
+
+ if (!defined("SBL_BATCH")) {
+ // start session.
+ $session = $bus->get("session");
+ $session->start();
+ l("START SESSION: " . $session->getName() . "=" . $session->getId());
+ }
+
+ // default page title.
+ $bus->get("response")->setResponse("pageTitle", "Sabel");
+
+ // $request = $bus->get("request");
+ // if ($request->isPost()) $this->trim($request);
+ }
+
+ /**
+ * strip whitespace from post values.
+ */
+ private function trim($request)
+ {
+ $func = (extension_loaded("mbstring")) ? "mb_trim" : "trim";
+
+ if ($values = $request->fetchPostValues()) {
+ foreach ($values as &$value) {
+ if ($value === null || is_array($value)) continue;
+ $result = $func($value);
+ $value = ($result === "") ? null : $result;
+ }
+
+ $request->setPostValues($values);
+ }
+ }
+}
diff --git a/Test/data/application/lib/processor/Request.php b/Test/data/application/lib/processor/Request.php
new file mode 100755
index 0000000..0a0fa4f
--- /dev/null
+++ b/Test/data/application/lib/processor/Request.php
@@ -0,0 +1,67 @@
+has("request")) {
+ $request = $bus->get("request");
+ } else {
+ $uri = $this->getRequestUri($bus);
+ $request = new Sabel_Request_Object($uri);
+
+ if (SBL_SECURE_MODE) {
+ $_GET = remove_nullbyte($_GET);
+ $_POST = remove_nullbyte($_POST);
+ }
+
+ $request->setGetValues($_GET);
+ $request->setPostValues($_POST);
+
+ if (isset($_SERVER["REQUEST_METHOD"])) {
+ $request->method($_SERVER["REQUEST_METHOD"]);
+ }
+
+ $httpHeaders = array();
+ foreach ($_SERVER as $key => $val) {
+ if (strpos($key, "HTTP") === 0) {
+ $httpHeaders[$key] = $val;
+ }
+ }
+
+ $request->setHttpHeaders($httpHeaders);
+ $bus->set("request", $request);
+ }
+
+ l("REQUEST URI: /" . $request->getUri(true));
+
+ // ajax request.
+ if ($request->getHttpHeader("X-Requested-With") === "XMLHttpRequest") {
+ $bus->set("NO_LAYOUT", true);
+ $bus->set("IS_AJAX_REQUEST", true);
+ }
+ }
+
+ protected function getRequestUri($bus)
+ {
+ $uri = (isset($_SERVER["REQUEST_URI"])) ? $_SERVER["REQUEST_URI"] : "/";
+
+ if (isset($_SERVER["SCRIPT_NAME"]) && strpos($_SERVER["SCRIPT_NAME"], "/index.php") >= 1) {
+ $uri = substr($uri, strlen($_SERVER["SCRIPT_NAME"]));
+ $bus->set("NO_VIRTUAL_HOST", true);
+ }
+
+ if (defined("NO_REWRITE_PREFIX") && isset($_GET[NO_REWRITE_PREFIX])) {
+ $uri = substr($uri, strlen(NO_REWRITE_PREFIX) + 2);
+ $parsed = parse_url($uri);
+ if (isset($parsed["query"])) {
+ parse_str($parsed["query"], $_GET);
+ }
+
+ unset($_GET[NO_REWRITE_PREFIX]);
+ $bus->set("NO_REWRITE_MODULE", true);
+ }
+
+ return normalize_uri($uri);
+ }
+}
diff --git a/Test/data/application/lib/processor/Response.php b/Test/data/application/lib/processor/Response.php
new file mode 100755
index 0000000..696b172
--- /dev/null
+++ b/Test/data/application/lib/processor/Response.php
@@ -0,0 +1,75 @@
+ "afterAction");
+
+ public function execute(Sabel_Bus $bus)
+ {
+ $bus->set("response", new Sabel_Response_Object());
+ }
+
+ public function afterAction($bus)
+ {
+ $response = $bus->get("response");
+ $response->setResponses(array_merge(
+ $response->getResponses(),
+ $bus->get("controller")->getAttributes()
+ ));
+
+ if ($response->getStatus()->isServerError()) {
+ $exception = Sabel_Context::getContext()->getException();
+ if (!is_object($exception)) return;
+
+ $eol = ((ENVIRONMENT & DEVELOPMENT) > 0) ? " " : PHP_EOL;
+ $msg = get_class($exception) . ": "
+ . $exception->getMessage() . $eol
+ . "At: " . date("r") . $eol . $eol
+ . Sabel_Exception_Printer::printTrace($exception, $eol, true);
+
+ if ((ENVIRONMENT & PRODUCTION) > 0) {
+
+ } else {
+ $response->setResponse("exception_message", $msg);
+ }
+
+ l(PHP_EOL . str_replace(" ", PHP_EOL, $msg), SBL_LOG_ERR);
+ }
+ }
+
+ public function shutdown(Sabel_Bus $bus)
+ {
+ $response = $bus->get("response");
+ $redirector = $response->getRedirector();
+
+ if ($redirector->isRedirected()) {
+ if (($url = $redirector->getUrl()) !== "") {
+ $response->setLocation($url);
+ } else {
+ $token = $bus->get("request")->getValueWithMethod("token");
+ $hasToken = !empty($token);
+ $hasParams = $redirector->hasParameters();
+ $location = $redirector->getUri();
+
+ if ($hasToken) {
+ $glue = ($hasParams) ? "&" : "?";
+ $location .= $glue . "token={$token}";
+ }
+
+ $session = $bus->get("session");
+ if ($session->isStarted() && !$session->isCookieEnabled()) {
+ $glue = ($hasToken || $hasParams) ? "&" : "?";
+ $location .= $glue . $session->getName() . "=" . $session->getId();
+ }
+
+ if (function_exists("get_uri_prefix")) {
+ $location = get_uri_prefix() . "/" . ltrim($location, "/");
+ }
+
+ $response->setLocation($location);
+ }
+ }
+
+ $response->outputHeader();
+ }
+}
diff --git a/Test/data/application/lib/processor/Router.php b/Test/data/application/lib/processor/Router.php
new file mode 100755
index 0000000..dc93785
--- /dev/null
+++ b/Test/data/application/lib/processor/Router.php
@@ -0,0 +1,22 @@
+get("request");
+ $config = $bus->getConfig("map");
+ $config->configure();
+
+ if ($candidate = $config->getValidCandidate($request->getUri())) {
+ $request->setParameterValues($candidate->getUriParameters());
+ $destination = $candidate->getDestination();
+ l("DESTINATION: " . $destination);
+ $bus->set("destination", $destination);
+ Sabel_Context::getContext()->setCandidate($candidate);
+ } else {
+ $message = __METHOD__ . "() didn't match to any routing configuration.";
+ throw new Sabel_Exception_Runtime($message);
+ }
+ }
+}
diff --git a/Test/data/application/lib/processor/Session.php b/Test/data/application/lib/processor/Session.php
new file mode 100755
index 0000000..91ee9ed
--- /dev/null
+++ b/Test/data/application/lib/processor/Session.php
@@ -0,0 +1,20 @@
+has("session")) {
+ $bus->set("session", Sabel_Session_PHP::create());
+ }
+ }
+
+ public function shutdown(Sabel_Bus $bus)
+ {
+ $session = $bus->get("session");
+
+ if ($session->isStarted() && !$session->isCookieEnabled() && ini_get("session.use_trans_sid") === "0") {
+ output_add_rewrite_var($session->getName(), $session->getId());
+ }
+ }
+}
diff --git a/Test/data/application/lib/processor/View.php b/Test/data/application/lib/processor/View.php
new file mode 100755
index 0000000..8eba9c2
--- /dev/null
+++ b/Test/data/application/lib/processor/View.php
@@ -0,0 +1,92 @@
+ "initViewObject");
+
+ /**
+ * @var Sabel_View
+ */
+ private $view = null;
+
+ public function initViewObject($bus)
+ {
+ list ($m, $c, $a) = $bus->get("destination")->toArray();
+
+ $view = new Sabel_View_Object("controller", new Sabel_View_Location_File(
+ $m . DS . VIEW_DIR_NAME . DS . $c . DS)
+ );
+
+ $view->addLocation("module", new Sabel_View_Location_File($m . DS . VIEW_DIR_NAME . DS));
+ $view->addLocation("app", new Sabel_View_Location_File(VIEW_DIR_NAME . DS));
+
+ if ($renderer = $bus->get("renderer")) {
+ $view->setRenderer($renderer);
+ } else {
+ $view->setRenderer(new Sabel_View_Renderer());
+ }
+
+ $this->view = $view;
+ $bus->set("view", $view);
+ $bus->get("controller")->setAttribute("view", $view);
+ }
+
+ public function execute(Sabel_Bus $bus)
+ {
+ $response = $bus->get("response");
+ if ($response->isRedirected()) return;
+
+ $controller = $bus->get("controller");
+ $responses = $response->getResponses();
+ $contents = (isset($responses["contents"])) ? $responses["contents"] : "";
+
+ $view = $this->getView(
+ $response->getStatus(),
+ $bus->get("destination")->getAction(),
+ $bus->get("IS_AJAX_REQUEST") === true
+ );
+
+ if ($contents === "") {
+ if ($location = $view->getValidLocation()) {
+ $contents = $view->rendering($location, $responses);
+ } elseif (!$controller->isExecuted()) {
+ $response->getStatus()->setCode(Sabel_Response::NOT_FOUND);
+ if ($location = $view->getValidLocation("notFound")) {
+ $contents = $view->rendering($location, $responses);
+ } else {
+ $contents = "404 Not Found ";
+ }
+ }
+ }
+
+ if ($bus->get("NO_LAYOUT")) {
+ $bus->set("result", $contents);
+ } else {
+ $layout = (isset($responses["layout"])) ? $responses["layout"] : DEFAULT_LAYOUT_NAME;
+ if ($location = $view->getValidLocation($layout)) {
+ $responses["contentForLayout"] = $contents;
+ $bus->set("result", $view->rendering($location, $responses));
+ } else { // no layout.
+ $bus->set("result", $contents);
+ }
+ }
+ }
+
+ protected function getView($status, $action, $isAjax = false)
+ {
+ if ($status->isFailure()) {
+ $tplName = lcfirst(str_replace(" ", "", $status->getReason()));
+ if ($location = $this->view->getValidLocation($tplName)) {
+ $this->view->setName($tplName);
+ } elseif ($status->isClientError()) {
+ $this->view->setName("clientError");
+ } else {
+ $this->view->setName("serverError");
+ }
+ } elseif ($this->view->getName() === "") {
+ $this->view->setName(($isAjax) ? "{$action}.ajax" : $action);
+ }
+
+ return $this->view;
+ }
+}
diff --git a/Test/data/application/lib/schema/example.php b/Test/data/application/lib/schema/example.php
new file mode 100755
index 0000000..18c41d8
--- /dev/null
+++ b/Test/data/application/lib/schema/example.php
@@ -0,0 +1,37 @@
+ Sabel_Db_Type::INT,
+ 'min' => 0,
+ 'max' => PHP_INT_MAX,
+ 'increment' => false,
+ 'nullable' => false,
+ 'primary' => true,
+ 'default' => null);
+
+ $cols['column2'] = array('type' => Sabel_Db_Type::STRING,
+ 'max' => 255,
+ 'increment' => false,
+ 'nullable' => false,
+ 'primary' => false,
+ 'default' => null);
+
+ return $cols;
+ }
+
+ public function getProperty()
+ {
+ $property = array();
+
+ $property["tableEngine"] = null;
+ $property["uniques"] = null;
+ $property["fkeys"] = null;
+
+ return $property;
+ }
+}
diff --git a/Test/data/php.gif b/Test/data/php.gif
new file mode 100755
index 0000000..f352c73
Binary files /dev/null and b/Test/data/php.gif differ
diff --git a/bin/sabel b/bin/sabel
new file mode 100755
index 0000000..7ac913b
--- /dev/null
+++ b/bin/sabel
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+if [ -z "$SABEL_HOME" ] ; then
+ SABEL_HOME="/path/to/Sabel"
+fi
+
+if (test -z "$PHP_COMMAND") ; then
+ export PHP_COMMAND=php
+fi
+
+$PHP_COMMAND -d html_errors=off -qC $SABEL_HOME/generator/generator.php $*
diff --git a/bin/sabel.bat b/bin/sabel.bat
new file mode 100755
index 0000000..7c7c80c
--- /dev/null
+++ b/bin/sabel.bat
@@ -0,0 +1,23 @@
+@echo off
+
+if "%OS%"=="Windows_NT" @setlocal
+
+set SABEL=c:\php\includes\Sabel
+
+goto init
+
+:init
+
+if "%PHP_COMMAND%" == "" goto no_phpcommand
+
+%PHP_COMMAND% -d html_errors=off -d open_basedir= -q "%SABEL%\generator\generator.php" %1 %2 %3 %4 %5 %6 %7 %8 %9
+
+goto cleanup
+
+:no_phpcommand
+set PHP_COMMAND=php
+goto init
+
+:cleanup
+if "%OS%"=="Windows_NT" @endlocal
+rem pause
diff --git a/bin/sakle b/bin/sakle
new file mode 100755
index 0000000..c8d2256
--- /dev/null
+++ b/bin/sakle
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+if [ -z "$SABEL_HOME" ] ; then
+ SABEL_HOME="/path/to/Sabel"
+fi
+
+if (test -z "$PHP_COMMAND") ; then
+ export PHP_COMMAND=php
+fi
+
+$PHP_COMMAND -d html_errors=off -qC $SABEL_HOME/sabel/Sakle.php $*
diff --git a/bin/sakle.bat b/bin/sakle.bat
new file mode 100755
index 0000000..786c13c
--- /dev/null
+++ b/bin/sakle.bat
@@ -0,0 +1,23 @@
+@echo off
+
+if "%OS%"=="Windows_NT" @setlocal
+
+set SABEL=c:\php\includes\Sabel
+
+goto init
+
+:init
+
+if "%PHP_COMMAND%" == "" goto no_phpcommand
+
+%PHP_COMMAND% -d html_errors=off -d open_basedir= -q "%SABEL%\sabel\Sakle.php" %1 %2 %3 %4 %5 %6 %7 %8 %9
+
+goto cleanup
+
+:no_phpcommand
+set PHP_COMMAND=php
+goto init
+
+:cleanup
+if "%OS%"=="Windows_NT" @endlocal
+rem pause
diff --git a/changelog b/changelog
new file mode 100755
index 0000000..a69f86c
--- /dev/null
+++ b/changelog
@@ -0,0 +1,169 @@
+2010-07-13 ebin
+
+ * sabel/db/mysql/Metadata.php: MySQL5.1以降用の外部キー取得SQLに誤りがあった
+ ため修正。(他のスキーマの同名テーブルの外部キー情報も取得してしまう不具合)
+
+2010-07-13 ebin
+
+ * generator/skeleton/en/lib/form/Object.php: 複数のインプット名によるフォー
+ ムバリデーションの際、カンマで連結したインプット名をsubmitメソッドに渡さなく
+ てもいいように変更。
+
+2010-05-10 ebin
+
+ * sabel/map/Candidate.php: uriメソッドのバグ修正。
+
+2010-05-08 ebin
+
+ * sabel/test/*: test関連のクラスのリファクタリング。モデル(DB)を使用しない
+ Fixtureに対応。テスト実行時にtestsディレクトリをインクルードパスに追加するよ
+ うに変更。
+
+2010-05-02 ebin
+
+ * sabel/response/Redirector.php: uri()にもパラメータを渡せるように変更。
+ flagmentに対応。
+
+2010-04-29 ebin
+
+ * generator/skeleton/en/tasks/Install.php: アドオンインストーラ修正。
+ Sabel_Xml_AttributesクラスにtoArray()メソッド追加。
+
+2010-04-29 ebin
+
+ * generator/skeleton/en/tasks/Install.php: アドオンインストーラ実装。
+
+2010-04-28 ebin
+
+ * sabel/db/mysql/Driver.php: mysql_set_charset関数がない環境でも
+ mysql_set_charsetが実行されて落ちる問題の修正。
+
+2010-04-28 ebin
+
+ * sabel/xml/Element.php: insertNextSibling()のバグ修正。
+
+2010-04-27 ebin
+
+ * generator/skeleton/en/config/INIT.php : APP_ENCODING定数の追加。それにとも
+ なう若干の改修。Sabel_Util_String::mbTrim()のバグ修正。
+
+2010-04-26 ebin
+
+ * Test/*: テストを全体的に修正。
+
+2010-03-12 ebin
+
+ * generator/skeleton/*: フォーム周りを若干更新。
+
+2009-12-02 ebin
+
+ * sabel/test/*: 他パッケージの更新に追い付いてない問題の修正。
+
+2009-12-02 ebin
+
+ * sabel/http/*: httpパッケージ更新。移植完了。
+
+2009-12-01 ebin
+
+ * sabel/http/*: Zend Frameworkから移植(CurlとかProxyとか未完了)。
+
+2009-11-30 ebin
+
+ * sabel/kvs/Xml.php: ファイルロック実装。
+
+2009-11-27 ebin
+
+ * sabel/session/*: sessionパッケージ改修。
+
+2009-11-27 ebin
+
+ * sabel/functions/core.php: get_server_name()関数追加。environment()関数の削
+ 除。それに関わる箇所の変更。lib/Cache.php作り直し。
+
+2009-11-27 ebin
+
+ * sabel/rss/*: rssパッケージ改修。
+
+2009-11-24 ebin
+
+ * sabel/xml/Element.php: Sabel.jsに合わせ、insertPreviousSibling,
+ insertNextSiblingメソッド実装。insertBefore, insertAfterメソッド改修。
+
+2009-11-24 ebin
+
+ * sabel/controller/Page.php: __get()と__set()をpublicに修正。
+ * sabel/cache: キャッシュクラス内部でKVSパッケージを使用するように変更。
+
+2009-11-24 ebin
+
+ * sabel/db/Model.php: Sabel_Db_ModelにselectForUpdateメソッド追加。
+
+2009-11-24 ebin
+
+ * sabel/kvs: KVS(Key-Value-Store)パッケージの追加。
+
+2009-11-23 hamaco
+
+ * Sabel.js (Sabel.Widget.Calendar): 色々と改善。
+ * Sabel.js (Sabel.Element): insertPreviousSibling, insertNextSiblingメソッド追加。
+
+2009-11-23 MoriReo
+
+ * sabel/Container.php: 設定クラスのbind->toにインスタンスを指定可能に変更。
+
+2009-11-22 MoriReo
+
+ * sabel/Preference: パッケージを追加。
+
+2009-11-21 Ebin
+
+ * lib/Cache.php: lib/cache(キャッシュユーティリティ)を作成。
+
+2009-11-19 Ebin
+
+ * sabel/Functions.php: 削除し、sabel/functions/core.phpと
+ sabel/functions/db.phpに分けた。
+
+2009-11-18 Ebin
+
+ * sabel/xml/Document.php: newできないように変更。
+
+2009-11-17 Ebin
+
+ * sabel/xml/*: 使い易さの改善。
+
+2009-11-14 MoriReo
+
+ * sabel/Container.php: 名前付きでの設定インタフェイスを削除。コードの改善。
+
+2009-11-13 Ebin
+
+ * sabel/Bus.php: プロセッサINSERT用のメソッドinsertProcessor追加。
+
+2009-11-12 MoriReo
+
+ * sabel/Container.php: Sabel_Containerのload()時に、複数の設定クラスを
+ 指定できるように変更。
+
+2009-11-11 MoriReo
+
+ * sabel/Container.php: Sabel_AspectからStaticProxyを削除。
+
+2009-11-10 Ebin
+
+ * sabel/Functions.php: get_mime_type()関数追加。それに応じて
+ Sabel_Mail_Mime_Html::addImage()のMIMEタイプを省略できるように変更。
+
+2009-11-10 Ebin
+
+ * generator/skeleton/en/lib/form/Object.php: toHidden()メソッド追加(フォームの
+ 値を全てhiddenで書き出す)
+
+2009-11-09 Ebin
+
+ * sabel/Functions.php: is_ipaddr()関数の正規表現を修正
+
+2009-11-09 hamaco
+
+ * Sabel.js (Sabel.Event): getTargetメソッドの返り値をSabel.Elementに変更
+
diff --git a/generator/generator.php b/generator/generator.php
new file mode 100755
index 0000000..2aa3212
--- /dev/null
+++ b/generator/generator.php
@@ -0,0 +1,147 @@
+
+ * @author Ebine Yutaka
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class SabelScaffold
+{
+ const DEFAULT_LANGUAGE = "en";
+
+ protected $ignores = array();
+ protected $overwrite = array();
+ protected $lang = "en";
+
+ protected $targetDir = "";
+ protected $skeletonDir = "";
+ protected $basedirNameLength = "";
+
+ public function __construct($args, $skeletonDir)
+ {
+ $this->skeletonDir = $skeletonDir;
+ $this->basedirNameLength = strlen($skeletonDir) + 3;
+ $this->targetDir = $this->getTargetDir($args);
+ $this->readOptions($args);
+ }
+
+ public function create($dir = null)
+ {
+ if ($dir === null) {
+ $dir = $this->skeletonDir . DS . self::DEFAULT_LANGUAGE;
+ }
+
+ foreach (scandir($dir) as $item) {
+ if ($item{0} === "." && $item !== ".htaccess") continue;
+
+ $fullPath = $dir . DS . $item;
+ $targetItem = substr($fullPath, $this->basedirNameLength + 1);
+ $targetPath = $this->targetDir . DS . $targetItem;
+
+ if (is_dir($fullPath)) {
+ if (isset($this->ignore[$targetItem])) {
+ Sabel_Console::message("ignore '{$targetItem}'.");
+ } else {
+ if (is_dir($targetPath)) {
+ Sabel_Console::warning("'{$targetItem}' already exists.");
+ } else {
+ Sabel_Console::success("create $targetItem");
+ mkdir($targetPath);
+ }
+
+ $this->create($fullPath);
+ }
+ } else {
+ if ($this->lang !== self::DEFAULT_LANGUAGE) {
+ $_target = substr($fullPath, strlen($this->skeletonDir) + 4); // DS(1) + lang(2) + DS(1)
+ $_target = $this->skeletonDir . DS . $this->lang . DS . $_target;
+ if (is_dir($_target) || is_file($_target)) {
+ $fullPath = $_target;
+ }
+ }
+
+ if (isset($this->ignore[$targetItem])) {
+ Sabel_Console::message("ignore '{$targetItem}'.");
+ } elseif (is_file($targetPath)) {
+ Sabel_Console::warning("'{$targetItem}' already exists.");
+ } else {
+ Sabel_Console::success("create $targetItem");
+ copy($fullPath, $targetPath);
+ }
+ }
+ }
+ }
+
+ public function chmod()
+ {
+ $dirs = array("cache", "cache" . DS . "sabel", "data" , "logs");
+
+ foreach ($dirs as $dir) {
+ chmod($this->targetDir . DS . $dir, 0777);
+ }
+ }
+
+ protected function getTargetDir($args)
+ {
+ if (in_array("-d", $args, true)) {
+ $dir = $args[array_search("-d", $args) + 1];
+ } else {
+ $dir = getcwd();
+ }
+
+ if (!is_dir($dir)) mkdir($dir);
+ return $dir;
+ }
+
+ protected function readOptions($args)
+ {
+ if (in_array("--overwrite", $args, true)) {
+ $index = array_search("--overwrite", $args) + 1;
+ for ($i = $index, $c = count($args); $i < $c; $i++) {
+ if (substr($args[$i], 0, 2) === "--") break;
+ $path = $this->targetDir . DS . $args[$i];
+ if (is_file($path)) {
+ unlink($path);
+ } elseif (is_dir($path)) {
+ $fs = new Sabel_Util_FileSystem($path);
+ $fs->rmdir();
+ }
+ }
+ }
+
+ if (in_array("--ignore", $args, true)) {
+ $index = array_search("--ignore", $args) + 1;
+ for ($i = $index, $c = count($args); $i < $c; $i++) {
+ if (substr($args[$i], 0, 2) === "--") break;
+ $this->ignore[$args[$i]] = 1;
+ }
+ }
+
+ if (Sabel_Console::hasOption("l", $args)) {
+ if (($lang = Sabel_Console::getOption("l", $args)) !== null) {
+ $lang = strtolower($lang);
+ if (is_dir($this->skeletonDir . DS . $lang)) {
+ $this->lang = $lang;
+ }
+ }
+ }
+ }
+}
+
+if (!defined("TEST_CASE")) {
+ if (!defined("DS")) define("DS", DIRECTORY_SEPARATOR);
+ $sabel = realpath(dirname(__FILE__) . DS . ".." . DS . "sabel");
+ require_once ($sabel . DS . "Object.php");
+ require_once ($sabel . DS . "Console.php");
+ require_once ($sabel . DS . "util" . DS . "fileSystem" . DS . "Base.php");
+ require_once ($sabel . DS . "util" . DS . "FileSystem.php");
+
+ $scaffold = new SabelScaffold($_SERVER["argv"], dirname(__FILE__) . DS . "skeleton");
+ $scaffold->create();
+ $scaffold->chmod();
+}
diff --git a/generator/skeleton/en/app/helpers/Date.php b/generator/skeleton/en/app/helpers/Date.php
new file mode 100755
index 0000000..e40a9f6
--- /dev/null
+++ b/generator/skeleton/en/app/helpers/Date.php
@@ -0,0 +1,389 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Helpers_Date
+{
+ const NORMAL = 0;
+ const ATOM = 1;
+ const RSS = 2;
+ const COOKIE = 3;
+ const ISO = 4;
+ const RFC822 = 5;
+ const RFC850 = 6;
+ const RFC1036 = 7;
+ const RFC1123 = 8;
+ const RFC2822 = 9;
+ const RFC = 10;
+ const W3C = 11;
+
+ private static $formats = array(self::NORMAL => array(
+ "all" => "Y-m-d H:i:s",
+ "date" => "Y-m-d",
+ "time" => "H:i:s"),
+
+ self::ATOM => array(
+ "all" => "c",
+ "date" => "Y-m-d",
+ "time" => "H:i:sP"),
+
+ self::RSS => array(
+ "all" => "r",
+ "date" => "D, d M Y",
+ "time" => "H:i:s O"),
+
+ self::COOKIE => array(
+ "all" => "l, d-M-y H:i:s T",
+ "date" => "l, d-M-y",
+ "time" => "H:i:s T"),
+
+ self::ISO => array(
+ "all" => "Y-m-d\TH:i:sO",
+ "date" => "Y-m-d",
+ "time" => "H:i:sO"),
+
+ self::RFC822 => array(
+ "all" => "D, d M y H:i:s O",
+ "date" => "D, d M y",
+ "time" => "H:i:s O"),
+
+ self::RFC850 => array(
+ "all" => "l, d-M-y H:i:s T",
+ "date" => "l, d-M-y",
+ "time" => "H:i:s T"),
+
+ self::RFC1036 => array(
+ "all" => "D, d M y H:i:s O",
+ "date" => "D, d M y",
+ "time" => "H:i:s O"),
+
+ self::RFC1123 => array(
+ "all" => "r",
+ "date" => "D, d M Y",
+ "time" => "H:i:s O"),
+
+ self::RFC2822 => array(
+ "all" => "r",
+ "date" => "D, d M Y",
+ "time" => "H:i:s O"),
+
+ self::RFC => array(
+ "all" => "r",
+ "date" => "D, d M Y",
+ "time" => "H:i:s O"),
+
+ self::W3C => array(
+ "all" => "c",
+ "date" => "Y-m-d",
+ "time" => "H:i:sP"));
+
+ protected
+ $timestamp = null,
+ $format = self::NORMAL;
+
+ public static function format($date, $format)
+ {
+ return date(self::$formats[$format]["all"], strtotime($date));
+ }
+
+ public function __construct($arg = null)
+ {
+ if ($arg === null) {
+ $this->timestamp = time();
+ } elseif (is_string($arg)) {
+ $this->timestamp = strtotime($arg);
+ } elseif (is_array($arg)) {
+ $y = (isset($arg["y"])) ? $arg["y"] : date("Y");
+ $m = (isset($arg["m"])) ? $arg["m"] : date("m");
+ $d = (isset($arg["d"])) ? $arg["d"] : 1;
+ $h = (isset($arg["h"])) ? $arg["h"] : 0;
+ $i = (isset($arg["i"])) ? $arg["i"] : 0;
+ $s = (isset($arg["s"])) ? $arg["s"] : 0;
+
+ $this->timestamp = mktime($h, $i, $s, $m, $d, $y);
+ } else {
+ throw new Exception("Helpers_Date::__construct() invalid parameter.");
+ }
+ }
+
+ public function __toString()
+ {
+ return $this->getDateTime();
+ }
+
+ public function setFormat($format)
+ {
+ $this->format = $format;
+
+ return $this;
+ }
+
+ public function getDatetime()
+ {
+ return date(self::$formats[$this->format]["all"], $this->timestamp);
+ }
+
+ public function getDate()
+ {
+ return date(self::$formats[$this->format]["date"], $this->timestamp);
+ }
+
+ public function getTime()
+ {
+ return date(self::$formats[$this->format]["time"], $this->timestamp);
+ }
+
+ public function getYear($twoDigits = false)
+ {
+ if ($twoDigits) {
+ return date("y", $this->timestamp);
+ } else {
+ return date("Y", $this->timestamp);
+ }
+ }
+
+ public function getMonth($withLeadingZeros = true)
+ {
+ if ($withLeadingZeros) {
+ return date("m", $this->timestamp);
+ } else {
+ return date("n", $this->timestamp);
+ }
+ }
+
+ public function getTextualMonth($short = true)
+ {
+ if ($short) {
+ return date("M", $this->timestamp);
+ } else {
+ return date("F", $this->timestamp);
+ }
+ }
+
+ public function getDay($withLeadingZeros = true)
+ {
+ if ($withLeadingZeros) {
+ return date("d", $this->timestamp);
+ } else {
+ return date("j", $this->timestamp);
+ }
+ }
+
+ public function getLastDay()
+ {
+ $timestamp = mktime($this->getHour(),
+ $this->getMinute(),
+ $this->getSecond(),
+ $this->getMonth() + 1,
+ 0,
+ $this->getYear());
+
+ return date("d", $timestamp);
+ }
+
+ public function getHour($withLeadingZeros = true)
+ {
+ if ($withLeadingZeros) {
+ return date("H", $this->timestamp);
+ } else {
+ return date("G", $this->timestamp);
+ }
+ }
+
+ public function getHourBy12Format($withLeadingZeros = true)
+ {
+ if ($withLeadingZeros) {
+ return date("h", $this->timestamp);
+ } else {
+ return date("g", $this->timestamp);
+ }
+ }
+
+ public function getMinute()
+ {
+ return date("i", $this->timestamp);
+ }
+
+ public function getSecond()
+ {
+ return date("s", $this->timestamp);
+ }
+
+ public function getMeridiem($lower = true)
+ {
+ return date(($lower) ? "a" : "A", $this->timestamp);
+ }
+
+ public function getTextualWeek($short = true)
+ {
+ if ($short) {
+ return date("D", $this->timestamp);
+ } else {
+ return date("l", $this->timestamp);
+ }
+ }
+
+ public function getWeekNumber()
+ {
+ return date("w", $this->timestamp);
+ }
+
+ public function ymd($sep = "-")
+ {
+ return $this->getYear() . $sep . $this->getMonth() . $sep . $this->getDay();
+ }
+
+ public function his($sep = ":")
+ {
+ return $this->getHour() . $sep . $this->getMinute() . $sep . $this->getSecond();
+ }
+
+ public function incYear($year = 1)
+ {
+ $year = $this->getYear() + $year;
+
+ $this->timestamp = mktime($this->getHour(),
+ $this->getMinute(),
+ $this->getSecond(),
+ $this->getMonth(),
+ $this->getDay(),
+ $year);
+
+ return $year;
+ }
+
+ public function decYear($year = 1)
+ {
+ $year = $this->getYear() - $year;
+
+ $this->timestamp = mktime($this->getHour(),
+ $this->getMinute(),
+ $this->getSecond(),
+ $this->getMonth(),
+ $this->getDay(),
+ $year);
+
+ return $year;
+ }
+
+ public function incMonth($month = 1)
+ {
+ $month = $this->getMonth() + $month;
+
+ $this->timestamp = mktime($this->getHour(),
+ $this->getMinute(),
+ $this->getSecond(),
+ $month,
+ $this->getDay(),
+ $this->getYear());
+
+ return $month;
+ }
+
+ public function decMonth($month = 1)
+ {
+ $month = $this->getMonth() - $month;
+
+ $this->timestamp = mktime($this->getHour(),
+ $this->getMinute(),
+ $this->getSecond(),
+ $month,
+ $this->getDay(),
+ $this->getYear());
+
+ return $month;
+ }
+
+ public function incDay($day = 1)
+ {
+ $this->timestamp += 86400 * $day;
+
+ return $this->getDay();
+ }
+
+ public function decDay($day = 1)
+ {
+ $this->timestamp -= 86400 * $day;
+
+ return $this->getDay();
+ }
+
+ public function incHour($hour = 1)
+ {
+ $this->timestamp += 3600 * $hour;
+
+ return $this->getHour();
+ }
+
+ public function decHour($hour = 1)
+ {
+ $this->timestamp -= 3600 * $hour;
+
+ return $this->getHour();
+ }
+
+ public function incMinute($min = 1)
+ {
+ $this->timestamp += 60 * $min;
+
+ return $this->getMinute();
+ }
+
+ public function decMinute($min = 1)
+ {
+ $this->timestamp -= 60 * $min;
+
+ return $this->getMinute();
+ }
+
+ public function incSecond($second = 1)
+ {
+ $this->timestamp += $second;
+
+ return $this->getSecond();
+ }
+
+ public function decSecond($second = 1)
+ {
+ $this->timestamp -= $second;
+
+ return $this->getSecond();
+ }
+
+ public function y()
+ {
+ return $this->getYear();
+ }
+
+ public function m()
+ {
+ return $this->getMonth();
+ }
+
+ public function d()
+ {
+ return $this->getDay();
+ }
+
+ public function h()
+ {
+ return $this->getHour();
+ }
+
+ public function i()
+ {
+ return $this->getMinute();
+ }
+
+ public function s()
+ {
+ return $this->getSecond();
+ }
+}
diff --git a/generator/skeleton/en/app/helpers/Js.php b/generator/skeleton/en/app/helpers/Js.php
new file mode 100755
index 0000000..6f72abc
--- /dev/null
+++ b/generator/skeleton/en/app/helpers/Js.php
@@ -0,0 +1,89 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Helpers_Js
+{
+ public static function effectUpdater($sourceId, $replaceId, $effect = "Slide")
+ {
+ $include = <<
+INC;
+
+ $script = <<
+new Sabel.Event(window, "load", function() {
+ var updater = new Sabel.PHP.EffectUpdater("%s", "%s");
+
+ Sabel.get("%s").observe("click", function(evt) {
+ updater.fire(this.href);
+ Sabel.Event.preventDefault(evt);
+ });
+});
+
+JS;
+
+ $buf = array();
+ $buf[] = sprintf($include, linkto("js/helpers/EffectUpdater.js"));
+ $buf[] = sprintf($script, $replaceId, $effect, $sourceId);
+
+ return join($buf, "\n");
+ }
+
+ public static function ajaxPager($replaceId, $pagerClass = "sbl_pager")
+ {
+ $include = <<
+INC;
+
+ $buf = array();
+ $buf[] = sprintf($include, linkto("js/helpers/EffectUpdater.js"));
+ $buf[] = sprintf($include, linkto("js/helpers/AjaxPager.js"));
+ $buf[] = "\n";
+ $buf[] = '';
+
+ return join($buf, "") . "\n";
+ }
+
+ /* @todo
+ public static function formValidator($formObj, $errBox = "sbl_errmsg")
+ {
+ $include = <<
+INC;
+
+ $model = $formObj->getModel();
+ $columns = $model->getColumns();
+ $errMsgs = Sabel_Db_Validate_Config::getMessages();
+ $lNames = Sabel_Db_Model_Localize::getColumnNames($model->getName());
+
+ $data = array("data" => array(), "errors" => $errMsgs);
+ foreach ($columns as $c) {
+ $name = $c->name;
+ if (isset($lNames[$c->name])) {
+ $c->name = $lNames[$c->name];
+ }
+
+ $data["data"][$name] = array_change_key_case((array) $c, CASE_UPPER);
+ }
+
+ $buf = array();
+ $buf[] = sprintf($include, linkto("js/helpers/FormValidator.js"));
+ $buf[] = "\n";
+ $buf[] = '';
+
+ return join($buf, "") . "\n";
+ }
+ */
+}
diff --git a/generator/skeleton/en/app/helpers/application.php b/generator/skeleton/en/app/helpers/application.php
new file mode 100755
index 0000000..78245c3
--- /dev/null
+++ b/generator/skeleton/en/app/helpers/application.php
@@ -0,0 +1,87 @@
+%s', uri($uri), $anchor);
+ } else {
+ return sprintf('%s ', uri($uri), $uriQuery, $anchor);
+ }
+}
+
+function ah($param, $anchor, $uriQuery = "")
+{
+ return a($param, h($anchor), $uriQuery);
+}
+
+/**
+ * create uri for css, image, js, etc...
+ */
+function linkto($file)
+{
+ if ($bus = Sabel_Context::getContext()->getBus()) {
+ if ($bus->get("NO_VIRTUAL_HOST") && defined("URI_PREFIX")) {
+ return URI_PREFIX . "/" . $file;
+ }
+ }
+
+ return "/" . $file;
+}
+
+function get_uri_prefix($secure = false, $absolute = false)
+{
+ $prefix = "";
+
+ if ($secure || $absolute) {
+ $server = get_server_name();
+ $prefix = (($secure) ? "https" : "http") . "://" . $server;
+ }
+
+ if ($bus = Sabel_Context::getContext()->getBus()) {
+ if ($bus->get("NO_VIRTUAL_HOST") && defined("URI_PREFIX")) {
+ $prefix .= URI_PREFIX;
+ }
+ }
+
+ return $prefix;
+}
+
+/**
+ * create uri
+ */
+function uri($uri, $secure = false, $absolute = false)
+{
+ $context = Sabel_Context::getContext();
+ return get_uri_prefix($secure, $absolute) . "/" . $context->getCandidate()->uri($uri);
+}
+
+/**
+ * Internal request.
+ */
+function __include($uri, $values = array(), $method = Sabel_Request::GET, $withLayout = false)
+{
+ $requester = new Sabel_Request_Internal($method);
+ $requester->values($values)->withLayout($withLayout);
+ return $requester->request($uri)->getResult();
+}
+
+function mb_trim($string)
+{
+ $string = new Sabel_Util_String($string);
+ return $string->trim()->toString();
+}
+
+function to_date($date, $format)
+{
+ return Helpers_Date::format($date, constant("Helpers_Date::" . $format));
+}
diff --git a/generator/skeleton/en/app/index/controllers/Index.php b/generator/skeleton/en/app/index/controllers/Index.php
new file mode 100755
index 0000000..684d87e
--- /dev/null
+++ b/generator/skeleton/en/app/index/controllers/Index.php
@@ -0,0 +1,5 @@
+Welcome
+
+
diff --git a/generator/skeleton/en/app/logics/Base.php b/generator/skeleton/en/app/logics/Base.php
new file mode 100755
index 0000000..e72545b
--- /dev/null
+++ b/generator/skeleton/en/app/logics/Base.php
@@ -0,0 +1,14 @@
+aspect("Logics_Base")->annotate(
+ "transaction", array("Logics_Aspects_Transaction")
+ );
+ }
+}
diff --git a/generator/skeleton/en/app/logics/Result.php b/generator/skeleton/en/app/logics/Result.php
new file mode 100755
index 0000000..efbdbff
--- /dev/null
+++ b/generator/skeleton/en/app/logics/Result.php
@@ -0,0 +1,103 @@
+set($value);
+ }
+
+ public function success()
+ {
+ $this->isSuccess = true;
+
+ return $this;
+ }
+
+ public function isSuccess()
+ {
+ return $this->isSuccess;
+ }
+
+ public function failure()
+ {
+ $this->isSuccess = false;
+
+ return $this;
+ }
+
+ public function isFailure()
+ {
+ return !$this->isSuccess;
+ }
+
+ public function set($value)
+ {
+ $this->result = $value;
+
+ return $this;
+ }
+
+ public function get()
+ {
+ return $this->result;
+ }
+
+ public function __toString()
+ {
+ $r = $this->result;
+
+ if (is_object($r)) {
+ if ($r instanceof Sabel_Object) {
+ return $r->toString();
+ } elseif (method_exists($r, "__toString")) {
+ return $r->__toString();
+ } else {
+ $message = "could not be converted to string.";
+ throw new Sabel_Exception_Runtime(__METHOD__ . "() " . $message);
+ }
+ } elseif (is_bool($r)) {
+ return ($r) ? "true" : "false";
+ } else {
+ return (string)$r;
+ }
+ }
+
+ public function isString()
+ {
+ return is_string($this->result);
+ }
+
+ public function isInt()
+ {
+ return is_int($this->result);
+ }
+
+ public function isFloat()
+ {
+ return is_float($this->result);
+ }
+
+ public function isBoolean()
+ {
+ return is_bool($this->result);
+ }
+
+ public function isArray()
+ {
+ return is_array($this->result);
+ }
+
+ public function isNull()
+ {
+ return ($this->result === null);
+ }
+
+ public function isNotNull()
+ {
+ return ($this->result !== null);
+ }
+}
diff --git a/generator/skeleton/en/app/logics/aspects/Transaction.php b/generator/skeleton/en/app/logics/aspects/Transaction.php
new file mode 100755
index 0000000..4966d3d
--- /dev/null
+++ b/generator/skeleton/en/app/logics/aspects/Transaction.php
@@ -0,0 +1,27 @@
+proceed();
+
+ if (!$active) {
+ Sabel_Db_Transaction::commit();
+ }
+
+ return $result;
+ } catch (Exception $e) {
+ if (!$active) {
+ Sabel_Db_Transaction::rollback();
+ }
+
+ throw $e;
+ }
+ }
+}
diff --git a/generator/skeleton/en/app/views/clientError.tpl b/generator/skeleton/en/app/views/clientError.tpl
new file mode 100755
index 0000000..05df538
--- /dev/null
+++ b/generator/skeleton/en/app/views/clientError.tpl
@@ -0,0 +1,5 @@
+4xx Client Error
+
+
+ The request contains bad syntax or cannot be fulfilled.
+
diff --git a/generator/skeleton/en/app/views/error.tpl b/generator/skeleton/en/app/views/error.tpl
new file mode 100755
index 0000000..83bc106
--- /dev/null
+++ b/generator/skeleton/en/app/views/error.tpl
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
diff --git a/generator/skeleton/en/app/views/forbidden.tpl b/generator/skeleton/en/app/views/forbidden.tpl
new file mode 100755
index 0000000..e57c9b0
--- /dev/null
+++ b/generator/skeleton/en/app/views/forbidden.tpl
@@ -0,0 +1,5 @@
+
403 Forbidden
+
+
+ Your request was denied.
+
diff --git a/generator/skeleton/en/app/views/layout.tpl b/generator/skeleton/en/app/views/layout.tpl
new file mode 100755
index 0000000..280ad0f
--- /dev/null
+++ b/generator/skeleton/en/app/views/layout.tpl
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/generator/skeleton/en/app/views/linkedPager.tpl b/generator/skeleton/en/app/views/linkedPager.tpl
new file mode 100755
index 0000000..49339b4
--- /dev/null
+++ b/generator/skeleton/en/app/views/linkedPager.tpl
@@ -0,0 +1,12 @@
+
+ hasPrev() || $paginator->hasNext()) : ?>
+
+
+
\ No newline at end of file
diff --git a/generator/skeleton/en/app/views/notFound.tpl b/generator/skeleton/en/app/views/notFound.tpl
new file mode 100755
index 0000000..c50bdfd
--- /dev/null
+++ b/generator/skeleton/en/app/views/notFound.tpl
@@ -0,0 +1,5 @@
+
404 Not Found
+
+
+ Sorry, the page you requested was not found.
+
diff --git a/generator/skeleton/en/app/views/pager.tpl b/generator/skeleton/en/app/views/pager.tpl
new file mode 100755
index 0000000..af60315
--- /dev/null
+++ b/generator/skeleton/en/app/views/pager.tpl
@@ -0,0 +1,15 @@
+
diff --git a/generator/skeleton/en/app/views/serverError.tpl b/generator/skeleton/en/app/views/serverError.tpl
new file mode 100755
index 0000000..1752fd5
--- /dev/null
+++ b/generator/skeleton/en/app/views/serverError.tpl
@@ -0,0 +1,11 @@
+
5xx Server Error
+
+
+ The server encountered an internal error and was unable to complete your request.
+
+
+ if (isset($exception_message)) : ?>
+
+ = $exception_message ?>
+
+ endif ?>
diff --git a/generator/skeleton/en/config/Addon.php b/generator/skeleton/en/config/Addon.php
new file mode 100755
index 0000000..ab29835
--- /dev/null
+++ b/generator/skeleton/en/config/Addon.php
@@ -0,0 +1,15 @@
+ "Processor_Addon",
+ "request" => "Processor_Request",
+ "response" => "Processor_Response",
+ "router" => "Processor_Router",
+ "session" => "Processor_Session",
+ "helper" => "Processor_Helper",
+ "controller" => "Processor_Controller",
+ "initializer" => "Processor_Initializer",
+ "action" => "Processor_Action",
+ "view" => "Processor_View"
+ );
+
+ protected $interfaces = array(
+ "request" => "Sabel_Request",
+ "response" => "Sabel_Response",
+ "session" => "Sabel_Session",
+ "view" => "Sabel_View",
+ "controller" => "Sabel_Controller_Page"
+ );
+
+ protected $configs = array(
+ "map" => "Config_Map",
+ "addon" => "Config_Addon",
+ "database" => "Config_Database"
+ );
+
+ public function getProcessors()
+ {
+ $baseDir = RUN_BASE . DS . LIB_DIR_NAME . DS . "processor" . DS;
+
+ foreach (array_keys($this->processors) as $name) {
+ Sabel::fileUsing($baseDir . ucfirst($name) . ".php", true);
+ }
+
+ return $this->processors;
+ }
+}
diff --git a/generator/skeleton/en/config/Container.php b/generator/skeleton/en/config/Container.php
new file mode 100755
index 0000000..12f7bd5
--- /dev/null
+++ b/generator/skeleton/en/config/Container.php
@@ -0,0 +1,11 @@
+ array(
+ "package" => "sabel.db.*",
+ "host" => "localhost",
+ "database" => "dbname",
+ "user" => "user",
+ "password" => "password")
+ );
+ break;
+
+ case TEST:
+ $params = array("default" => array(
+ "package" => "sabel.db.*",
+ "host" => "localhost",
+ "database" => "dbname",
+ "user" => "user",
+ "password" => "password")
+ );
+ break;
+
+ case DEVELOPMENT:
+ $params = array("default" => array(
+ "package" => "sabel.db.*",
+ "host" => "localhost",
+ "database" => "dbname",
+ "user" => "user",
+ "password" => "password")
+ );
+ break;
+ }
+
+ return $params;
+ }
+}
diff --git a/generator/skeleton/en/config/INIT.php b/generator/skeleton/en/config/INIT.php
new file mode 100755
index 0000000..522d7ef
--- /dev/null
+++ b/generator/skeleton/en/config/INIT.php
@@ -0,0 +1,59 @@
+route("default")
+ ->uri(":controller/:action")
+ ->module("index")
+ ->defaults(array(
+ ":controller" => "index",
+ ":action" => "index")
+ );
+
+ $this->route("notfound")
+ ->uri("*")
+ ->module("index")
+ ->controller("index")
+ ->action("notFound");
+ }
+}
diff --git a/generator/skeleton/en/config/environment.php b/generator/skeleton/en/config/environment.php
new file mode 100755
index 0000000..8b6c830
--- /dev/null
+++ b/generator/skeleton/en/config/environment.php
@@ -0,0 +1,22 @@
+ 0) {
+ ini_set("display_errors", "0");
+ ini_set("log_errors", "1");
+ error_reporting(E_ALL);
+ define("SBL_LOG_LEVEL", SBL_LOG_ERR);
+} else {
+ ini_set("display_errors", "1");
+ error_reporting(E_ALL|E_STRICT);
+ define("SBL_LOG_LEVEL", SBL_LOG_ALL);
+}
+
+//define("SERVICE_DOMAIN", "www.example.com");
+//define("FILEINFO_MAGICDB", "/usr/share/misc/magic");
diff --git a/generator/skeleton/en/lib/Cache.php b/generator/skeleton/en/lib/Cache.php
new file mode 100755
index 0000000..d29c9ab
--- /dev/null
+++ b/generator/skeleton/en/lib/Cache.php
@@ -0,0 +1,40 @@
+addServer(/* $host, $port = 11211, $weight = 1 */);
+ break;
+ default:
+ $message = __METHOD__ . "() invalid cache backend.";
+ throw new Sabel_Exception_Runtime($message);
+ }
+
+ return $storage;
+ }
+}
diff --git a/generator/skeleton/en/lib/LinkedPaginator.php b/generator/skeleton/en/lib/LinkedPaginator.php
new file mode 100755
index 0000000..a839902
--- /dev/null
+++ b/generator/skeleton/en/lib/LinkedPaginator.php
@@ -0,0 +1,103 @@
+lastPage = $num;
+ }
+
+ public function prev($text, $attrs = array())
+ {
+ return $this->createLink($text, $this->getUriQuery($this->viewer->getPrevious()), $attrs);
+ }
+
+ public function next($text, $attrs = array())
+ {
+ return $this->createLink($text, $this->getUriQuery($this->viewer->getNext()), $attrs);
+ }
+
+ public function hasPrev()
+ {
+ $attrs = $this->attributes;
+ return ($attrs[$attrs["pageKey"]] > 1);
+ }
+
+ public function hasNext()
+ {
+ return $this->hasNext;
+ }
+
+ public function build($limit, array $getValues = array())
+ {
+ $page = 1;
+ $pageKey = $this->attributes["pageKey"];
+
+ if (isset($getValues[$pageKey])) {
+ $page = $getValues[$pageKey];
+ if (!is_numeric($page) || $page < 1) $page = 1;
+ }
+
+ $model = $this->model;
+ $attributes =& $this->attributes;
+
+ unset($getValues[$pageKey]);
+ unset($getValues[ini_get("session.name")]);
+ $attributes["uriQuery"] = http_build_query($getValues, "", "&");
+
+ $count = $this->lastPage * $limit;
+
+ $attributes["count"] = $count;
+ $attributes["limit"] = $limit;
+ $attributes[$pageKey] = $page;
+
+ $pager = new Sabel_View_Pager($count, $limit);
+ $pager->setPageNumber($page);
+ $attributes["viewer"] = new Sabel_View_PageViewer($pager);
+
+ if ($count === 0) {
+ $attributes["offset"] = 0;
+ $attributes["results"] = array();
+ $model->clear();
+ } else {
+ $offset = $pager->getSqlOffset();
+ $this->_setOrderBy($getValues);
+ $model->setLimit($limit + 1);
+ $model->setOffset($offset);
+
+ $attributes["offset"] = $offset;
+ $attributes["results"] = $model->{$this->method}();
+
+ $results = $attributes["results"];
+ if (count($results) > $limit) {
+ array_pop($results);
+ $attributes["results"] = $results;
+
+ if ($page < $this->lastPage) {
+ $this->hasNext = true;
+ }
+ }
+ }
+
+ return $this;
+ }
+
+ protected function createLink($text, $query, $attrs)
+ {
+ $_attrs = "";
+ if (is_array($attrs) && !empty($attrs)) {
+ $tmp = array();
+ foreach ($attrs as $attr => $value) {
+ $tmp[] = $attr . '="' . h($value) . '"';
+ }
+
+ $_attrs = " " . implode(" ", $tmp);
+ }
+
+ $format = '
%s ';
+ return sprintf($format, $_attrs, $this->uri, $query, h($text));
+ }
+}
diff --git a/generator/skeleton/en/lib/Paginator.php b/generator/skeleton/en/lib/Paginator.php
new file mode 100755
index 0000000..e72a686
--- /dev/null
+++ b/generator/skeleton/en/lib/Paginator.php
@@ -0,0 +1,226 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Paginator extends Sabel_Object
+{
+ /**
+ * @var object Sabel_Db_Model or Sabel_Db_Join
+ */
+ protected $model = null;
+
+ /**
+ * @var boolean
+ */
+ protected $isJoin = false;
+
+ /**
+ * @var string
+ */
+ protected $method = "select";
+
+ /**
+ * @var array
+ */
+ protected $attributes = array();
+
+ /**
+ * @array
+ */
+ protected $defaultOrder = array();
+
+ /**
+ * @array or false
+ */
+ protected $orderColumns = false;
+
+ public function __construct($model)
+ {
+ if (is_string($model)) {
+ $model = MODEL($model);
+ } elseif ($model instanceof Sabel_Db_Finder) {
+ $model = $model->getRawInstance();
+ }
+
+ if (is_model($model)) {
+ $model->autoReinit(false);
+ } elseif ($model instanceof Sabel_Db_Join) {
+ $model->getModel()->autoReinit(false);
+ $this->isJoin = true;
+ } else {
+ $message = __METHOD__ . "() invalid instance.";
+ throw new Sabel_Exception_Runtime($message);
+ }
+
+ $this->model = $model;
+ $this->attributes["pageKey"] = "page";
+ }
+
+ public function __set($key, $value)
+ {
+ $this->attributes[$key] = $value;
+ }
+
+ public function __get($key)
+ {
+ if (isset($this->attributes[$key])) {
+ return $this->attributes[$key];
+ } else {
+ return null;
+ }
+ }
+
+ public function getPageNumber()
+ {
+ $pageKey = $this->attributes["pageKey"];
+ return $this->$pageKey;
+ }
+
+ public function getUriQuery($page)
+ {
+ $pageKey = $this->attributes["pageKey"];
+ if (!isset($this->attributes["uriQuery"])) {
+ return "{$pageKey}={$page}";
+ } else {
+ if (($query = $this->attributes["uriQuery"]) === "") {
+ return "{$pageKey}={$page}";
+ } else {
+ return $query . "&{$pageKey}=" . $page;
+ }
+ }
+ }
+
+ public function setCondition($arg1, $arg2 = null)
+ {
+ $this->model->setCondition($arg1, $arg2);
+
+ return $this;
+ }
+
+ public function setDefaultOrder($column, $mode = "asc")
+ {
+ $this->defaultOrder[$column] = $mode;
+
+ return $this;
+ }
+
+ public function setOrderColumns($columns)
+ {
+ $this->orderColumns = $columns;
+
+ return $this;
+ }
+
+ public function setMethod($method)
+ {
+ $this->method = $method;
+
+ return $this;
+ }
+
+ public function build($limit, array $getValues = array())
+ {
+ $page = 1;
+ $pageKey = $this->attributes["pageKey"];
+
+ if (isset($getValues[$pageKey])) {
+ $page = $getValues[$pageKey];
+ if (!is_numeric($page) || $page < 1) $page = 1;
+ }
+
+ $model = $this->model;
+ $attributes =& $this->attributes;
+
+ unset($getValues[$pageKey]);
+ $attributes["uriQuery"] = http_build_query($getValues, "", "&");
+ $count = ($this->isJoin) ? $model->getCount(false) : $model->getCount();
+
+ $attributes["count"] = $count;
+ $attributes["limit"] = $limit;
+ $attributes["page"] = $page;
+
+ $pager = new Sabel_View_Pager($count, $limit);
+ $pager->setPageNumber($page);
+ $attributes["viewer"] = new Sabel_View_PageViewer($pager);
+
+ if ($count === 0) {
+ $attributes["offset"] = 0;
+ $attributes["results"] = array();
+ $model->clear();
+ } else {
+ $offset = $pager->getSqlOffset();
+ $this->_setOrderBy($getValues);
+ $model->setLimit($limit);
+ $model->setOffset($offset);
+
+ $attributes["offset"] = $offset;
+ $attributes["results"] = $model->{$this->method}();
+ }
+
+ return $this;
+ }
+
+ protected function _setOrderBy($getValues)
+ {
+ $orderValues = array();
+ $orderColumns = $this->orderColumns;
+
+ if ($orderColumns !== false) {
+ $oColNum = count($orderColumns);
+ $pageKey = $this->attributes["pageKey"];
+
+ if ($this->isJoin) {
+ $columns = $this->model->getModel()->getColumnNames();
+ } else {
+ $columns = $this->model->getColumnNames();
+ }
+
+ foreach ($getValues as $key => $val) {
+ if (preg_match('/^[A-Z]/', $key{0}) === 1 && strpos($key, "_") !== false) {
+ list ($mname, $cname) = explode("_", $key, 2);
+ $key = $mname . "." . $cname;
+ } else {
+ if (!in_array($key, $columns, true)) continue;
+ }
+
+ if ($oColNum === 0 || in_array($key, $orderColumns, true)) {
+ $orderValues[$key] = $val;
+ }
+ }
+ }
+
+ if (empty($orderValues)) {
+ if (empty($this->defaultOrder)) {
+ return;
+ } else {
+ $orderValues = $this->defaultOrder;
+ }
+ }
+
+ $model = $this->model;
+ $orders = array();
+
+ if (empty($orderColumns)) {
+ foreach ($orderValues as $column => $order) {
+ $order = strtolower($order);
+ if ($order !== "asc" && $order !== "desc") $order = "asc";
+ $model->setOrderBy($column, $order);
+ }
+ } else {
+ foreach ($orderColumns as $column) {
+ if (!isset($orderValues[$column])) continue;
+
+ $order = strtolower($orderValues[$column]);
+ if ($order !== "asc" && $order !== "desc") $order = "asc";
+ $model->setOrderBy($column, $order);
+ }
+ }
+ }
+}
diff --git a/generator/skeleton/en/lib/Validator.php b/generator/skeleton/en/lib/Validator.php
new file mode 100755
index 0000000..9abe3ce
--- /dev/null
+++ b/generator/skeleton/en/lib/Validator.php
@@ -0,0 +1,15 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Validator extends Sabel_Validator
+{
+ // Your custom validation methods.
+}
diff --git a/generator/skeleton/en/lib/form/Html.php b/generator/skeleton/en/lib/form/Html.php
new file mode 100755
index 0000000..021ec17
--- /dev/null
+++ b/generator/skeleton/en/lib/form/Html.php
@@ -0,0 +1,221 @@
+name = $name;
+ } else {
+ $message = __METHOD__ . "() argument must be a string.";
+ throw new Sabel_Exception_InvalidArgument($message);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param mixed $value
+ *
+ * @return self
+ */
+ public function setValue($value)
+ {
+ $this->value = $value;
+
+ return $this;
+ }
+
+ /**
+ * @param string $attrs
+ *
+ * @throws Sabel_Exception_InvalidArgument
+ * @return self
+ */
+ public function setAttributes($attributes)
+ {
+ if (is_string($attributes)) {
+ $this->attributes = $attributes;
+ } else {
+ $message = __METHOD__ . "() argument must be a string.";
+ throw new Sabel_Exception_InvalidArgument($message);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return self
+ */
+ public function clear()
+ {
+ $this->name = "";
+ $this->value = null;
+ $this->attributes = "";
+
+ return $this;
+ }
+
+ public function text()
+ {
+ $html = $this->openTag("input") . 'type="text" ';
+ $html .= 'name="' . $this->name . '" value="' . $this->value . '" />';
+
+ return $html;
+ }
+
+ public function password()
+ {
+ $html = $this->openTag("input") . 'type="password" ';
+ $html .= 'name="' . $this->name . '" value="' . $this->value . '" />';
+
+ return $html;
+ }
+
+ public function textarea()
+ {
+ $html = $this->openTag("textarea");
+ $html .= 'name="' . $this->name . '">' . $this->value . '';
+
+ return $html;
+ }
+
+ public function hidden()
+ {
+ $html = $this->openTag("input") . 'type="hidden" ';
+ $html .= 'name="' . $this->name . '" value="' . $this->value . '" />';
+
+ return $html;
+ }
+
+ public function file()
+ {
+ return $this->openTag("input") . 'type="file" name="' . $this->name . '" />';
+ }
+
+ public function select($data)
+ {
+ $options = array();
+ $value = $this->value;
+
+ foreach ($data as $v => $text) {
+ if (!is_empty($value) && $v == $value) {
+ $tag = '';
+ } else {
+ $tag = ' ';
+ }
+
+ $options[] = $tag . htmlescape($text) . ' ';
+ }
+
+ $html = $this->openTag("select") . 'name="' . $this->name . '">';
+ return $html . implode(PHP_EOL, $options) . PHP_EOL . "";
+ }
+
+ public function radio($data)
+ {
+ static $rdonm = 0;
+
+ $count = 0;
+ $html = array();
+ $name = $this->name;
+ $value = $this->value;
+
+ // remove id.
+ $attrs = preg_replace('/(^id="[^"]*"| id="[^"]*")/', '', $this->attributes);
+
+ foreach ($data as $v => $text) {
+ $_id = "radio_" . $rdonm++;
+ $radio = $this->openTag("input", 'id="' . $_id . '" ' . $attrs) . 'type="radio" ';
+ $radio .= 'name="' . $name . '" value="' . htmlescape($v) . '"';
+
+ if ($count === 0 && is_empty($value) || !is_empty($value) && $v == $value) {
+ $radio .= ' checked="checked"';
+ }
+
+ $radio .= ' />' . htmlescape($text) . ' ';
+ $html[] = $radio;
+
+ $count++;
+ }
+
+ return implode(" " . PHP_EOL, $html);
+ }
+
+ public function checkbox($data)
+ {
+ static $chknm = 0;
+
+ $html = array();
+ $name = $this->name;
+ $value = $this->value;
+
+ if (!is_array($value)) {
+ $value = array();
+ }
+
+ // remove id.
+ $attrs = preg_replace('/(^id="[^"]*"| id="[^"]*")/', '', $this->attributes);
+
+ foreach ($data as $v => $text) {
+ $_id = "checkbox_" . $chknm++;
+ $check = $this->openTag("input", 'id="' . $_id . '" ' . $attrs) . 'type="checkbox" ';
+ $check .= 'name="' . $name . '[]" value="' . htmlescape($v) . '"';
+
+ if (!is_empty($value) && in_array($v, $value)) {
+ $check .= ' checked="checked"';
+ }
+
+ $check .= ' />' . htmlescape($text) . ' ';
+ $html[] = $check;
+ }
+
+ return implode(" " . PHP_EOL, $html);
+ }
+
+ public function datetime($yearRange, $withSecond, $includeBlank)
+ {
+ $datetime = new Form_Html_Date_Datetime($this->name, $this->value);
+ return $datetime->toHtml($yearRange, $withSecond, $includeBlank);
+ }
+
+ public function date($yearRange, $includeBlank)
+ {
+ $date = new Form_Html_Date_Object($this->name, $this->value);
+ return $date->toHtml($yearRange, $includeBlank);
+ }
+
+ protected function openTag($tagName, $attributes = null)
+ {
+ if ($attributes === null) {
+ $attributes = $this->attributes;
+ }
+
+ if (is_empty($attributes)) {
+ return "<{$tagName} ";
+ } else {
+ return "<{$tagName} {$attributes} ";
+ }
+ }
+}
diff --git a/generator/skeleton/en/lib/form/Model.php b/generator/skeleton/en/lib/form/Model.php
new file mode 100755
index 0000000..25e0eef
--- /dev/null
+++ b/generator/skeleton/en/lib/form/Model.php
@@ -0,0 +1,169 @@
+modelName = $model->getName();
+ } else {
+ if (is_empty($this->modelName)) {
+ $exp = explode("_", get_class($this));
+ $this->modelName = array_pop($exp);
+ }
+
+ $model = (is_empty($id)) ? MODEL($this->modelName) : MODEL($this->modelName, $id);
+ }
+
+ $this->setModel($model);
+ }
+
+ public function setModel($model)
+ {
+ if (is_string($model)) {
+ $this->model = MODEL($model);
+ } elseif ($model instanceof Sabel_Db_Model) {
+ $this->model = $model;
+ } else {
+ $message = __METHOD__ . "() argument must be a string or an instance of model.";
+ throw new Sabel_Exception_Runtime();
+ }
+
+ $this->values = $this->model->toArray();
+ }
+
+ public function getModel()
+ {
+ return $this->model;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function validate(Sabel_Validator $validator = null)
+ {
+ if ($validator === null) {
+ $validator = $this->buildValidator();
+ }
+
+ $validator->validate($this->values);
+ $errors = $validator->getErrors();
+
+ if ($uniques = $this->model->getMetadata()->getUniques()) {
+ $this->uniqueCheck($uniques, $errors);
+ }
+
+ $this->errors = $errors;
+
+ return empty($this->errors);
+ }
+
+ public function buildValidator()
+ {
+ $validator = $this->createValidator();
+
+ $this->setupModelValidator($validator);
+ $this->setupValidator($validator);
+
+ return $validator;
+ }
+
+ public function save()
+ {
+ $this->model->setValues($this->values);
+
+ return $this->model->save();
+ }
+
+ protected function uniqueCheck(array $uniques, array &$errors)
+ {
+ $model = $this->model;
+
+ $pkey = $model->getMetadata()->getPrimaryKey();
+ if (!is_array($pkey)) $pkey = array($pkey);
+
+ $inputValues = $this->values;
+ foreach ($uniques as $_uniques) {
+ $fetch = true;
+ $values = array();
+ $finder = new Sabel_Db_Finder($model->getName(), $pkey);
+
+ foreach ($_uniques as $unique) {
+ if (isset($inputValues[$unique])) {
+ $values[] = $inputValues[$unique];
+ $finder->eq($unique, $inputValues[$unique]);
+ } else {
+ $fetch = false;
+ break;
+ }
+ }
+
+ if (!$fetch) continue;
+
+ $_model = $finder->fetch();
+ if ($_model->isSelected()) {
+ $isValid = true;
+ if ($model->isSelected()) { // update
+ foreach ($pkey as $column) {
+ if ($_model->$column !== $model->$column) {
+ $isValid = false;
+ break;
+ }
+ }
+ } else { // insert
+ $isValid = false;
+ }
+
+ if (!$isValid) {
+ $names = array();
+ foreach ($_uniques as $unique) {
+ $names[] = $this->getDisplayName($unique);
+ }
+
+ $errors[] = implode(", ", $names) . ' "' . implode(", ", $values) . '" already exists.';
+ }
+ }
+ }
+ }
+
+ protected function setupModelValidator(Sabel_Validator $validator)
+ {
+ $metadata = $this->model->getMetadata();
+ $columns = $metadata->getColumns();
+
+ $validators = $this->validators;
+ foreach ($this->inputNames as $inputName) {
+ if (!isset($columns[$inputName])) continue;
+
+ $column = $columns[$inputName];
+ if ($column->increment) continue;
+
+ if (!$column->nullable) {
+ $validator->add($column->name, "required");
+ }
+
+ if ($column->isString()) {
+ $validator->add($column->name, "strwidth({$column->max})");
+ } elseif ($column->isNumeric()) {
+ $validator->add($column->name, "max({$column->max})");
+ $validator->add($column->name, "min({$column->min})");
+
+ if ($column->isInt()) {
+ $validator->add($column->name, "integer");
+ } else { // float, double
+ $validator->add($column->name, "numeric");
+ }
+ } elseif ($column->isBoolean()) {
+ $validator->add($column->name, "boolean");
+ } elseif ($column->isDate()) {
+ $validator->add($column->name, "date");
+ } elseif ($column->isDatetime()) {
+ $validator->add($column->name, "datetime");
+ }
+ }
+ }
+}
diff --git a/generator/skeleton/en/lib/form/Object.php b/generator/skeleton/en/lib/form/Object.php
new file mode 100755
index 0000000..b29f7aa
--- /dev/null
+++ b/generator/skeleton/en/lib/form/Object.php
@@ -0,0 +1,379 @@
+nameSpace = $nameSpace;
+
+ return $this;
+ }
+
+ public function getNameSpace()
+ {
+ return $this->nameSpace;
+ }
+
+ /**
+ * @param array $names
+ *
+ * @return self
+ */
+ public function setDisplayNames(array $displayNames)
+ {
+ $this->displayNames = $displayNames;
+
+ return $this;
+ }
+
+ /**
+ * @param string $inputName
+ *
+ * @return string
+ */
+ public function getDisplayName($inputName)
+ {
+ if (isset($this->displayNames[$inputName])) {
+ return $this->displayNames[$inputName];
+ } else {
+ return $inputName;
+ }
+ }
+
+ public function n($inputName)
+ {
+ return $this->getDisplayName($inputName);
+ }
+
+ /**
+ * @param array $inputNames
+ *
+ * return self
+ */
+ public function setInputNames(array $inputNames)
+ {
+ $this->inputNames = $inputNames;
+
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getInputNames()
+ {
+ return $this->inputNames;
+ }
+
+ /**
+ * @param array $errors
+ *
+ * @return void
+ */
+ public function setErrors($errors)
+ {
+ $this->errors = $errors;
+ }
+
+ /**
+ * @return array
+ */
+ public function getErrors()
+ {
+ return $this->errors;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function hasError()
+ {
+ return !empty($this->errors);
+ }
+
+ public function text($name, $attrs = "")
+ {
+ return $this->getHtmlWriter($name, $this->createInputName($name), $attrs)->text();
+ }
+
+ public function password($name, $attrs = "")
+ {
+ return $this->getHtmlWriter($name, $this->createInputName($name), $attrs)->password();
+ }
+
+ public function textarea($name, $attrs = "")
+ {
+ return $this->getHtmlWriter($name, $this->createInputName($name), $attrs)->textarea();
+ }
+
+ public function hidden($name, $attrs = "")
+ {
+ return $this->getHtmlWriter($name, $this->createInputName($name), $attrs)->hidden();
+ }
+
+ public function select($name, $values, $attrs = "")
+ {
+ if (is_string($values) && strpos($values, ":") !== false) {
+ list ($from, $to) = explode(":", $values);
+ if (is_number($from) && is_number($to)) {
+ $buf = array();
+ for ($i = $from; $i <= $to; $i++) {
+ $buf[$i] = $i;
+ }
+
+ $values = $buf;
+ } else {
+ trigger_error("argument is not a number: {$values}", E_USER_WARNING);
+ }
+ }
+
+ return $this->getHtmlWriter($name, $this->createInputName($name), $attrs)->select($values);
+ }
+
+ public function radio($name, $values, $attrs = "")
+ {
+ return $this->getHtmlWriter($name, $this->createInputName($name), $attrs)->radio($values);
+ }
+
+ public function checkbox($name, $values, $attrs = "")
+ {
+ return $this->getHtmlWriter($name, $this->createInputName($name), $attrs)->checkbox($values);
+ }
+
+ public function datetime($name, $yearRange = null, $withSecond = false, $includeBlank = false)
+ {
+ $writer = $this->getHtmlWriter($name, $this->createInputName("_datetime") . "[{$name}]");
+ return $writer->datetime($yearRange, $withSecond, $includeBlank);
+ }
+
+ public function date($name, $yearRange = null, $includeBlank = false)
+ {
+ $writer = $this->getHtmlWriter($name, $this->createInputName("_date") . "[{$name}]");
+ return $writer->date($yearRange, $includeBlank);
+ }
+
+ public function file($name, $attrs = "")
+ {
+ return $this->getHtmlWriter($name, $this->createInputName($name), $attrs)->file();
+ }
+
+ public function submit(array $values, array $inputNames = array())
+ {
+ if (empty($values)) {
+ return $this;
+ }
+
+ if (empty($inputNames)) {
+ $inputNames = $this->inputNames;
+ } else {
+ $this->inputNames = $inputNames;
+ }
+
+ foreach ($values as $inputName => $value) {
+ if ($inputName === "_datetime" || $inputName === "_date") {
+ list ($k, ) = each($value);
+ if (!in_array($k, $inputNames, true)) {
+ continue;
+ } elseif ($inputName === "_datetime") {
+ foreach ($value as $key => $date) {
+ if (!isset($date["s"])) {
+ $date["s"] = "00";
+ }
+
+ if ($this->isValidDateValue($date, true)) {
+ $this->set(
+ $key,
+ $date["y"] . "-" .
+ $date["m"] . "-" .
+ $date["d"] . " " .
+ $date["h"] . ":" .
+ $date["i"] . ":" .
+ $date["s"]
+ );
+ } else {
+ $this->set($key, null);
+ }
+ }
+ } elseif ($inputName === "_date") {
+ foreach ($value as $key => $date) {
+ if ($this->isValidDateValue($date)) {
+ $this->set($key, "{$date['y']}-{$date['m']}-{$date['d']}");
+ } else {
+ $this->set($key, null);
+ }
+ }
+ }
+ } elseif (!in_array($inputName, $inputNames, true)) {
+ continue;
+ } else {
+ $this->set($inputName, $value);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function validate(Sabel_Validator $validator = null)
+ {
+ if ($validator === null) {
+ $validator = $this->buildValidator();
+ }
+
+ $validator->validate($this->values);
+ $this->errors = $validator->getErrors();
+
+ return empty($this->errors);
+ }
+
+ public function toHidden($values = null, $var = null)
+ {
+ $html = array();
+
+ if ($values === null) {
+ $values = $this->values;
+ }
+
+ foreach ($values as $k => $v) {
+ if ($var !== null) {
+ $k = "{$var}[{$k}]";
+ }
+
+ if (is_array($v)) {
+ $html[] = $this->toHidden($v, $k);
+ } else {
+ $html[] = ' ';
+ }
+ }
+
+ return implode(PHP_EOL, $html);
+ }
+
+ protected function createValidator()
+ {
+ $validator = new Validator();
+ $validator->register($this);
+ $validator->setDisplayNames($this->displayNames);
+
+ return $validator;
+ }
+
+ protected function buildValidator()
+ {
+ $validator = $this->createValidator();
+ $this->setupValidator($validator);
+
+ return $validator;
+ }
+
+ protected function isValidDateValue($values, $isDatetime = false)
+ {
+ $keys = array("y", "m", "d");
+
+ if ($isDatetime) {
+ $keys = array_merge($keys, array("h", "i", "s"));
+ }
+
+ foreach ($keys as $key) {
+ if (!isset($values[$key]) || $values[$key] === "") {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ protected function getHtmlWriter($name, $inputName, $attrs = "")
+ {
+ static $htmlWriter = null;
+
+ if ($htmlWriter === null) {
+ $htmlWriter = new Form_Html();
+ } else {
+ $htmlWriter->clear();
+ }
+
+ $value = $this->get($name);
+
+ if (is_string($value)) {
+ $value = htmlescape($value, APP_ENCODING);
+ }
+
+ return $htmlWriter->setName($inputName)->setValue($value)->setAttributes($attrs);
+ }
+
+ protected function createInputName($inputName)
+ {
+ if (is_empty($this->nameSpace)) {
+ return $inputName;
+ } else {
+ return $this->nameSpace . "[{$inputName}]";
+ }
+ }
+
+ protected function setupValidator(Sabel_Validator $validator)
+ {
+ $keys = array();
+
+ $validators = $this->validators;
+ foreach ($this->inputNames as $inputName) {
+ $keys[$inputName] = true;
+ if (!isset($validators[$inputName])) continue;
+
+ $validator->add($inputName, $validators[$inputName]);
+
+ unset($validators[$inputName]);
+ }
+
+ if ($validators) {
+ foreach ($validators as $inputName => $v) {
+ if (strpos($inputName, ",") === false) continue;
+
+ $comp = true;
+ foreach (explode(",", $inputName) as $_inputName) {
+ if (!isset($keys[$_inputName])) {
+ $comp = false;
+ break;
+ }
+ }
+
+ if ($comp) {
+ $validator->add($inputName, $v);
+ }
+ }
+ }
+ }
+}
diff --git a/generator/skeleton/en/lib/form/html/date/Base.php b/generator/skeleton/en/lib/form/html/date/Base.php
new file mode 100755
index 0000000..aa82059
--- /dev/null
+++ b/generator/skeleton/en/lib/form/html/date/Base.php
@@ -0,0 +1,95 @@
+name = $name;
+ $this->value = $value;
+ }
+
+ protected function numSelect($type, $name, $start, $end, $includeBlank)
+ {
+ $html = array('');
+
+ if ($includeBlank) {
+ $html[] = ' ';
+ }
+
+ $val = $this->selectedValue($type);
+
+ if ($start > $end) {
+ for ($i = $start; $i >= $end; $i--) {
+ if ($i === $val) {
+ $html[] = '' . $i . ' ';
+ } else {
+ $html[] = '' . $i . ' ';
+ }
+ }
+ } else {
+ for ($i = $start; $i <= $end; $i++) {
+ if ($i === $val) {
+ $html[] = '' . $i . ' ';
+ } else {
+ $html[] = '' . $i . ' ';
+ }
+ }
+ }
+
+ return implode(PHP_EOL, $html) . PHP_EOL . " ";
+ }
+
+ protected function selectedValue($type)
+ {
+ if ($this->timestamp === null) {
+ return null;
+ }
+
+ switch ($type) {
+ case "y":
+ return (int)date("Y", $this->timestamp);
+
+ case "m":
+ return (int)date("n", $this->timestamp);
+
+ case "d":
+ return (int)date("j", $this->timestamp);
+
+ case "h":
+ return (int)date("G", $this->timestamp);
+
+ case "i":
+ return (int)date("i", $this->timestamp);
+
+ case "s":
+ return (int)date("s", $this->timestamp);
+ }
+ }
+
+ protected function getYearRange($yearRange)
+ {
+ if (!is_array($yearRange) && !is_string($yearRange) || is_empty($yearRange)) {
+ return array(1970, 2037);
+ } elseif (is_array($yearRange)) {
+ return $yearRange;
+ } elseif (strpos($yearRange, ":")) {
+ return explode(":", $yearRange);
+ } else {
+ $char = $yearRange{0};
+ $ny = (int)date("Y");
+
+ if ($char === "-") {
+ return array($ny + $yearRange, $ny);
+ } elseif ($char === "+") {
+ return array($ny, $ny + $yearRange);
+ } else {
+ $t = (int)floor($yearRange / 2);
+ return array($ny - $t, $ny + ($yearRange - $t));
+ }
+ }
+ }
+}
diff --git a/generator/skeleton/en/lib/form/html/date/Datetime.php b/generator/skeleton/en/lib/form/html/date/Datetime.php
new file mode 100755
index 0000000..0dd4764
--- /dev/null
+++ b/generator/skeleton/en/lib/form/html/date/Datetime.php
@@ -0,0 +1,29 @@
+value === null) {
+ $this->timestamp = ($includeBlank) ? null : time();
+ } else {
+ $this->timestamp = strtotime($this->value);
+ }
+
+ $name = $this->name;
+ list ($first, $last) = $this->getYearRange($yearRange);
+
+ $html = array();
+ $html[] = $this->numSelect("y", $name, $first, $last, $includeBlank);
+ $html[] = $this->numSelect("m", $name, 1, 12, $includeBlank);
+ $html[] = $this->numSelect("d", $name, 1, 31, $includeBlank);
+ $html[] = $this->numSelect("h", $name, 0, 23, $includeBlank);
+ $html[] = $this->numSelect("i", $name, 0, 59, $includeBlank);
+
+ if ($withSecond) {
+ $html[] = $this->numSelect("s", $name, 0, 59, $includeBlank);
+ }
+
+ return implode(" ", $html);
+ }
+}
diff --git a/generator/skeleton/en/lib/form/html/date/Object.php b/generator/skeleton/en/lib/form/html/date/Object.php
new file mode 100755
index 0000000..8efd619
--- /dev/null
+++ b/generator/skeleton/en/lib/form/html/date/Object.php
@@ -0,0 +1,23 @@
+value === null) {
+ $this->timestamp = ($includeBlank) ? null : time();
+ } else {
+ $this->timestamp = strtotime($this->value);
+ }
+
+ $name = $this->name;
+ list ($first, $last) = $this->getYearRange($yearRange);
+
+ $html = array();
+ $html[] = $this->numSelect("y", $name, $first, $last, $includeBlank);
+ $html[] = $this->numSelect("m", $name, 1, 12, $includeBlank);
+ $html[] = $this->numSelect("d", $name, 1, 31, $includeBlank);
+
+ return implode(" ", $html);
+ }
+}
diff --git a/generator/skeleton/en/lib/processor/Action.php b/generator/skeleton/en/lib/processor/Action.php
new file mode 100755
index 0000000..cb967d6
--- /dev/null
+++ b/generator/skeleton/en/lib/processor/Action.php
@@ -0,0 +1,42 @@
+
+ * @author Ebine Yutaka
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Processor_Action extends Sabel_Bus_Processor
+{
+ public function execute(Sabel_Bus $bus)
+ {
+ $response = $bus->get("response");
+ $controller = $bus->get("controller");
+
+ if ($response->isFailure() || $response->isRedirected()) return;
+
+ $action = $bus->get("destination")->getAction();
+ $controller->setAction($action);
+
+ try {
+ $controller->initialize();
+
+ if ($response->isSuccess() && !$response->isRedirected()) {
+ $controller->execute();
+ }
+
+ $controller->finalize();
+ } catch (Exception $e) {
+ $response->getStatus()->setCode(Sabel_Response::INTERNAL_SERVER_ERROR);
+ Sabel_Context::getContext()->setException($e);
+ }
+
+ if ($controller->getAttribute("layout") === false) {
+ $bus->set("NO_LAYOUT", true);
+ }
+ }
+}
diff --git a/generator/skeleton/en/lib/processor/Addon.php b/generator/skeleton/en/lib/processor/Addon.php
new file mode 100755
index 0000000..c674e05
--- /dev/null
+++ b/generator/skeleton/en/lib/processor/Addon.php
@@ -0,0 +1,25 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Processor_Addon extends Sabel_Bus_Processor
+{
+ public function execute(Sabel_Bus $bus)
+ {
+ $config = $bus->getConfig("addon");
+ $addons = $config->configure();
+
+ foreach ($addons as $addon) {
+ $className = ucfirst($addon) . "_Addon";
+ $instance = new $className();
+ $instance->execute($bus);
+ }
+ }
+}
diff --git a/generator/skeleton/en/lib/processor/Controller.php b/generator/skeleton/en/lib/processor/Controller.php
new file mode 100755
index 0000000..e33007c
--- /dev/null
+++ b/generator/skeleton/en/lib/processor/Controller.php
@@ -0,0 +1,66 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Processor_Controller extends Sabel_Bus_Processor
+{
+ protected $virtualControllerName = "SabelVirtualController";
+
+ public function execute(Sabel_Bus $bus)
+ {
+ $destination = $bus->get("destination");
+ if (($controller = $this->createController($destination)) === null) {
+ $controller = $this->createVirtualController();
+ }
+
+ if ($response = $bus->get("response")) {
+ $controller->setResponse($response);
+ if ($controller instanceof $this->virtualControllerName) {
+ $response->getStatus()->setCode(Sabel_Response::NOT_FOUND);
+ }
+ }
+
+ if ($request = $bus->get("request")) {
+ $controller->setRequest($request);
+ }
+
+ if ($session = $bus->get("session")) {
+ $controller->setSession($session);
+ }
+
+ $bus->set("controller", $controller);
+ }
+
+ protected function createController($destination)
+ {
+ list ($module, $controller,) = $destination->toArray();
+ $class = ucfirst($module) . "_Controllers_" . ucfirst($controller);
+
+ if (Sabel::using($class)) {
+ l("create controller '{$class}'");
+ return new $class();
+ } else {
+ l("controller '{$class}' not found", SBL_LOG_WARN);
+ return null;
+ }
+ }
+
+ protected function createVirtualController()
+ {
+ $className = $this->virtualControllerName;
+ if (!class_exists($className, false)) {
+ eval ("class $className extends Sabel_Controller_Page {}");
+ }
+
+ l("create virtual controller '{$className}'");
+
+ return new $className();
+ }
+}
diff --git a/generator/skeleton/en/lib/processor/Helper.php b/generator/skeleton/en/lib/processor/Helper.php
new file mode 100755
index 0000000..e984316
--- /dev/null
+++ b/generator/skeleton/en/lib/processor/Helper.php
@@ -0,0 +1,34 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Processor_Helper extends Sabel_Bus_Processor
+{
+ public function execute(Sabel_Bus $bus)
+ {
+ $destination = $bus->get("destination");
+ $moduleName = $destination->getModule();
+ $controllerName = $destination->getController();
+
+ $sharedHelper = "application";
+ $commonHelpers = MODULES_DIR_PATH . DS . HELPERS_DIR_NAME;
+ $moduleHelpers = MODULES_DIR_PATH . DS . $moduleName . DS . HELPERS_DIR_NAME;
+
+ $helpers = array();
+
+ $helpers[] = $commonHelpers . DS . $sharedHelper;
+ $helpers[] = $moduleHelpers . DS . $sharedHelper;
+ $helpers[] = $moduleHelpers . DS . $controllerName;
+
+ foreach ($helpers as $helper) {
+ Sabel::fileUsing($helper . ".php", true);
+ }
+ }
+}
diff --git a/generator/skeleton/en/lib/processor/Initializer.php b/generator/skeleton/en/lib/processor/Initializer.php
new file mode 100755
index 0000000..5922c77
--- /dev/null
+++ b/generator/skeleton/en/lib/processor/Initializer.php
@@ -0,0 +1,28 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Processor_Initializer extends Sabel_Bus_Processor
+{
+ public function execute(Sabel_Bus $bus)
+ {
+ Sabel_Db_Config::initialize($bus->getConfig("database"));
+
+ if (!is_cli() && ($session = $bus->get("session")) !== null) {
+ $session->start();
+ l("START SESSION: " . $session->getName() . "=" . $session->getId());
+ }
+
+ // default page title.
+ if ($response = $bus->get("response")) {
+ $response->setResponse("pageTitle", "Sabel");
+ }
+ }
+}
diff --git a/generator/skeleton/en/lib/processor/Request.php b/generator/skeleton/en/lib/processor/Request.php
new file mode 100755
index 0000000..1967399
--- /dev/null
+++ b/generator/skeleton/en/lib/processor/Request.php
@@ -0,0 +1,88 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Processor_Request extends Sabel_Bus_Processor
+{
+ public function execute(Sabel_Bus $bus)
+ {
+ if ($bus->has("request")) {
+ $request = $bus->get("request");
+ } else {
+ $uri = $this->getRequestUri($bus);
+ $request = new Sabel_Request_Object($uri);
+
+ if (SBL_SECURE_MODE) {
+ $_GET = remove_nullbyte($_GET);
+ $_POST = remove_nullbyte($_POST);
+ }
+
+ $request->setGetValues($_GET);
+ $request->setPostValues($_POST);
+
+ $this->setFiles($request);
+
+ if (isset($_SERVER["REQUEST_METHOD"])) {
+ $request->method($_SERVER["REQUEST_METHOD"]);
+ }
+
+ $httpHeaders = array();
+ foreach ($_SERVER as $key => $val) {
+ if (strpos($key, "HTTP") === 0) {
+ $httpHeaders[$key] = $val;
+ }
+ }
+
+ $request->setHttpHeaders($httpHeaders);
+ $bus->set("request", $request);
+ }
+
+ l("REQUEST URI: /" . $request->getUri(true));
+
+ // Ajax request.
+ if ($request->getHttpHeader("X-Requested-With") === "XMLHttpRequest") {
+ $bus->set("NO_LAYOUT", true);
+ $bus->set("IS_AJAX_REQUEST", true);
+ }
+ }
+
+ protected function getRequestUri($bus)
+ {
+ $uri = (isset($_SERVER["REQUEST_URI"])) ? $_SERVER["REQUEST_URI"] : "/";
+
+ if (!is_cli() && isset($_SERVER["SCRIPT_NAME"]) && $_SERVER["SCRIPT_NAME"] !== "/index.php") {
+ $bus->set("NO_VIRTUAL_HOST", true);
+
+ $pubdir = substr(RUN_BASE . DS . "public", strlen($_SERVER["DOCUMENT_ROOT"]));
+ define("URI_PREFIX", $pubdir);
+
+ $uri = substr(str_replace("/index.php", "", $uri), strlen($pubdir));
+ }
+
+ return normalize_uri($uri);
+ }
+
+ protected function setFiles(Sabel_Request $request)
+ {
+ if (!empty($_FILES)) {
+ foreach ($_FILES as $name => $_FILE) {
+ if (isset($_FILE["tmp_name"]) && isset($_FILE["size"]) && $_FILE["size"] > 0) {
+ $file = new Sabel_Request_File();
+ $file->name = (isset($_FILE["name"])) ? $_FILE["name"] : "";
+ $file->type = (isset($_FILE["type"])) ? $_FILE["type"] : "";
+ $file->path = $_FILE["tmp_name"];
+ $file->size = $_FILE["size"];
+
+ $request->setFile($name, $file);
+ }
+ }
+ }
+ }
+}
diff --git a/generator/skeleton/en/lib/processor/Response.php b/generator/skeleton/en/lib/processor/Response.php
new file mode 100755
index 0000000..d787d9d
--- /dev/null
+++ b/generator/skeleton/en/lib/processor/Response.php
@@ -0,0 +1,81 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Processor_Response extends Sabel_Bus_Processor
+{
+ protected $afterEvents = array("action" => "afterAction");
+
+ public function execute(Sabel_Bus $bus)
+ {
+ $bus->set("response", new Sabel_Response_Object());
+ }
+
+ public function afterAction($bus)
+ {
+ $response = $bus->get("response");
+ $response->setResponses(array_merge(
+ $response->getResponses(),
+ $bus->get("controller")->getAttributes()
+ ));
+
+ if ($response->getStatus()->isServerError()) {
+ $exception = Sabel_Context::getContext()->getException();
+ if (!is_object($exception)) return;
+
+ $eol = ((ENVIRONMENT & DEVELOPMENT) > 0) ? " " : PHP_EOL;
+ $msg = get_class($exception) . ": "
+ . $exception->getMessage() . $eol
+ . "At: " . date("r") . $eol . $eol
+ . Sabel_Exception_Printer::printTrace($exception, $eol, true);
+
+ if ((ENVIRONMENT & PRODUCTION) > 0) {
+
+ } else {
+ $response->setResponse("exception_message", $msg);
+ }
+
+ l(PHP_EOL . str_replace(" ", PHP_EOL, $msg), SBL_LOG_ERR);
+ }
+ }
+
+ public function shutdown(Sabel_Bus $bus)
+ {
+ $response = $bus->get("response");
+ $redirector = $response->getRedirector();
+
+ if ($redirector->isRedirected()) {
+ if (($url = $redirector->getUrl()) !== "") {
+ $response->setLocation($url);
+ } else {
+ $location = $redirector->getUri(true, false);
+
+ if (($session = $bus->get("session")) !== null) {
+ if ($session->isStarted() && !$session->isCookieEnabled()) {
+ $glue = ($redirector->hasParameters()) ? "&" : "?";
+ $location .= $glue . $session->getName() . "=" . $session->getId();
+ }
+ }
+
+ if (($flagment = $redirector->getFlagment()) !== "") {
+ $location .= "#{$flagment}";
+ }
+
+ if (function_exists("get_uri_prefix")) {
+ $location = get_uri_prefix() . $location;
+ }
+
+ $response->setLocation($location);
+ }
+ }
+
+ $response->outputHeader();
+ }
+}
diff --git a/generator/skeleton/en/lib/processor/Router.php b/generator/skeleton/en/lib/processor/Router.php
new file mode 100755
index 0000000..172e1fd
--- /dev/null
+++ b/generator/skeleton/en/lib/processor/Router.php
@@ -0,0 +1,31 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Processor_Router extends Sabel_Bus_Processor
+{
+ public function execute(Sabel_Bus $bus)
+ {
+ $request = $bus->get("request");
+ $config = $bus->getConfig("map");
+ $config->configure();
+
+ if ($candidate = $config->getValidCandidate($request->getUri())) {
+ $request->setParameterValues(array_map("urldecode", $candidate->getUriParameters()));
+ $destination = $candidate->getDestination();
+ l("DESTINATION: " . $destination);
+ $bus->set("destination", $destination);
+ Sabel_Context::getContext()->setCandidate($candidate);
+ } else {
+ $message = __METHOD__ . "() didn't match to any routing configuration.";
+ throw new Sabel_Exception_Runtime($message);
+ }
+ }
+}
diff --git a/generator/skeleton/en/lib/processor/Session.php b/generator/skeleton/en/lib/processor/Session.php
new file mode 100755
index 0000000..dc7a4fe
--- /dev/null
+++ b/generator/skeleton/en/lib/processor/Session.php
@@ -0,0 +1,31 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Processor_Session extends Sabel_Bus_Processor
+{
+ public function execute(Sabel_Bus $bus)
+ {
+ if (!$bus->has("session")) {
+ $bus->set("session", Sabel_Session_PHP::create());
+ }
+ }
+
+ public function shutdown(Sabel_Bus $bus)
+ {
+ $session = $bus->get("session");
+
+ if ($session->isStarted()) {
+ if (!$session->isCookieEnabled() && ini_get("session.use_trans_sid") === "0") {
+ output_add_rewrite_var($session->getName(), $session->getId());
+ }
+ }
+ }
+}
diff --git a/generator/skeleton/en/lib/processor/View.php b/generator/skeleton/en/lib/processor/View.php
new file mode 100755
index 0000000..056c36d
--- /dev/null
+++ b/generator/skeleton/en/lib/processor/View.php
@@ -0,0 +1,96 @@
+
+ * @copyright 2004-2008 Mori Reo
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+class Processor_View extends Sabel_Bus_Processor
+{
+ protected $afterEvents = array("controller" => "initViewObject");
+
+ public function initViewObject($bus)
+ {
+ list ($m, $c, $a) = $bus->get("destination")->toArray();
+
+ $view = new Sabel_View_Object("controller", new Sabel_View_Location_File(
+ $m . DS . VIEW_DIR_NAME . DS . $c . DS
+ ));
+
+ $view->addLocation("module", new Sabel_View_Location_File($m . DS . VIEW_DIR_NAME . DS));
+ $view->addLocation("app", new Sabel_View_Location_File(VIEW_DIR_NAME . DS));
+
+ if ($renderer = $bus->get("renderer")) {
+ $view->setRenderer($renderer);
+ } else {
+ $view->setRenderer(new Sabel_View_Renderer());
+ }
+
+ $bus->set("view", $view);
+ $bus->get("controller")->setAttribute("view", $view);
+ }
+
+ public function execute(Sabel_Bus $bus)
+ {
+ $response = $bus->get("response");
+ if ($response->isRedirected()) return;
+
+ $controller = $bus->get("controller");
+ $responses = $response->getResponses();
+ $contents = (isset($responses["contents"])) ? $responses["contents"] : "";
+
+ $view = $this->setTemplateName(
+ $bus->get("view"),
+ $response->getStatus(),
+ $bus->get("destination")->getAction(),
+ $bus->get("IS_AJAX_REQUEST") === true
+ );
+
+ if (is_empty($contents)) {
+ if ($location = $view->getValidLocation()) {
+ $contents = $view->rendering($location, $responses);
+ } elseif (!$controller->isExecuted()) {
+ $response->getStatus()->setCode(Sabel_Response::NOT_FOUND);
+ if ($location = $view->getValidLocation("notFound")) {
+ $contents = $view->rendering($location, $responses);
+ } else {
+ $contents = "404 Not Found ";
+ }
+ }
+ }
+
+ if ($bus->get("NO_LAYOUT")) {
+ $bus->set("result", $contents);
+ } else {
+ $layout = (isset($responses["layout"])) ? $responses["layout"] : DEFAULT_LAYOUT_NAME;
+ if ($location = $view->getValidLocation($layout)) {
+ $responses["contentForLayout"] = $contents;
+ $bus->set("result", $view->rendering($location, $responses));
+ } else { // no layout.
+ $bus->set("result", $contents);
+ }
+ }
+ }
+
+ protected function setTemplateName(Sabel_View $view, $status, $action, $isAjax = false)
+ {
+ if ($status->isFailure()) {
+ $tplName = lcfirst(str_replace(" ", "", $status->getReason()));
+ if ($location = $view->getValidLocation($tplName)) {
+ $view->setName($tplName);
+ } elseif ($status->isClientError()) {
+ $view->setName("clientError");
+ } else {
+ $view->setName("serverError");
+ }
+ } elseif ($view->getName() === "") {
+ $view->setName(($isAjax) ? "{$action}.ajax" : $action);
+ }
+
+ return $view;
+ }
+}
diff --git a/generator/skeleton/en/lib/schema/example.php b/generator/skeleton/en/lib/schema/example.php
new file mode 100755
index 0000000..18c41d8
--- /dev/null
+++ b/generator/skeleton/en/lib/schema/example.php
@@ -0,0 +1,37 @@
+ Sabel_Db_Type::INT,
+ 'min' => 0,
+ 'max' => PHP_INT_MAX,
+ 'increment' => false,
+ 'nullable' => false,
+ 'primary' => true,
+ 'default' => null);
+
+ $cols['column2'] = array('type' => Sabel_Db_Type::STRING,
+ 'max' => 255,
+ 'increment' => false,
+ 'nullable' => false,
+ 'primary' => false,
+ 'default' => null);
+
+ return $cols;
+ }
+
+ public function getProperty()
+ {
+ $property = array();
+
+ $property["tableEngine"] = null;
+ $property["uniques"] = null;
+ $property["fkeys"] = null;
+
+ return $property;
+ }
+}
diff --git a/generator/skeleton/en/logs/sabel.log b/generator/skeleton/en/logs/sabel.log
new file mode 100755
index 0000000..e69de29
diff --git a/generator/skeleton/en/migration/system/N_SblKvs_create.php b/generator/skeleton/en/migration/system/N_SblKvs_create.php
new file mode 100755
index 0000000..8fedc1f
--- /dev/null
+++ b/generator/skeleton/en/migration/system/N_SblKvs_create.php
@@ -0,0 +1,5 @@
+column("key")->type(_STRING)->length(64)->primary(true);
+$create->column("value")->type(_TEXT);
+$create->column("timeout")->type(_INT)->value(0);
diff --git a/generator/skeleton/en/migration/system/N_SblPreference_create.php b/generator/skeleton/en/migration/system/N_SblPreference_create.php
new file mode 100755
index 0000000..6708d92
--- /dev/null
+++ b/generator/skeleton/en/migration/system/N_SblPreference_create.php
@@ -0,0 +1,21 @@
+column("namespace")
+ ->type(_STRING)
+ ->length(64);
+
+$create->column("key")
+ ->type(_STRING)
+ ->length(64);
+
+$create->column("value")
+ ->type(_STRING)
+ ->length(1024);
+
+$create->column("type")
+ ->type(_STRING)
+ ->length(32);
+
+$create->primary(array("namespace", "key"));
+
+$create->options("engine", "InnoDB");
\ No newline at end of file
diff --git a/generator/skeleton/en/migration/system/N_SblSession_create.php b/generator/skeleton/en/migration/system/N_SblSession_create.php
new file mode 100755
index 0000000..2c165eb
--- /dev/null
+++ b/generator/skeleton/en/migration/system/N_SblSession_create.php
@@ -0,0 +1,11 @@
+column("sid")->type(_STRING)
+ ->length(32) # md5
+ //->length(40) # sha1
+ ->primary(true);
+
+$create->column("data")->type(_TEXT);
+
+$create->column("timeout")->type(_INT)
+ ->value(0);
diff --git a/generator/skeleton/en/migration/system/N_SblTemplate_create.php b/generator/skeleton/en/migration/system/N_SblTemplate_create.php
new file mode 100755
index 0000000..c6a375a
--- /dev/null
+++ b/generator/skeleton/en/migration/system/N_SblTemplate_create.php
@@ -0,0 +1,11 @@
+column("name")->type(_STRING)
+ ->nullable(false);
+
+$create->column("namespace")->type(_STRING)
+ ->nullable(false);
+
+$create->column("contents")->type(_TEXT);
+
+$create->primary(array("name", "namespace"));
diff --git a/generator/skeleton/en/public/.htaccess b/generator/skeleton/en/public/.htaccess
new file mode 100755
index 0000000..cb2535a
--- /dev/null
+++ b/generator/skeleton/en/public/.htaccess
@@ -0,0 +1,9 @@
+
+ RewriteEngine On
+
+ RewriteCond %{REQUEST_URI} \/\..+$
+ RewriteRule .* /notfound [R,L]
+
+ RewriteCond %{REQUEST_FILENAME} !-f
+ RewriteRule !\.(js|ico|swf|css|html)$ index.php [QSA,L]
+
diff --git a/generator/skeleton/en/public/css/admin.css b/generator/skeleton/en/public/css/admin.css
new file mode 100755
index 0000000..706bff4
--- /dev/null
+++ b/generator/skeleton/en/public/css/admin.css
@@ -0,0 +1,285 @@
+body {
+ padding: 0;
+ margin: 0;
+ font: .74em Arial, sans-serif;
+ line-height: 1.5em;
+ background: #fff url("images/top.jpg") repeat-x top;
+ color: #454545;
+}
+
+a {
+ color: #DA3B3B;
+ background: inherit;
+}
+
+a:hover {
+ color: #DA3B3B;
+ background: inherit;
+ text-decoration: underline;
+}
+
+p {
+ margin: 5px 0;
+}
+
+h1 {
+ font: bold 1.8em Arial, Sans-Serif;
+ padding: 8px 0 4px 0;
+ margin: 0;
+ letter-spacing: -1px;
+}
+
+h2 {
+ font: bold 1.6em Arial, Sans-Serif;
+ letter-spacing: -1px;
+}
+
+h3 {
+ padding: 4px 0;
+ margin: 0;
+}
+
+ul {
+ margin: 0;
+ padding : 0;
+ list-style: none;
+}
+
+img {
+ border: 0;
+}
+
+hr {
+ height: 1px;
+ border-style: none;
+ color: #d0d0d0;
+ background-color: #C0C0C0;
+ margin: 10px 0;
+}
+
+.content {
+ margin: 0px auto;
+ width: 800px;
+}
+
+/* Top part */
+#top {
+ padding-top: 20px;
+ background: transparent;
+ height: 101px;
+}
+
+#top h1 {
+ font: bold 1.8em Arial, Sans-Serif;
+ padding: 8px 0 4px 0;
+ margin: 0;
+ letter-spacing: 1px;
+ color: #FFF;
+}
+
+#top h2 {
+ font: bold 1.2em Arial, Sans-Serif;
+ letter-spacing: 0px;
+ color: #FFF;
+ margin: 0;
+ padding: 0;
+}
+
+#top #icons {
+ float: right;
+ margin: 20px 0;
+ padding: 0;
+}
+
+#top #icons img {
+ padding-right: 2px;
+ border: 0;
+}
+
+/* Second part */
+#prec {
+ height: 30px;
+ margin: 0;
+ padding: 0;
+ background: #F6F6F6;
+ border-bottom: 1px solid #DCDCDC;
+ overflow: hidden;
+ margin-bottom: 15px;
+}
+
+#wrap {
+ margin: 0 auto;
+ width: 800px;
+}
+
+#wrap #pic {
+ float: right;
+ background: #FF0000 url("images/mainimg.jpg") no-repeat;
+ height: 199px;
+ width: 589px;
+ overflow: hidden;
+}
+
+#wrap #menu {
+ padding: 0;
+ margin: 0;
+ background: inherit;
+}
+
+#wrap #menu a {
+ padding-left: 25px;
+ font: 0.9em Arial, Sans-Serif;
+ text-decoration: none;
+ background: #F6F6F6 url(images/lm-li.gif) no-repeat left;
+}
+
+#wrap #menu ul {
+ padding: 10px 0;
+}
+
+#wrap #menu li {
+ line-height: 26px;
+ background: #F6F6F6 url("images/li-line.gif") no-repeat bottom left;
+ list-style: none;
+}
+
+/* Advertising */
+#ad {
+ padding-left: 95px;
+ min-height: 10px;
+ margin-bottom: 15px;
+}
+
+#ad a, #left_side a {
+ background: #FFF;
+ text-decoration: none;
+}
+
+#ad a:hover, #left_side a:hover {
+ text-decoration: underline;
+}
+
+/* main area */
+#main {
+
+}
+
+#contents {
+ background: #FFF;
+ margin-bottom: 10px;
+ width: 800px;
+}
+
+#contents h3 {
+ background: #FFF url(images/hbg.gif) repeat-x;
+ height: 30px;
+ padding: 5px 0 0 0px;
+ margin: 0;
+ font: bold 1.2em Arial, Sans-Serif;
+}
+
+#contents h3 span {
+ background: url(images/h-art.gif) no-repeat left;
+ padding: 1px 10px 0 23px;
+ line-height: 22px;
+ margin: 0;
+}
+
+#contents p {
+ color: #454545;
+ padding: 0 5px 0 5px;
+ text-align: justify;
+}
+
+#contents img {
+ float: left;
+ padding: 0 10px 5px 0;
+}
+
+#contents blockquote {
+ padding-left: 10px;
+ border-left: 2px solid #DA4040;
+ margin: 10px 0 10px 25px;
+}
+
+#contents .date {
+ border-top: 1px dotted #ccc;
+ padding: 5px 0;
+ margin: 10px 0 25px 0;
+ text-align: right;
+}
+
+#contents .rs {
+ float: right;
+ margin: 0 10px;
+ border: 1px solid #ddd;
+ padding: 5px;
+ background: #f5f5f5;
+}
+
+#contents ul {
+ list-style-position: inside;
+ margin-left: 2px;
+}
+
+#contents ul li {
+ list-style-type: square;
+ margin-left: 15px;
+}
+
+#contents ul ul li {
+ list-style: none;
+ margin-left: 10px;
+ list-style-type: lower-alpha;
+ list-style-position: inside;
+}
+
+/* The footer */
+#footer {
+ clear: both;
+ border-top: 1px solid #DCDCDC;
+ margin: 0 0 3em 0;
+ color: #777;
+ background: #fff;
+}
+
+#footer .right {
+ float: right;
+ margin-top: 10px;
+ text-align: right;
+ background: #FFF;
+}
+
+#footer a {
+ text-decoration: none;
+ background: #FFF;
+}
+
+#config_area {
+ display: none;
+ position: absolute;
+ top: 100px;
+ width: 600px;
+}
+
+#black_layer {
+ display: none;
+ width: 100%;
+ height: 100%;
+ filter: alpha(opacity=65);
+ background-color: #000;
+ top: 0px;
+ left: 0px;
+ position: absolute;
+ -moz-opacity: 0.65;
+ opacity: 0.65;
+}
+
+#edit_textarea {
+ height: 400px;
+ width: 600px;
+ font-family: 'Courier', 'MS Courier New',
+ 'Prestige', 'Everson Mono',
+ 'MS ゴシック', 'MS 明朝',
+ 'Osaka-等幅', 'Monaco';
+}
diff --git a/generator/skeleton/en/public/css/default.css b/generator/skeleton/en/public/css/default.css
new file mode 100755
index 0000000..0fab813
--- /dev/null
+++ b/generator/skeleton/en/public/css/default.css
@@ -0,0 +1,279 @@
+/** --------------------
+ * 余白
+ */
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+ul,
+ol,
+li,
+dl,
+dt,
+dd,
+form,
+fieldset,
+p,
+div,
+pre,
+code,
+body,
+blockquote,
+samp,
+dir,
+legend,
+marquee,
+menu,
+td,
+th {
+ margin: 0px 0px 0px 0px;
+ padding: 0px 0px 0px 0px;
+}
+
+/** --------------------
+ * 外枠
+ */
+fieldset,
+frame,
+frameset,
+img {
+ border-width: 0px 0px 0px 0px;
+ border-style: none none none none;
+}
+
+/** --------------------
+ * IE6,7用 文字の大きさを補正
+ */
+code,
+kbd,
+pre,
+samp,
+tt {
+ font-size: 100%;
+}
+
+/** --------------------
+ * 見出し
+ */
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-weight: normal;
+}
+
+/** --------------------
+ * 文書作成者やサイト運営者の連絡先、文書に関する情報
+ */
+address {
+ font-style: normal;
+}
+
+/** --------------------
+ * テーブル
+ */
+table {
+ border-collapse: collapse;
+ table-layout: auto;
+}
+
+table,
+th,
+td {
+ border: 1px solid;
+}
+
+caption {
+ text-align: center;
+ caption-side: top;
+}
+
+th {
+ vertical-align: top;
+ font-weight: normal;
+ text-align: left;
+ empty-cells: show;
+}
+
+td {
+ vertical-align: top;
+ text-align: left;
+ empty-cells: show;
+}
+
+/** --------------------
+ * 整形済みテキスト
+ */
+pre {
+ white-space: pre;
+}
+
+/**
+ * IE6,7用 letter-spacingを指定した要素内で連続したbr要素の偶数個目が無視されるバグの回避
+ */
+br {
+ letter-spacing: normal;
+}
+
+/**
+ * 水平線
+ */
+hr {
+ height: 1px;
+ border-style: none;
+ color: #d0d0d0;
+ background-color: #C0C0C0;
+ margin: 10px 0;
+}
+
+/** --------------------
+ * body
+ */
+body {
+ line-height: 1.1;
+ color: #444;
+ background: #fff url("images/headerBg.gif") repeat-x 0 0;
+ font-family: メイリオ,Meiryo,"ヒラギノ角ゴ Pro W3","Hiragino Kaku Gothic Pro",Osaka−等幅,Osaka-Mono,"MS Pゴシック","MS PGothic",Sans-Serif;
+ font-size: small;
+}
+
+/** --------------------
+ * header
+ */
+div#header {
+ margin-bottom: 5px;
+ padding-bottom: 14px;
+}
+
+div#header h1 {
+ height: 80px;
+ background: url("images/headerLogo.gif") no-repeat 0 0;
+ text-indent: -9999px;
+}
+
+/** --------------------
+ * contents
+ */
+div#contents {
+ margin: 0 auto;
+ width: 770px;
+ line-height: 1.6;
+ min-height: 350px;
+}
+
+.ie_6 div#contents {
+ height: 350px;
+}
+
+#contents table.sbl_basicTable {
+ width: 90%;
+}
+
+#contents table.sbl_basicTable th {
+ background-color: #e8efff;
+ border: 1px solid #ccc;
+}
+
+#contents table.sbl_basicTable td {
+ border: 1px solid #ccc;
+ padding: 0 5px;
+}
+
+/** --------------------
+ * error
+ */
+div.sbl_error {
+ margin: 10px 0;
+ padding: 10px;
+ border: 2px solid #cc6655;
+ background-color: #feeae6;
+}
+
+div.sbl_error ul {
+ list-style-type: none;
+}
+
+div.sbl_error ul li {
+ padding-left: 13px;
+ background: url("images/icon_1.gif") no-repeat 0 50%;
+}
+
+
+/** --------------------
+ * pager
+ */
+div.sbl_pager {
+ margin: 10px 0;
+}
+
+div.sbl_pager::after {
+ content: ".";
+ display: block;
+ visibility: hidden;
+ height: 0px;
+ font-size: 0;
+ line-height: 0;
+ clear: both;
+}
+
+.ie_6 div.sbl_pager,
+.ie_7 div.sbl_pager {
+ zoom: 1;
+}
+
+div.sbl_pager span,
+div.sbl_pager a {
+ margin-right: 5px;
+ padding: 1px 7px;
+ border: 1px solid #999;
+ display: block;
+ float: left;
+ color: #999;
+ text-decoration: none;
+}
+
+div.sbl_pager span {
+ background-color: #eee;
+ font-weight: bold;
+}
+
+div.sbl_pager a {
+ text-decoration: none;
+}
+
+div.sbl_pager a.prev,
+div.sbl_pager a.next {
+ background: #fbfbfb url("images/pagerBg.gif") repeat-x 0 100%;
+}
+
+div.sbl_pager a.prev {
+ margin-right: 10px;
+}
+
+div.sbl_pager a.next {
+ margin-right: 0px;
+ margin-left: 5px;
+}
+
+div.sbl_pager a:hover {
+ color: #fff;
+ background: #999;
+}
+
+/** --------------------
+ * footer
+ */
+div#footer {
+ margin-top: 5px;
+ padding: 12px 20px;
+ background: #fff url("images/footerBg.gif") repeat-x 0 0;
+ text-align: center;
+}
+
+div#footer p {
+ margin-top: 5px;
+}
diff --git a/generator/skeleton/en/public/css/images/footerBg.gif b/generator/skeleton/en/public/css/images/footerBg.gif
new file mode 100755
index 0000000..6b780b5
Binary files /dev/null and b/generator/skeleton/en/public/css/images/footerBg.gif differ
diff --git a/generator/skeleton/en/public/css/images/headerBg.gif b/generator/skeleton/en/public/css/images/headerBg.gif
new file mode 100755
index 0000000..e36d4ea
Binary files /dev/null and b/generator/skeleton/en/public/css/images/headerBg.gif differ
diff --git a/generator/skeleton/en/public/css/images/headerLogo.gif b/generator/skeleton/en/public/css/images/headerLogo.gif
new file mode 100755
index 0000000..520ebb1
Binary files /dev/null and b/generator/skeleton/en/public/css/images/headerLogo.gif differ
diff --git a/generator/skeleton/en/public/css/images/icon_1.gif b/generator/skeleton/en/public/css/images/icon_1.gif
new file mode 100755
index 0000000..bf86d2c
Binary files /dev/null and b/generator/skeleton/en/public/css/images/icon_1.gif differ
diff --git a/generator/skeleton/en/public/css/images/pagerBg.gif b/generator/skeleton/en/public/css/images/pagerBg.gif
new file mode 100755
index 0000000..9603589
Binary files /dev/null and b/generator/skeleton/en/public/css/images/pagerBg.gif differ
diff --git a/generator/skeleton/en/public/css/images/top.jpg b/generator/skeleton/en/public/css/images/top.jpg
new file mode 100755
index 0000000..276100d
Binary files /dev/null and b/generator/skeleton/en/public/css/images/top.jpg differ
diff --git a/generator/skeleton/en/public/css/sabeljs.css b/generator/skeleton/en/public/css/sabeljs.css
new file mode 100755
index 0000000..3fea86f
--- /dev/null
+++ b/generator/skeleton/en/public/css/sabeljs.css
@@ -0,0 +1,208 @@
+.sbl_calendarFrame {
+ padding: 16px 1px 1px 1px;
+ border: 1px solid #444;
+ background: #999 url("../images/js/calendarFrameBg.gif") repeat-x 0 0;
+ position: absolute;
+ width: 218px;
+ line-height: 1.5;
+}
+
+.sbl_calendarFrame a.sbl_cal_close {
+ position: absolute;
+ top: 2px;
+ right: 2px;
+ width: 12px;
+ height: 12px;
+ background: url("../images/js/calendarClose.gif") no-repeat 0 0;
+ text-indent: -9999px;
+ cursor: pointer;
+}
+
+.sbl_calendar {
+ border: solid #444;
+ border-width: 1px 0 0 1px;
+ background-color: #fff;
+}
+
+.sbl_calendar .sbl_cal_header {
+ padding: 2px;
+ border: solid #444;
+ border-width: 0 1px 1px 0;
+ position: relative;
+ color: #835e4c;
+ font-weight: bold;
+ text-align: center;
+}
+
+* html .sbl_calendar .sbl_cal_header,
+* html .sbl_calendar .sbl_cal_weekdays,
+* html .sbl_calendar .sbl_cal_days {
+ zoom: 1;
+}
+
+*+html .sbl_calendar .sbl_cal_header,
+*+html .sbl_calendar .sbl_cal_weekdays,
+*+html .sbl_calendar .sbl_cal_days {
+ zoom: 1;
+}
+
+.sbl_calendar .sbl_cal_header::after,
+.sbl_calendar .sbl_cal_weekdays::after,
+.sbl_calendar .sbl_cal_days::after {
+ content: ".";
+ display: block;
+ visibility: hidden;
+ height: 0px;
+ font-size: 0;
+ line-height: 0;
+ clear: both;
+}
+
+.sbl_calendar .sbl_cal_header a.sbl_page_l {
+ position: absolute;
+ width: 30px;
+ height: 15px;
+ top: 4px;
+ left: 4px;
+ background: url("../images/js/calendarLeftArrow.gif") no-repeat 0 0;
+ cursor: pointer;
+}
+
+.sbl_calendar .sbl_cal_header a.sbl_page_r {
+ position: absolute;
+ width: 30px;
+ height: 15px;
+ top: 4px;
+ right: 4px;
+ background: url("../images/js/calendarRightArrow.gif") no-repeat 100% 0;
+ cursor: pointer;
+}
+
+.sbl_calendar .sbl_cal_weekdays div,
+.sbl_calendar div.selectable,
+.sbl_calendar div.nonselectable {
+ padding: 0px 2px;
+ float: left;
+ width: 26px;
+ border: solid #444;
+ border-width: 0 1px 1px 0;
+ overflow: hidden;
+}
+
+.sbl_calendar div.selectable,
+.sbl_calendar div.nonselectable {
+ text-align: right;
+}
+
+.sbl_calendar .sbl_cal_weekdays div {
+ color: #835e4c;
+ background-color: #fbf6e0;
+ font-weight: bold;
+ text-align: center;
+}
+
+.sbl_calendar div.nonselectable {
+ color: #bbb;
+}
+
+.sbl_calendar .sbl_cal_days div {
+ background-color: #ffffff;
+}
+
+.sbl_calendar div.selected {
+ background-color: #b99068;
+}
+
+.sbl_calendar div.hover {
+ background-color: #decebe;
+}
+
+.sbl_calendar div.saturday {
+ background-color: #e0e8fb;
+}
+
+.sbl_calendar div.sunday {
+ background-color: #fbe3e0;
+}
+
+div.sbl_dropdown {
+ position: relative;
+ zoom: 1;
+}
+
+div.sbl_dropdown:after {
+ content: ".";
+ display: block;
+ visibility: hidden;
+ height: 0px;
+ font-size: 0;
+ line-height: 0;
+ clear: both;
+}
+
+ul.sbl_dropdown_list,
+ul.sbl_dropdown_list li,
+ul.sbl_dropdown_list ul {
+ margin: 0;
+ padding: 0;
+}
+
+ul.sbl_dropdown_list {
+ border-top: 1px solid #666;
+ border-right: 1px solid #666;
+ float: left;
+ line-height: 2.4;
+ font-size: small;
+ white-space: nowrap;
+}
+
+ul.sbl_dropdown_list li {
+ padding: 0 30px 0 10px;
+ border-bottom: 1px solid #666;
+ border-left: 1px solid #666;
+ float: left;
+ background: #eee;
+ list-style-type: none;
+ cursor: pointer;
+}
+
+ul.sbl_dropdown_list li.sbl_noclick {
+ cursor: default;
+}
+
+ul.sbl_dropdown_list li.hover {
+ background-color: #d6d6d6;
+}
+
+ul.sbl_dropdown_list li ul,
+ul.sbl_dropdown_list li ul li {
+ border: 0 none;
+ float: none;
+}
+
+ul.sbl_dropdown_list li ul {
+ border-top: 1px solid #666;
+ border-left: 1px solid #666;
+ border-right: 1px solid #666;
+ position: absolute;
+ display: none;
+}
+
+ul.sbl_dropdown_list li ul li {
+ padding-right: 50px;
+ border-top: 1px solid #fff;
+ border-bottom: 1px solid #666;
+ border-left: 3px solid #a03232;
+ background: #f6f6f6;
+}
+
+ul.sbl_dropdown_list li ul li.icon {
+ background-image: url("../images/js/dropdownRightArrow.gif");
+ background-repeat: no-repeat;
+ background-position: 100% 50%;
+}
+
+/** IE6 hack */
+* html ul.sbl_dropdown_list li span {
+ display: inline-block;
+}
diff --git a/generator/skeleton/en/public/images/js/calendarClose.gif b/generator/skeleton/en/public/images/js/calendarClose.gif
new file mode 100755
index 0000000..0c00d10
Binary files /dev/null and b/generator/skeleton/en/public/images/js/calendarClose.gif differ
diff --git a/generator/skeleton/en/public/images/js/calendarFrameBg.gif b/generator/skeleton/en/public/images/js/calendarFrameBg.gif
new file mode 100755
index 0000000..31489b8
Binary files /dev/null and b/generator/skeleton/en/public/images/js/calendarFrameBg.gif differ
diff --git a/generator/skeleton/en/public/images/js/calendarLeftArrow.gif b/generator/skeleton/en/public/images/js/calendarLeftArrow.gif
new file mode 100755
index 0000000..e25c1af
Binary files /dev/null and b/generator/skeleton/en/public/images/js/calendarLeftArrow.gif differ
diff --git a/generator/skeleton/en/public/images/js/calendarRightArrow.gif b/generator/skeleton/en/public/images/js/calendarRightArrow.gif
new file mode 100755
index 0000000..a889e76
Binary files /dev/null and b/generator/skeleton/en/public/images/js/calendarRightArrow.gif differ
diff --git a/generator/skeleton/en/public/images/js/dropdownRightArrow.gif b/generator/skeleton/en/public/images/js/dropdownRightArrow.gif
new file mode 100755
index 0000000..8ad4fc5
Binary files /dev/null and b/generator/skeleton/en/public/images/js/dropdownRightArrow.gif differ
diff --git a/generator/skeleton/en/public/images/orderAsc.gif b/generator/skeleton/en/public/images/orderAsc.gif
new file mode 100755
index 0000000..1edaa16
Binary files /dev/null and b/generator/skeleton/en/public/images/orderAsc.gif differ
diff --git a/generator/skeleton/en/public/images/orderDesc.gif b/generator/skeleton/en/public/images/orderDesc.gif
new file mode 100755
index 0000000..f06a9ff
Binary files /dev/null and b/generator/skeleton/en/public/images/orderDesc.gif differ
diff --git a/generator/skeleton/en/public/images/powered-by-sabel.gif b/generator/skeleton/en/public/images/powered-by-sabel.gif
new file mode 100755
index 0000000..1e5756a
Binary files /dev/null and b/generator/skeleton/en/public/images/powered-by-sabel.gif differ
diff --git a/generator/skeleton/en/public/index.php b/generator/skeleton/en/public/index.php
new file mode 100755
index 0000000..13d2b70
--- /dev/null
+++ b/generator/skeleton/en/public/index.php
@@ -0,0 +1,24 @@
+ 0) {
+ Sabel::init();
+ echo Sabel_Bus::create()->run(new Config_Bus());
+ Sabel::shutdown();
+} else {
+ echo Sabel_Bus::create()->run(new Config_Bus());
+}
+
+ob_flush();
diff --git a/generator/skeleton/en/public/js/Sabel.js b/generator/skeleton/en/public/js/Sabel.js
new file mode 100755
index 0000000..de31b59
--- /dev/null
+++ b/generator/skeleton/en/public/js/Sabel.js
@@ -0,0 +1,3808 @@
+/**
+ * SabelJS 1.2
+ * Header
+ *
+ * @author Hamanaka Kazuhiro
+ * @copyright 2004-2008 Hamanaka Kazuhiro
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+ */
+
+window.Sabel = {};
+
+Sabel.PHP = {};
+
+Sabel.emptyFunc = function() {};
+
+Sabel.QueryObject = function(object) {
+ this.data = object;
+};
+
+Sabel.QueryObject.prototype = {
+ has: function(key) {
+ return !!(this.data[key] !== undefined);
+ },
+
+ get: function(key) {
+ return this.data[key] || null;
+ },
+
+ set: function(key, val) {
+ this.data[key] = val;
+ return this;
+ },
+
+ unset: function(key) {
+ delete this.data[key];
+ },
+
+ serialize: function() {
+ var data = this.data, buf = new Array();
+ for (var key in data) {
+ if (Sabel.Object.isArray(data[key])) {
+ Sabel.Array.each(data[key], function(val) {
+ buf[buf.length] = key + "=" + encodeURIComponent(val);
+ });
+ } else {
+ buf[buf.length] = key + "=" + encodeURIComponent(data[key]);
+ }
+ }
+
+ return buf.join("&");
+ }
+};
+
+
+Sabel.Uri = function(uri)
+{
+ uri = uri || location.href;
+
+ var result = Sabel.Uri.pattern.exec(uri);
+
+ if (result === null) {
+ var urlPrefix = location.protocol + "//" + location.hostname;
+
+ if (uri[0] === "/") {
+ uri = urlPrefix + uri;
+ } else {
+ var currentPath = location.pathname.substr(0, location.pathname.lastIndexOf("/")+1);
+ uri = urlPrefix + currentPath + uri;
+ }
+ var result = Sabel.Uri.pattern.exec(uri);
+ }
+
+ for (var i = 0, len = result.length; i < len; i++) {
+ this[Sabel.Uri.keyNames[i]] = result[i] || "";
+ }
+ this['parseQuery'] = Sabel.Uri.parseQuery(this.query);
+};
+
+Sabel.Uri.pattern = /^((\w+):\/\/(?:(\w+)(?::(\w+))?@)?([^:\/]*)(?::(\d+))?)(?:([^?#]+?)(?:\/(\w+\.\w+))?)?(?:\?((?:[^]+)(?:&[^]*)*))?(?:#([^#]+))?$/;
+Sabel.Uri.keyNames = ['uri', 'url', 'protocol', 'user', 'password', 'domain', 'port', 'path', 'filename', 'query', 'hash'];
+
+Sabel.Uri.parseQuery = function(query)
+{
+ if (query === undefined) return {};
+ var queries = query.split("&"), parsed = {};
+
+ for (var i = 0, len = queries.length; i < len; i++) {
+ if (queries[i] == "") continue;
+ var q = queries[i].split("=");
+ parsed[q[0]] = q[1] || "";
+ }
+
+ return new Sabel.QueryObject(parsed);
+};
+
+Sabel.Uri.prototype = {
+ has: function(key) {
+ return this.parseQuery.has(key);
+ },
+
+ get: function(key) {
+ return this.parseQuery.get(key);
+ },
+
+ set: function(key, value) {
+ this.parseQuery.set(key, value);
+ return this;
+ },
+
+ unset: function(key) {
+ this.parseQuery.unset(key);
+ return this;
+ },
+
+ getQueryObj: function() {
+ return this.parseQuery;
+ },
+
+ toString: function() {
+ var uri = this.url + this.path;
+
+ if (this.filename !== "") uri += "/" + this.filename;
+ if ((query = this.parseQuery.serialize())) uri += "?" + query;
+ return uri;
+ }
+};
+
+Sabel.Environment = (function() {
+ var scripts = document.getElementsByTagName("script");
+ var uri = scripts[scripts.length - 1].src;
+
+ this._env = parseInt(Sabel.Uri.parseQuery(uri.substring(uri.indexOf("?") + 1)));
+
+ return this;
+})();
+Sabel.Environment.PRODUCTION = 10;
+Sabel.Environment.TEST = 5;
+Sabel.Environment.DEVELOPMENT = 1;
+
+Sabel.Environment.isDevelopment = function() {
+ return this._env === Sabel.Environment.DEVELOPMENT;
+};
+
+Sabel.Environment.isTest = function() {
+ return this._env === Sabel.Environment.TEST;
+};
+
+Sabel.Environment.isProduction = function() {
+ return this._env === Sabel.Environment.PRODUCTION;
+};
+
+Sabel.Window = {
+ getWidth: function() {
+ if (document.compatMode === "BackCompat" || (Sabel.UserAgent.isOpera && Sabel.UserAgent.version < 9.5)) {
+ return document.body.clientWidth;
+ } else if (Sabel.UserAgent.isSafari) {
+ return window.innerWidth;
+ } else {
+ return document.documentElement.clientWidth;
+ }
+ },
+
+ getHeight: function() {
+ if (document.compatMode === "BackCompat" || (Sabel.UserAgent.isOpera && Sabel.UserAgent.version < 9.5)) {
+ return document.body.clientHeight;
+ } else if (Sabel.UserAgent.isSafari) {
+ return window.innerHeight;
+ } else {
+ return document.documentElement.clientHeight;
+ }
+ },
+
+ getScrollWidth: function() {
+ if (document.compatMode === "CSS1Compat") {
+ var width = document.documentElement.scrollWidth;
+ } else {
+ var width = document.body.scrollWidth;
+ }
+ var clientWidth = Sabel.Window.getWidth();
+ return (clientWidth > width) ? clientWidth : width;
+ },
+
+ getScrollHeight: function() {
+ if (document.compatMode === "CSS1Compat") {
+ var height = document.documentElement.scrollHeight;
+ } else {
+ var height = document.body.scrollHeight;
+ }
+ var clientHeight = Sabel.Window.getHeight();
+ return (clientHeight > height) ? clientHeight : height;
+ },
+
+ getScrollLeft: function() {
+ if (document.compatMode === "CSS1Compat") {
+ return document.documentElement.scrollLeft;
+ } else {
+ return document.body.scrollLeft;
+ }
+ },
+
+ getScrollTop: function() {
+ if (document.compatMode === "CSS1Compat") {
+ return document.documentElement.scrollTop;
+ } else {
+ return document.body.scrollTop;
+ }
+ }
+};
+
+Sabel.UserAgent = new function() {
+ var ua = navigator.userAgent, w = window;
+ this.ua = ua;
+
+ this.isIE = false;
+ this.isFirefox = false;
+ this.isSafari = false;
+ this.isOpera = false;
+ this.isChrome = false;
+ this.isMozilla = false;
+
+ if (w.ActiveXObject) { // ActiveXObjectが存在すればIE
+ this.isIE = true;
+ } else if (w.opera) { // window.operaが存在すればOpera
+ this.isOpera = true;
+ this.version = opera.version();
+ } else if (w.execScript) { // execScriptが存在すればChrome (IEも存在するが既にチェック済)
+ this.isChrome = true;
+ } else if (w.getMatchedCSSRules) { // getMatchedCSSRulesが存在すればSafari3 (Chromeも存在するが既にチェック済)
+ //} else if (w.defaultstatus) { // Safari2対応ならこっち
+ this.isSafari = true;
+ } else if (w.Components) { // Componentsが存在すればGecko
+ this.isFirefox = true;
+ } else {
+ this.isMozilla = /Mozilla/.test(ua);
+ }
+ if (this.version === undefined) {
+ var matches = /(MSIE |Firefox\/|Version\/|Chrome\/)([0-9.]+)/.exec(ua);
+ this.version = matches ? matches[2] : "";
+ }
+
+ this.isWindows = /Win/.test(ua);
+ this.isMac = /Mac/.test(ua);
+ this.isLinux = /Linux/.test(ua);
+ this.isBSD = /BSD/.test(ua);
+ this.isIPhone = /iPhone/.test(ua);
+};
+
+Sabel.Window.lineFeedCode = (Sabel.UserAgent.isIE) ? "\r" : "\n";
+
+
+Sabel.Object = {
+ _cache: new Array(),
+
+ create: function(object, parent) {
+ if (typeof object === "undefined") return {};
+
+ object = Object(object);
+
+ switch (typeof object) {
+ case "function":
+ return object;
+ case "object":
+ var func = function() {};
+ func.prototype = object;
+ if (parent) Sabel.Object.extend(func.prototype, parent, true);
+ if (!func.prototype.isAtomic) Sabel.Object.extend(func.prototype, this.Methods, true);
+
+ var obj = new func;
+ if (obj.isAtomic()) {
+ obj.toString = function() { return object.toString.apply(object, arguments); };
+ obj.valueOf = function() { return object.valueOf.apply(object, arguments); };
+ }
+ }
+
+ return obj;
+ },
+
+ extend: function(child, parent, curry, override) {
+ for (var prop in parent) {
+ if (typeof child[prop] !== "undefined" && override !== true) continue;
+ if (typeof parent[prop] !== "function") {
+ child[prop] = parent[prop];
+ } else {
+ child[prop] = (curry === true) ? Sabel.Object._tmp(parent[prop]) : parent[prop];
+ }
+ }
+
+ return child;
+ },
+
+ _tmp: function(method) {
+ return this._cache[method] = this._cache[method] || function() {
+ var args = new Array(this);
+ args.push.apply(args, arguments);
+ return method.apply(method, args);
+ }
+ }
+};
+
+
+Sabel.Object.Methods = {
+ isAtomic: function(object) {
+ switch (object.constructor) {
+ case String:
+ case Number:
+ case Boolean:
+ return true;
+ default:
+ return false;
+ }
+ },
+
+ isString: function(object) {
+ return object.constructor === String;
+ },
+
+ isNumber: function(object) {
+ return object.constructor === Number;
+ },
+
+ isBoolean: function(object) {
+ return object.constructor === Boolean;
+ },
+
+ isArray: function(object) {
+ return object.constructor === Array;
+ },
+
+ isFunction: function(object) {
+ return object.constructor === Function;
+ },
+
+ clone: function(object) {
+ return Sabel.Object.create(object);
+ },
+
+ getName: function(object) {
+ return object.constructor;
+ },
+
+ hasMethod: function(object, method) {
+ return (object[method] !== undefined);
+ }
+};
+
+Sabel.Object.extend(Sabel.Object, Sabel.Object.Methods);
+Sabel.Class = function() {
+ if (typeof arguments[0] === "function") {
+ var superKlass = arguments[0];
+ } else {
+ var superKlass = function() {};
+ }
+ var methods = Array.prototype.pop.call(arguments) || Sabel.Object;
+
+ var tmpKlass = function() {};
+ tmpKlass.prototype = superKlass.prototype;
+
+ var subKlass = function() {
+ this.__super__ = superKlass;
+ if (typeof methods.init === "function") {
+ methods.init.apply(this, arguments);
+ } else {
+ this.__super__.apply(this, arguments);
+ }
+ delete this.__super__;
+ }
+
+ subKlass.prototype = new tmpKlass;
+ switch (subKlass.prototype.constructor) {
+ case String: case Number: case Boolean:
+ subKlass.prototype.toString = function() {
+ return superKlass.toString.apply(superKlass, arguments);
+ };
+ subKlass.prototype.valueOf = function() {
+ return superKlass.valueOf.apply(superKlass, arguments);
+ };
+ }
+
+ if (methods) {
+ for (var name in methods) subKlass.prototype[name] = methods[name];
+
+ var ms = ["toString", "valueOf"];
+ for (var i = 0, len = ms.length; i < len; i++) {
+ if (methods.hasOwnProperty(ms[i]))
+ subKlass.prototype[ms[i]] = methods[ms[i]];
+ }
+
+ subKlass.prototype.constructor = subKlass;
+ }
+ return subKlass;
+};
+
+Sabel.String = new Sabel.Class(String, {
+ init: function(string) {
+ this._string = string;
+ },
+
+ toString: function() {
+ return this._string;
+ },
+
+ valueOf: function() {
+ return this._string;
+ },
+
+ _set: function(string) {
+ this._string = string;
+ return this;
+ },
+
+ chr: function() {
+ return this._set(String.fromCharCode.apply(String, this._string.split(',')));
+ },
+
+ explode: function(delimiter) {
+ return this._string.split(delimiter);
+ },
+
+ htmlspecialchars: function(quote_style) {
+ var string = this._string.replace(/&/g, "&").replace(//g, ">");
+
+ switch (quote_style) {
+ case 3: case "ENT_QUOTES":
+ string = string.replace(/'/g, "'");
+ case 2: case "ENT_COMPAT":
+ string = string.replace(/"/g, """);
+ case 0: case "ENT_NOQUOTES":
+ }
+ return this._set(string);
+ },
+
+ lcfirst: function() {
+ var str = this._string;
+ return this._set(str.charAt(0).toLowerCase() + str.substring(1));
+ },
+
+ ltrim: function() {
+ return this._set(this._string.replace(/^\s+/, ""));
+ },
+
+ nl2br: function() {
+ return this._set(this._string.replace(/(\r?\n)/g, " $1"));
+ },
+
+ ord: function() {
+ return this._set(this._string.charCodeAt(0));
+ },
+
+ rtrim: function() {
+ return this._set(this._string.replace(/\s+$/, ""));
+ },
+
+ repeat: function(multiplier) {
+ var tmp = "";
+ for (var i = 0; i < multiplier; i++) {
+ tmp += this._string;
+ }
+ return this._set(tmp);
+ },
+
+ shuffle: function() {
+ var tmp = this._string.split("");
+ var i = tmp.length;
+
+ while (i) {
+ var j = Math.floor(Math.random() * i);
+ var t = tmp[--i];
+ tmp[i] = tmp[j];
+ tmp[j] = t;
+ }
+ return tmp.join("");
+ },
+
+ sprintf: function(/* mixed args */) {
+ var args = arguments;
+
+ var i = 0, v, o;
+
+ var pattern = /%(?:([0-9]+)\$)?(-)?([0]|\'.)?([0-9]*)(?:\.([0-9]+))?([bcdfFosxX])/g;
+ var replaced = this.replace(pattern, function(all, key, sign, padding, alignment, precision, match) {
+ v = (key) ? args[--key] : args[i++];
+
+ if (precision) precision = parseInt(precision);
+ switch (match) {
+ case "b":
+ v = v.toString(2);
+ break;
+ case "c":
+ v = String.fromCharCode(v);
+ break;
+ case "f": case "F":
+ if (precision) v = parseFloat(v).toFixed(precision);
+ break;
+ case "o":
+ v = v.toString(8);
+ break;
+ case "s":
+ v = v.substring(0, precision || v.length);
+ break;
+ case "x":
+ v = v.toString(16);
+ break;
+ case "X":
+ v = v.toString(16).toUpperCase();
+ break;
+ }
+
+ if (alignment) {
+ var len = alignment - v.toString().length;
+ padding = (padding) ? padding.charAt(padding.length - 1) : " ";
+ var t = new Sabel.String(padding).repeat(len);
+
+ v = (sign === "-") ? v + t : t + v;
+ }
+
+ return v;
+ });
+
+ return replaced;
+ },
+
+ str_pad: function(pad_length, pad_string /* = " " */, pad_type /* = 1 */) {
+ var string = this._string
+ var repeat = (pad_length - string.length);
+ var left = right = 0;
+ pad_string = new Sabel.String(pad_string || " ");
+
+ switch (pad_type) {
+ case 0:
+ left = repeat;
+ break;
+ case 2:
+ left = Math.floor(repeat / 2);
+ right = Math.ceil(repeat / 2);
+ break;
+ case 1:
+ default:
+ right = repeat;
+ break;
+ }
+
+ if (left > 0) {
+ string = pad_string.repeat(left).substr(0, left) + string;
+ }
+
+ if (right > 0) {
+ string += pad_string.repeat(right).substr(0, right);
+ }
+
+ return this._set(string);
+ },
+
+ trim: function() {
+ var str = this._string;
+ return this._set(str.replace(/(^\s+|\s+$)/g, ""));
+ },
+
+ format: function(obj) {
+ var replaceFunc = function(target, key) {
+ return (obj[key] !== undefined) ? obj[key] : "";
+ };
+ return this._string.replace(/%(\w+)%/g, replaceFunc).replace(/#\{(\w+)\}/g, replaceFunc);
+ },
+
+ ucfirst: function() {
+ var str = this._string;
+ return this._set(str.charAt(0).toUpperCase() + str.substring(1));
+ },
+
+
+ capitalize: function() {
+ this._set(this._string.toLowerCase());
+ return this.ucfirst();
+ },
+
+ camelize: function() {
+ var str = this._string;
+ return this._set(str.replace(/-([a-z])/g, function(dummy, match) {
+ return match.toUpperCase();
+ }));
+ },
+
+ decamelize: function() {
+ return this._set(this._string.replace(/\w[A-Z]/g, function(match) {
+ return match.charAt(0) + "-" + match.charAt(1).toLowerCase();
+ }));
+ },
+
+ truncate: function(length, truncation) {
+ truncation = truncation || "";
+
+ return this._set(this._string.substring(0, length) + truncation);
+ },
+
+ clean: function() {
+ return this._set(this._string.replace(/\s{2,}/g, " "));
+ },
+
+ toInt: function() {
+ return parseInt(this._string, 10);
+ },
+
+ toFloat: function() {
+ return parseFloat(this._string);
+ }
+});
+
+Sabel.String.prototype.chop = Sabel.String.prototype.rtrim;
+Sabel.String.prototype.times = Sabel.String.prototype.repeat;
+
+var methods = [
+ "anchor", "big", "blink", "bold", "charAt", "charCodeAt", "concat",
+ "decodeURI", "decodeURI_Component", "encodeURI", "encodeURI_Component",
+ "enumerate", "escape", "fixed", "fontcolor", "fontsize", "fromCharCode",
+ "getProperty", "indexOf", "italics", "lastIndexOf", "link", "localeCompare",
+ "match", "replace", "resolve", "search", "slice", "small", "split", "strike",
+ "sub", "substr", "substring", "sup", "toLocaleLowerCase", "toLocaleUpperCase",
+ "toLowerCase", "toSource", "toUpperCase", "unescape", "uneval"
+];
+for (var i = 0, len = methods.length; i < len; i++) {
+ var method = methods[i];
+ Sabel.String.prototype[method] = (function(method) {
+ return function() {
+ return this._set(method.apply(this, arguments));
+ }
+ })(String.prototype[method]);
+};
+
+Sabel.Number = function(number) {
+ return Sabel.Object.create(number, Sabel.Number)
+};
+
+Sabel.Number._units = ["", "k", "M", "G", "T", "P", "E", "Z", "Y"];
+Sabel.Number.toHumanReadable = function(number, unit, ext) {
+ if (typeof number !== "number") throw "number is not Number object.";
+ var i = 0;
+
+ while (number > unit) {
+ i++;
+ number = number / unit;
+ }
+
+ return number.toFixed(1) + Sabel.Number._units[i];
+};
+
+//Sabel.Number.numberFormat
+
+Sabel.Number.between = function(number, min, max) {
+ return number >= min && number <= max;
+};
+
+Sabel.Array = function(iterable) {
+ if (typeof iterable === "undefined") {
+ iterable = new Array();
+ } else if (iterable.constructor === String) {
+ iterable = new Array(iterable);
+ } else if (iterable.toArray) {
+ iterable = iterable.toArray();
+ } else {
+ var buf = new Array();
+ Sabel.Array.each(iterable, function(v) { buf[buf.length] = v; });
+ iterable = buf;
+ }
+
+ return Sabel.Object.extend(iterable, Sabel.Array, true);
+};
+
+Sabel.Array.each = function(array, callback) {
+ for (var i = 0, len = array.length; i < len; i++) {
+ var r = callback(array[i], i);
+ if (r === "BREAK") break;
+ }
+ return array;
+};
+
+Sabel.Array.map = function(array, callback) {
+ var results = new Array();
+ for (var i = 0, len = array.length; i < len; i++) {
+ results[i] = callback(array[i]);
+ }
+ return results;
+};
+
+Sabel.Array.concat = function(array, iterable) {
+ if (iterable.length === undefined) return array;
+
+ if (iterable.toArray) iterable = iterable.toArray();
+
+ Sabel.Array.each(iterable, function(data) {
+ array[array.length] = data;
+ });
+ return array;
+};
+
+Sabel.Array.inject = function(array, method) {
+ var buf = new Array();
+ Sabel.Array.each(array, function(data) {
+ if (method(data) === true) buf[buf.length] = data;
+ });
+ return buf;
+};
+
+
+Sabel.Array.callmap = function(/*array, method, args */) {
+ var args = Sabel.Array(arguments);
+ var array = args.shift(), method = args.shift();
+ for (var i = 0, len = array. length; i < len; i++) {
+ array[i][method].apply(array[i], args);
+ }
+ return array;
+};
+
+Sabel.Array.include = function(array, value) {
+ for (var i = 0, len = array.length; i < len; i++) {
+ if (array[i] === value) return true;
+ }
+ return false;
+};
+
+Sabel.Array.sum = function(array) {
+ var result = 0;
+ for (var i = 0, len = array.length; i < len; i++) {
+ result += array[i];
+ }
+ return result;
+};
+
+Sabel.Array.unique = function(array) {
+ var result = new Array();
+ Sabel.Array.each(array, function(val) {
+ if (result.indexOf(val) === -1) {
+ result.push(val);
+ }
+ });
+ return result;
+};
+
+Sabel.Object.extend(Sabel.Array, Sabel.Object.Methods);
+Sabel.Function = function(method) {
+ return Sabel.Object.create(method, Sabel.Function);
+};
+
+Sabel.Function.bind = function() {
+ var args = Sabel.Array(arguments);
+ var method = args.shift(), object = args.shift();
+
+ return function() {
+ return method.apply(object, args.concat(Sabel.Array(arguments)));
+ }
+};
+
+Sabel.Function.bindWithEvent = function() {
+ var args = Sabel.Array(arguments);
+ var method = args.shift(), object = args.shift();
+
+ return function(event) {
+ return method.apply(object, [event || window.event].concat(args));
+ }
+};
+
+Sabel.Function.delay = function(method, delay) {
+ var args = Sabel.Array(arguments);
+ method = args.shift();
+ delay = args.shift() || 1000;
+ scope = args.shift() || method;
+ setTimeout(function() { method.apply(scope, args); }, delay);
+};
+
+Sabel.Function.curry = function() {
+ var args = Sabel.Array(arguments), method = args.shift();
+
+ return function() {
+ return method.apply(method, args.concat(Sabel.Array(arguments)));
+ }
+};
+
+Sabel.Function.restraint = function(method, obj) {
+ var methodArgs = Sabel.Function.getArgumentNames(method);
+ var arglen = methodArgs.length;
+
+ var args = new Array(arglen);
+ for (var i = 0; i < arglen; i++) {
+ args[i] = obj[methodArgs[i]];
+ }
+
+ return function() {
+ var ary = Sabel.Array(arguments);
+ for (var i = 0; i < arglen; i++) {
+ if (args[i] === undefined) args[i] = ary.shift();
+ }
+ method.apply(method, args);
+ }
+};
+
+Sabel.Function.getArgumentNames = function(method) {
+ var str = method.toString();
+ argNames = str.match(/^[\s]*function[\s\w]*\((.*)\)/)[1].split(",");
+ for (var i = 0, len = argNames.length; i < len; i++) {
+ new Sabel.String(argNames[i]).trim();
+ }
+ return (argNames[0] === "") ? new Array() : argNames;
+};
+
+Sabel.Object.extend(Sabel.Function, Sabel.Object.Methods);
+
+
+Sabel.Dom = {
+ getElementById: function(element, extend) {
+ if (typeof element === "string") {
+ element = document.getElementById(element);
+ }
+
+ return (element) ? (extend === false) ? element : Sabel.Element(element) : null;
+ },
+
+ getElementsByClassName: function(className, element, ext) {
+ element = (element) ? Sabel.get(element, false) : document;
+
+ if (element.getElementsByClassName) {
+ return element.getElementsByClassName(className);
+ } else {
+ var elms = element.getElementsByTagName("*");
+ var pat = new RegExp("(?:^|\\s)" + className + "(?:\\s|$)");
+
+ var buf = (ext) ? Sabel.Elements() : new Array();
+ Sabel.Array.each(elms, function(elm) {
+ if (pat.test(elm.className)) buf.push(elm);
+ });
+ return buf;
+ }
+ },
+
+ getElementsBySelector: function(selector, root) {
+ var h = Sabel.Dom.Selector.handlers;
+ root = root || document;
+ var elms = [root], founds = new Array(), prev;
+
+ if (document.querySelectorAll) {
+ try {
+ Sabel.Array.each(root.querySelectorAll(selector), function(el) {
+ founds.push(el);
+ });
+ return new Sabel.Elements(founds);
+ } catch (e) {}
+ }
+
+ while (selector && selector !== prev) {
+ if (selector.indexOf(',') === 0) {
+ founds = founds.concat(elms);
+ elms = [document];
+ selector = selector.replace(/^,/, "");
+ }
+ prev = selector;
+
+ ms = Sabel.Dom.Selector.pattern.exec(selector);
+ ms[2] = (ms[2] || "*").toUpperCase();
+ if (ms[1]) {
+ elms = h.combinator(elms, ms[1], ms[2]);
+ if (ms[3] /* ID */)
+ elms = h.id(elms, ms[3]);
+ } else {
+ if (ms[3] /* ID */) {
+ elms = h.id(elms, ms[3], ms[2]);
+ } else if (ms[2] /* TAG */) {
+ elms = h.tagName(elms, ms[2]);
+ }
+ }
+
+ if (ms[4] /* CLASS */)
+ elms = h.className(elms, ms[4]);
+
+ if (ms[5] /* ATTR */)
+ elms = h.attr(elms, ms[5]);
+
+ if (ms[6] /* PSEUDO */)
+ elms = h.pseudo(elms, ms[6]);
+
+ selector = selector.replace(ms[0], "");
+ }
+ return new Sabel.Elements(founds.concat(elms));
+ },
+
+ getElementsByXPath: function(xpath, root) {
+ var result = document.evaluate(xpath, root || document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
+
+ if (result.snapshotLength) {
+ var buf = new Array(result.snapshotLength), i = 0, tmp;
+ while((tmp = result.snapshotItem(i))) buf[i++] = tmp;
+ return new Sabel.Elements(buf);
+ } else {
+ return new Sabel.Elements();
+ }
+ }
+};
+
+Sabel.get = Sabel.Dom.getElementById;
+Sabel.find = Sabel.Dom.getElementsBySelector;
+Sabel.xpath = Sabel.Dom.getElementsByXPath;
+
+Sabel.Dom.Selector = {
+ pattern: new RegExp(
+ "^\\s*" + // Space
+ "([~>+])?\\s*" + //
+ "(\\w+|\\*)?" + // TagName
+ "(?:#(\\w+))?" + // ID
+ "((?:\\.[a-zA-Z0-9_-]+)+)?" + // ClassName
+ "((?:\\[@?\\w+(?:[$^!~*|]?=['\"]?.+['\"]?)?\\])*)?" + // Attribute
+ "((?::[\\w-]+(?:\\([^\\s]+\\))?)*)" //
+ ),
+
+ handlers: {
+ tagName: function(nodes, tagName) {
+ var founds = new Array(), elm, i = 0;
+ while ((elm = nodes[i++])) {
+ Sabel.Dom.Selector.concat(founds, elm.getElementsByTagName(tagName));
+ }
+ return Sabel.Dom.Selector.clear(founds, "_added");
+ },
+
+ id: function(nodes, id, tagName) {
+ var founds = new Array(), elm, i = 0;
+ id = id.replace("#", "");
+
+ var tmpElm = document.getElementById(id);
+ if (tagName === "*" || tmpElm.nodeName === tagName) {
+ if (nodes[0] === document) {
+ founds.push(tmpElm);
+ } else {
+ while ((elm = nodes[i++])) {
+ if (Sabel.Element.isContain(elm, tmpElm)) {
+ founds.push(tmpElm);
+ break;
+ }
+ }
+ }
+ } else {
+ while ((elm = nodes[i++])) {
+ if (elm.getAttribute("id") === id) {
+ founds[founds.length] = elm;
+ break;
+ }
+ }
+ }
+ return founds;
+ },
+
+ className: function(nodes, className) {
+ var founds = new Array(), elm, i = 0;
+ var classNames = className.split("."), cr = new Array();
+ classNames.shift();
+
+ for (var j = 0, len = classNames.length; j < len; j++)
+ cr.push(new RegExp("(?:^|\\s+)" + classNames[j] + "(?:\\s+|$)"));
+
+ var c, cn, flag, k;
+ while ((elm = nodes[i++])) {
+ k = 0;
+ flag = true;
+
+ // @todo use Sabel.Element.getAttr
+ c = elm.className;
+ if (!c || elm._added === true) continue;
+
+ while ((cn = cr[k++])) {
+ if (!cn.test(c)) {
+ flag = false;
+ break;
+ }
+ }
+ if (flag === true) {
+ elm._added = true;
+ founds.push(elm);
+ }
+ }
+ return Sabel.Dom.Selector.clear(founds, "_added");
+ },
+
+ combinator: function(nodes, combName, tagName) {
+ var founds = new Array(), elm, i = 0, buf;
+ switch (combName) {
+ case "+":
+ while ((elm = nodes[i++])) {
+ while ((elm = elm.nextSibling) && elm.nodeType !== 1);
+ if (elm && (elm.nodeName === tagName || tagName === "*")) {
+ founds[founds.length] = elm;
+ }
+ }
+ break;
+ case "~":
+ while ((elm = nodes[i++])) {
+ while ((elm = elm.nextSibling) && elm._added !== true) {
+ if (elm.nodeName === tagName || tagName === "*") {
+ elm._added = true;
+ founds.push(elm);
+ }
+ }
+ }
+ Sabel.Dom.Selector.clear(founds, "_added");
+ break;
+ case ">":
+ while ((elm = nodes[i++])) {
+ buf = elm.getElementsByTagName(tagName);
+ for (var j = 0, c; c = buf[j]; j++) {
+ if (c.parentNode === elm) founds.push(c);
+ }
+ }
+ break;
+ }
+ return founds;
+ },
+
+ attr: function(nodes, attr) {
+ var founds = new Array(), elm, i = 0;
+ var attrPattern = /\[@?(\w+)(?:([$^!~*|]?=)(['"]?)(.+?)\3)?\]/;
+ var attrs = attr.match(/\[@?(\w+)(?:([$^!~*|]?=)(['"]?)(.+?)\3)?\]/g), attrRegex = new Array(), at,a;
+
+ for (var j = 0, len = attrs.length; j < len; ++j) {
+ buf = attrPattern.exec(attrs[j]);
+ if (buf[2] == "|=" && buf[1] !== "lang" && buf[1] !== "hreflang") {
+ return founds;
+ }
+ attrRegex.push(buf);
+ }
+
+ checkNode:
+ while ((elm = nodes[i++])) {
+ if (elm._added) continue;
+ var k = 0;
+
+ while ((at = attrRegex[k++])) {
+ if (at[1] === 'class' && window.ActiveXObject) at[1] = 'className';
+ a = elm.getAttribute(at[1]);
+
+ switch (at[2]) {
+ case "=":
+ if (a !== at[4]) continue checkNode;
+ break;
+ case "!=":
+ if (a === at[4]) continue checkNode;
+ break;
+ case "~=":
+ if ((" "+a+" ").indexOf(" "+at[4]+" ") === -1) continue checkNode;
+ break;
+ case "^=":
+ if ((a||"").indexOf(at[4]) !== 0) continue checkNode;
+ break;
+ case "$=":
+ if ((a||"").lastIndexOf(at[4]) !== ((a||"").length - at[4].length)) continue checkNode;
+ break;
+ case "*=":
+ if ((a||"").indexOf(at[4]) === -1) continue checkNode;
+ break;
+ case "|=":
+ if ((a+"-").toLowerCase().indexOf((at[4]+"-").toLowerCase()) !== 0)
+ continue checkNode;
+ break;
+ default:
+ if (!a) continue checkNode;
+ break;
+ }
+ }
+ elm._added = true;
+ founds.push(elm);
+ }
+ return Sabel.Dom.Selector.clear(founds, "_added");
+ },
+
+ pseudo: function(nodes, pseudo) {
+ var founds = new Array(), elm, i = 0;
+ var ps = pseudo.replace("):", ") :").match(/:[\w-]+(\([^\s]+\))?/g);
+
+ var buf;
+
+ var chk = function(nodes, next, check, isNot) {
+ var founds = new Array(), i = 0, elm;
+ isNot = (isNot) ? true : false;
+
+ while ((elm = nodes[i++])) {
+ var buf = elm;
+ var checkValue = (check === "nodeName") ? elm.nodeName : 1;
+ while ((buf = buf[next]) && buf[check] !== checkValue);
+ if ((buf === null) !== isNot) founds.push(elm);
+ }
+ return founds;
+ };
+
+ var chkNth = function(nodes, x, s, init, nextprop, checkNodeName) {
+ s = parseInt(s);
+ var founds = new Array(), i = 0, elm;
+ var searched = new Array();
+ while ((elm = nodes[i++])) {
+ var parent = elm.parentNode;
+ if (parent._searched !== true) {
+ var next = s;
+ var cn = parent[init], counter = 0;
+ while (cn) {
+ if (checkNodeName === true) {
+ if (cn.nodeName === elm.nodeName) {
+ if (++counter === next) {
+ founds.push(elm);
+ next += x;
+ }
+ }
+ } else {
+ if (cn.nodeType === 1) {
+ if (++counter === next) {
+ if (cn.nodeName === elm.nodeName) {
+ founds.push(elm);
+ }
+ next += x;
+ }
+ }
+ }
+ cn = cn[nextprop];
+ }
+ parent._searched = true;
+ searched.push(parent);
+ }
+ }
+ Sabel.Dom.Selector.clear(searched, "_searched");
+ return founds;
+ };
+
+ var getSeq = function(expression) {
+ var nc = /(?:(odd|even)|((?:[1-9]\d*)?n)([+-]\d+)?)/.exec(expression)
+ if (nc[1]) {
+ if (nc[1] === "odd") var x = 2, s = 1;
+ else var x = 2, s = 2;
+ } else if (nc[2]) {
+ var x = parseInt(nc[2], 10) || 1, s = nc[3] || 0;
+ if (s < 1) {
+ s += x;
+ }
+ }
+ return [s, x];
+ };
+
+ for (var j = 0, len = ps.length; j < len; ++j) {
+ // @todo ここにgがあるとおかしくなる(opera)
+ buf = /:([\w-]+)(?:\(([^\s]+)\))?/.exec(ps[j]);
+ switch(buf[1]) {
+ case "first-child":
+ founds = chk(nodes, "previousSibling", "nodeType");
+ break;
+ case "last-child":
+ founds = chk(nodes, "nextSibling", "nodeType");
+ break;
+ case "only-child":
+ founds = chk(nodes, "previousSibling", "nodeType");
+ founds = chk(founds, "nextSibling", "nodeType");
+ break;
+ case "nth-child":
+ var seq = getSeq(buf[2]);
+ founds = chkNth(nodes, seq[1], seq[0], "firstChild", "nextSibling", false);
+
+ return founds;
+ case "nth-last-child":
+ var seq = getSeq(buf[2]);
+ founds = chkNth(nodes, seq[1], seq[0], "lastChild", "previousSibling", false);
+
+ return founds;
+ case "first-of-type":
+ founds = chk(nodes, "previousSibling", "nodeName");
+ break;
+ case "last-of-type":
+ founds = chk(nodes, "nextSibling", "nodeName");
+ break;
+ case "only-of-type":
+ founds = chk(nodes, "previousSibling", "nodeName");
+ founds = chk(founds, "nextSibling", "nodeName");
+ break;
+ case "nth-of-type":
+ var seq = getSeq(buf[2]);
+ founds = chkNth(nodes, seq[1], seq[0], "firstChild", "nextSibling", true);
+ break;
+ case "nth-last-of-type":
+ var seq = getSeq(buf[2]);
+ founds = chkNth(nodes, seq[1], seq[0], "lastChild", "previousSibling", true);
+ break;
+ case "empty":
+ while ((elm = nodes[i++])) {
+ if (elm.childNodes.length === 0) {
+ founds.push(elm);
+ }
+ }
+ break;
+ case "contains":
+ var val = buf[2].replace(/['"]+/g, "");
+ while ((elm = nodes[i++])) {
+ if (Sabel.Element.getTextContent(elm).indexOf(val) !== -1) {
+ founds.push(elm);
+ }
+ }
+ break;
+ case "not":
+ var chkFunc = function(nodes, func) {
+ var founds = new Array(), i = 0, elm;
+ while ((elm = nodes[i++])) {
+ if (func(elm)) founds.push(elm);
+ }
+ return founds;
+ };
+
+ var mats = Sabel.Dom.Selector.pattern.exec(buf[2]);
+ if (mats[2] /* TAG */) {
+ founds = nodes;
+ if (mats[2] !== "*") {
+ var tagName = mats[2].toUpperCase();
+ founds = chkFunc(nodes, function(elm) {
+ return elm.nodeName !== tagName;
+ });
+ }
+ } else if (mats[3] /* ID */) {
+ var id = mats[3].replace("#", "");
+ founds = chkFunc(nodes, function(elm) {
+ return elm.getAttribute("id") !== id;
+ });
+ } else if (mats[4] /* CLASS */) {
+ var className = new RegExp("(?:^|\\s+)" + mats[4].replace(".", "") + "(?:\\s+|$)");
+ var klass = (window.ActiveXObject) ? "className" : "class";
+ founds = chkFunc(nodes, function(elm) {
+ return !className.test(elm.getAttribute(klass));
+ });
+ } else if (mats[5] /* ATTR */) {
+ var b = /\[@?(\w+)(?:([$^!~*|]?=)(['"]?)(.+?)\3)?\]/.exec(mats[5]);
+ if (b[2] == "|=" && b[1] !== "lang" && b[1] !== "hreflang") return [];
+
+ if (b[1] === 'class' && window.ActiveXObject) b[1] = 'className';
+ founds = chkFunc(nodes, function(elm) {
+ a = elm.getAttribute(b[1]) || "";
+ switch (b[2]) {
+ case "=":
+ return a !== b[4];
+ case "!=":
+ return a === b[4];
+ case "~=":
+ return (" "+a+" ").indexOf(" "+b[4]+" ") === -1;
+ case "^=":
+ return a.indexOf(b[4]) !== 0;
+ case "$=":
+ return a.lastIndexOf(b[4]) !== (a.length - b[4].length);
+ case "*=":
+ return a.indexOf(b[4]) === -1;
+ case "|=":
+ return (a+"-").toLowerCase().indexOf((b[4]+"-").toLowerCase()) !== 0;
+ default:
+ return !a;
+ }
+ });
+ } else if (mats[6] /* PSEUDO */) {
+ var ps = mats[6].substr(1);
+ var pat = new RegExp("(\\w+(?:-[\\w-]+)?)(?:\\(([^)]+)\\))?");
+ var buf = pat.exec(ps);
+ switch (buf[1]) {
+ case "first-child":
+ founds = chk(nodes, "previousSibling", "nodeType", true);
+ break;
+ case "last-child":
+ founds = chk(nodes, "nextSibling", "nodeType", true);
+ break;
+ case "only-child":
+ founds = chk(nodes, "previousSibling", "nodeType", true);
+ founds = founds.concat(chk(nodes, "nextSibling", "nodeType", true));
+ founds = Sabel.Elements.unique(founds);
+ break;
+ case "nth-child":
+ var seq = getSeq(buf[2]);
+ founds = chkNth(nodes, seq[1], seq[0], "firstChild", "nextSibling", false, true);
+
+ return founds;
+ case "nth-last-child":
+ var seq = getSeq(buf[2]);
+ founds = chkNth(nodes, seq[1], seq[0], "lastChild", "previousSibling", false, true);
+
+ return founds;
+ case "first-of-type":
+ founds = chk(nodes, "previousSibling", "nodeName", true);
+ break;
+ case "last-of-type":
+ founds = chk(nodes, "nextSibling", "nodeName", true);
+ break;
+ case "only-of-type":
+ founds = chk(nodes, "previousSibling", "nodeName", true);
+ founds = founds.concat(chk(nodes, "nextSibling", "nodeName", true));
+ founds = Sabel.Elements.unique(founds);
+ break;
+ case "nth-of-type":
+ var seq = getSeq(buf[2]);
+ founds = chkNth(nodes, seq[1], seq[0], "firstChild", "nextSibling", true, true);
+ break;
+ case "nth-last-of-type":
+ var seq = getSeq(buf[2]);
+ founds = chkNth(nodes, seq[1], seq[0], "lastChild", "previousSibling", true, true);
+ break;
+ case "empty":
+ while ((elm = nodes[i++])) {
+ if (elm.childNodes.length !== 0)
+ founds.push(elm);
+ }
+ break;
+ case "contains":
+ var val = buf[2].replace(/(^['"]|['"]$)/g, "");
+ while ((elm = nodes[i++])) {
+ if (Sabel.Element.getTextContent(elm).indexOf(val) === -1) {
+ founds.push(elm);
+ }
+ }
+ break;
+ }
+ }
+ break;
+ }
+ }
+ return new Sabel.Elements(founds);
+ }
+ },
+
+ concat: function(array, iterable) {
+ for (var i = 0, data; (data = iterable[i]); i++) {
+ if (data._added !== true) {
+ data._added = true;
+ array[array.length] = data;
+ }
+ }
+ return array;
+ },
+
+ clear: function(nodes, prop) {
+ for (var i = 0, len = nodes.length; i < len; ++i) {
+ nodes[i][prop] = null;
+ }
+ return nodes;
+ }
+};
+
+Sabel.Element = function(element) {
+ if (typeof element === "string") {
+ element = document.createElement(element);
+ } else if (typeof element !== "object") {
+ // @todo throw exception ??
+ return null;
+ } else if (element._extended === true) {
+ return element;
+ }
+
+ return Sabel.Object.extend(element, Sabel.Element, true);
+};
+
+Sabel.Element._extended = true;
+
+Sabel.Element.get = function(element, id) {
+ var elm = Sabel.get(id), parent = elm.parentNode;
+
+ do {
+ if (parent == element) return elm;
+ } while ((parent = parent.parentNode));
+
+ return null;
+};
+
+Sabel.Element.find = function(element, selector) {
+ return Sabel.Dom.getElementsBySelector(selector, Sabel.get(element, false));
+};
+
+Sabel.Element._ieAttrMap = {
+ "class": "className",
+ "checked": "defaultChecked",
+ "for": "htmlFor",
+ "colspan": "colSpan",
+ "bgcolor": "bgColor",
+ "tabindex": "tabIndex",
+ "accesskey": "accessKey"
+};
+
+Sabel.Element.setAttr = function(element, name, value) {
+ if (Sabel.UserAgent.isIE && Sabel.UserAgent.version < 8) {
+ if (name === "style") {
+ element.style.cssText = value;
+ return element;
+ }
+
+ name = Sabel.Element._ieAttrMap[name] || name;
+ }
+ element.setAttribute(name, value);
+
+ return element;
+};
+
+Sabel.Element.append = function(element, child, text, attributes) {
+ element = Sabel.get(element);
+
+ if (typeof child === "string") {
+ child = document.createElement(child);
+ if (text) child.innerHTML = text;
+ if (attributes) {
+ for (var name in attributes)
+ Sabel.Element.setAttr(child, name, attributes[name]);
+ }
+ }
+
+ element.appendChild(new Sabel.Element(child));
+ return child;
+};
+
+Sabel.Element.getDefaultDisplay = function(element) {
+ var el = document.createElement(Sabel.get(element).nodeName);
+ document.body.appendChild(el);
+ var display = Sabel.Element.getStyle(el, "display");
+ Sabel.Element.remove(el);
+ return display;
+};
+
+Sabel.Element.show = function(element, value) {
+ Sabel.get(element, false).style.display = value || Sabel.Element.getDefaultDisplay(element);
+};
+
+Sabel.Element.hide = function(element) {
+ Sabel.get(element, false).style.display = "none";
+};
+
+Sabel.Element.hasClass = function(element, className) {
+ element = Sabel.get(element, false);
+
+ var pattern = new RegExp("(?:^|\\s+)" + className + "(?:\\s+|$)");
+ return pattern.test(element.className);
+};
+
+Sabel.Element.addClass = function(element, className) {
+ if (Sabel.Element.hasClass(element, className)) return element;
+
+ element = Sabel.get(element, false);
+ element.className = element.className + " " + className;
+ return element;
+}
+
+Sabel.Element.removeClass = function(element, className) {
+ element = Sabel.get(element, false);
+ element.className = element.className.replace(
+ new RegExp("(?:^|\\s+)" + className + "(?:\\s+|$)"), " "
+ );
+
+ return element;
+};
+
+Sabel.Element.replaceClass = function(element, oldClassName, newClassName) {
+ element = Sabel.get(element, false);
+ element.className = element.className.replace(
+ new RegExp("(^|\\s+)" + oldClassName + "(\\s+|$)"), "$1"+newClassName+"$2"
+ );
+
+ return element;
+};
+
+Sabel.Element.hasAttribute = function(element, attribute) {
+ element = Sabel.get(element, false);
+ if (element.hasAttribute) return element.hasAttribute(attribute);
+ var node = element.getAttributeNode(attribute);
+ return node && node.specified;
+};
+
+if (Sabel.UserAgent.isIE) {
+ Sabel.Element.getStyle = function(element, property) {
+ element = Sabel.get(element, false);
+ property = (property === "float") ? "styleFloat" : new Sabel.String(property).camelize();
+
+ var style = element.currentStyle;
+ return style[property];
+ };
+} else {
+ Sabel.Element.getStyle = function(element, property) {
+ element = Sabel.get(element, false);
+ // Operaでelementがwindowだった時の対策
+ // CSSでdisplay: noneが指定されている時の対策
+ if (element === null || element.nodeType === undefined) return null;
+ property = (property === "float") ? "cssFloat" : new Sabel.String(property).camelize();
+
+ var css = document.defaultView.getComputedStyle(element, "")
+ return css[property];
+ };
+}
+
+Sabel.Element.setStyle = function(element, styles) {
+ element = Sabel.get(element, false);
+
+ if (arguments.length === 3) {
+ Sabel.Element._setStyle(element, styles, arguments[2]);
+ } else if (typeof styles === "string") {
+ element.style.cssText += ";" + styles;
+ } else {
+ for (var prop in styles) {
+ Sabel.Element._setStyle(element, prop, styles[prop]);
+ }
+ }
+
+ return element;
+};
+
+Sabel.Element._setStyle = function(element, property, value) {
+ var method = "set" + new Sabel.String(property).ucfirst();
+ if (typeof Sabel.Element[method] !== "undefined") {
+ Sabel.Element[method](element, value);
+ } else {
+ element.style[property] = value;
+ }
+};
+
+Sabel.Element.deleteStyle = function(element, styles) {
+ element = Sabel.get(element, false);
+
+ if (typeof styles === "string") {
+ element.style[styles] = "";
+ } else {
+ for (var i = 0, key; key = styles[i]; i++) {
+ element.style[key] = "";
+ }
+ }
+
+ return element;
+};
+
+Sabel.Element.insertAfter = function(element, newChild, refChild) {
+ element = Sabel.get(element, false);
+ if (element.lastChild == refChild) {
+ element.appendChild(newChild);
+ } else {
+ refChild = Sabel.get(refChild);
+ element.insertBefore(newChild, refChild.getNextSibling());
+ }
+ return element;
+};
+
+Sabel.Element.insertPreviousSibling = function(element, sibling) {
+ element = Sabel.get(element);
+ element.parentNode.insertBefore(sibling, element);
+ return element;
+}
+
+Sabel.Element.insertNextSibling = function(element, sibling) {
+ element = Sabel.get(element);
+ element.getParentNode().insertAfter(sibling, element);
+ return element;
+}
+
+Sabel.Element.setHeight = function(element, value) {
+ element = Sabel.get(element, false);
+ if (value !== "" && typeof value === "number") value = value + "px";
+ element.style.height = value;
+ return element;
+};
+
+Sabel.Element.setWidth = function(element, value) {
+ element = Sabel.get(element, false);
+ if (value !== "" && typeof value === "number") value = value + "px";
+ element.style.width = value;
+ return element;
+};
+
+Sabel.Element.setOpacity = function(element, value) {
+ element = Sabel.get(element, false);
+
+ if (Sabel.UserAgent.isIE) {
+ element.style.filter = "alpha(opacity=" + value * 100 + ")";
+ } else {
+ element.style.opacity = value;
+ }
+};
+
+Sabel.Element.getBackGroundColor = function(el) {
+ var color;
+ el = Sabel.get(el);
+ do {
+ color = Sabel.Element.getStyle(el, "backgroundColor");
+ if (color !== "" && color !== "transparent" &&
+ (color.indexOf("rgba") === -1 || color.slice(-2) !== "0)")) break;
+ } while ((el = el.parentNode));
+ return (color !== "transparent") ? color : "#ffffff";
+};
+
+Sabel.Element.getCumulativeTop = function(element) {
+ return Sabel.Element.getRegion(element).top;
+};
+
+Sabel.Element.getCumulativeLeft = function(element) {
+ return Sabel.Element.getRegion(element).left;
+};
+
+Sabel.Element.getCumulativePositions = function(element) {
+ var rect = Sabel.Element.getRegion(element);
+ return {top: rect.top, left: rect.left};
+};
+
+Sabel.Element.getOffsetTop = function(element) {
+ element = Sabel.get(element, false);
+
+ var position = element.offsetTop;
+
+ if (Sabel.UserAgent.isOpera) {
+ var parent = element.offsetParent;
+ if (parent.nodeName !== "BODY") {
+ position -= parseInt(Sabel.Element.getStyle(parent, "borderTopWidth"));
+ }
+ }
+
+ return position;
+};
+
+Sabel.Element.getOffsetLeft = function(element) {
+ element = Sabel.get(element, false);
+
+ var position = element.offsetLeft;
+
+ if (Sabel.UserAgent.isOpera) {
+ var parent = element.offsetParent;
+ if (parent.nodeName !== "BODY") {
+ position -= parseInt(Sabel.Element.getStyle(parent, "borderLeftWidth"));
+ }
+ }
+
+ return position;
+};
+
+Sabel.Element.getOffsetPositions = function(element) {
+ return {
+ left: this.getOffsetLeft(element),
+ top: this.getOffsetTop(element)
+ };
+};
+
+Sabel.Element.getDimensions = function(element, ignoreBorder) {
+ element = Sabel.get(element, false);
+ if (element.nodeType !== 1) return {};
+
+ var style = element.style;
+
+ if (Sabel.Element.getStyle(element, "display") !== "none") {
+ var dimensions = {
+ width: element.offsetWidth,
+ height: element.offsetHeight
+ };
+ } else {
+ var oldV = style.visibility;
+ var oldP = style.positions;
+ var oldD = "none";
+
+ style.visibility = "hidden";
+ style.positions = "absolute";
+ style.display = "block";
+
+ var dimensions = {
+ width: element.offsetWidth,
+ height: element.offsetHeight
+ };
+
+ style.visibility = oldV;
+ style.positions = oldP;
+ style.display = oldD;
+ }
+
+ if (ignoreBorder === true) {
+ // parseFloat Fx3 fix
+ // || 0 IE7 fix
+ dimensions.width -= Math.round(parseFloat(Sabel.Element.getStyle(element, "borderLeftWidth")))||0
+ + Math.round(parseFloat(Sabel.Element.getStyle(element, "borderRightWidth")))||0;
+ dimensions.height -= Math.round(parseFloat(Sabel.Element.getStyle(element, "borderTopWidth")))||0
+ + Math.round(parseFloat(Sabel.Element.getStyle(element, "borderBottomWidth")))||0;
+ }
+
+ return dimensions;
+};
+
+Sabel.Element.getWidth = function(element, ignoreBorder) {
+ return Sabel.Element.getDimensions(element, ignoreBorder).width;
+};
+
+Sabel.Element.getHeight = function(element, ignoreBorder) {
+ return Sabel.Element.getDimensions(element, ignoreBorder).height;
+};
+
+Sabel.Element.getRegion = function(element) {
+ element = Sabel.get(element);
+ if (element.parentNode === null || element.offsetParent === null) {
+ return false;
+ }
+
+ if (element.getBoundingClientRect) {
+ var rect = element.getBoundingClientRect();
+
+ var st = Sabel.Window.getScrollTop() - document.documentElement.clientTop;
+ var sl = Sabel.Window.getScrollLeft() - document.documentElement.clientLeft;
+
+ return {
+ top: Math.round(rect.top + st), right: Math.round(rect.right + sl),
+ bottom: Math.round(rect.bottom + st), left: Math.round(rect.left + sl),
+ toString: function() {
+ return new Sabel.String("{top: #{top}, right: #{right}, bottom: #{bottom}, left: #{left}}").format(this);
+ }
+ };
+ } else {
+ var wh = Sabel.Element.getDimensions(element);
+ var rect = {top: element.offsetTop, left: element.offsetLeft};
+
+ var add = function(t, l) {
+ rect.top += parseInt(t) || 0;
+ rect.left += parseInt(l) || 0;
+ };
+
+ while ((element = element.offsetParent)) {
+ add(element.offsetTop, element.offsetLeft);
+
+ if (Sabel.UserAgent.isOpera === false) {
+ var borderTop = Sabel.Element.getStyle(element, "borderTopWidth");
+ var borderLeft = Sabel.Element.getStyle(element, "borderLeftWidth");
+ add(borderTop, borderLeft);
+
+ if (Sabel.UserAgent.isMozilla) {
+ var of = Sabel.Element.getStyle(element, "overflow");
+ if (!Sabel.Array.include(["visible", "inherit"], of)) {
+ add(borderTop, borderLeft);
+ }
+ }
+
+ if (Sabel.Array.include(["BODY", "HTML"], element.tagName)) {
+ if (document.compatMode === "CSS1Compat") {
+ var html = document.getElementsByTagName('html')[0];
+ add(
+ Sabel.Element.getStyle(html, "marginTop"),
+ Sabel.Element.getStyle(html, "marginLeft")
+ );
+
+ if (Sabel.UserAgent.isIE) {
+ add(
+ Sabel.Element.getStyle(element, "marginTop"),
+ Sabel.Element.getStyle(element, "marginLeft")
+ );
+ add(
+ Sabel.Element.getStyle(html, "borderTopWidth"),
+ Sabel.Element.getStyle(html, "borderLeftWidth")
+ );
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ return {
+ top: rect.top, right: rect.left + wh.width,
+ bottom: rect.top + wh.height, left: rect.left,
+ toString: function() {
+ return new Sabel.String("{top: #{top}, right: #{right}, bottom: #{bottom}, left: #{left}}").format(this);
+ }
+ };
+ }
+};
+
+Sabel.Element.remove = function(element) {
+ element = Sabel.get(element, false);
+ element.parentNode.removeChild(element);
+};
+
+Sabel.Element.update = function(element, contents) {
+ element = Sabel.get(element, false);
+
+ var newEl = document.createElement(element.nodeName);
+ newEl.id = element.id;
+ newEl.className = element.className;
+ newEl.innerHTML = contents;
+
+ element.parentNode.replaceChild(newEl, element);
+
+ return Sabel.get(newEl);
+};
+
+Sabel.Element.getXPath = function(element) {
+ var buf = new Array(), idx, tagName = element.tagName;
+
+ do {
+ if (element.getAttribute("id")) {
+ buf.unshift('id("' + element.getAttribute("id") + '")');
+ return buf.join("/");
+ } else {
+ if (document.getElementsByTagName(element.nodeName).length === 1) {
+ buf.unshift("/" + element.nodeName.toLowerCase());
+ break;
+ } else {
+ idx = Sabel.Element.getPreviousSiblings(element, element.nodeName).length + 1;
+ buf.unshift(element.nodeName.toLowerCase() + "[" + idx + "]");
+ }
+ }
+ } while ((element = element.parentNode) && element.nodeType === 1);
+ return "/" + buf.join("/");
+};
+
+if (Sabel.UserAgent.isIE) {
+ Sabel.Element.getTextContent = function(element) {
+ return element.innerText;
+ };
+} else {
+ Sabel.Element.getTextContent = function(element) {
+ return element.textContent;
+ };
+}
+
+Sabel.Element.observe = function(element, eventName, handler, useCapture, scope) {
+ element = Sabel.get(element, false);
+ if (element._events === undefined) element._events = {};
+ if (element._events[eventName] === undefined) element._events[eventName] = new Array();
+
+ var evt = new Sabel.Event(element, eventName, handler, useCapture, scope);
+ element._events[eventName].push(evt);
+
+ return evt;
+};
+
+Sabel.Element.stopObserve = function(element, eventName, handler) {
+ element = Sabel.get(element, false);
+ var events = (element._events) ? element._events[eventName] : "";
+ if (events.constructor === Array) {
+ if (typeof handler === "function") {
+ Sabel.Array.each(events, function(e) { if (e.getHandler() === handler) e.stop(); });
+ } else {
+ Sabel.Array.each(events, function(e) { e.stop(); });
+ }
+ }
+
+ return element;
+};
+
+Sabel.Element.analyze = function(element) {
+ element = Sabel.get(element, false)
+ var as = element.attributes, buf = new Array(), attr, i = 0;
+
+ buf.push("<" + element.tagName.toLowerCase());
+ if (Sabel.UserAgent.isIE) {
+ var def = document.createElement(element.nodeName);
+
+ var defAttr;
+ while ((attr = as[i++])) {
+ if (typeof attr.nodeValue !== "string") continue;
+
+ defAttr = def.getAttributeNode(attr.nodeName);
+ if (defAttr != null && attr.nodeValue === defAttr.nodeValue) continue;
+
+ buf[buf.length] = attr.nodeName + '="' + attr.nodeValue + '"';
+ }
+ } else {
+ while ((attr = as[i++])) {
+ buf[buf.length] = attr.nodeName + '="' + attr.nodeValue + '"';
+ }
+ }
+
+ return buf.join(" ") + ">";
+};
+
+Sabel.Element.getParentNode = function(element, tagName) {
+ tagName = (tagName || "").toUpperCase();
+ element = Sabel.get(element, false);
+ while ((element = element.parentNode)) {
+ if (tagName === "" || tagName === element.tagName)
+ return new Sabel.Element(element);
+ }
+ return null;
+};
+
+Sabel.Element.getChildElements = function(element, tagName) {
+ tagName = (tagName || "").toUpperCase();
+ var buf = new Array();
+ element = Sabel.get(element, false);
+ Sabel.Array.each(element.childNodes, function(elm) {
+ if (elm.nodeType === 1) {
+ if (tagName === "" || tagName === elm.tagName) buf[buf.length] = elm;
+ }
+ });
+ return buf;
+};
+
+Sabel.Element.getFirstChild = function(element, tagName) {
+ element = Sabel.Element.getChildElements(element, tagName)[0];
+ return (element) ? new Sabel.Element(element) : null;
+};
+
+Sabel.Element.getLastChild = function(element, tagName) {
+ var elms = Sabel.Element.getChildElements(element, tagName);
+ return new Sabel.Element(elms[elms.length - 1]);
+};
+
+Sabel.Element.getNextSibling = function(element) {
+ while ((element = element.nextSibling)) {
+ if (element.nodeType === 1) return new Sabel.Element(element);
+ }
+ return null;
+};
+
+Sabel.Element.getPreviousSibling = function(element) {
+ while ((element = element.previousSibling)) {
+ if (element.nodeType === 1) return new Sabel.Element(element);
+ }
+ return null;
+};
+
+Sabel.Element.getPreviousSiblings = function(element, nodeName) {
+ nodeName = (nodeName || "").toUpperCase();
+ var buf = new Array();
+ while ((element = element.previousSibling)) {
+ if (element.nodeType === 1) {
+ if (nodeName === "" || nodeName === element.nodeName)
+ buf[buf.length] = element;
+ }
+ }
+ return buf;
+};
+
+Sabel.Element.getNextSiblings = function(element, nodeName) {
+ nodeName = (nodeName || "").toUpperCase();
+ var buf = new Array();
+ while ((element = element.nextSibling)) {
+ if (element.nodeType === 1) {
+ if (nodeName === "" || nodeName === element.nodeName)
+ buf[buf.length] = element;
+ }
+ }
+ return buf;
+};
+
+Sabel.Element.getNodeIndex = function(element, reverse, ofType) {
+ var ret;
+ if (ofType === true) {
+ ret = Sabel.Element._getOfTypeNodeIndex(element, reverse);
+ } else {
+ ret = Sabel.Element._getNodeIndex(element,reverse);
+ };
+ return ret;
+};
+
+Sabel.Element._getNodeIndex = function(element, reverse) {
+ var parentNode = element.parentNode;
+
+ var childNodes = parentNode.childNodes;
+ var propName = (reverse === true) ? "__cachedLastIdx"
+ : "__cachedIdx";
+ if (parentNode.__cachedLength === childNodes.length) {
+ if (element[propName]) {
+ return element[propName];
+ }
+ }
+ parentNode.__cachedLength = childNodes.length;
+
+ if (reverse === true) {
+ childNodes = new Sabel.Array(childNodes).reverse();
+ }
+
+ for (var i = 0, idx = 1, child; child = childNodes[i]; i++) {
+ if (child.nodeType == 1) child[propName] = idx++;
+ }
+
+ return element[propName];
+};
+
+Sabel.Element._getOfTypeNodeIndex = function(element, reverse) {
+ var parentNode = element.parentNode;
+ var childNodes = parentNode.childNodes;
+ var propName = (reverse === true) ? "__cachedLastOfTypeIdx"
+ : "__cachedOfTypeIdx";
+
+ if (parentNode.__cachedLength === childNodes.length) {
+ if (element[propName]) {
+ return element[propName];
+ }
+ }
+
+ if (reverse === true) {
+ childNodes = new Sabel.Array(childNodes).reverse();
+ }
+ parentNode.__cachedLength = childNodes.length;
+
+ for (var i = 0, idx = 1, child; child = childNodes[i]; i++) {
+ if (child.tagName === element.tagName && child.nodeType === 1) {
+ child[propName] = idx++;
+ }
+ }
+
+ return element[propName];
+};
+
+Sabel.Element.isContain = function(element, other) {
+ if (element === document) element = document.body;
+
+ if (element.contains) {
+ // IE, Opera, Safari
+ return element.contains(other);
+ } else {
+ // Firefox
+ return !!(element.compareDocumentPosition(other) & element.DOCUMENT_POSITION_CONTAINED_BY);
+ }
+};
+
+Sabel.CSS = {};
+
+Sabel.CSS.rgbToHex = function(rgb) {
+ var hex = "#";
+ Sabel.Array.each(rgb, function(num) {
+ hex += new Sabel.String("%02x").sprintf(num.toString(16));
+ });
+ return hex;
+};
+
+Sabel.CSS.getRGB = function(color) {
+ if (color.indexOf("#") === 0) {
+ if (color.length === 4) {
+ return [
+ parseInt(color.charAt(1) + color.charAt(1), 16),
+ parseInt(color.charAt(2) + color.charAt(2), 16),
+ parseInt(color.charAt(3) + color.charAt(3), 16)
+ ];
+ } else if (color.length === 7) {
+ return [
+ parseInt(color.substr(1,2), 16),
+ parseInt(color.substr(3,2), 16),
+ parseInt(color.substr(5,2), 16)
+ ];
+ }
+ } else if (color.search("\(([0-9, ]+)\)") !== -1) {
+ return RegExp.$1.replace(/ /g, "").split(",");
+ } else if (typeof color === "Array" && color.length === 3) {
+ return color;
+ } else {
+ if (color in Sabel.CSS.colorNames) return Sabel.CSS.colorNames[color];
+ }
+ return null;
+}
+
+Sabel.CSS.colorNames = {
+ black: [ 0, 0, 0],
+ blue: [ 0, 0, 255],
+ green: [ 0, 128, 0],
+ lime: [ 0, 255, 0],
+ cyan: [ 0, 255, 255],
+ purple: [128, 0, 128],
+ gray: [128, 128, 128],
+ silver: [192, 192, 192],
+ red: [255, 0, 0],
+ magenta: [255, 0, 255],
+ orange: [255, 165, 0],
+ pink: [255, 192, 203],
+ yellow: [255, 255, 0],
+ white: [255, 255, 255]
+};
+
+Sabel.Object.extend(Sabel.Element, Sabel.Object.Methods);
+
+Sabel.Elements = function(elements) {
+ if (typeof elements === "undefined") {
+ elements = new Sabel.Array();
+ // @todo 普通のSabel.Elementが引っかかる
+ } else if (elements._extended === true) {
+ return elements;
+ } else if (elements.constructor !== Array) {
+ return null;
+ } else {
+ elements = new Sabel.Array(elements);
+ }
+
+ return Sabel.Object.extend(elements, Sabel.Elements, true, true);
+};
+
+Sabel.Elements._extended = true;
+
+Sabel.Elements.add = function(elements, element) {
+ elements[elements.length] = element;
+};
+
+Sabel.Elements.item = function(elements, pos) {
+ var elm = elements[pos];
+
+ return (elm) ? new Sabel.Element(elm) : null;
+};
+
+Sabel.Elements.observe = function(elements, eventName, handler, useCapture, scope) {
+ Sabel.Array.each(elements, function(elm) {
+ Sabel.Element.observe(elm, eventName, handler, useCapture, scope);
+ });
+};
+
+Sabel.Elements.stopObserve = function(elements, eventName, handler) {
+ Sabel.Array.each(elements, function(elm) {
+ Sabel.Element.stopObserve(elm, eventName, handler);
+ });
+};
+
+Sabel.Elements.each = function(elements, callback) {
+ var i = 0, el;
+ while((el = elements.item(i))) callback(el, i++);
+ return elements;
+};
+
+Sabel.Elements.unique = function(elements) {
+ var finds = Sabel.Array.inject(elements, function(elm) {
+ if (elm._searched === true) return false;
+ elm._searched = true;
+ return true;
+ });
+
+ Sabel.Array.each(finds, function(elm) { elm._searched = false; });
+ return finds;
+};
+
+Sabel.Object.extend(Sabel.Elements, Sabel.Object.Methods);
+
+Sabel.Iterator = function(iterable) {
+ this.items = Sabel.Array(iterable);
+ this.index = -1;
+};
+
+Sabel.Iterator.prototype = {
+ hasPrev: function() {
+ return this.index > 0;
+ },
+
+ hasNext: function() {
+ return this.index < this.items.length-1;
+ },
+
+ prev: function() {
+ return this.index > -1 ? this.items[--this.index] || null : null;
+ },
+
+ next: function() {
+ return this.hasNext() ? this.items[++this.index] || null : null;
+ }
+};
+
+
+if (typeof window.XMLHttpRequest === "undefined") {
+ window.XMLHttpRequest = function() {
+ var http;
+ var objects = ["Msxml2.XMLHTTP.6.0", "Msxml2.XMLHTTP.3.0", "Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
+ for (var i = 0, obj; obj = objects[i]; i++) {
+ try {
+ http = new ActiveXObject(obj);
+ window.XMLHttpRequest = function() {
+ return new ActiveXObject(obj);
+ }
+ break;
+ } catch (e) {}
+ }
+ return http;
+ }
+};
+
+Sabel.Ajax = function() {
+ this.init.apply(this, arguments);
+};
+
+Sabel.Ajax.prototype = {
+ init: function() {
+ this.xmlhttp = new XMLHttpRequest();
+ this.completed = false;
+ },
+
+ request: function(url, options) {
+ var xmlhttp = this.xmlhttp;
+ options = this.setOptions(options);
+
+ this.completed = false;
+ this._abort();
+
+ if (options.method === "get") {
+ url += ((url.indexOf("?") !== -1) ? "&" : "?") + options.params;
+ }
+
+ xmlhttp.open(options.method, url, options.async);
+ xmlhttp.onreadystatechange = Sabel.Function.bind(this.onStateChange, this);
+
+ this.setRequestHeaders();
+ if (options.timeout) {
+ if (typeof xmlhttp.timeout !== "undefined") {
+ xmlhttp.timeout = options.timeout;
+ xmlhttp.ontimeout = Sabel.Function.bind(this.abort, this);
+ } else {
+ this.timer = setTimeout(Sabel.Function.bind(this.abort, this), options.timeout);
+ }
+ }
+ xmlhttp.send((options.method === "post") ? options.params : "");
+
+ if (options.async === false) {
+ this.onStateChange();
+ }
+ },
+
+ abort: function() {
+ if (this._abort()) this.options.onTimeout.apply(this.options.scope);
+ },
+
+ _abort: function() {
+ var xmlhttp = this.xmlhttp;
+ if (xmlhttp.readyState !== 4) {
+ xmlhttp.onreadystatechange = Sabel.emptyFunc;
+ xmlhttp.abort();
+
+ return true;
+ }
+ return false;
+ },
+
+ updater: function(element, url, options) {
+ options = options || {};
+
+ var onComplete = options.onComplete || function() {};
+ options.onComplete = function(response) {
+ Sabel.get(element).innerHTML = response.responseText;
+ onComplete(response);
+ }
+
+ this.request(url, options);
+ },
+
+ setOptions: function(options) {
+ if (options === undefined) options = {};
+
+ var defaultOptions = {
+ method: "post",
+ params: "",
+ contentType: "application/x-www-form-urlencoded",
+ charset: "UTF-8",
+ onComplete: function(){},
+ onSuccess: function(){},
+ onFailure: function(){},
+ onTimeout: function(){},
+ scope: null,
+ async: true,
+ autoEval: false
+ };
+ Sabel.Object.extend(options, defaultOptions);
+ options.method = options.method.toLowerCase();
+
+ if (options.params instanceof Object) {
+ options.params = new Sabel.QueryObject(options.params).serialize();
+ }
+
+ return (this.options = options);
+ },
+
+ setRequestHeaders: function() {
+ var headers = {
+ "X-Requested-With": "XMLHttpRequest",
+ "Accept": "text/javascript, text/html, application/xml, text/xml, */*"
+ };
+ var xmlhttp = this.xmlhttp;
+ var options = this.options;
+
+ if (options.method === "post") {
+ headers["Content-Type"] = options.contentType + "; charset=" + options.charset;
+ }
+
+ if (typeof options.headers === "object") {
+ headers = Sabel.Object.extend(options.headers, headers);
+ }
+
+ for (var key in headers) {
+ xmlhttp.setRequestHeader(key, headers[key]);
+ }
+ },
+
+ onStateChange: function() {
+ if (this.completed === true) return;
+
+ if (this.xmlhttp.readyState === 4) {
+ this.completed = true;
+ clearTimeout(this.timer);
+
+ var options = this.options;
+ var response = this.getResponses();
+ options["on" + (this.isSuccess() ? "Success" : "Failure")].call(options.scope, response);
+ options.onComplete.call(options.scope, response);
+
+ this.xmlhttp.onreadystatechange = Sabel.emptyFunc;
+ }
+ },
+
+ getResponses: function() {
+ var xmlhttp = this.xmlhttp;
+ var response = new Object();
+ response.responseXML = xmlhttp.responseXML;
+ response.responseText = this.responseFilter(xmlhttp.responseText);
+ if (this.options.autoEval === true) {
+ response.responseJson = eval("eval(" + response.responseText+ ")");
+ }
+ response.status = xmlhttp.status;
+ response.statusText = xmlhttp.statusText;
+ return response;
+ },
+
+ isSuccess: function() {
+ var status = this.xmlhttp.status;
+ return (status && (status >= 200 && status < 300));
+ },
+
+ responseFilter: function(text) {
+ if (Sabel.UserAgent.isKHTML) {
+ var esc = escape(text);
+ if (esc.indexOf("%u") < 0 && esc.indexOf("%") > -1) {
+ text = decodeURIComponent(esc);
+ }
+ }
+ return text;
+ }
+};
+
+Sabel.History = function() {
+ this.init.apply(this, arguments);
+};
+
+Sabel.History.prototype = {
+ currentHash: "",
+ callback: null,
+ timer: null,
+
+ init: function(callback) {
+ this.callback = callback || function() {}
+ var hash = this._getHash(document);
+
+ if (hash !== "") this.callback(hash);
+
+ if (typeof window.onhashchange === "undefined") {
+ this.timer = setInterval(Sabel.Function.bind(this._check, this), 300);
+ } else {
+ new Sabel.Event(window, "hashchange", this._check, false, this);
+ }
+ },
+
+ load: function(hash) {
+ this._setHash(hash.replace(/^#/, ""), true);
+ },
+
+ _check: function() {
+ var hash = this._getHash(document);
+
+ if (hash !== this.currentHash) {
+ this._setHash(hash);
+ }
+ },
+
+ _setHash: function(hash, isUpdate) {
+ if (isUpdate === true) location.hash = "#" + hash;
+ this.currentHash = hash;
+ if (hash !== "") this.callback(hash);
+ },
+
+ _getHash: function(target) {
+ return new Sabel.Uri(target.location.href).hash.replace(/^[^#]*#/, "");
+ }
+};
+
+if (Sabel.UserAgent.isIE && Sabel.UserAgent.version < 8) {
+ Sabel.History.prototype.init = function(callback) {
+ this.callback = callback || function() {}
+ var hash = this._getHash(document);
+
+ this.iframe = document.createElement(' ');
+ html.push('Close ');
+
+ this.rootElement.getFirstChild().innerHTML = html.join("\n");
+ this.rootElement.show();
+
+ var find = Sabel.Dom.getElementsByClassName;
+
+ var close = find("sbl_cal_close", this.rootElement, true).item(0);
+ Sabel.Element.observe(close, "click", this.hide, false, this);
+
+ var l = find("sbl_page_l", this.rootElement, true).item(0);
+ Sabel.Element.observe(l, "click", this.prevMonth, false, this);
+
+ var r = find("sbl_page_r", this.rootElement, true).item(0);
+ Sabel.Element.observe(r, "click", this.nextMonth, false, this);
+
+ var el = find("sbl_cal_days", this.rootElement, true).item(0);
+ Sabel.Element.observe(el, "mouseover", Sabel.Function.bind(this.mouseOver, this));
+ Sabel.Element.observe(el, "mouseout", Sabel.Function.bind(this.mouseOut, this));
+ Sabel.Element.observe(el, "mousedown", Sabel.Function.bindWithEvent(this.mouseDown, this));
+
+ if (this.options.useSelectBox === true) {
+ this.selectBox = Sabel.find(".sbl_cal_header select");
+ this.selectBox.observe("change", this._changeDate, false, this);
+ }
+
+ if (day !== undefined) {
+ var selected = Sabel.Dom.getElementsByClassName("selected", this.rootElement, true);
+ if (selected.length > 0)
+ Sabel.Element.removeClass(selected[0], "selected");
+
+ Sabel.Element.addClass(find("day"+day, this.rootElement)[0], "selected");
+ }
+
+ this.show();
+ },
+
+ show: function()
+ {
+ this.rootElement.show();
+ },
+
+ hide: function()
+ {
+ this.rootElement.hide();
+ }
+}
+
+
+Sabel.Widget.Dropdown = new Sabel.Class({
+ hoverElements: null,
+ lastElement: null,
+ event: null,
+ moveTimer: null,
+ leaveTimer: null,
+
+ init: function() {
+ var root = Sabel.find("div.sbl_dropdown > ul.sbl_dropdown_list").item(0);
+ var elms = root.find("> li");
+
+ this.setup();
+
+ elms.each(function(el) {
+ el.addClass("root");
+ });
+
+ root.find("li li").each(function(el) {
+ if (el.getFirstChild("UL")) el.addClass("icon");
+ });
+
+ var self = this;
+ root.observe("mouseenter", function() {
+ if (self.leaveTimer) clearTimeout(self.leaveTimer);
+ self.event = new Sabel.Event(document, "mousemove", function(e) {
+ self.targetElm = Sabel.Event.getTarget(e);
+ if (self.moveTimer) clearTimeout(self.moveTimer);
+ self.moveTimer = setTimeout(function() {
+ self.moveHandler();
+ }, 10);
+ }, false, self);
+ });
+ root.observe("mouseleave", this.leaveHandler, false, this);
+ root.observe("mousedown", this.clickHandler, false, this);
+ },
+
+ moveHandler: function() {
+ var el = this.targetElm, child;
+ if (el.tagName == "SPAN") el = el.parentNode;
+ if (el.tagName !== "LI" || this.lastElement === el) return;
+ this.lastElement = el = new Sabel.Element(el);
+
+ this.hoverElements = Sabel.Array.inject(this.hoverElements, function(elm) {
+ if (Sabel.Element.isContain(elm, el) === false) {
+ elm.style.display = "none";
+ return false;
+ }
+ return true;
+ });
+
+ Sabel.find(".hover").each(function(elm) {
+ if (elm.isContain(el) === false)
+ elm.removeClass("hover");
+ });
+ el.addClass("hover");
+
+ if ((child = el.getFirstChild("UL")) === null) return;
+
+ if (el.hasClass("root")) {
+ child.setStyle({
+ display: "block",
+ top: (el.getOffsetTop() + el.getHeight(true)) + "px",
+ left: el.getOffsetLeft() + "px"
+ });
+ } else {
+ var borderWidth = parseInt(el.getParentNode().getStyle("borderTopWidth"));
+ child.setStyle({
+ display: "block",
+ top: el.getOffsetTop() - borderWidth + "px",
+ left: el.getWidth() + "px"
+ });
+ }
+
+ this.hoverElements.unshift(child);
+ },
+
+ leaveHandler: function(e) {
+ var self = this;
+ this.leaveTimer = setTimeout(function() {
+ Sabel.Array.each(self.hoverElements, function(el) {
+ el.hide();
+ });
+ Sabel.find(".hover").each(function(el) {
+ el.removeClass("hover");
+ });
+ self.setup();
+ }, 250);
+ },
+
+ clickHandler: function() {
+ var el = new Sabel.Element(this.targetElm);
+ var href = (el.getFirstChild("span") || el).getAttribute("href");;
+ if (href !== null) location.href = href;
+ },
+
+ setup: function() {
+ this.hoverElements = new Sabel.Array();
+ this.lastElement = null;
+ this.moveTimer = null;
+ this.leaveTimer = null;
+ if (this.event) this.event.stop();
+ }
+});
diff --git a/generator/skeleton/en/public/js/helpers/AjaxPager.js b/generator/skeleton/en/public/js/helpers/AjaxPager.js
new file mode 100755
index 0000000..094ba60
--- /dev/null
+++ b/generator/skeleton/en/public/js/helpers/AjaxPager.js
@@ -0,0 +1,33 @@
+Sabel.PHP.AjaxPager = function(replaceId, pagerClass) {
+ this.replaceId = Sabel.get(replaceId, false);
+ this.pagerSelector = "." + (pagerClass || "sbl_pager") + " a";
+
+ this.ef = new Sabel.PHP.EffectUpdater(this.replaceId, "Slide", {
+ callback: Sabel.Function.bind(this.init, this)
+ });
+
+ this.init();
+ this.history = new Sabel.History(Sabel.Function.bind(this.callback, this));
+};
+
+Sabel.PHP.AjaxPager.prototype = {
+ init: function() {
+ var self = this;
+ Sabel.find(this.pagerSelector).observe("click", function(evt) {
+ try {
+ if (this.pathname.lastIndexOf(this.search) > -1) {
+ var path = "/" + this.pathname.replace(/^\//, "");
+ } else {
+ var path = "/" + this.pathname.replace(/^\//, "") + this.search;
+ }
+ self.history.load(path);
+ }catch(e) {}
+ Sabel.Event.preventDefault(evt);
+ });
+ },
+
+ callback: function(uri) {
+ this.ef.fire(uri);
+ }
+};
+
diff --git a/generator/skeleton/en/public/js/helpers/AjaxUploader.js b/generator/skeleton/en/public/js/helpers/AjaxUploader.js
new file mode 100755
index 0000000..2abe156
--- /dev/null
+++ b/generator/skeleton/en/public/js/helpers/AjaxUploader.js
@@ -0,0 +1,226 @@
+/* Sabel JS - %VERSION%
+ *
+ * @author Hamanaka Kazuhiro
+ * @author Ebine yutaka
+ * @copyright 2004-2008 Hamanaka Kazuhiro
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
+/*---------------------------------------------------------------------------*/
+
+Sabel.PHP.AjaxUploader = function() {
+ this.init.apply(this, arguments)
+};
+
+Sabel.PHP.AjaxUploader.prototype = {
+ IFRAME_NAME: "sbl_uploader_target_iframe",
+ message: "preparing transfer...",
+ errorMessage: "upload error.",
+ updateInterval: 200, // msec
+
+ uploadFrame: null,
+
+ successCallback: null,
+ errorCallback: null,
+
+ incrementValue: 0,
+ totalSize: 0,
+ currentSize: 0,
+ uploadedSize: 0,
+ currentPercent: 0.0,
+
+ interval: 700,
+ intervalIncr: 100,
+ lastTime: null,
+
+ reqCount: 0,
+ ajaxReqTime: 0,
+ totalReqTime: 0,
+
+ _bindedGetProgress: null,
+ _bindedShowProgress: null,
+
+ init: function(form, uri, progress, successCallback, errorCallback) {
+ var self = this;
+
+ this.uri = uri;
+ this.ajax = new Sabel.Ajax();
+
+ this.successCallback = successCallback || Sabel.emptyFunc;
+ this.errorCallback = errorCallback || Sabel.emptyFunc;
+
+ this._bindedGetProgress = Sabel.Function.bind(this.getProgress, this);
+ this._bindedShowProgress = Sabel.Function.bind(this.showProgress, this);
+
+ this.formElm = Sabel.get(form);
+
+ this.formElm.observe("submit", function(evt) {
+ self._init();
+ if (self.progressBar) Sabel.Element.remove(self.progressBar);
+ self.uploadFrame = self._createIframe();
+
+ var elm = evt.srcElement || this;
+ elm.setAttribute("target", self.IFRAME_NAME);
+ Sabel.get(progress).appendChild(self._createProgressField());
+ self.startProgress();
+
+ this.style.display = "none";
+ });
+ },
+
+ _init: function() {
+ this.timer = null;
+ this.tTimer = null;
+ this.interval = 700;
+
+ this.reqCount = 0;
+ this.ajaxReqTime = 0;
+ this.totalReqTime = 0;
+
+ this.incrementValue = 0;
+ this.totalSize = 0;
+ this.currentSize = 0;
+ this.uploadedSize = 0;
+ this.currentPercent = 0.0;
+ },
+
+ setMessage: function(msg) {
+ this.message = msg;
+ },
+
+ setErorMessage: function(msg) {
+ this.errorMessage = msg;
+ },
+
+ startProgress: function() {
+ this.progressTexts[0].innerHTML = this.message;
+ this.lastTime = new Date().getTime();
+ this.tTimer = setTimeout(this._bindedGetProgress, this.updateInterval);
+ },
+
+ endProgress: function() {
+ clearInterval(this.timer);
+ clearInterval(this.tTimer);
+
+ this.currentPercent = 100.0;
+ this._exec();
+
+ var self = this;
+ setTimeout(function() {
+ Sabel.Element.remove(self.formElm);
+ Sabel.Element.remove(self.uploadFrame);
+ }, 0);
+
+ this.successCallback();
+ },
+
+ getProgress: function() {
+ this.ajaxReqTime = new Date().getTime();
+ this.ajax.request(this.uri,
+ { method: "get",
+ //timeout: 1000,
+ headers: { "If-Modified-Since": new Date(0) },
+ onSuccess: this._bindedShowProgress,
+ onTimeout: this._bindedShowProgress
+ });
+ },
+
+ showProgress: function(res) {
+ if (res) {
+ this.reqCount++;
+ this.totalReqTime += (new Date().getTime() - this.ajaxReqTime);
+
+ eval("var json = " + res.responseText);
+
+ if (json === false) {
+ var self = this;
+ Sabel.Array.each(this.progressTexts, function(el) {
+ el.innerHTML = self.errorMessage;
+ });
+ this.errorCallback();
+ return Sabel.Element.remove(this.uploadFrame);
+ }
+
+ if (this.currentPercent == 0.0) { // first response
+ this.progressTexts[0].innerHTML = "";
+ this.totalSize = json.total;
+ this.timer = setInterval(Sabel.Function.bind(this.progress, this), this.updateInterval);
+ }
+
+ var uploaded = json.current - this.uploadedSize;
+ this.uploadedSize = json.current;
+ var percent = this.uploadedSize / this.totalSize * 100;
+
+ var time = new Date().getTime();
+ var reqTime = time - this.lastTime;
+ this.lastTime = time;
+
+ var totalTime = reqTime / (uploaded / this.totalSize);
+ var refreshNum = ((this.totalReqTime / this.reqCount) + this.interval) / this.updateInterval;
+ this.incrementValue = (this.updateInterval * 100 / totalTime) + ((percent - this.currentPercent) / refreshNum);
+ if (this.incrementValue < 0) this.incrementValue = 0.01;
+
+ this.interval = Math.min(2000, this.interval + this.intervalIncr)
+ }
+
+ if (this.currentPercent < 100) this.tTimer = setTimeout(this._bindedGetProgress, this.interval);
+ },
+
+ progress: function() {
+ this.currentPercent = Math.min(99.9, this.currentPercent + this.incrementValue);
+ this._exec();
+ },
+
+ _exec: function() {
+ var percent = this.currentPercent;
+ Sabel.Element.setStyle(this.progressElm, {"width": percent + "%"});
+
+ var percentText = percent.toFixed(1) + ' %';
+ Sabel.Array.each(this.progressTexts, function(el) {
+ el.innerHTML = percentText;
+ });
+
+ this.statusText.innerHTML = Sabel.Number.toHumanReadable(this.totalSize * percent / 100, 1024) + "byte / "
+ + Sabel.Number.toHumanReadable(this.totalSize, 1024) + "byte ( "
+ + Sabel.Number.toHumanReadable(this.totalSize * this.incrementValue / 10 * 8, 1024) + "bps )";
+ },
+
+ _createProgressField: function() {
+ var parent, frame, border, progress, text, status;
+
+ (parent = document.createElement("div")).className = "sbl_progress";
+ (frame = document.createElement("div")).className = "sbl_progress_border_frame";
+ (border = document.createElement("div")).className = "sbl_progress_border";
+ (progress = document.createElement("div")).className = "sbl_progress_bar";
+ (text = document.createElement("span")).className = "sbl_progress_text";
+ (status = document.createElement("div")).className = "sbl_progress_status";
+
+ parent.appendChild(frame).appendChild(border);
+ parent.appendChild(status)
+ border.appendChild(text.cloneNode(false))
+ border.appendChild(progress).appendChild(text);
+
+ this.progressBar = parent;
+ this.progressElm = progress;
+ this.progressTexts = Sabel.Dom.getElementsByClassName("sbl_progress_text", border);
+ this.statusText = status;
+
+ return parent;
+ },
+
+ _createIframe: function() {
+ var self = this;
+
+ try {
+ var iframe = document.createElement('