<commit_before><?php // vim:ts=4:sw=4:et:fdm=marker
/**
 * Base class for Command-Line Applications. The purpose of Application class
 * is to initialize all the other classes and aid their connectivity. API
 * class can be accessed from any object through $this->api property.
 *
 * API classes are derrived from AbstractView because normally they would have
 * a template and will be able to render themselves consistently to any other
 * view in the system. Although App_CLI does not do any rendering, it's descendants
 * do
 *
 * @link http://agiletoolkit.org/doc/api
*//*
==ATK4===================================================
   This file is part of Agile Toolkit 4
    http://agiletoolkit.org/

   (c) 2008-2013 Agile Toolkit Limited <<EMAIL>>
   Distributed under Affero General Public License v3 and
   commercial license.

   See LICENSE or LICENSE_COM for more information
 =====================================================ATK4=*/
class App_CLI extends AbstractView
{
    /**
     * In a typical application, one connection to the database is enough for
     * majority of applications. Calling $api->dbConnect will read Database
     * data from config file and store it in $db property. If you requires
     * a more advanced connectivity or multiple connections, you can manually
     * initialize more database connections.
     *
     * @see dbConnect()
     */
    public $db=null;

    /**
     * App_CLI implements a API for accessing your application configuration.
     * Once configuration file is read, data is saved inside this property.
     *
     * @see getConfig()
     * @see readConfig()
     */
    public $config = array();

    /**
     * This is config relative location to the initializing file. By default
     * it's in the same folder, but you can move it one folder up ".." or
     * inside "config" sub-folder by setting value to 'config' to better
     * reflect your application layout.
     */
    public $config_location = '.';

    /**
     * Config files. If you have extra configuration files you want
     * to be automatically loaded, please redefine this property. You must
     * do this before Application class constructor is executed.
     *
     * If you are loading additional config later, use readConfig() instead
     *
     * Order of this array is important.
     */
    public $config_files = array('config-default','config');

    /**
     * Contains list of loaded config files
     */
    public $config_files_loaded = array();

    /**
     * Without logger, API will dump out errors and exceptions in a very brief
     * and straigtforward way. Logger is a controller which enhances error
     * output and in most cases you do need one. Logger can be further configured
     * to either output detailed errors or show brief message instead.
     *
     * @see Logger
     */
    public $logger=null;

    /**
     * If you want to use your own logger class, redefine this property
     */
    public $logger_class='Logger';

    /**
     * PathFinder is a controller which is responsible for locating resources,
     * such as PHP includes, JavaScript files, templates, etc. API Initializes
     * PathFinder as soon as possible, then defines "Locations" which describe
     * type of data found in different folders.
     */
    public $pathfinder=null;

    /**
     * If you would want to use your own PathFinder class, you must change
     * this property and include it.
     */
    protected $pathfinder_class='PathFinder';

    /**
     * Change a different Page Manager class
     */
    protected $pagemanager_class='Controller_PageManager';

    /**
     * Set to array('debug'=>true) to debug Page Manager
     */
    protected $pagemanager_options=null;


    /**
     * This is a major version of Agile Toolkit. The API of Agile Toolkit is
     * very well established and changes rarely. Your application would generally
     * be compatible throughout the same major version of Agile Tooolkit.
     *
     * @see requires();
     */
    public $atk_version=4.3;

    /**
     * If you want Agile Toolkit to be compatible with 4.2 version, include
     * compatibility controller. For more information see:
     *
     * http://www.agiletoolkit.org/compat42
     *
     * @var boolean
     */
    public $compat_42=false;

    /**
     * Some Agile Toolkit classes contain references to profiler. Profiler
     * would be initialized early and reference would be kept in this variable.
     * Profiler measures relative time it took in certain parts of your
     * application to help you find a slow-perfoming parts of application.
     *
     * By default $pr points to empty profiler object, which implements empty
     * methods. All the lines referencing $pr myst be prefixed with the
     * 4-symbol sequence "/ ** /" (no spaces). If you want to speed up Agile
     * Toolkit further, you can eliminate all lines started with this sequence
     * from your source code.
     */
    /**/public $pr;

    /**
     * Object in Agile Toolkit contain $name property which is derrived from
     * the owher object and keeps extending as you add objects deeper into
     * run-time tree. Sometimes that may generate long names. Long names are
     * difficult to read, they increase HTML output size but most importantly
     * they may be restricted by security extensions such as SUHOSIN.
     *
     * Agile Toolkit implements a mechanism which will replace common beginning
     * of objects with an abbreviation thus keeping object name length under
     * control. This variable defines the maximum length of the object's $name.
     * Be mindful that some objects will concatinate theri name with fields,
     * so the maximum letgth of GET argument names can exceed this value by
     * the length of your field.
     *
     * We recommend you to increase SUHOSIN get limits if you encounter any
     * problems. Set this value to "false" to turn off name shortening.
     */
    public $max_name_length=60;

    /**
     * As more names are shortened, the substituted part is being placed into
     * this hash and the value contains the new key. This helps to avoid creating
     * many sequential prefixes for the same character sequenece.
     */
    public $unique_hashes=array();

    /**
     * This is the default locale for the application. You change this manually
     * inside application API class or use some controller which will pull this
     * variable out of the URL. This variable will be respected throughout the
     * framework
     */
    public $locale='en_US';

    // {{{ Start-up of application
    /**
     * Regular objects in Agile Toolkit use init() and are added through add().
     * Application class is differente, you use "new" keyword because it's the
     * first class to be created. That's why constructor will perform quite a
     * bit of initialization.
     *
     * Do not redefine constructor but instead use init();
     *
     * $realm defines a top-level name of your application. This impacts all
     * id= prefixes in your HTML code, form field names and many other things,
     * such as session name. If you have two application classes which are part
     * of same web app and may want to use same realm, but in other cases it's
     * preferably that you keep realm unique on your domain in the interests
     * of security.
     *
     * @param string $realm Will become $app->name
     */
    function __construct($realm = null)
    {
        if (!$realm) {
            $realm=get_class($this);
        }
        $this->owner = $this;
        $this->name  = $realm;
        $this->api   = // compatibility
        $this->app   = $this;

        // Profiler is a class for benchmarking your application. All calls to pr
        /**/$this->pr=new Dummy();

        try {

            $this->_beforeInit();

            $this->init();
        } catch (Exception $e) {

            // This exception is used to abort initialisation of the objects but when
            // normal rendering is still required
            if ($e instanceof Exception_StopInit) {
                return;
            }

            // Handles output of the exception
            $this->caughtException($e);
        }
    }

    /**
     * Finds out which page is requested. We don't need this method
     * for CLI, but others might need it
     */
    function _beforeInit()
    {
        // Loads all configuration files
        $this->readAllConfig();
        $this->add($this->pathfinder_class);
    }
    // }}}

    // {{{ Management of Global Methods
    /**
     * Agile Toolkit objects allow method injection. This is quite similar
     * to technique used in JavaScript:
     *
     *     obj.test = function() { .. }
     *
     * All non-existant method calls on all Agile Toolkit objects will be
     * tried against local table of registered methods and then against
     * global registered methods.
     *
     * addGlobalmethod allows you to register a globally-recognized for all
     * agile toolkit object. PHP is not particularly fast about executing
     * methods like that, but this technique can be used for adding
     * backward-compatibility or debugging, etc.
     *
     * @param string   $name     Name of the method
     * @param callable $callable Calls your function($object, $arg1, $arg2)
     *
     * @see AbstractObject::hasMethod()
     * @see AbstractObject::__call()
     *
     * @return void
     */
    function addGlobalMethod($name, $callable)
    {
        if ($this->hasMethod($name)) {
            throw $this->exception('Registering method twice')
                ->addMoreInfo('name', $name);
        }
        $this->addHook('global-method-'.$name, $callable);
    }
    /**
     * Returns if a global method with such name was defined
     *
     * @param string $name Name of the method
     *
     * @return boolean if registered
     */
    function hasGlobalMethod($name){
        return isset($this->hooks['global-method-'.$name]);
    }
    /** Removes global method */
    function removeGlobalMethod($name){
        $this->removeHook('global-method-'.$name);
    }
    // }}}

    // {{{ Localization
    /** Redefine this function to introduce your localization. Agile Toolkit will pass all system strings
     * through this method. If some methods are not properly passed through, please fork Agile Toolkit in
     * http://github.com/atk4/atk4/ , modify, commit, push your fix and notify authors of Agile Toolkit
     * using contact form on http://agiletoolkit.org/contact
     *
     * See file CONTRIBUTING
     */
    function _($str){

        $x=$this->hook('localizeString',array($str));
        if($x)return $x[0];

        return $str;
    }
    // }}}

    // {{{ PathFinder and PageManager bindings
    /** Find relative path to the resource respective to the current directory. */
    function locate($type,$filename='',$return='relative'){
        return $this->pathfinder->locate($type,$filename,$return);
    }
    /** Calculate URL pointing to specified resource */
    function locateURL($type,$filename=''){
        return $this->pathfinder->locate($type,$filename,'url');
    }
    /** Return full system path to specified resource */
    function locatePath($type,$filename=''){
        return $this->pathfinder->locate($type,$filename,'path');
    }
    /** Add new location with additional resources */
    function addLocation($location,$contents){
        return $this->pathfinder->addLocation($location,$contents);
    }
    /** Returns base URL of this Web application installation. If you require
     * link to a page, you can use URL::useAbsoluteURL();
     *
     * @see URL::useAbsoluteURL() */
    function getBaseURL(){
        return $this->pm->base_path;
    }
    /** Generates URL for specified page. Useful for building links on pages or emails. Returns URL object. */
    function url($page=null,$arguments=array()){
        if(is_object($page) && $page instanceof URL){
            // we receive URL
            return $page->setArguments($arguments);
        }
        if(is_array($page)){
            $p=$page[0];
            unset($page[0]);
            $arguments=$page;
            $page=$p;
        }
        $url=$this->add('URL');
        unset($this->elements[$url->short_name]);   // garbage collect URLs
        if(strpos($page,'http://')===0 || strpos($page,'https://')===0) $url->setURL($page);
        else $url->setPage($page);
        return $url->setArguments($arguments);
    }

    function getStickyArguments(){
        return array();
    }
    /** @obsolete use url() */
    function getDestinationURL($page=null,$arguments=array()){ return $this->url($page,$arguments); }
    // }}}

    // {{{ Error handling
    /** Initialize logger or return existing one */
    function getLogger($class_name=undefined){
        if(is_null($this->logger)){
            $this->logger=$this->add($class_name===undefined?$this->logger_class:$class_name);
        }
        return $this->logger;
    }
    /** Is executed if exception is raised during execution. Re-define to have custom handling of exceptions system-wide */
    function caughtException($e){
        $this->hook('caught-exception',array($e));
        echo get_class($e),": ".$e->getMessage();
        exit;
    }
    /** @obsolete */
    function outputWarning($msg,$shift=0){
        if($this->hook('output-warning',array($msg,$shift)))return true;
        echo "warning: $msg\n";
    }
    /** @obsolete */
    function outputDebug($object, $msg, $shift=0){
        if($this->hook('output-debug',array($object, $msg,$shift)))return true;
        echo "debug: $msg\n";
    }
    // }}}

    // {{{ Configuration File Handling

    /**
     * readAllConfig - will include all files as they are defined in
     * $this->config_files from folder $config_location
     */
    function readAllConfig() {
        // If configuration files are not there - will silently ignore
        foreach ($this->config_files as $file) {
            $this->readConfig ($file);
        }

        $tz = $this->getConfig('timezone',null);
        if(!is_null($tz) && function_exists('date_default_timezone_set')){
            // with seting default timezone
            date_default_timezone_set($tz);
        }
    }

    /** Executed when trying to access config parameter which is not find in the file */
    function configExceptionOrDefault($default,$exceptiontext){
        if($default!='_config_get_false')return $default;
        throw new BaseException($exceptiontext);
    }
    /**
     * Read config file and store it in $this->config. Use getConfig() to access
     */
    function readConfig($file='config.php'){
        $orig_file = $file;

        if (strpos ($file,'.php') != strlen($file)-4 ) {
            $file .= '.php';
        }

        if(strpos($file,'/')===false){
            $file=getcwd().'/'.$this->config_location.'/'.$file;
        }

        if (file_exists($file)) {
            // some tricky thing to make config be read in some cases it could not in simple way
            unset($config);

            $config=&$this->config;
            $this->config_files_loaded[]=$file;
            include $file;

            unset($config);
            return true;
        }

        return false;
    }
    /** Manually set configuration option */
    function setConfig($config=array(),$val=UNDEFINED){
        if($val!==UNDEFINED)return $this->setConfig(array($config=>$val));
        if(!$config)$config=array();
        if(!$this->config)$this->config=array();
        $this->config=array_merge($this->config,$config);
    }
    /** Load config if necessary and look up corresponding setting */
    function getConfig($path, $default_value = undefined){
        /**
         * For given path such as 'dsn' or 'logger/log_dir' returns
         * corresponding config value. Throws ExceptionNotConfigured if not set.
         *
         * To find out if config is set, do this:
         *
         * $var_is_set=true;
         * try { $app->getConfig($path); } catch ExceptionNotConfigured($e) { $var_is_set=false; };
         */
        $parts = explode('/',$path);
        $current_position = $this->config;
        foreach($parts as $part){
            if(!array_key_exists($part,$current_position)){
                if($default_value!==undefined)return $default_value;
                throw $this->exception("Configuration parameter is missing in config.php",'NotConfigured')
                    ->addMoreInfo('config_files_loaded',$this->config_files_loaded)
                    ->addMoreInfo("missign_line"," \$config['".join("']['",explode('/',$path))."']");
            }else{
                $current_position = $current_position[$part];
            }
        }
        return $current_position;
    }
    // }}}

    // {{{ Version handling
    /** Determine version of Agile Toolkit or specified plug-in */
    private $version_cache=null;
    function getVersion($of='atk'){
        // TODO: get version of add-on
        if(!$this->version_cache){
            $f=$this->app->pathfinder->atk_location->base_path.DIRECTORY_SEPARATOR.'VERSION';
            if(file_exists($f)){
                $this->version_cache=trim(file_get_contents($f));
            }else{
                $this->version_cache='4.0.1';
            }
        }
        return $this->version_cache;
    }
    /** Verifies version. Should be used by addons. For speed improvement, redefine this into empty function */
    function requires($addon='atk',$v,$location = null){
        $cv=$this->getVersion($addon);
        if(version_compare($cv,$v)<0){
            if($addon=='atk'){
                $e=$this->exception('Agile Toolkit version is too old');
            }else{
                $e=$this->exception('Add-on is outdated')
                    ->addMoreInfo('addon',$addon);
            }
            $e->addMoreInfo('required',$v)
                ->addMoreInfo('you have',$cv);
            if($location)$e->addMoreInfo('download_location',$location);
            throw $e;
        }

        // Possibly we need to enable compatibility version
        if($addon=='atk'){
            if(
                version_compare($v,'4.2')<0 &&
                version_compare($v,'4.1.4')>=0
            ){
                $this->add('Controller_Compat');
                return true;
            }
        }
        return true;
    }
    /** @obsolete use @requires */
    function versionRequirement($v,$location = null){
        return $this->requires('atk',$v,$location);
    }
    // }}}

    // {{{ Database connection handling
    /** Use database configuration settings from config file to establish default connection */
    function dbConnect($dsn=null){
        return $this->db=$this->add('DB')->connect($dsn);
    }
    /** Attempts to connect, but does not raise exception on failure. */
    function tryConnect($dsn){
        $this->db=DBlite::tryConnect($dsn);
    }
    // }}}

    // {{{ Helper / utility methods
    /**
     * Normalize field or identifier name. Can also be used in URL normalization.
     * This will replace all non alpha-numeric characters with separator.
     * Multiple separators in a row is replaced with one.
     * Separators in beginning and at the end of name are removed.
     *
     * Sample input:  "Hello, Dear Jon!"
     * Sample output: "Hello_Dear_Jon"
     *
     * @param string $name      String to process
     * @param string $separator Character acting as separator
     *
     * @return string           Normalized string
     */
    function normalizeName($name, $separator = '_')
    {
        if (strlen($separator)==0) {<commit_msg>method was using obsolete class name<commit_after><?php // vim:ts=4:sw=4:et:fdm=marker
/**
 * Base class for Command-Line Applications. The purpose of Application class
 * is to initialize all the other classes and aid their connectivity. API
 * class can be accessed from any object through $this->api property.
 *
 * API classes are derrived from AbstractView because normally they would have
 * a template and will be able to render themselves consistently to any other
 * view in the system. Although App_CLI does not do any rendering, it's descendants
 * do
 *
 * @link http://agiletoolkit.org/doc/api
*//*
==ATK4===================================================
   This file is part of Agile Toolkit 4
    http://agiletoolkit.org/

   (c) 2008-2013 Agile Toolkit Limited <<EMAIL>>
   Distributed under Affero General Public License v3 and
   commercial license.

   See LICENSE or LICENSE_COM for more information
 =====================================================ATK4=*/
class App_CLI extends AbstractView
{
    /**
     * In a typical application, one connection to the database is enough for
     * majority of applications. Calling $api->dbConnect will read Database
     * data from config file and store it in $db property. If you requires
     * a more advanced connectivity or multiple connections, you can manually
     * initialize more database connections.
     *
     * @see dbConnect()
     */
    public $db=null;

    /**
     * App_CLI implements a API for accessing your application configuration.
     * Once configuration file is read, data is saved inside this property.
     *
     * @see getConfig()
     * @see readConfig()
     */
    public $config = array();

    /**
     * This is config relative location to the initializing file. By default
     * it's in the same folder, but you can move it one folder up ".." or
     * inside "config" sub-folder by setting value to 'config' to better
     * reflect your application layout.
     */
    public $config_location = '.';

    /**
     * Config files. If you have extra configuration files you want
     * to be automatically loaded, please redefine this property. You must
     * do this before Application class constructor is executed.
     *
     * If you are loading additional config later, use readConfig() instead
     *
     * Order of this array is important.
     */
    public $config_files = array('config-default','config');

    /**
     * Contains list of loaded config files
     */
    public $config_files_loaded = array();

    /**
     * Without logger, API will dump out errors and exceptions in a very brief
     * and straigtforward way. Logger is a controller which enhances error
     * output and in most cases you do need one. Logger can be further configured
     * to either output detailed errors or show brief message instead.
     *
     * @see Logger
     */
    public $logger=null;

    /**
     * If you want to use your own logger class, redefine this property
     */
    public $logger_class='Logger';

    /**
     * PathFinder is a controller which is responsible for locating resources,
     * such as PHP includes, JavaScript files, templates, etc. API Initializes
     * PathFinder as soon as possible, then defines "Locations" which describe
     * type of data found in different folders.
     */
    public $pathfinder=null;

    /**
     * If you would want to use your own PathFinder class, you must change
     * this property and include it.
     */
    protected $pathfinder_class='PathFinder';

    /**
     * Change a different Page Manager class
     */
    protected $pagemanager_class='Controller_PageManager';

    /**
     * Set to array('debug'=>true) to debug Page Manager
     */
    protected $pagemanager_options=null;


    /**
     * This is a major version of Agile Toolkit. The API of Agile Toolkit is
     * very well established and changes rarely. Your application would generally
     * be compatible throughout the same major version of Agile Tooolkit.
     *
     * @see requires();
     */
    public $atk_version=4.3;

    /**
     * If you want Agile Toolkit to be compatible with 4.2 version, include
     * compatibility controller. For more information see:
     *
     * http://www.agiletoolkit.org/compat42
     *
     * @var boolean
     */
    public $compat_42=false;

    /**
     * Some Agile Toolkit classes contain references to profiler. Profiler
     * would be initialized early and reference would be kept in this variable.
     * Profiler measures relative time it took in certain parts of your
     * application to help you find a slow-perfoming parts of application.
     *
     * By default $pr points to empty profiler object, which implements empty
     * methods. All the lines referencing $pr myst be prefixed with the
     * 4-symbol sequence "/ ** /" (no spaces). If you want to speed up Agile
     * Toolkit further, you can eliminate all lines started with this sequence
     * from your source code.
     */
    /**/public $pr;

    /**
     * Object in Agile Toolkit contain $name property which is derrived from
     * the owher object and keeps extending as you add objects deeper into
     * run-time tree. Sometimes that may generate long names. Long names are
     * difficult to read, they increase HTML output size but most importantly
     * they may be restricted by security extensions such as SUHOSIN.
     *
     * Agile Toolkit implements a mechanism which will replace common beginning
     * of objects with an abbreviation thus keeping object name length under
     * control. This variable defines the maximum length of the object's $name.
     * Be mindful that some objects will concatinate theri name with fields,
     * so the maximum letgth of GET argument names can exceed this value by
     * the length of your field.
     *
     * We recommend you to increase SUHOSIN get limits if you encounter any
     * problems. Set this value to "false" to turn off name shortening.
     */
    public $max_name_length=60;

    /**
     * As more names are shortened, the substituted part is being placed into
     * this hash and the value contains the new key. This helps to avoid creating
     * many sequential prefixes for the same character sequenece.
     */
    public $unique_hashes=array();

    /**
     * This is the default locale for the application. You change this manually
     * inside application API class or use some controller which will pull this
     * variable out of the URL. This variable will be respected throughout the
     * framework
     */
    public $locale='en_US';

    // {{{ Start-up of application
    /**
     * Regular objects in Agile Toolkit use init() and are added through add().
     * Application class is differente, you use "new" keyword because it's the
     * first class to be created. That's why constructor will perform quite a
     * bit of initialization.
     *
     * Do not redefine constructor but instead use init();
     *
     * $realm defines a top-level name of your application. This impacts all
     * id= prefixes in your HTML code, form field names and many other things,
     * such as session name. If you have two application classes which are part
     * of same web app and may want to use same realm, but in other cases it's
     * preferably that you keep realm unique on your domain in the interests
     * of security.
     *
     * @param string $realm Will become $app->name
     */
    function __construct($realm = null)
    {
        if (!$realm) {
            $realm=get_class($this);
        }
        $this->owner = $this;
        $this->name  = $realm;
        $this->api   = // compatibility
        $this->app   = $this;

        // Profiler is a class for benchmarking your application. All calls to pr
        /**/$this->pr=new Dummy();

        try {

            $this->_beforeInit();

            $this->init();
        } catch (Exception $e) {

            // This exception is used to abort initialisation of the objects but when
            // normal rendering is still required
            if ($e instanceof Exception_StopInit) {
                return;
            }

            // Handles output of the exception
            $this->caughtException($e);
        }
    }

    /**
     * Finds out which page is requested. We don't need this method
     * for CLI, but others might need it
     */
    function _beforeInit()
    {
        // Loads all configuration files
        $this->readAllConfig();
        $this->add($this->pathfinder_class);
    }
    // }}}

    // {{{ Management of Global Methods
    /**
     * Agile Toolkit objects allow method injection. This is quite similar
     * to technique used in JavaScript:
     *
     *     obj.test = function() { .. }
     *
     * All non-existant method calls on all Agile Toolkit objects will be
     * tried against local table of registered methods and then against
     * global registered methods.
     *
     * addGlobalmethod allows you to register a globally-recognized for all
     * agile toolkit object. PHP is not particularly fast about executing
     * methods like that, but this technique can be used for adding
     * backward-compatibility or debugging, etc.
     *
     * @param string   $name     Name of the method
     * @param callable $callable Calls your function($object, $arg1, $arg2)
     *
     * @see AbstractObject::hasMethod()
     * @see AbstractObject::__call()
     *
     * @return void
     */
    function addGlobalMethod($name, $callable)
    {
        if ($this->hasMethod($name)) {
            throw $this->exception('Registering method twice')
                ->addMoreInfo('name', $name);
        }
        $this->addHook('global-method-'.$name, $callable);
    }
    /**
     * Returns if a global method with such name was defined
     *
     * @param string $name Name of the method
     *
     * @return boolean if registered
     */
    function hasGlobalMethod($name){
        return isset($this->hooks['global-method-'.$name]);
    }
    /** Removes global method */
    function removeGlobalMethod($name){
        $this->removeHook('global-method-'.$name);
    }
    // }}}

    // {{{ Localization
    /** Redefine this function to introduce your localization. Agile Toolkit will pass all system strings
     * through this method. If some methods are not properly passed through, please fork Agile Toolkit in
     * http://github.com/atk4/atk4/ , modify, commit, push your fix and notify authors of Agile Toolkit
     * using contact form on http://agiletoolkit.org/contact
     *
     * See file CONTRIBUTING
     */
    function _($str){

        $x=$this->hook('localizeString',array($str));
        if($x)return $x[0];

        return $str;
    }
    // }}}

    // {{{ PathFinder and PageManager bindings
    /** Find relative path to the resource respective to the current directory. */
    function locate($type,$filename='',$return='relative'){
        return $this->pathfinder->locate($type,$filename,$return);
    }
    /** Calculate URL pointing to specified resource */
    function locateURL($type,$filename=''){
        return $this->pathfinder->locate($type,$filename,'url');
    }
    /** Return full system path to specified resource */
    function locatePath($type,$filename=''){
        return $this->pathfinder->locate($type,$filename,'path');
    }
    /** Add new location with additional resources */
    function addLocation($location,$contents){
        return $this->pathfinder->addLocation($location,$contents);
    }
    /** Returns base URL of this Web application installation. If you require
     * link to a page, you can use URL::useAbsoluteURL();
     *
     * @see URL::useAbsoluteURL() */
    function getBaseURL(){
        return $this->pm->base_path;
    }
    /** Generates URL for specified page. Useful for building links on pages or emails. Returns URL object. */
    function url($page=null,$arguments=array()){
        if(is_object($page) && $page instanceof URL){
            // we receive URL
            return $page->setArguments($arguments);
        }
        if(is_array($page)){
            $p=$page[0];
            unset($page[0]);
            $arguments=$page;
            $page=$p;
        }
        $url=$this->add('URL');
        unset($this->elements[$url->short_name]);   // garbage collect URLs
        if(strpos($page,'http://')===0 || strpos($page,'https://')===0) $url->setURL($page);
        else $url->setPage($page);
        return $url->setArguments($arguments);
    }

    function getStickyArguments(){
        return array();
    }
    /** @obsolete use url() */
    function getDestinationURL($page=null,$arguments=array()){ return $this->url($page,$arguments); }
    // }}}

    // {{{ Error handling
    /** Initialize logger or return existing one */
    function getLogger($class_name=undefined){
        if(is_null($this->logger)){
            $this->logger=$this->add($class_name===undefined?$this->logger_class:$class_name);
        }
        return $this->logger;
    }
    /** Is executed if exception is raised during execution. Re-define to have custom handling of exceptions system-wide */
    function caughtException($e){
        $this->hook('caught-exception',array($e));
        echo get_class($e),": ".$e->getMessage();
        exit;
    }
    /** @obsolete */
    function outputWarning($msg,$shift=0){
        if($this->hook('output-warning',array($msg,$shift)))return true;
        echo "warning: $msg\n";
    }
    /** @obsolete */
    function outputDebug($object, $msg, $shift=0){
        if($this->hook('output-debug',array($object, $msg,$shift)))return true;
        echo "debug: $msg\n";
    }
    // }}}

    // {{{ Configuration File Handling

    /**
     * readAllConfig - will include all files as they are defined in
     * $this->config_files from folder $config_location
     */
    function readAllConfig() {
        // If configuration files are not there - will silently ignore
        foreach ($this->config_files as $file) {
            $this->readConfig ($file);
        }

        $tz = $this->getConfig('timezone',null);
        if(!is_null($tz) && function_exists('date_default_timezone_set')){
            // with seting default timezone
            date_default_timezone_set($tz);
        }
    }

    /** Executed when trying to access config parameter which is not find in the file */
    function configExceptionOrDefault($default,$exceptiontext){
        if($default!='_config_get_false')return $default;
        throw new BaseException($exceptiontext);
    }
    /**
     * Read config file and store it in $this->config. Use getConfig() to access
     */
    function readConfig($file='config.php'){
        $orig_file = $file;

        if (strpos ($file,'.php') != strlen($file)-4 ) {
            $file .= '.php';
        }

        if(strpos($file,'/')===false){
            $file=getcwd().'/'.$this->config_location.'/'.$file;
        }

        if (file_exists($file)) {
            // some tricky thing to make config be read in some cases it could not in simple way
            unset($config);

            $config=&$this->config;
            $this->config_files_loaded[]=$file;
            include $file;

            unset($config);
            return true;
        }

        return false;
    }
    /** Manually set configuration option */
    function setConfig($config=array(),$val=UNDEFINED){
        if($val!==UNDEFINED)return $this->setConfig(array($config=>$val));
        if(!$config)$config=array();
        if(!$this->config)$this->config=array();
        $this->config=array_merge($this->config,$config);
    }
    /** Load config if necessary and look up corresponding setting */
    function getConfig($path, $default_value = undefined){
        /**
         * For given path such as 'dsn' or 'logger/log_dir' returns
         * corresponding config value. Throws ExceptionNotConfigured if not set.
         *
         * To find out if config is set, do this:
         *
         * $var_is_set=true;
         * try { $app->getConfig($path); } catch ExceptionNotConfigured($e) { $var_is_set=false; };
         */
        $parts = explode('/',$path);
        $current_position = $this->config;
        foreach($parts as $part){
            if(!array_key_exists($part,$current_position)){
                if($default_value!==undefined)return $default_value;
                throw $this->exception("Configuration parameter is missing in config.php",'NotConfigured')
                    ->addMoreInfo('config_files_loaded',$this->config_files_loaded)
                    ->addMoreInfo("missign_line"," \$config['".join("']['",explode('/',$path))."']");
            }else{
                $current_position = $current_position[$part];
            }
        }
        return $current_position;
    }
    // }}}

    // {{{ Version handling
    /** Determine version of Agile Toolkit or specified plug-in */
    private $version_cache=null;
    function getVersion($of='atk'){
        // TODO: get version of add-on
        if(!$this->version_cache){
            $f=$this->app->pathfinder->atk_location->base_path.DIRECTORY_SEPARATOR.'VERSION';
            if(file_exists($f)){
                $this->version_cache=trim(file_get_contents($f));
            }else{
                $this->version_cache='4.0.1';
            }
        }
        return $this->version_cache;
    }
    /** Verifies version. Should be used by addons. For speed improvement, redefine this into empty function */
    function requires($addon='atk',$v,$location = null){
        $cv=$this->getVersion($addon);
        if(version_compare($cv,$v)<0){
            if($addon=='atk'){
                $e=$this->exception('Agile Toolkit version is too old');
            }else{
                $e=$this->exception('Add-on is outdated')
                    ->addMoreInfo('addon',$addon);
            }
            $e->addMoreInfo('required',$v)
                ->addMoreInfo('you have',$cv);
            if($location)$e->addMoreInfo('download_location',$location);
            throw $e;
        }

        // Possibly we need to enable compatibility version
        if($addon=='atk'){
            if(
                version_compare($v,'4.2')<0 &&
                version_compare($v,'4.1.4')>=0
            ){
                $this->add('Controller_Compat');
                return true;
            }
        }
        return true;
    }
    /** @obsolete use @requires */
    function versionRequirement($v,$location = null){
        return $this->requires('atk',$v,$location);
    }
    // }}}

    // {{{ Database connection handling
    /** Use database configuration settings from config file to establish default connection */
    function dbConnect($dsn=null){
        return $this->db=$this->add('DB')->connect($dsn);
    }
    // }}}

    // {{{ Helper / utility methods
    /**
     * Normalize field or identifier name. Can also be used in URL normalization.
     * This will replace all non alpha-numeric characters with separator.
     * Multiple separators in a row is replaced with one.
     * Separators in beginning and at the end of name are removed.
     *
     * Sample input:  "Hello, <NAME>!"
     * Sample output: "Hello_Dear_Jon"
     *
     * @param string $name      String to process
     * @param string $separator Character acting as separator
     *
     * @return string           Normalized string
     */
    function normalizeName($name, $separator = '_')
    {
        if (strlen($separator)==0) {
{'fraction_non_alphanumeric': 0.11720958663372641, 'fraction_numerical': 0.002495988589766447, 'mean_word_length': 2.4175298111236834, 'pattern_counts': {'":': 2, '<': 14, '<?xml version=': 0, '>': 189, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6461784', 'n_tokens_mistral': 10515, 'n_tokens_neox': 10276, 'n_words': 3565}