text:
<?php
use Kwf\FileWatcher\Event;
class Kwf_Util_ClearCache_Watcher
{
    private static function _startsWith($str, $startWith)
    {
        if ($str == $startWith) return true;
        if (substr($str, 0, strlen($startWith)) == $startWith) return true;
        return false;
    }
    private static function _endsWith($str, $endsWith)
    {
        if ($str == $endsWith) return true;
        if (substr($str, -strlen($endsWith)) == $endsWith) return true;
        return false;
    }

    private static function _informDuckcast($cacheType)
    {
        if (Kwf_Config::getValue('debug.duckcast.host')) {
            echo "Inform Duckcast ...";

            try {
                file_get_contents(
                    'http://'.Kwf_Config::getValue('debug.duckcast.host')
                        .':'.Kwf_Config::getValue('debug.duckcast.port').'/watcher?cacheType='.$cacheType
                );
            } catch (Exception $e) {
                echo " [".$e->getMessage()."]\n";
                return;
            }
            echo " [ok]\n";
        }
    }

    public static function watch()
    {
        if (Kwf_Config::getValue('whileUpdatingShowMaintenancePage')) {
            throw new Kwf_Exception_Client("Disable whileUpdatingShowMaintenancePage in config to use clear-cache-watcher");
        }
        $bufferUsecs = 200*1000;

        $watchPaths = array();
        if (Kwf_Config::getValue('application.id') == 'kwf') {
            //for ccw in kwf itself (where cwd is tests subdir)
            $watchPaths[] = realpath(getcwd().'/..');
        } else {
            $watchPaths[] = getcwd();
        }

        if (strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {

            $ret = array();
            exec('ps ax -o pid,ppid,user,args', $out);
            $processesByParent = array();
            foreach ($out as $o) {
                if (preg_match('#^\s*([0-9]+)\s+([0-9]+)\s+([a-zA-Z0-9-_]+)\s+(.*)#', $o, $m)) {
                    $pid = (int)$m[1];
                    $ppid = (int)$m[2];
                    if (!isset($processesByParent[$ppid])) $processesByParent[$ppid] = array();
                    $processesByParent[$ppid][] = $pid;
                }
            }
            foreach ($out as $o) {
                if (preg_match('#^\s*([0-9]+)\s+([0-9]+)\s+([a-zA-Z0-9-_]+)\s+(.*)#', $o, $m)) {
                    $pid = (int)$m[1];
                    $cmd = $m[4];
                    $user = $m[3];
                    if (getmypid() == $pid) continue;
                    $cmd = explode(' ', $cmd);
                    if (substr(trim($cmd[0]), -3) != 'php') continue;
                    unset($cmd[0]);
                    if (substr($cmd[1], -13) != 'bootstrap.php' && $cmd[1] != '/usr/local/bin/vps') continue;
                    unset($cmd[1]);
                    $cmdWithoutArgs= '';
                    $args = '';
                    foreach ($cmd as $i=>$c) {
                        if (substr($c, 0, 2)=='--') {
                            $args = implode(' ', $cmd);
                            break;
                        }
                        $cmdWithoutArgs .= $c.' ';
                        unset($cmd[$i]);
                    }
                    if (substr(trim($cmdWithoutArgs), 0, 19)=='clear-cache-watcher') {
                        echo "there is already a clear-cache-watcher running for your user: ".trim(`pwdx $pid`)."\n";
                        echo "klling it...\n";
                        if (isset($processesByParent[$pid])) {
                            foreach ($processesByParent[$pid] as $i) {
                                posix_kill($i, SIGINT);
                            }
                        }
                        posix_kill($pid, SIGINT);
                        echo "\n";
                    }
                }
            }
        }

        require VENDOR_PATH.'/autoload.php';
        $watcher = Kwf\FileWatcher\Watcher::create($watchPaths);
        $watcher->setExcludePatterns(array(
            '*magick*',
            '.nfs*',
            '/.git/*',
            '*.kate-swp',
            '~',
            '/cache/*',
            '/log/*',
            '/temp/*',
            '/data/index/*',
            '/benchmarklog',
            '/querylog',
            '/eventlog',
            '/build/*',
            '/Gruntfile.js',
            '/node_modules/*',
            '/.idea/*'
        ));
        $watcher->setQueueSizeLimit(100);
        $watcher->setFollowLinks(true);

        $output = new Symfony\Component\Console\Output\ConsoleOutput();
        $verbosityLevelMap = array(
            Psr\Log\LogLevel::NOTICE => Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL,
            Psr\Log\LogLevel::INFO   => Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL,
            //Psr\Log\LogLevel::DEBUG  => Symfony\Component\Console\Output\OutputInterface::VERBOSITY_NORMAL,
        );
        $watcher->setLogger(new Symfony\Component\Console\Logger\ConsoleLogger($output, $verbosityLevelMap));

        if ($watcher instanceof Kwf\FileWatcher\Backend\Poll) {
            //don't use poll backend, that's too slow
            if (stristr(PHP_OS, 'LINUX')) {
                throw new Kwf_Exception_Client(
                    "To use clear-cache-watcher you need either inotifywait or watchmedo installed and in your \$PATH:\n".
                    "- install inotify-tools package to get inotifywait (see https://github.com/rvoicilas/inotify-tools/wiki)\n".
                    "- install python-watchdog (see http://pythonhosted.org/watchdog/#easy-installation)"
                );
            } else {
                throw new Kwf_Exception_Client(
                    "To use clear-cache-watcher you need watchmedo installed and in your \$PATH:\n".
                    "- install python-watchdog (see http://pythonhosted.org/watchdog/#easy-installation)"
                );
            }
        }

        $watcher->addListener('filewatcher.queue_full', function($e) {
            echo "more than 100 events (".count($e->eventsQueue)."), did you switch branches or something?\n";
            echo "I'm giving up.\n";
            //TODO: clear-cache and restart clear-cache-watcher
            $watcher->stop();
        });

        $watcher->addListener('filewatcher.delete', array(__CLASS__, '_handleEvent'));
        $watcher->addListener('filewatcher.create', array(__CLASS__, '_handleEvent'));
        $watcher->addListener('filewatcher.modify', array(__CLASS__, '_handleEvent'));
        $watcher->addListener('filewatcher.move',   array(__CLASS__, '_handleEvent'));

        $watcher->start();
        exit;
    }

    public static function _handleEvent($event)
    {
        $eventStart = microtime(true);
        Kwf_Cache_Simple::resetZendCache(); //reset to re-fetch namespace
        if (substr($event->filename, -4)=='.css' || substr($event->filename, -3)=='.js' || substr($event->filename, -5)=='.scss' || substr($event->filename, -15)=='.underscore.tpl') {
            echo "asset modified\n";
            if ($event instanceof Event\Modify) {

                //there is no cache for individual files, scss cache is handled in Kwf_Assets_Dependency_File_Scss using mtime

                $assetsType = substr($event->filename, strrpos($event->filename, '.')+1);
                if ($assetsType == 'scss') $assetsType = 'css';
                if ($assetsType == 'tpl') $assetsType = 'js';
                self::_clearAssetsAll($assetsType);

            } else if ($event instanceof Event\Create || $event instanceof Event\Delete || $event instanceof Event\Move) {

                self::_clearAssetsDependencies();

                $assetsType = substr($event->filename, strrpos($event->filename, '.')+1);
                if ($assetsType == 'scss') $assetsType = 'css';
                if ($assetsType == 'tpl') $assetsType = 'js';
                self::_clearAssetsAll($assetsType);
            }

        } else if (self::_endsWith($event->filename, '/dependencies.ini')) {
            if ($event instanceof Event\Modify) {

                self::_clearAssetsDependencies();

                self::_clearAssetsAll();
            }
        } else if (preg_match('#/config([^/]*)?\.ini$#', $event->filename)) { //config.ini, configPoi.ini, config.local.ini (in any directory)
            if ($event instanceof Event\Modify) {

                $section = call_user_func(array(Kwf_Setup::$configClass, 'getDefaultConfigSection'));
                $cacheId = 'config_'.str_replace('-', '_', $section);
                Kwf_Config_Cache::getInstance()->remove($cacheId);
                echo "removed config cache\n";

                $apcCacheId = $cacheId.getcwd();
                $cacheIds = array(
                    $apcCacheId,
                    $apcCacheId.'mtime',
                );
                $simpleCacheStaticPrefixes = array(
                    'config-',
                    'configAr-',
                );

                if (Kwf_Util_Apc::isAvailable()) {
                    self::_clearApcCache(array(
                        'cacheIds' => $cacheIds,
                        'clearCacheSimpleStatic' => $simpleCacheStaticPrefixes
                    ));
                    echo "cleared apc config cache\n";
                } else {
                    foreach ($simpleCacheStaticPrefixes as $i) {
                        Kwf_Cache_SimpleStatic::clear($i);
                    }
                }

                $cmd = Kwf_Config::getValue('server.phpCli')." bootstrap.php clear-cache --type=setup";
                exec($cmd, $out, $ret);
                echo "cleared setup.php cache";
                if ($ret) echo " [FAILED]";
                echo "\n";
            }
        } else if (preg_match('#(Acl|MenuConfig)\.php$#', $event->filename)) {
            if ($event instanceof Event\Modify) {
                Kwf_Acl::clearCache();
                echo "cleared acl cache...\n";
            }
        } else if (self::_endsWith($event->filename, '.twig')) {
            $loader = new Twig_Loader_Filesystem('.');
            $twig = new Twig_Environment($loader, array(
                'cache' => 'cache/twig'
            ));
            $cacheFile = $event->filename;
            $cacheFile = substr($cacheFile, strlen(getcwd())+1);
            $cacheFile = $twig->getCacheFilename($cacheFile);

            if (file_exists($cacheFile)) {
                unlink($cacheFile);
                echo "cleared twig cache file '$cacheFile' for template '$event->filename'\n";
            }
        }

        if (self::_startsWith($event->filename, getcwd().'/components')
            || self::_startsWith($event->filename, getcwd().'/theme')
            || self::_startsWith($event->filename, KWF_PATH.'/Kwc')
            || (defined('VKWF_PATH') && self::_startsWith($event->filename, VKWF_PATH.'/Vkwc'))
        ) {
            $cls = null;
            foreach (explode(PATH_SEPARATOR, get_include_path()) as $ip) {
                if (substr($ip, 0, 1) == '.') $ip = getcwd().substr($ip, 1);
                if (substr($ip, 0, 1) != '/') $ip = getcwd().'/'.$ip;
                if (self::_startsWith($event->filename, $ip)) {
                    $cls = str_replace('/', '_', substr($event->filename, strlen($ip)+1, -4));
                }
            }

            if (!$cls) {
                echo "unknown component class?!\n";
                return;
            }
            if (!self::_endsWith($event->filename, '/Component.php')) {
                //other component file
                $cls = substr($cls, 0, strrpos($cls, '_')).'_Component';
            }
            echo "component $cls\n";

            if (!@class_exists($cls)) {
                echo "parse error: $cls\n";
                return;
            }

            $matchingClasses = array();
            try {
                foreach (Kwc_Abstract::getComponentClasses() as $c) {
                    if (is_instance_of($c, $cls)) {
                        $matchingClasses[] = $c;
                    }
                }
            } catch (Exception $e) {}

            if (self::_endsWith($event->filename, '/Admin.php') ||
                       self::_endsWith($event->filename, '/Master.tpl') ||
                       self::_endsWith($event->filename, '/Component.tpl') ||
                       self::_endsWith($event->filename, '/Partial.tpl') ||
                       self::_endsWith($event->filename, '/Master.twig') ||
                       self::_endsWith($event->filename, '/Component.twig') ||
                       self::_endsWith($event->filename, '/Partial.twig') ||
                       self::_endsWith($event->filename, '/Controller.php') ||
                       self::_endsWith($event->filename, '/FrontendForm.php') ||
                       self::_endsWith($event->filename, '/Form.php') ||
                       self::_endsWith($event->filename, '/Component.css') ||
                       self::_endsWith($event->filename, '/Component.scss')
            ) {
                if ($event instanceof Event\Create || $event instanceof Event\Delete) {
                    echo "recalculate 'componentFiles' setting because comopnent file got removed/added...\n";
                    self::_clearComponentSettingsCache($matchingClasses, 'componentFiles');
                }
            }

            if (   self::_endsWith($event->filename, '/Component.php') ||
                self::_endsWith($event->filename, '/FrontendForm.php') //viewCache for forms is enabled
            ) {
                if ($event instanceof Event\Modify) {
                    self::_clearComponentSettingsCache($matchingClasses);

                    //view cache can depend on settings
                    $s = new Kwf_Model_Select();
                    $s->whereEquals('component_class', $matchingClasses);
                    self::_deleteViewCache($s);
                }
            } else if (self::_endsWith($event->filename, '/Component.css') || self::_endsWith($event->filename, '/Component.scss')) {
                //MODIFY already handled above (assets)
                //CREATE/DELETE also handled above
            } else if (self::_endsWith($event->filename, '/Master.tpl') || self::_endsWith($event->filename, '/Master.twig')) {
                if ($event instanceof Event\Modify) {
                    $s = new Kwf_Model_Select();
                    //all component_classes
                    $s->whereEquals('type', 'master');
                    self::_deleteViewCache($s);
                }
            } else if (self::_endsWith($event->filename, '/Component.tpl') || self::_endsWith($event->filename, '/Component.twig')) {
                if ($event instanceof Event\Modify) {
                    $s = new Kwf_Model_Select();
                    $s->whereEquals('component_class', $matchingClasses);
                    $s->whereEquals('type', 'component');
                    $s->whereEquals('renderer', 'component');
                    self::_deleteViewCache($s);
                }
            } else if (self::_endsWith($event->filename, '/Partial.tpl') || self::_endsWith($event->filename, '/Partial.twig')) {
                if ($event instanceof Event\Modify) {
                    $s = new Kwf_Model_Select();
                    $s->whereEquals('component_class', $matchingClasses);
                    $s->whereEquals('type', 'partial');
                    self::_deleteViewCache($s);
                }
            } else if (self::_endsWith($event->filename, '/Mail.html.tpl') || self::_endsWith($event->filename, '/Mail.html.twig')) {
                if ($event instanceof Event\Modify) {
                    $s = new Kwf_Model_Select();
                    $s->whereEquals('component_class', $matchingClasses);
                    $s->whereEquals('type', 'component');
                    $s->whereEquals('renderer', 'mail_html');
                    self::_deleteViewCache($s);
                }
            } else if (self::_endsWith($event->filename, '/Mail.txt.tpl') || self::_endsWith($event->filename, '/Mail.txt.twig')) {
                if ($event instanceof Event\Modify) {
                    $s = new Kwf_Model_Select();
                    $s->whereEquals('component_class', $matchingClasses);
                    $s->whereEquals('type', 'component');
                    $s->whereEquals('renderer', 'mail_txt');
                    self::_deleteViewCache($s);
                }
            }
        }
        echo "finished in ".round((microtime(true)-$eventStart)*1000, 2)."ms\n\n";
    }

    private static function _clearApcCache($params)
    {
        echo "APC: ";
        foreach ($params as $k=>$i) {
            if (is_array($i)) {
                $params[$k] = implode(',', $i);
            }
        }
        Kwf_Util_Apc::callClearCacheByCli($params, array('outputFn' => 'printf'));
    }

    private static function _getComponentClassesFromGeneratorsSetting($generators)
    {
        $ret = array();
        foreach ($generators as $gen) {
            $cmpClasses = $gen['component'];
            if (!is_array($cmpClasses)) $cmpClasses = array($cmpClasses);
            foreach ($cmpClasses as $cmpClass) {
                if ($cmpClass) $ret[] = $cmpClass;
            }
        }
        return array_unique($ret);
    }

    private static function _clearComponentSettingsCache($componentClasses, $setting = null)
    {
        Kwf_Component_Abstract::resetSettingsCache();

        $settings = Kwf_Component_Settings::_getSettingsCached();

        $dependenciesChanged = false;
        $generatorssChanged = false;
        $dimensionsChanged = false;
        $menuConfigChanged = false;
        foreach ($componentClasses as $c) {
            Kwf_Component_Settings::$_rebuildingSettings = true;
            if ($setting) {
                //a single setting changed
                $newSettings = $settings[$c];
                $newSettings[$setting] = Kwc_Abstract::getSetting($c, $setting);
            } else {
                //all settings might have changed
                $newSettings = Kwf_Component_Settings::_getSettingsIncludingPreComputed($c);
            }
            Kwf_Component_Settings::$_rebuildingSettings = false;

            if ($newSettings['assets'] != $settings[$c]['assets']
                || $newSettings['assetsAdmin'] != $settings[$c]['assetsAdmin']
            ) {
                $dependenciesChanged = true;
            }
            if ($newSettings['generators'] != $settings[$c]['generators']) {
                $generatorssChanged = true;
                $oldChildComponentClasses = self::_getComponentClassesFromGeneratorsSetting($settings[$c]['generators']);
                $newChildComponentClasses = self::_getComponentClassesFromGeneratorsSetting($newSettings['generators']);
            }
            if (isset($newSettings['dimensions']) && $newSettings['dimensions'] != $settings[$c]['dimensions']) {
                $dimensionsChanged = true;
            }
            if (isset($newSettings['menuConfig']) && $newSettings['menuConfig'] != $settings[$c]['menuConfig']) {
                $menuConfigChanged = true;
            }
            $settings[$c] = $newSettings;
        }

        echo "refreshed component settings cache...\n";

        if ($dependenciesChanged) {
            echo "assets changed...\n";
            self::_clearAssetsDependencies();
            self::_clearAssetsAll();
        }

        $clearCacheSimpleStatic = array(
            'has-', //Kwf_Component_Abstract::hasSetting
            'cs-', //Kwf_Component_Abstract::getSetting
            'flag-', //Kwf_Component_Abstract::getFlag
            'componentClasses-', //Kwf_Component_Abstract::getComponentClasses
            'recCCGen-', //Kwf_Component_Data::getRecursiveChildComponents
            'genInst-', //Kwf_Component_Generator_Abstract::getInstances
            'childComponentClasses-', //Kwf_Component_Generator_Abstract::getChildComponentClasses
        );

        if ($generatorssChanged) {
            echo "generators changed...\n";
            echo count(Kwc_Abstract::getComponentClasses())." component classes (previously)\n";

            $m = Kwf_Component_Cache::getInstance()->getModel('url');
            foreach ($m->getRows() as $r) {
                Kwf_Cache_Simple::delete('url-'.$r->url);
                $r->delete();
            }
            foreach ($newChildComponentClasses as $cmpClass) {
                if (!in_array($cmpClass, Kwc_Abstract::getComponentClasses())) {
                    self::_loadSettingsRecursive($settings, $cmpClass);
                }
            }
            $removedComponentClasses = array_diff($oldChildComponentClasses, $newChildComponentClasses);
            foreach ($removedComponentClasses as $removedCls) {
                self::_removeSettingsRecursive($settings, $removedCls);
            }
        }
        file_put_contents('build/component/settings', serialize($settings));

        echo "cleared component settings apc cache...\n";
        if (Kwf_Util_Apc::isAvailable()) {
            self::_clearApcCache(array(
                'clearCacheSimpleStatic' => $clearCacheSimpleStatic,
            ));
        } else {
            foreach ($clearCacheSimpleStatic as $i) {
                Kwf_Cache_SimpleStatic::clear($i);
            }
        }
        echo "\n";

        if ($dimensionsChanged) {
            echo "dimensions changed...\n";
            $clearCacheSimple = array();
            foreach ($componentClasses as $c) {
                $idPrefix = str_replace(array('.', '>'), array('___', '____'), $c) . '_';
                $clearCacheSimple[] = 'media-output-'.$idPrefix;
                $clearCacheSimple[] = 'media-output-mtime-'.$idPrefix;
                foreach (glob('cache/media/'.$idPrefix.'*') as $f) {
                    echo $f." [DELETED]\n";
                    unlink($f);
                }
            }
            Kwf_Cache_Simple::delete($clearCacheSimple);
            echo "cleared media cache...\n";
        }
        if ($menuConfigChanged) {
            echo "menu config changed...\n";
            Kwf_Acl::clearCache();
            echo "cleared acl cache...\n";
        }

        $dependentComponentClasses = array();
        foreach (Kwc_Abstract::getComponentClasses() as $c) {
            if (strpos($c, '.') !== false) {
                $params = substr($c, strpos($c, '.')+1);
                foreach ($componentClasses as $i) {
                    if (strpos($params, $i)!==false) {
                        $dependentComponentClasses[] = $c;
                    }
                }
            }
        }
        if ($dependentComponentClasses) {
            echo "dependent componentClasses: ".count($dependentComponentClasses)." (Cc, Trl)\n";
            echo implode(', ', $dependentComponentClasses)."\n";
            self::_clearComponentSettingsCache($dependentComponentClasses, $setting);
        }
    }

    private static function _loadSettingsRecursive(&$settings, $cmpClass)
    {
        echo "$cmpClass is brand new! (not yet in componentClasses)\n";
        Kwf_Component_Settings::$_rebuildingSettings = true;
        $settings[$cmpClass] = Kwf_Component_Settings::_getSettingsIncludingPreComputed($cmpClass);
        Kwf_Component_Settings::$_rebuildingSettings = false;
        foreach (self::_getComponentClassesFromGeneratorsSetting($settings[$cmpClass]['generators']) as $c) {
            if (!isset($settings[$c])) {
                self::_loadSettingsRecursive($settings, $c);
            }
        }
    }

    private static function _removeSettingsRecursive(&$settings, $removedCls)
    {
        echo "removed component class: $removedCls\n";

        $stillUsed = false;
        foreach (Kwc_Abstract::getComponentClasses() as $cls) {
            if (!isset($settings[$cls])) continue;
            if ($cls != $removedCls && in_array($removedCls, self::_getComponentClassesFromGeneratorsSetting($settings[$cls]['generators']))) {
                $stillUsed = true;
                break;
            }
        }
        if (!$stillUsed) {
            echo "not used anymore, removed it really\n";
            $generators = $settings[$removedCls]['generators'];
            unset($settings[$removedCls]);
            foreach (self::_getComponentClassesFromGeneratorsSetting($generators) as $c) {
                self::_removeSettingsRecursive($settings, $c);
            }
        }

    }

    private static function _deleteViewCache(Kwf_Model_Select $s)
    {
        $countDeleted = Kwf_Component_Cache::getInstance()->deleteViewCache($s);
        echo "deleted ".$countDeleted." view cache entries\n";
    }

    private static function _clearAssetsDependencies()
    {
        //there is no cache containting dependencies
    }

    private static function _clearAssetsAll($fileType = null)
    {
        if (!$fileType) {
            self::_clearAssetsAll('js');
            self::_clearAssetsAll('css');
            return;
        }
        $fileNames = array(
            'cache/assets/output-cache-ids-'.$fileType,
            'build/assets/output-cache-ids-'.$fileType,
        );
        $assetsCacheIds = array();
        $assetsBuildCacheIds = array();
        $simpleStaticCacheIds = array();
        foreach ($fileNames as $fileName) {
            if (file_exists($fileName)) {
                $cacheIds = file($fileName);
                unlink($fileName);
                foreach ($cacheIds as $cacheId) {
                    $cacheId = trim($cacheId);
                    echo $cacheId."\n";
                    $assetsCacheIds[] = $cacheId;
                    $assetsBuildCacheIds[] = $cacheId;
                    $simpleStaticCacheIds[] = 'as_'.$cacheId.'_gzip';
                    $simpleStaticCacheIds[] = 'as_'.$cacheId.'_deflate';
                    $assetsCacheIds[] = $cacheId.'_map';
                    $assetsBuildCacheIds[] = $cacheId.'_map';
                    $simpleStaticCacheIds[] = 'as_'.$cacheId.'_map_gzip';
                    $simpleStaticCacheIds[] = 'as_'.$cacheId.'_map_deflate';
                }
            }
        }
        if ($assetsCacheIds) {
            Kwf_Assets_Cache::getInstance()->remove($assetsCacheIds);
        }
        if ($assetsBuildCacheIds) {
            Kwf_Assets_BuildCache::getInstance()->remove($assetsBuildCacheIds);
        }
        if ($simpleStaticCacheIds) {
            Kwf_Cache_SimpleStatic::_delete($simpleStaticCacheIds);
        }

        $a = new Kwf_Util_Build_Types_Assets();
        $a->flagAllPackagesOutdated($fileType);

        self::_informDuckcast($fileType);

        if ($fileType == 'css') {
            self::_clearAssetsAll('0.css');
            self::_clearAssetsAll('1.css');
            self::_clearAssetsAll('ie8.css');
        }
        if ($fileType == 'js') {
            self::_clearAssetsAll('defer.js');
        }

    }
}

redpajama_set_name: RedPajamaGithub


text:
import AsyncQueue from '../utils/asyncQueueStructures';

let asyncQueue = new AsyncQueue();

const endAsync = () => ({ type: 'ASYNC_INACTIVE' });

const startAsync = () => ({ type: 'ASYNC_ACTIVE' });

const callAsync = (dispatchFn, ...args) => (dispatch, getState) => {
  if (!asyncQueue.size) {
    asyncQueue.listenForEnd(dispatch.bind(null, endAsync()));
  }
  asyncQueue.add(dispatchFn, getState().async.delay, ...args);
  dispatch(startAsync());
};

const setDelay = (newDelay) => {
  return {
    type: 'SET_DELAY',
    newDelay
  };
};

export default {
  callAsync,
  endAsync,
  setDelay,
  startAsync
};


text:
package com.xinyu.mwp.entity;

import com.xinyu.mwp.annotation.FieldJsonKey;

/**
 * Created by Benjamin on 17/1/12.
 */

public class DealAllGoodsTopEntity extends BaseEntity {
    @FieldJsonKey("money")
    private String money;
    @FieldJsonKey("hands")
    private String hands;
    @FieldJsonKey("orders")
    private String orders;

    public String getMoney() {
        return money;
    }

    public void setMoney(String money) {
        this.money = money;
    }

    public String getHands() {
        return hands;
    }

    public void setHands(String hands) {
        this.hands = hands;
    }

    public String getOrders() {
        return orders;
    }

    public void setOrders(String orders) {
        this.orders = orders;
    }
}


text:
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ListListOperator.cs" company="PropertyTools">
//   Copyright (c) 2014 PropertyTools contributors
// </copyright>
// <summary>
//   Represents an operator for DataGrid when its ItemsSource is of type IList>IList<.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace PropertyTools.Wpf
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;

    /// <summary>
    /// Represents an operator for <see cref="DataGrid" /> when its ItemsSource is of type <see cref="IList" />><see cref="IList" /><.
    /// </summary>
    public class ListListOperator : DataGridOperator
    {
        public override bool CanDeleteColumns(DataGrid owner)
        {
            return true;
        }

        public override bool CanInsertColumns(DataGrid owner)
        {
            return true;
        }

        public override bool DeleteItem(DataGrid owner, int index)
        {
            var list = owner.ItemsSource;
            if (list == null)
            {
                return false;
            }

            if (index < 0 || index >= list.Count)
            {
                return false;
            }

            if (owner.ItemsInColumns)
            {
                foreach (var row in owner.ItemsSource.OfType<IList>().Where(row => index < row.Count))
                {
                    row.RemoveAt(index);
                }
            }
            else
            {
                list.RemoveAt(index);
            }

            return true;
        }

        public override void DeleteColumns(DataGrid owner)
        {
            if (owner.ColumnHeadersSource != null)
            {
                var from = Math.Min(owner.CurrentCell.Column, owner.SelectionCell.Column);
                var to = Math.Max(owner.CurrentCell.Column, owner.SelectionCell.Column);
                for (var i = to; i >= from; i--)
                {
                    owner.ColumnHeadersSource.RemoveAt(i);
                }
            }

            base.DeleteColumns(owner);
        }

        public override void InsertColumns(DataGrid owner)
        {
            var from = Math.Min(owner.CurrentCell.Column, owner.SelectionCell.Column);
            var to = Math.Max(owner.CurrentCell.Column, owner.SelectionCell.Column);
            for (var i = from; i <= to; i++)
            {
                this.InsertColumnHeader(owner, i);
            }

            base.InsertColumns(owner);
        }

        /// <summary>
        /// Insert column header to ColumnHeadersSource.
        /// </summary>
        /// <param name="owner">The data grid.</param>
        /// <param name="index">The position.</param>
        private void InsertColumnHeader(DataGrid owner, int index)
        {
            if (owner.ColumnHeadersSource == null)
            {
                return;
            }

            var newItem = owner.CreateColumnHeader(index);
            if (index >= 0 && index < owner.ColumnHeadersSource.Count)
            {
                owner.ColumnHeadersSource.Insert(index, newItem);
            }
            else
            {
                owner.ColumnHeadersSource.Add(newItem);
            }
        }

        /// <summary>
        /// Generate column definitions based on a list of items.
        /// </summary>
        /// <param name="list">The list of items.</param>
        /// <returns>A sequence of column definitions.</returns>
        protected override IEnumerable<ColumnDefinition> GenerateColumnDefinitions(IList list)
        {
            var innerType = TypeHelper.GetInnerTypeOfList(list) ?? typeof(object);
            var firstRow = list.Cast<IList>().FirstOrDefault();
            var columns = firstRow?.Count ?? 0;
            for (var ii = 0; ii < columns; ii++)
            {
                yield return
                     new ColumnDefinition
                     {
                         Header = innerType.Name,
                         HorizontalAlignment = this.DefaultHorizontalAlignment,
                         Width = this.DefaultColumnWidth
                     };
            }
        }

        /// <summary>
        /// Gets the item in cell.
        /// </summary>
        /// <param name="owner">The owner.</param>
        /// <param name="cell">The cell reference.</param>
        /// <returns>
        /// The <see cref="object" />.
        /// </returns>
        public override object GetItem(DataGrid owner, CellRef cell)
        {
            var list = owner.ItemsSource;
            var rowIndex = cell.Row;
            var columnIndex = cell.Column;
            if (list == null || rowIndex < 0 || columnIndex < 0)
            {
                return null;
            }

            if (rowIndex >= list.Count)
            {
                return null;
            }

            var row = list[rowIndex] as IList;
            if (row == null || columnIndex >= row.Count)
            {
                return null;
            }

            return ((IList)list[rowIndex])[columnIndex];
        }

        /// <summary>
        /// Inserts item to <see cref="DataGrid" />.
        /// </summary>
        /// <param name="owner">The owner.</param>
        /// <param name="index">The index.</param>
        /// <returns>
        /// Returns <c>true</c> if insertion is successful, <c>false</c> otherwise.
        /// </returns>
        public override bool InsertItem(DataGrid owner, int index)
        {
            var list = owner.ItemsSource;
            if (list == null)
            {
                return false;
            }

            var itemType = TypeHelper.GetItemType(list);

            var newList = this.CreateItem(owner, itemType) as IList;

            var innerType = TypeHelper.GetInnerTypeOfList(list);
            if (innerType == null)
            {
                return false;
            }

            if (owner.ItemsInRows)
            {
                if (newList != null)
                {
                    for (var ii = 0; ii < owner.Columns; ii++)
                    {
                        newList.Add(this.CreateItem(owner, innerType));
                    }

                    if (index < 0)
                    {
                        list.Add(newList);
                    }
                    else
                    {
                        list.Insert(index, newList);
                    }
                }
            }
            else
            {
                // insert/append one new element to each list.
                foreach (var row in list.OfType<IList>())
                {
                    var newItem = this.CreateItem(owner, innerType);
                    if (index < 0)
                    {
                        row.Add(newItem);
                    }
                    else
                    {
                        row.Insert(index, newItem);
                    }
                }
            }

            return true;
        }

        /// <summary>
        /// Sets value to item in cell.
        /// </summary>
        /// <param name="owner">The owner.</param>
        /// <param name="cell">The cell reference.</param>
        /// <param name="value">The value.</param>
        public override void SetValue(DataGrid owner, CellRef cell, object value)
        {
            var list = owner.ItemsSource;
            if (list == null || cell.Row < 0 || cell.Column < 0 || cell.Row >= list.Count)
            {
                return;
            }

            var row = list[cell.Row] as IList;
            if (row == null || cell.Column >= row.Count)
            {
                return;
            }

            row[cell.Column] = value;
        }

        /// <summary>
        /// Gets the binding path for the specified cell.
        /// </summary>
        /// <param name="owner">The owner.</param>
        /// <param name="cell">The cell.</param>
        /// <returns>
        /// The binding path
        /// </returns>
        public override string GetBindingPath(DataGrid owner, CellRef cell)
        {
            return $"[{cell.Row}][{cell.Column}]";
        }
    }
}


text:
let helper = require("./contacts-helper");

/*
   if running inside a web worker, need to pass debug messages to main thread as workers do not have console access
*/
let console_log = (msg) => { postMessage({ type: 'log', message: msg }); }
let console_dump = (msg) => { postMessage({ type: 'dump', message: msg }); }

/*
   fields:        desired fields to retrieve from phone storage backend
   searchTerm:    search only for contacts whose display_name start with this term
   debug          whether to print debug messages to the console
   worker:        wether we are running inside a web worker
*/
exports.getContactsFromBackend = ((fields,searchTerm,debug,worker) => {
  try {

    /* variables used in ios backend query */

    let contacts = []; // array containing contacts object to return
    let columnsToFetch = helper.getIOSQueryColumns(fields); // columns to return for each row

    var timer = new Date().getTime();

    /* getting all contacts */

    if (searchTerm===undefined) {
      var store = new CNContactStore(),
          error,
          fetch = CNContactFetchRequest.alloc().initWithKeysToFetch(columnsToFetch),
          nativeMutableArray = new NSMutableArray();

      fetch.unifyResults = true;
      fetch.predicate = null;

      store.enumerateContactsWithFetchRequestErrorUsingBlock(fetch, error, (c,s) => {
        nativeMutableArray.addObject(c);

        //transform raw data into desired data structure */
        contacts.push(helper.convertNativeCursorToContact(c,fields));
      });

      if (error && worker) { postMessage({ type: 'error', message: error }); }
      if (error && !worker) { return({ type: 'error', message: error }); }
    }

    /* getting contacts by searchTerm */

    if (searchTerm) {
      var store = new CNContactStore(),
          error,
          foundContacts = store.unifiedContactsMatchingPredicateKeysToFetchError(
            CNContact.predicateForContactsMatchingName(searchTerm),
            columnsToFetch, error
          );

      if (error && worker) { postMessage({ type: 'error', message: error }); }
      if (error && !worker) { return({ type: 'error', message: error }); }

      for (var i=0; i<foundContacts.count; i++) {
        //transform raw data into desired data structure */
        contacts.push(helper.convertNativeCursorToContact(foundContacts[i],fields));
      }
    }

    if (debug && worker) { console_log(`Processing data completed in ${(new Date().getTime() - timer)} ms!`); }
    if (debug && !worker) { console.log(`Processing data completed in ${(new Date().getTime() - timer)} ms!`); }

    return({ type: 'result', message: contacts });
  } catch (e) { return({ type: 'error', message: e }); }
});

/*
   fields:        desired fields to retrieve from phone storage backend
   searchTerm:    search only for contacts whose display_name start with this term
   debug          whether to print debug messages to the console
*/
exports.getContactFromBackendById = ((contactId,fields,debug) => {
  try {

    /* variables used in ios backend query */

    let contact = {}; // contact object to return
    let columnsToFetch = helper.getIOSQueryColumns(fields); // columns to return for each row

    var timer = new Date().getTime();

    /* getting single contact by contact_id */

    var store = new CNContactStore(),
        error,
        foundContact = store.unifiedContactWithIdentifierKeysToFetchError(contactId,columnsToFetch,error);

    if (error) { return({ type: 'error', message: error }); }

    //transform raw data into desired data structure */
    contact = helper.convertNativeCursorToContact(foundContact,fields);

    if (debug) { console.log(`Processing data completed in ${(new Date().getTime() - timer)} ms!`); }

    return({ type: 'result', message: contact });
  } catch (e) { return({ type: 'error', message: e }); }
});


text:
<?php
  include 'header.inc';
  include 'body.inc';
  include 'logosearchmenu.inc';

  $gitname = trim(escapeshellcmd(strip_tags($_GET["gitname"])));
  if (empty($gitname)) {
    die("error: missing git name");
  }
  if (strpos($gitname, ".") !== false) {
      $projectname = explode(".", $gitname)[0];
  } else {
      $projectname = $gitname;
  }
?>

Draw a cover image for the <?php echo $projectname; ?> project

  <button onClick="saveToTemporaryFile();">Save image</button>

<?php
include 'backlink.inc';
include 'footer.inc';
?>


text:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>uk.co.inhealthcare.open</groupId>
		<artifactId>open-inhealthcare</artifactId>
		<version>0.0.1-SNAPSHOT</version>
	</parent>

	<artifactId>open-inhealthcare-core</artifactId>
	<name>Open Inhealthcare Core</name>

	<dependencies>

		<dependency>
			<groupId>uk.co.inhealthcare.open</groupId>
			<artifactId>open-itk-api</artifactId>
			<version>${project.version}</version>
		</dependency>
		<dependency>
			<groupId>uk.co.inhealthcare.open</groupId>
			<artifactId>open-itk-base</artifactId>
			<version>${project.version}</version>
		</dependency>

		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<artifactId>camel-spring</artifactId>
			<groupId>org.apache.camel</groupId>
			<version>2.13.0</version>
		</dependency>
		<dependency>
			<artifactId>spring-test</artifactId>
			<groupId>org.springframework</groupId>
			<version>2.5.5</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.17</version>
		</dependency>
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-mail</artifactId>
			<version>2.13.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-hl7</artifactId>
			<version>2.13.0</version>
		</dependency>
		<dependency>
			<groupId>org.apache.camel</groupId>
			<artifactId>camel-mina2</artifactId>
			<version>2.13.0</version>
		</dependency>
		<dependency>
			<groupId>ca.uhn.hapi</groupId>
			<artifactId>hapi-structures-v24</artifactId>
			<version>2.1</version>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
			<version>1.7.7</version>
		</dependency>
		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-log4j12</artifactId>
			<version>1.7.7</version>
		</dependency>
		<dependency>
			<groupId>org.jbehave</groupId>
			<artifactId>jbehave-core</artifactId>
			<version>4.0-beta-8</version>
		</dependency>
	</dependencies>

</project>


text:
{% extends "admin/change_form.html" %}
{% load i18n admin_modify %}

{% block bodyclass %}change-form{% endblock %}

{% block breadcrumbs %}{% if not is_popup %}

     {% trans "Home" %} ›
     {% trans "mailchimp overview" %} ›
     {{ campaign.mc.subject|truncatewords:18 }}

{% endif %}{% endblock %}

{% block content %}

{% trans "Campaign Information:" %} {{ campaign.mc.subject }}

{% trans "Sent Date" %}

{{ campaign.sent_date }}

{% trans "View on Mailchimp" %}

{% trans "Click here" %}

			{% if campaign.object %}

{% trans "object" %}

{{ campaign.object }}

			{% endif %}

{% endblock %}


text:
#include "call_observer.h"

#include <tuple>

#include "spy_base.h"

using core::Interval;

namespace {

// Minimum byte gap between memory observations before globbing together.
const size_t MEMORY_MERGE_THRESHOLD = 256;

// Size of the temporary heap buffer to use when the scratch stack buffer is
// filled.
const size_t SCRATCH_BUFFER_SIZE = 512*1024;

// Maximum size of the CallObserver's extras list.
const size_t MAX_EXTRAS = 16;

// Buffer creating function for scratch allocator.
std::tuple<uint8_t*, size_t> createBuffer(size_t request_size,
                                                 size_t min_buffer_size) {
    size_t size =
        request_size > min_buffer_size ? request_size : min_buffer_size;
    return std::make_tuple(new uint8_t[size], size);
}

// Buffer releasing function for scratch allocator.
void releaseBuffer(uint8_t* buffer) { delete[] buffer; }

}  // anonymous namespace

namespace gapii {
// Creates a CallObserver with a given spy and applies the memory space for
// observation data from the spy instance.
CallObserver::CallObserver(SpyBase* spy_p, uint8_t api)
    : mSpyPtr(spy_p),
      mCurrentCommandName(nullptr),
      mObserveApplicationPool(spy_p->shouldObserveApplicationPool()),
      mScratch(
          [](size_t size) { return createBuffer(size, SCRATCH_BUFFER_SIZE); },
          [](uint8_t* buffer) { return releaseBuffer(buffer); }),
      mError(GLenum::GL_NO_ERROR),
      mApi(api) {
    mPendingObservations.setMergeThreshold(MEMORY_MERGE_THRESHOLD);
    mExtras = mScratch.vector<google::protobuf::Message*>(MAX_EXTRAS);
}

// Releases the observation data memory at the end.
CallObserver::~CallObserver() {}

void CallObserver::read(const void* base, uint64_t size) {
    if (!mSpyPtr->should_trace(mApi)) return;
    if (size > 0) {
        uintptr_t start = reinterpret_cast<uintptr_t>(base);
        uintptr_t end = start + static_cast<uintptr_t>(size);
        mPendingObservations.merge(Interval<uintptr_t>{start, end});
    }
}

void CallObserver::write(const void* base, uint64_t size) {
    if (!mSpyPtr->should_trace(mApi)) return;
    if (size > 0) {
        uintptr_t start = reinterpret_cast<uintptr_t>(base);
        uintptr_t end = start + static_cast<uintptr_t>(size);
        mPendingObservations.merge(Interval<uintptr_t>{start, end});
    }
}

void CallObserver::observePending() {
    if (!mSpyPtr->should_trace(mApi)) return;
    for (auto p : mPendingObservations) {
        core::Vector<uint8_t> data(reinterpret_cast<uint8_t*>(p.start()),
                                    p.end() - p.start());
        core::Id id = core::Id::Hash(data.data(), data.count());
        if (mSpyPtr->getResources().count(id) == 0) {
            atom_pb::Resource resource;
            resource.set_id(reinterpret_cast<const char*>(id.data), sizeof(id.data));
            resource.set_data(data.data(), data.count());
            mSpyPtr->getEncoder(mApi)->message(&resource);
            mSpyPtr->getResources().emplace(id);
        }
        auto observation = new memory_pb::Observation();
        observation->set_base(p.start());
        observation->set_size(data.count());
        observation->set_id(reinterpret_cast<const char*>(id.data), sizeof(id.data));
        addExtra(observation);
    }
    mPendingObservations.clear();
}

void CallObserver::invoke() {
    observePending();
    addExtra(new atom_pb::Invoke());
}

void CallObserver::encodeAndDeleteCommand(::google::protobuf::Message* cmd) {
    observePending();
    auto encoder = mSpyPtr->getEncoder(mApi);
    encoder->message(cmd);
    delete cmd;
    for (auto extra : mExtras) {
        encoder->message(extra);
        delete extra;
    }
    mExtras.clear();
}

bool CallObserver::isActive() const {
    return mSpyPtr->should_trace(mApi);
}

}  // namespace gapii


text:
package org.broadleafcommerce.vendor.authorizenet.web.expression;

import org.broadleafcommerce.common.payment.PaymentGatewayType;
import org.broadleafcommerce.common.web.payment.expression.AbstractPaymentGatewayFieldExtensionHandler;
import org.broadleafcommerce.common.web.payment.expression.PaymentGatewayFieldExtensionManager;
import org.broadleafcommerce.payment.service.gateway.AuthorizeNetConfiguration;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import net.authorize.AuthNetField;

/**
 * @author Chad Harchar (charchar)
 * @deprecated - Transparent Redirect is no longer used in favor of Accept.js integration
 */
@Deprecated
@Service("blAuthorizeNetFieldExtensionHandler")
public class AuthorizeNetFieldExtensionHandler extends AbstractPaymentGatewayFieldExtensionHandler {

    @Resource(name = "blPaymentGatewayFieldExtensionManager")
    protected PaymentGatewayFieldExtensionManager extensionManager;

    @Resource(name = "blAuthorizeNetConfiguration")
    protected AuthorizeNetConfiguration configuration;

    @PostConstruct
    public void init() {
        if (isEnabled()) {
            extensionManager.registerHandler(this);
        }
    }

    @Override
    public String getCreditCardHolderName() {
        return null;
    }

    @Override
    public String getCreditCardType() {
        return null;
    }

    @Override
    public String getCreditCardNum() {
        return AuthNetField.X_CARD_NUM.getFieldName();
    }

    @Override
    public String getCreditCardExpDate() {
        return AuthNetField.X_EXP_DATE.getFieldName();
    }

    @Override
    public String getCreditCardExpMonth() {
        return null;
    }

    @Override
    public String getCreditCardExpYear() {
        return null;
    }

    @Override
    public String getCreditCardCvv() {
        return AuthNetField.X_CARD_CODE.getFieldName();
    }

    @Override
    public String getBillToAddressFirstName() {
        return AuthNetField.X_FIRST_NAME.getFieldName();
    }

    @Override
    public String getBillToAddressLastName() {
        return AuthNetField.X_LAST_NAME.getFieldName();
    }

    @Override
    public String getBillToAddressCompanyName() {
        return AuthNetField.X_COMPANY.getFieldName();
    }

    @Override
    public String getBillToAddressLine1() {
        return AuthNetField.X_ADDRESS.getFieldName();
    }

    @Override
    public String getBillToAddressLine2() {
        return null;
    }

    @Override
    public String getBillToAddressCityLocality() {
        return AuthNetField.X_CITY.getFieldName();
    }

    @Override
    public String getBillToAddressStateRegion() {
        return AuthNetField.X_STATE.getFieldName();
    }

    @Override
    public String getBillToAddressPostalCode() {
        return AuthNetField.X_ZIP.getFieldName();
    }

    @Override
    public String getBillToAddressCountryCode() {
        return AuthNetField.X_COUNTRY.getFieldName();
    }

    @Override
    public String getBillToAddressPhone() {
        return AuthNetField.X_PHONE.getFieldName();
    }

    @Override
    public String getBillToAddressEmail() {
        return AuthNetField.X_EMAIL.getFieldName();
    }

    @Override
    public String getShipToAddressFirstName() {
        return AuthNetField.X_SHIP_TO_FIRST_NAME.getFieldName();
    }

    @Override
    public String getShipToAddressLastName() {
        return AuthNetField.X_SHIP_TO_LAST_NAME.getFieldName();
    }

    @Override
    public String getShipToAddressCompanyName() {
        return AuthNetField.X_SHIP_TO_COMPANY.getFieldName();
    }

    @Override
    public String getShipToAddressLine1() {
        return AuthNetField.X_SHIP_TO_ADDRESS.getFieldName();
    }

    @Override
    public String getShipToAddressLine2() {
        return null;
    }

    @Override
    public String getShipToAddressCityLocality() {
        return AuthNetField.X_SHIP_TO_CITY.getFieldName();
    }

    @Override
    public String getShipToAddressStateRegion() {
        return AuthNetField.X_SHIP_TO_STATE.getFieldName();
    }

    @Override
    public String getShipToAddressPostalCode() {
        return AuthNetField.X_SHIP_TO_ZIP.getFieldName();
    }

    @Override
    public String getShipToAddressCountryCode() {
        return AuthNetField.X_SHIP_TO_COUNTRY.getFieldName();
    }

    @Override
    public String getShipToAddressPhone() {
        return null;
    }

    @Override
    public String getShipToAddressEmail() {
        return null;
    }

    public PaymentGatewayType getHandlerType() {
        return configuration.getGatewayType();
    }
}


text:
from copy import deepcopy
from typing import Optional, Sequence, Tuple, List, Dict, TYPE_CHECKING, Set

from .lnutil import SENT, RECEIVED, LOCAL, REMOTE, HTLCOwner, UpdateAddHtlc, Direction, FeeUpdate
from .util import bh2u, bfh

if TYPE_CHECKING:
    from .json_db import StoredDict

class HTLCManager:

    def __init__(self, log:'StoredDict', *, initial_feerate=None):

        if len(log) == 0:
            initial = {
                'adds': {},              # "side who offered htlc" -> htlc_id -> htlc
                'locked_in': {},         # "side who offered htlc" -> action -> htlc_id -> whose ctx -> ctn
                'settles': {},           # "side who offered htlc" -> action -> htlc_id -> whose ctx -> ctn
                'fails': {},             # "side who offered htlc" -> action -> htlc_id -> whose ctx -> ctn
                'fee_updates': {},       # "side who initiated fee update" -> action -> list of FeeUpdates
                'revack_pending': False,
                'next_htlc_id': 0,
                'ctn': -1,               # oldest unrevoked ctx of sub
            }
            # note: "htlc_id" keys in dict are str! but due to json_db magic they can *almost* be treated as int...
            log[LOCAL] = deepcopy(initial)
            log[REMOTE] = deepcopy(initial)
            log['unacked_local_updates2'] = {}

        if 'unfulfilled_htlcs' not in log:
            log['unfulfilled_htlcs'] = {}  # htlc_id -> onion_packet

        # maybe bootstrap fee_updates if initial_feerate was provided
        if initial_feerate is not None:
            assert type(initial_feerate) is int
            for sub in (LOCAL, REMOTE):
                if not log[sub]['fee_updates']:
                    log[sub]['fee_updates'][0] = FeeUpdate(rate=initial_feerate, ctn_local=0, ctn_remote=0)
        self.log = log
        self._init_maybe_active_htlc_ids()

    def ctn_latest(self, sub: HTLCOwner) -> int:
        """Return the ctn for the latest (newest that has a valid sig) ctx of sub"""
        return self.ctn_oldest_unrevoked(sub) + int(self.is_revack_pending(sub))

    def ctn_oldest_unrevoked(self, sub: HTLCOwner) -> int:
        """Return the ctn for the oldest unrevoked ctx of sub"""
        return self.log[sub]['ctn']

    def is_revack_pending(self, sub: HTLCOwner) -> bool:
        """Returns True iff sub was sent commitment_signed but they did not
        send revoke_and_ack yet (sub has multiple unrevoked ctxs)
        """
        return self.log[sub]['revack_pending']

    def _set_revack_pending(self, sub: HTLCOwner, pending: bool) -> None:
        self.log[sub]['revack_pending'] = pending

    def get_next_htlc_id(self, sub: HTLCOwner) -> int:
        return self.log[sub]['next_htlc_id']

    ##### Actions on channel:

    def channel_open_finished(self):
        self.log[LOCAL]['ctn'] = 0
        self.log[REMOTE]['ctn'] = 0
        self._set_revack_pending(LOCAL, False)
        self._set_revack_pending(REMOTE, False)

    def send_htlc(self, htlc: UpdateAddHtlc) -> UpdateAddHtlc:
        htlc_id = htlc.htlc_id
        if htlc_id != self.get_next_htlc_id(LOCAL):
            raise Exception(f"unexpected local htlc_id. next should be "
                            f"{self.get_next_htlc_id(LOCAL)} but got {htlc_id}")
        self.log[LOCAL]['adds'][htlc_id] = htlc
        self.log[LOCAL]['locked_in'][htlc_id] = {LOCAL: None, REMOTE: self.ctn_latest(REMOTE)+1}
        self.log[LOCAL]['next_htlc_id'] += 1
        self._maybe_active_htlc_ids[LOCAL].add(htlc_id)
        return htlc

    def recv_htlc(self, htlc: UpdateAddHtlc) -> None:
        htlc_id = htlc.htlc_id
        if htlc_id != self.get_next_htlc_id(REMOTE):
            raise Exception(f"unexpected remote htlc_id. next should be "
                            f"{self.get_next_htlc_id(REMOTE)} but got {htlc_id}")
        self.log[REMOTE]['adds'][htlc_id] = htlc
        self.log[REMOTE]['locked_in'][htlc_id] = {LOCAL: self.ctn_latest(LOCAL)+1, REMOTE: None}
        self.log[REMOTE]['next_htlc_id'] += 1
        self._maybe_active_htlc_ids[REMOTE].add(htlc_id)

    def send_settle(self, htlc_id: int) -> None:
        next_ctn = self.ctn_latest(REMOTE) + 1
        if not self.is_htlc_active_at_ctn(ctx_owner=REMOTE, ctn=next_ctn, htlc_proposer=REMOTE, htlc_id=htlc_id):
            raise Exception(f"(local) cannot remove htlc that is not there...")
        self.log[REMOTE]['settles'][htlc_id] = {LOCAL: None, REMOTE: next_ctn}

    def recv_settle(self, htlc_id: int) -> None:
        next_ctn = self.ctn_latest(LOCAL) + 1
        if not self.is_htlc_active_at_ctn(ctx_owner=LOCAL, ctn=next_ctn, htlc_proposer=LOCAL, htlc_id=htlc_id):
            raise Exception(f"(remote) cannot remove htlc that is not there...")
        self.log[LOCAL]['settles'][htlc_id] = {LOCAL: next_ctn, REMOTE: None}

    def send_fail(self, htlc_id: int) -> None:
        next_ctn = self.ctn_latest(REMOTE) + 1
        if not self.is_htlc_active_at_ctn(ctx_owner=REMOTE, ctn=next_ctn, htlc_proposer=REMOTE, htlc_id=htlc_id):
            raise Exception(f"(local) cannot remove htlc that is not there...")
        self.log[REMOTE]['fails'][htlc_id] = {LOCAL: None, REMOTE: next_ctn}

    def recv_fail(self, htlc_id: int) -> None:
        next_ctn = self.ctn_latest(LOCAL) + 1
        if not self.is_htlc_active_at_ctn(ctx_owner=LOCAL, ctn=next_ctn, htlc_proposer=LOCAL, htlc_id=htlc_id):
            raise Exception(f"(remote) cannot remove htlc that is not there...")
        self.log[LOCAL]['fails'][htlc_id] = {LOCAL: next_ctn, REMOTE: None}

    def send_update_fee(self, feerate: int) -> None:
        fee_update = FeeUpdate(rate=feerate,
                               ctn_local=None, ctn_remote=self.ctn_latest(REMOTE) + 1)
        self._new_feeupdate(fee_update, subject=LOCAL)

    def recv_update_fee(self, feerate: int) -> None:
        fee_update = FeeUpdate(rate=feerate,
                               ctn_local=self.ctn_latest(LOCAL) + 1, ctn_remote=None)
        self._new_feeupdate(fee_update, subject=REMOTE)

    def _new_feeupdate(self, fee_update: FeeUpdate, subject: HTLCOwner) -> None:
        # overwrite last fee update if not yet committed to by anyone; otherwise append
        d = self.log[subject]['fee_updates']
        #assert type(d) is StoredDict
        n = len(d)
        last_fee_update = d[n-1]
        if (last_fee_update.ctn_local is None or last_fee_update.ctn_local > self.ctn_latest(LOCAL)) \
                and (last_fee_update.ctn_remote is None or last_fee_update.ctn_remote > self.ctn_latest(REMOTE)):
            d[n-1] = fee_update
        else:
            d[n] = fee_update

    def send_ctx(self) -> None:
        assert self.ctn_latest(REMOTE) == self.ctn_oldest_unrevoked(REMOTE), (self.ctn_latest(REMOTE), self.ctn_oldest_unrevoked(REMOTE))
        self._set_revack_pending(REMOTE, True)

    def recv_ctx(self) -> None:
        assert self.ctn_latest(LOCAL) == self.ctn_oldest_unrevoked(LOCAL), (self.ctn_latest(LOCAL), self.ctn_oldest_unrevoked(LOCAL))
        self._set_revack_pending(LOCAL, True)

    def send_rev(self) -> None:
        self.log[LOCAL]['ctn'] += 1
        self._set_revack_pending(LOCAL, False)
        # htlcs
        for htlc_id in self._maybe_active_htlc_ids[REMOTE]:
            ctns = self.log[REMOTE]['locked_in'][htlc_id]
            if ctns[REMOTE] is None and ctns[LOCAL] <= self.ctn_latest(LOCAL):
                ctns[REMOTE] = self.ctn_latest(REMOTE) + 1
        for log_action in ('settles', 'fails'):
            for htlc_id in self._maybe_active_htlc_ids[LOCAL]:
                ctns = self.log[LOCAL][log_action].get(htlc_id, None)
                if ctns is None: continue
                if ctns[REMOTE] is None and ctns[LOCAL] <= self.ctn_latest(LOCAL):
                    ctns[REMOTE] = self.ctn_latest(REMOTE) + 1
        self._update_maybe_active_htlc_ids()
        # fee updates
        for k, fee_update in list(self.log[REMOTE]['fee_updates'].items()):
            if fee_update.ctn_remote is None and fee_update.ctn_local <= self.ctn_latest(LOCAL):
                fee_update.ctn_remote = self.ctn_latest(REMOTE) + 1

    def recv_rev(self) -> None:
        self.log[REMOTE]['ctn'] += 1
        self._set_revack_pending(REMOTE, False)
        # htlcs
        for htlc_id in self._maybe_active_htlc_ids[LOCAL]:
            ctns = self.log[LOCAL]['locked_in'][htlc_id]
            if ctns[LOCAL] is None and ctns[REMOTE] <= self.ctn_latest(REMOTE):
                ctns[LOCAL] = self.ctn_latest(LOCAL) + 1
        for log_action in ('settles', 'fails'):
            for htlc_id in self._maybe_active_htlc_ids[REMOTE]:
                ctns = self.log[REMOTE][log_action].get(htlc_id, None)
                if ctns is None: continue
                if ctns[LOCAL] is None and ctns[REMOTE] <= self.ctn_latest(REMOTE):
                    ctns[LOCAL] = self.ctn_latest(LOCAL) + 1
        self._update_maybe_active_htlc_ids()
        # fee updates
        for k, fee_update in list(self.log[LOCAL]['fee_updates'].items()):
            if fee_update.ctn_local is None and fee_update.ctn_remote <= self.ctn_latest(REMOTE):
                fee_update.ctn_local = self.ctn_latest(LOCAL) + 1

        # no need to keep local update raw msgs anymore, they have just been ACKed.
        self.log['unacked_local_updates2'].pop(self.log[REMOTE]['ctn'], None)

    def _update_maybe_active_htlc_ids(self) -> None:
        # - Loosely, we want a set that contains the htlcs that are
        #   not "removed and revoked from all ctxs of both parties". (self._maybe_active_htlc_ids)
        #   It is guaranteed that those htlcs are in the set, but older htlcs might be there too:
        #   there is a sanity margin of 1 ctn -- this relaxes the care needed re order of method calls.
        # - balance_delta is in sync with maybe_active_htlc_ids. When htlcs are removed from the latter,
        #   balance_delta is updated to reflect that htlc.
        sanity_margin = 1
        for htlc_proposer in (LOCAL, REMOTE):
            for log_action in ('settles', 'fails'):
                for htlc_id in list(self._maybe_active_htlc_ids[htlc_proposer]):
                    ctns = self.log[htlc_proposer][log_action].get(htlc_id, None)
                    if ctns is None: continue
                    if (ctns[LOCAL] is not None
                            and ctns[LOCAL] <= self.ctn_oldest_unrevoked(LOCAL) - sanity_margin
                            and ctns[REMOTE] is not None
                            and ctns[REMOTE] <= self.ctn_oldest_unrevoked(REMOTE) - sanity_margin):
                        self._maybe_active_htlc_ids[htlc_proposer].remove(htlc_id)
                        if log_action == 'settles':
                            htlc = self.log[htlc_proposer]['adds'][htlc_id]  # type: UpdateAddHtlc
                            self._balance_delta -= htlc.amount_msat * htlc_proposer

    def _init_maybe_active_htlc_ids(self):
        # first idx is "side who offered htlc":
        self._maybe_active_htlc_ids = {LOCAL: set(), REMOTE: set()}  # type: Dict[HTLCOwner, Set[int]]
        # add all htlcs
        self._balance_delta = 0  # the balance delta of LOCAL since channel open
        for htlc_proposer in (LOCAL, REMOTE):
            for htlc_id in self.log[htlc_proposer]['adds']:
                self._maybe_active_htlc_ids[htlc_proposer].add(htlc_id)
        # remove old htlcs
        self._update_maybe_active_htlc_ids()

    def discard_unsigned_remote_updates(self):
        """Discard updates sent by the remote, that the remote itself
        did not yet sign (i.e. there was no corresponding commitment_signed msg)
        """
        # htlcs added
        for htlc_id, ctns in list(self.log[REMOTE]['locked_in'].items()):
            if ctns[LOCAL] > self.ctn_latest(LOCAL):
                del self.log[REMOTE]['locked_in'][htlc_id]
                del self.log[REMOTE]['adds'][htlc_id]
                self._maybe_active_htlc_ids[REMOTE].discard(htlc_id)
        if self.log[REMOTE]['locked_in']:
            self.log[REMOTE]['next_htlc_id'] = max([int(x) for x in self.log[REMOTE]['locked_in'].keys()]) + 1
        else:
            self.log[REMOTE]['next_htlc_id'] = 0
        # htlcs removed
        for log_action in ('settles', 'fails'):
            for htlc_id, ctns in list(self.log[LOCAL][log_action].items()):
                if ctns[LOCAL] > self.ctn_latest(LOCAL):
                    del self.log[LOCAL][log_action][htlc_id]
        # fee updates
        for k, fee_update in list(self.log[REMOTE]['fee_updates'].items()):
            if fee_update.ctn_local > self.ctn_latest(LOCAL):
                self.log[REMOTE]['fee_updates'].pop(k)

    def store_local_update_raw_msg(self, raw_update_msg: bytes, *, is_commitment_signed: bool) -> None:
        """We need to be able to replay unacknowledged updates we sent to the remote
        in case of disconnections. Hence, raw update and commitment_signed messages
        are stored temporarily (until they are acked)."""
        # self.log['unacked_local_updates2'][ctn_idx] is a list of raw messages
        # containing some number of updates and then a single commitment_signed
        if is_commitment_signed:
            ctn_idx = self.ctn_latest(REMOTE)
        else:
            ctn_idx = self.ctn_latest(REMOTE) + 1
        l = self.log['unacked_local_updates2'].get(ctn_idx, [])
        l.append(raw_update_msg.hex())
        self.log['unacked_local_updates2'][ctn_idx] = l

    def get_unacked_local_updates(self) -> Dict[int, Sequence[bytes]]:
        #return self.log['unacked_local_updates2']
        return {int(ctn): [bfh(msg) for msg in messages]
                for ctn, messages in self.log['unacked_local_updates2'].items()}

    ##### Queries re HTLCs:

    def is_htlc_active_at_ctn(self, *, ctx_owner: HTLCOwner, ctn: int,
                              htlc_proposer: HTLCOwner, htlc_id: int) -> bool:
        htlc_id = int(htlc_id)
        if htlc_id >= self.get_next_htlc_id(htlc_proposer):
            return False
        settles = self.log[htlc_proposer]['settles']
        fails = self.log[htlc_proposer]['fails']
        ctns = self.log[htlc_proposer]['locked_in'][htlc_id]
        if ctns[ctx_owner] is not None and ctns[ctx_owner] <= ctn:
            not_settled = htlc_id not in settles or settles[htlc_id][ctx_owner] is None or settles[htlc_id][ctx_owner] > ctn
            not_failed = htlc_id not in fails or fails[htlc_id][ctx_owner] is None or fails[htlc_id][ctx_owner] > ctn
            if not_settled and not_failed:
                return True
        return False

    def htlcs_by_direction(self, subject: HTLCOwner, direction: Direction,
                           ctn: int = None) -> Dict[int, UpdateAddHtlc]:
        """Return the dict of received or sent (depending on direction) HTLCs
        in subject's ctx at ctn, keyed by htlc_id.

        direction is relative to subject!
        """
        assert type(subject) is HTLCOwner
        assert type(direction) is Direction
        if ctn is None:
            ctn = self.ctn_oldest_unrevoked(subject)
        d = {}
        # subject's ctx
        # party is the proposer of the HTLCs
        party = subject if direction == SENT else subject.inverted()
        if ctn >= self.ctn_oldest_unrevoked(subject):
            considered_htlc_ids = self._maybe_active_htlc_ids[party]
        else:  # ctn is too old; need to consider full log (slow...)
            considered_htlc_ids = self.log[party]['locked_in']
        for htlc_id in considered_htlc_ids:
            htlc_id = int(htlc_id)
            if self.is_htlc_active_at_ctn(ctx_owner=subject, ctn=ctn, htlc_proposer=party, htlc_id=htlc_id):
                d[htlc_id] = self.log[party]['adds'][htlc_id]
        return d

    def htlcs(self, subject: HTLCOwner, ctn: int = None) -> Sequence[Tuple[Direction, UpdateAddHtlc]]:
        """Return the list of HTLCs in subject's ctx at ctn."""
        assert type(subject) is HTLCOwner
        if ctn is None:
            ctn = self.ctn_oldest_unrevoked(subject)
        l = []
        l += [(SENT, x) for x in self.htlcs_by_direction(subject, SENT, ctn).values()]
        l += [(RECEIVED, x) for x in self.htlcs_by_direction(subject, RECEIVED, ctn).values()]
        return l

    def get_htlcs_in_oldest_unrevoked_ctx(self, subject: HTLCOwner) -> Sequence[Tuple[Direction, UpdateAddHtlc]]:
        assert type(subject) is HTLCOwner
        ctn = self.ctn_oldest_unrevoked(subject)
        return self.htlcs(subject, ctn)

    def get_htlcs_in_latest_ctx(self, subject: HTLCOwner) -> Sequence[Tuple[Direction, UpdateAddHtlc]]:
        assert type(subject) is HTLCOwner
        ctn = self.ctn_latest(subject)
        return self.htlcs(subject, ctn)

    def get_htlcs_in_next_ctx(self, subject: HTLCOwner) -> Sequence[Tuple[Direction, UpdateAddHtlc]]:
        assert type(subject) is HTLCOwner
        ctn = self.ctn_latest(subject) + 1
        return self.htlcs(subject, ctn)

    def was_htlc_preimage_released(self, *, htlc_id: int, htlc_sender: HTLCOwner) -> bool:
        settles = self.log[htlc_sender]['settles']
        if htlc_id not in settles:
            return False
        return settles[htlc_id][htlc_sender] is not None

    def all_settled_htlcs_ever_by_direction(self, subject: HTLCOwner, direction: Direction,
                                            ctn: int = None) -> Sequence[UpdateAddHtlc]:
        """Return the list of all HTLCs that have been ever settled in subject's
        ctx up to ctn, filtered to only "direction".
        """
        assert type(subject) is HTLCOwner
        if ctn is None:
            ctn = self.ctn_oldest_unrevoked(subject)
        # subject's ctx
        # party is the proposer of the HTLCs
        party = subject if direction == SENT else subject.inverted()
        d = []
        for htlc_id, ctns in self.log[party]['settles'].items():
            if ctns[subject] is not None and ctns[subject] <= ctn:
                d.append(self.log[party]['adds'][htlc_id])
        return d

    def all_settled_htlcs_ever(self, subject: HTLCOwner, ctn: int = None) \
            -> Sequence[Tuple[Direction, UpdateAddHtlc]]:
        """Return the list of all HTLCs that have been ever settled in subject's
        ctx up to ctn.
        """
        assert type(subject) is HTLCOwner
        if ctn is None:
            ctn = self.ctn_oldest_unrevoked(subject)
        sent = [(SENT, x) for x in self.all_settled_htlcs_ever_by_direction(subject, SENT, ctn)]
        received = [(RECEIVED, x) for x in self.all_settled_htlcs_ever_by_direction(subject, RECEIVED, ctn)]
        return sent + received

    def get_balance_msat(self, whose: HTLCOwner, *, ctx_owner=HTLCOwner.LOCAL, ctn: int = None,
                         initial_balance_msat: int) -> int:
        """Returns the balance of 'whose' in 'ctx' at 'ctn'.
        Only HTLCs that have been settled by that ctn are counted.
        """
        if ctn is None:
            ctn = self.ctn_oldest_unrevoked(ctx_owner)
        balance = initial_balance_msat
        if ctn >= self.ctn_oldest_unrevoked(ctx_owner):
            balance += self._balance_delta * whose
            considered_sent_htlc_ids = self._maybe_active_htlc_ids[whose]
            considered_recv_htlc_ids = self._maybe_active_htlc_ids[-whose]
        else:  # ctn is too old; need to consider full log (slow...)
            considered_sent_htlc_ids = self.log[whose]['settles']
            considered_recv_htlc_ids = self.log[-whose]['settles']
        # sent htlcs
        for htlc_id in considered_sent_htlc_ids:
            ctns = self.log[whose]['settles'].get(htlc_id, None)
            if ctns is None: continue
            if ctns[ctx_owner] is not None and ctns[ctx_owner] <= ctn:
                htlc = self.log[whose]['adds'][htlc_id]
                balance -= htlc.amount_msat
        # recv htlcs
        for htlc_id in considered_recv_htlc_ids:
            ctns = self.log[-whose]['settles'].get(htlc_id, None)
            if ctns is None: continue
            if ctns[ctx_owner] is not None and ctns[ctx_owner] <= ctn:
                htlc = self.log[-whose]['adds'][htlc_id]
                balance += htlc.amount_msat
        return balance

    def _get_htlcs_that_got_removed_exactly_at_ctn(
            self, ctn: int, *, ctx_owner: HTLCOwner, htlc_proposer: HTLCOwner, log_action: str,
    ) -> Sequence[UpdateAddHtlc]:
        if ctn >= self.ctn_oldest_unrevoked(ctx_owner):
            considered_htlc_ids = self._maybe_active_htlc_ids[htlc_proposer]
        else:  # ctn is too old; need to consider full log (slow...)
            considered_htlc_ids = self.log[htlc_proposer][log_action]
        htlcs = []
        for htlc_id in considered_htlc_ids:
            ctns = self.log[htlc_proposer][log_action].get(htlc_id, None)
            if ctns is None: continue
            if ctns[ctx_owner] == ctn:
                htlcs.append(self.log[htlc_proposer]['adds'][htlc_id])
        return htlcs

    def received_in_ctn(self, local_ctn: int) -> Sequence[UpdateAddHtlc]:
        """
        received htlcs that became fulfilled when we send a revocation.
        we check only local, because they are committed in the remote ctx first.
        """
        return self._get_htlcs_that_got_removed_exactly_at_ctn(local_ctn,
                                                               ctx_owner=LOCAL,
                                                               htlc_proposer=REMOTE,
                                                               log_action='settles')

    def sent_in_ctn(self, remote_ctn: int) -> Sequence[UpdateAddHtlc]:
        """
        sent htlcs that became fulfilled when we received a revocation
        we check only remote, because they are committed in the local ctx first.
        """
        return self._get_htlcs_that_got_removed_exactly_at_ctn(remote_ctn,
                                                               ctx_owner=REMOTE,
                                                               htlc_proposer=LOCAL,
                                                               log_action='settles')

    def failed_in_ctn(self, remote_ctn: int) -> Sequence[UpdateAddHtlc]:
        """
        sent htlcs that became failed when we received a revocation
        we check only remote, because they are committed in the local ctx first.
        """
        return self._get_htlcs_that_got_removed_exactly_at_ctn(remote_ctn,
                                                               ctx_owner=REMOTE,
                                                               htlc_proposer=LOCAL,
                                                               log_action='fails')

    ##### Queries re Fees:

    def get_feerate(self, subject: HTLCOwner, ctn: int) -> int:
        """Return feerate used in subject's commitment txn at ctn."""
        ctn = max(0, ctn)  # FIXME rm this
        # only one party can update fees; use length of logs to figure out which:
        assert not (len(self.log[LOCAL]['fee_updates']) > 1 and len(self.log[REMOTE]['fee_updates']) > 1)
        fee_log = self.log[LOCAL]['fee_updates']  # type: Sequence[FeeUpdate]
        if len(self.log[REMOTE]['fee_updates']) > 1:
            fee_log = self.log[REMOTE]['fee_updates']
        # binary search
        left = 0
        right = len(fee_log)
        while True:
            i = (left + right) // 2
            ctn_at_i = fee_log[i].ctn_local if subject==LOCAL else fee_log[i].ctn_remote
            if right - left <= 1:
                break
            if ctn_at_i is None:  # Nones can only be on the right end
                right = i
                continue
            if ctn_at_i <= ctn:  # among equals, we want the rightmost
                left = i
            else:
                right = i
        assert ctn_at_i <= ctn
        return fee_log[i].rate

    def get_feerate_in_oldest_unrevoked_ctx(self, subject: HTLCOwner) -> int:
        return self.get_feerate(subject=subject, ctn=self.ctn_oldest_unrevoked(subject))

    def get_feerate_in_latest_ctx(self, subject: HTLCOwner) -> int:
        return self.get_feerate(subject=subject, ctn=self.ctn_latest(subject))

    def get_feerate_in_next_ctx(self, subject: HTLCOwner) -> int:
        return self.get_feerate(subject=subject, ctn=self.ctn_latest(subject) + 1)


text:
package com.google.template.soy.incrementaldomsrc;

import static java.nio.charset.StandardCharsets.UTF_8;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;
import com.google.common.io.Files;
import com.google.template.soy.base.SoySyntaxException;
import com.google.template.soy.error.ErrorReporter;
import com.google.template.soy.html.passes.HtmlTransformVisitor;
import com.google.template.soy.internal.i18n.BidiGlobalDir;
import com.google.template.soy.internal.i18n.SoyBidiUtils;
import com.google.template.soy.jssrc.SoyJsSrcOptions;
import com.google.template.soy.jssrc.internal.OptimizeBidiCodeGenVisitor;
import com.google.template.soy.shared.internal.ApiCallScopeUtils;
import com.google.template.soy.shared.internal.GuiceSimpleScope;
import com.google.template.soy.shared.internal.MainEntryPointUtils;
import com.google.template.soy.shared.restricted.ApiCallScopeBindingAnnotations.ApiCall;
import com.google.template.soy.sharedpasses.opti.SimplifyVisitor;
import com.google.template.soy.soytree.SoyFileNode;
import com.google.template.soy.soytree.SoyFileSetNode;
import com.google.template.soy.soytree.TemplateRegistry;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Provider;

/**
 * Main entry point for the Incremental DOM JS Src backend (output target).
 *
 * <p>Important: Do not use outside of Soy code (treat as superpackage-private).
 */
public class IncrementalDomSrcMain {

  /** The scope object that manages the API call scope. */
  private final GuiceSimpleScope apiCallScope;

  /** The instanceof of SimplifyVisitor to use. */
  private final SimplifyVisitor simplifyVisitor;

  /** Provider for getting an instance of OptimizeBidiCodeGenVisitor. */
  private final Provider<OptimizeBidiCodeGenVisitor> optimizeBidiCodeGenVisitorProvider;

  /** Provider for getting an instance of GenJsCodeVisitor. */
  private final Provider<GenIncrementalDomCodeVisitor> genIncrementalDomCodeVisitorProvider;

  /**
   * @param apiCallScope The scope object that manages the API call scope.
   * @param simplifyVisitor The instance of SimplifyVisitor to use.
   * @param optimizeBidiCodeGenVisitorProvider Provider for getting an instance of
   *     OptimizeBidiCodeGenVisitor.
   * @param genIncrementalDomCodeVisitorProvider Provider for getting an instance of
   *     GenIncrementalDomCodeVisitor.
   */
  @Inject
  public IncrementalDomSrcMain(
      @ApiCall GuiceSimpleScope apiCallScope,
      SimplifyVisitor simplifyVisitor,
      Provider<OptimizeBidiCodeGenVisitor> optimizeBidiCodeGenVisitorProvider,
      Provider<GenIncrementalDomCodeVisitor> genIncrementalDomCodeVisitorProvider) {
    this.apiCallScope = apiCallScope;
    this.simplifyVisitor = simplifyVisitor;
    this.optimizeBidiCodeGenVisitorProvider = optimizeBidiCodeGenVisitorProvider;
    this.genIncrementalDomCodeVisitorProvider = genIncrementalDomCodeVisitorProvider;
  }

  /**
   * Generates Incremental DOM JS source code given a Soy parse tree, an options object, and an
   * optional bundle of translated messages.
   *
   * @param soyTree The Soy parse tree to generate JS source code for.
   * @param options The compilation options relevant to this backend.
   * @return A list of strings where each string represents the JS source code that belongs in one
   *     JS file. The generated JS files correspond one-to-one to the original Soy source files.
   * @throws SoySyntaxException If a syntax error is found.
   */
  public List<String> genJsSrc(
      SoyFileSetNode soyTree,
      TemplateRegistry registry,
      SoyIncrementalDomSrcOptions options,
      ErrorReporter errorReporter)
      throws SoySyntaxException {

    SoyJsSrcOptions incrementalJSSrcOptions = options.toJsSrcOptions();

    try (GuiceSimpleScope.InScope inScope = apiCallScope.enter()) {
      // Seed the scoped parameters.
      inScope.seed(SoyJsSrcOptions.class, incrementalJSSrcOptions);
      BidiGlobalDir bidiGlobalDir =
          SoyBidiUtils.decodeBidiGlobalDirFromJsOptions(
              incrementalJSSrcOptions.getBidiGlobalDir(),
              incrementalJSSrcOptions.getUseGoogIsRtlForBidiGlobalDir());
      ApiCallScopeUtils.seedSharedParams(inScope, null /* msgBundle */, bidiGlobalDir);

      // Do the code generation.
      optimizeBidiCodeGenVisitorProvider.get().exec(soyTree);
      simplifyVisitor.simplify(soyTree, registry);

      new HtmlTransformVisitor(errorReporter).exec(soyTree);

      new UnescapingVisitor().exec(soyTree);

      // Must happen after HtmlTransformVisitor, so it can infer context for {msg} nodes.
      new IncrementalDomExtractMsgVariablesVisitor().exec(soyTree);

      return genIncrementalDomCodeVisitorProvider.get().gen(soyTree, registry, errorReporter);
    }
  }

  /**
   * Generates Incremental DOM JS source files given a Soy parse tree, an options object, an
   * optional bundle of translated messages, and information on where to put the output files.
   *
   * @param soyTree The Soy parse tree to generate JS source code for.
   * @param jsSrcOptions The compilation options relevant to this backend.
   * @param outputPathFormat The format string defining how to build the output file path
   *     corresponding to an input file path.
   * @throws SoySyntaxException If a syntax error is found.
   * @throws IOException If there is an error in opening/writing an output JS file.
   */
  public void genJsFiles(
      SoyFileSetNode soyTree,
      TemplateRegistry templateRegistry,
      SoyIncrementalDomSrcOptions jsSrcOptions,
      String outputPathFormat,
      ErrorReporter errorReporter)
      throws IOException {

    List<String> jsFileContents = genJsSrc(soyTree, templateRegistry, jsSrcOptions, errorReporter);

    ImmutableList<SoyFileNode> srcsToCompile =
        ImmutableList.copyOf(
            Iterables.filter(soyTree.getChildren(), SoyFileNode.MATCH_SRC_FILENODE));

    if (srcsToCompile.size() != jsFileContents.size()) {
      throw new AssertionError(
          String.format(
              "Expected to generate %d code chunk(s), got %d",
              srcsToCompile.size(), jsFileContents.size()));
    }

    Multimap<String, Integer> outputs =
        MainEntryPointUtils.mapOutputsToSrcs(
            null /* locale */, outputPathFormat, "" /* inputPathsPrefix */, srcsToCompile);

    for (String outputFilePath : outputs.keySet()) {
      Writer out = Files.newWriter(new File(outputFilePath), UTF_8);
      try {
        boolean isFirst = true;
        for (int inputFileIndex : outputs.get(outputFilePath)) {
          if (isFirst) {
            isFirst = false;
          } else {
            // Concatenating JS files is not safe unless we know that the last statement from one
            // couldn't combine with the isFirst statement of the next.  Inserting a semicolon will
            // prevent this from happening.
            out.write("\n;\n");
          }
          out.write(jsFileContents.get(inputFileIndex));
        }
      } finally {
        out.close();
      }
    }
  }
}


text:
Acciones Correctivas y Preventivas

    Limpiar filtro / Ver todos

      <button class="btn btn-primary">Importar Acciones</button>

REALIZADOS

                        {{ EmployeeAlertsService.riskInsuranceExpired.meta.total_items }}
                        <ng-pluralize count="EmployeeAlertsService.riskInsuranceExpired.data.length"
                                      when="{'0': 'Trabajadores', '1': 'Trabajador', 'other': 'Trabajadores'}">
                        </ng-pluralize>

                        CON SCTR VENCIDO

PENDIENTES

                    {{ EmployeeAlertsService.medicalExamExpired.meta.total_items }}
                    <ng-pluralize count="EmployeeAlertsService.medicalExamExpired.data.length"
                                  when="{'0': 'Trabajadores', '1': 'Trabajador', 'other': 'Trabajadores'}">
                    </ng-pluralize>

                        CON EXAMEN MÉDICO VENCIDO

EN EJECUCIÓN

                    {{ EmployeeAlertsService.noInductionTraining.meta.total_items }}
                  <ng-pluralize count="EmployeeAlertsService.noInductionTraining.data.length"
                                when="{'0': 'Trabajadores', '1': 'Trabajador', 'other': 'Trabajadores'}">
                  </ng-pluralize>

                        SIN INDUCCIÓN

    <button type="button" class="btn btn-default">Ordenar por</button>
    <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">

      Ordenar por
    </button>

N° DNI

Nombres

Apellidos

    <form ng-submit="setEmployees()" class="input-group">
      <input ng-model="searchText" type="text" class="form-control" placeholder="DNI, Nombres, Apellidos ...">

      <button
          ng-click="setEmployees()"
          ng-disabled="EmployeeService.loadingEmployees"
          id="empSearchBtn"
          class="btn btn-default"
          type="button">
         Buscar</button>

    </form>

      <button type="button" class="btn btn-default btn-sm">
         Imprimir
      </button>
      <button type="button" class="btn btn-default btn-sm">
         Exportar
      </button>

      N° DNI
      Nombres y Apellidos

        {{ employee.id_number }}
      {{ employee.first_name }} {{ employee.middle_name }},
        {{ employee.last_name }} {{ employee.second_last_name }}

No se encontraron resultados.

  <pagination total-items="EmployeeService.totalItems"
              page="EmployeeService.currentPage"
              max-size="maxSize"
              class="pagination-sm"
              boundary-links="true"
              items-per-page="itemsPerPage"
              on-select-page="setEmployees(page)"
              first-text="Primero"
              last-text="Último"
              previous-text="Anterior"
              next-text="Siguiente">
  </pagination>


text:
package org.apache.pulsar.common.naming;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.util.concurrent.UncheckedExecutionException;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * Parser of a value from the namespace field provided in configuration.
 */
public class NamespaceName implements ServiceUnitId {

    private final String namespace;

    private final String tenant;
    private final String cluster;
    private final String localName;

    private static final LoadingCache<String, NamespaceName> cache = CacheBuilder.newBuilder().maximumSize(100000)
            .expireAfterAccess(30, TimeUnit.MINUTES).build(new CacheLoader<String, NamespaceName>() {
                @Override
                public NamespaceName load(String name) throws Exception {
                    return new NamespaceName(name);
                }
            });

    public static final NamespaceName SYSTEM_NAMESPACE = NamespaceName.get("pulsar/system");

    public static NamespaceName get(String tenant, String namespace) {
        validateNamespaceName(tenant, namespace);
        return get(tenant + '/' + namespace);
    }

    public static NamespaceName get(String tenant, String cluster, String namespace) {
        validateNamespaceName(tenant, cluster, namespace);
        return get(tenant + '/' + cluster + '/' + namespace);
    }

    public static NamespaceName get(String namespace) {
        if (namespace == null || namespace.isEmpty()) {
            throw new IllegalArgumentException("Invalid null namespace: " + namespace);
        }
        try {
            return cache.get(namespace);
        } catch (ExecutionException e) {
            throw (RuntimeException) e.getCause();
        } catch (UncheckedExecutionException e) {
            throw (RuntimeException) e.getCause();
        }
    }

    public static Optional<NamespaceName> getIfValid(String namespace) {
        NamespaceName ns = cache.getIfPresent(namespace);
        if (ns != null) {
            return Optional.of(ns);
        }

        if (namespace.length() == 0) {
            return Optional.empty();
        }

        // Example: my-tenant/my-namespace
        if (!namespace.contains("/")) {
            return Optional.empty();
        }

        return Optional.of(get(namespace));
    }

    private NamespaceName(String namespace) {
        // Verify it's a proper namespace
        // The namespace name is composed of <tenant>/<namespace>
        // or in the legacy format with the cluster name:
        // <tenant>/<cluster>/<namespace>
        try {

            String[] parts = namespace.split("/");
            if (parts.length == 2) {
                // New style namespace : <tenant>/<namespace>
                validateNamespaceName(parts[0], parts[1]);

                tenant = parts[0];
                cluster = null;
                localName = parts[1];
            } else if (parts.length == 3) {
                // Old style namespace: <tenant>/<cluster>/<namespace>
                validateNamespaceName(parts[0], parts[1], parts[2]);

                tenant = parts[0];
                cluster = parts[1];
                localName = parts[2];
            } else {
                throw new IllegalArgumentException("Invalid namespace format. namespace: " + namespace);
            }
        } catch (IllegalArgumentException | NullPointerException e) {
            throw new IllegalArgumentException("Invalid namespace format."
                    + " expected <tenant>/<namespace> or <tenant>/<cluster>/<namespace> "
                    + "but got: " + namespace, e);
        }
        this.namespace = namespace;
    }

    public String getTenant() {
        return tenant;
    }

    @Deprecated
    public String getCluster() {
        return cluster;
    }

    public String getLocalName() {
        return localName;
    }

    public boolean isGlobal() {
        return cluster == null || Constants.GLOBAL_CLUSTER.equalsIgnoreCase(cluster);
    }

    public String getPersistentTopicName(String localTopic) {
        return getTopicName(TopicDomain.persistent, localTopic);
    }

    /**
     * Compose the topic name from namespace + topic.
     *
     * @param domain
     * @param topic
     * @return
     */
    String getTopicName(TopicDomain domain, String topic) {
        if (domain == null) {
            throw new IllegalArgumentException("invalid null domain");
        }
        NamedEntity.checkName(topic);
        return String.format("%s://%s/%s", domain.toString(), namespace, topic);
    }

    @Override
    public String toString() {
        return namespace;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof NamespaceName) {
            NamespaceName other = (NamespaceName) obj;
            return Objects.equals(namespace, other.namespace);
        }

        return false;
    }

    @Override
    public int hashCode() {
        return namespace.hashCode();
    }

    public static void validateNamespaceName(String tenant, String namespace) {
        if ((tenant == null || tenant.isEmpty()) || (namespace == null || namespace.isEmpty())) {
            throw new IllegalArgumentException(
                    String.format("Invalid namespace format. namespace: %s/%s", tenant, namespace));
        }
        NamedEntity.checkName(tenant);
        NamedEntity.checkName(namespace);
    }

    public static void validateNamespaceName(String tenant, String cluster, String namespace) {
        if ((tenant == null || tenant.isEmpty()) || (cluster == null || cluster.isEmpty())
                || (namespace == null || namespace.isEmpty())) {
            throw new IllegalArgumentException(
                    String.format("Invalid namespace format. namespace: %s/%s/%s", tenant, cluster, namespace));
        }
        NamedEntity.checkName(tenant);
        NamedEntity.checkName(cluster);
        NamedEntity.checkName(namespace);
    }

    @Override
    public NamespaceName getNamespaceObject() {
        return this;
    }

    @Override
    public boolean includes(TopicName topicName) {
        return this.equals(topicName.getNamespaceObject());
    }

    /**
     * Returns true if this is a V2 namespace prop/namespace-name.
     * @return true if v2
     */
    public boolean isV2() {
        return cluster == null;
    }
}


text:
title: Host on AWS Amplify
linktitle: Host on AWS Amplify
description: Develop and deploy a cloud-powered web app with AWS Amplify.
date: 2018-01-31
publishdate: 2018-01-31
lastmod: 2018-01-31
categories: [hosting and deployment]
keywords: [amplify,hosting,deployment]
authors: [Nikhil Swaminathan]
menu:
  docs:
    parent: "hosting-and-deployment"
    weight: 10
weight: 10
sections_weight: 10
draft: false
aliases: []
toc: true
---

In this guide we'll walk through how to deploy and host your Hugo site using the [AWS Amplify Console](https://console.amplify.aws).

AWS Amplify is a combination of client library, CLI toolchain, and a Console for continuous deployment and hosting. The Amplify CLI and library allow developers to get up & running with full-stack cloud-powered applications with features like authentication, storage, serverless GraphQL or REST APIs, analytics, Lambda functions, & more. The Amplify Console provides continuous deployment and hosting for modern web apps (single page apps and static site generators). Continuous deployment allows developers to deploy updates to their web app on every code commit to their Git repository. Hosting includes features such as globally available CDNs, easy custom domain setup + HTTPS, feature branch deployments, and password protection.

## Pre-requisites

* [Sign up for an AWS Account](https://portal.aws.amazon.com/billing/signup?redirect_url=https%3A%2F%2Faws.amazon.com%2Fregistration-confirmation). There are no upfront charges or any term commitments to create an AWS account and signing up gives you immediate access to the AWS Free Tier.
* You have an account with GitHub, GitLab, or Bitbucket.
* You have completed the [Quick Start][] or have a Hugo website you are ready to deploy and share with the world.

## Hosting

1. Log in to the [AWS Amplify Console](https://console.aws.amazon.com/amplify/home) and choose Get Started under Deploy.
   ![Hugo Amplify](/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-gettingstarted.png)

1. Connect a branch from your GitHub, Bitbucket, GitLab, or AWS CodeCommit repository. Connecting your repository allows Amplify to deploy updates on every code commit to a branch.
   ![Hugo Amplify](/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-connect-repo.gif)

1.  Accept the default build settings. The Amplify Console automatically detects your Hugo build settings and output directory.
   ![Hugo Amplify](/images/hosting-and-deployment/hosting-on-aws-amplify/amplify-build-settings.png)

1. Review your changes and then choose **Save and deploy**. The Amplify Console will pull code from your repository, build changes to the backend and frontend, and deploy your build artifacts at `https://master.unique-id.amplifyapp.com`. Bonus: Screenshots of your app on different devices to find layout issues.

## Using a Newer Version of Hugo

If you need to use a different, perhaps newer, version of Hugo than the version currently supported by AWS Amplify:

1. Visit the [AWS Amplify Console](https://console.aws.amazon.com/amplify/home), and click the app you would like to modify
1. In the side navigation bar, Under App Settings, click **Build settings**
1. On the Build settings page, near the bottom, there is a section called **Build image settings**. Click **Edit**
1. Under **Live package updates**, click **Add package version override**
1. From the selection, click **Hugo** and ensure the version field says `latest`
1. Click **Save** to save the changes.

[Quick Start]: /getting-started/quick-start/