text:
import pandas as pd
import numpy as np
import inspect
from copy import deepcopy
from matplotlib import pyplot as plt
from matplotlib import rcParams
from matplotlib.backends.backend_pdf import PdfPages
plt.style.use('bmh')

import RuleSet
import r_funcs as rf
import policy_tools as pt
import projection_funcs as pf

def make_rsets(df, params_dict,
                xtrap=False, return_all_sums = False, return_setting_sums=False, return_sum = False,
                trim=False, _debug=False):
    '''Helper function to make rulesets objects based on input parameters

    Default just returns a dict of rulesets with the params added.

    Setting `xtrap` flag will return dict of rulesets populated with results of calling xtrap on each
     - i.e. it will actually do the extrapolation functions and assign to .past, .fut, .joined and .sum

    Setting `return_sum` or `return_setting_sums` flags will return summed datasets as expected.

    Setting `trim` with a tuple or list (start, end) will trim the output sum dfs
    '''

    cut_off = params_dict['cut_off']
    biol_shed = params_dict['biol_shed']
    non_biol_shed = params_dict['non_biol_shed']
    loe_delay = params_dict['loe_delay']
    biol_term_gr = params_dict['biol_term_gr']
    non_biol_term_gr = params_dict['non_biol_term_gr']
    biol_coh_gr = params_dict['biol_coh_gr']
    non_biol_coh_gr = params_dict['non_biol_coh_gr']
    n_pers = params_dict['n_pers']

    rsets = {}
    # existing product rulesets - NB MUST SET CUTOFF TO MINUS 1 TO AVOID OVERLAP
    rsets['biol_sec'] = RuleSet.RuleSet(df, name='biol_sec',
                               index_slice = dict(biol=True, setting='secondary', start_month=slice(None, cut_off-1, None)),
                               func = rf.r_trend,
                               f_args = dict(shed=biol_shed, term_gr=biol_term_gr, loe_delay=loe_delay))

    rsets['nonbiol_sec'] = RuleSet.RuleSet(df, name='nonbiol_sec',
                               index_slice = dict(biol=False, setting='secondary', start_month=slice(None, cut_off-1, None)),
                               func = rf.r_trend,
                               f_args = dict(shed=non_biol_shed, term_gr=non_biol_term_gr, loe_delay=loe_delay))

    rsets['biol_prim'] = RuleSet.RuleSet(df, name='biol_prim',
                               index_slice = dict(biol=True, setting='primary', start_month=slice(None, cut_off-1, None)),
                               func = rf.r_trend,
                               f_args = dict(shed=biol_shed, term_gr=biol_term_gr, loe_delay=loe_delay))

    rsets['nonbiol_prim'] = RuleSet.RuleSet(df, name='nonbiol_prim',
                               index_slice = dict(biol=False, setting='primary', start_month=slice(None, cut_off-1, None)),
                               func = rf.r_trend,
                               f_args = dict(shed=non_biol_shed, term_gr=non_biol_term_gr, loe_delay=loe_delay))

    # future launches rulesets
    rsets['biol_sec_fut'] = RuleSet.RuleSet(df, name='biol_sec_fut',
                               index_slice = dict(biol=True, setting='secondary', start_month=slice(cut_off, None, None)),
                               func = rf.r_fut_tr,
                               f_args = dict(shed=biol_shed, loe_delay=loe_delay, term_gr=biol_term_gr, coh_gr=biol_coh_gr, cut_off=cut_off))

    rsets['nonbiol_sec_fut'] = RuleSet.RuleSet(df, name='nonbiol_sec_fut',
                               index_slice = dict(biol=False, setting='secondary', start_month=slice(cut_off, None, None)),
                               func = rf.r_fut_tr,
                               f_args = dict(shed=non_biol_shed, loe_delay=loe_delay, term_gr=non_biol_term_gr, coh_gr=non_biol_coh_gr, cut_off=cut_off))

    rsets['biol_prim_fut'] = RuleSet.RuleSet(df, name='biol_prim_fut',
                               index_slice = dict(biol=True, setting='primary', start_month=slice(cut_off, None, None)),
                               func = rf.r_fut_tr,
                               f_args = dict(shed=biol_shed, loe_delay=loe_delay, term_gr=biol_term_gr, coh_gr=biol_coh_gr, cut_off=cut_off))

    rsets['nonbiol_prim_fut'] = RuleSet.RuleSet(df, name='nonbiol_prim_fut',
                               index_slice = dict(biol=False, setting='primary', start_month=slice(cut_off, None, None)),
                               func = rf.r_fut_tr,
                               f_args = dict(shed=non_biol_shed, loe_delay=loe_delay, term_gr=non_biol_term_gr, coh_gr=non_biol_coh_gr, cut_off=cut_off))

    if xtrap or return_all_sums or return_setting_sums or return_sum:
        for r in rsets:
            if _debug: print('xtrapping rset ', r, end=" ")
            rsets[r].xtrap(n_pers, _debug=False)
            if _debug: print(' ..OK')

    # if any sums reqd, make the full set
    if return_all_sums or return_setting_sums or return_sum:
        if _debug: print('making all sums')
        sums = pd.concat([rsets[x].summed for x in rsets], axis=1)
        if trim:
            sums = sums.loc[slice(pd.Period(trim[0], freq='M'), pd.Period(trim[1], freq='M'),None),:]

    # if all sums reqd, just return
    if return_all_sums:
        return sums

    # if sums by setting reqd
    elif return_setting_sums:
        if _debug: print('making sums by setting')
        sums = sums.groupby(lambda x: 'sec' in x, axis=1).sum()
        sums.columns = ['Primary', 'Secondary']
        return sums

    # if a single sum reqd
    elif return_sum:
        if _debug: print('making single sum')
        sums = sums.sum(axis=1)
        if return_setting_sums or return_all_sums:
            print('Returning single sum only - to return all sums, or by setting you need to turn off the `return_sum` flag')
        return sums

    # default returns the whole rulesets (with or without dfs depending on if xtrap was called)
    else: return rsets

##____________________________________________________________________________________________________________##

def load_spend_dset(path='c://Users//groberta//Work//data_accelerator/spend_data_proc/consol_ey_dset/adjusted_consol_dset_15MAR18.pkl',
                    add_start_m=True, trim_to_financial_year='2017-03', _debug=True):
    '''Helper function to load the spend dataset using latest inputs - which have been adjusted to match DH Finance (on latest iteration)

    Option to trim to the last month of DH Finance projections - which is the last point at which we have a proper multiplier

    Currently adds a start month (optionally, default true).  This is needed to make slices of the df in the rulesest,
    but may be better done in generating the original spend dset. (TODO)
    '''
    pad = 40

    if _debug:
        print('Path for inputs spend df\n-->', path)

    df = pd.read_pickle(path)

    if _debug:
        print('\nlast period is'.ljust(pad), df.columns[-1], end="")

    if trim_to_financial_year:
        df = df.loc[:,:trim_to_financial_year]

        if _debug:
            print('\ntrimmed, last period now'.ljust(pad), df.columns[-1])

    # add a start month
    if add_start_m:
        start_m_ind = pd.PeriodIndex(df.index.get_level_values(level='adjusted_launch_date'), freq='M', name='start_month')
        df.set_index(start_m_ind, append=True, inplace=True)
        if _debug:
            print('\nadded a start month, index now\n-->', df.index.names)

    return df

##____________________________________________________________________________________________________________##

def plot_rset_projs(rs_dict_in, rsets=None, selection=None, num_plots=12, n_pers=120, xlims=None, show_phases=True,
                    agg_filename=None, file_suffix=None, out_folder='figs/test_plot_rset/', _debug=False):

    '''Helper function for plotting projections of individual products in rulesets.

    For a dict of rulesets, sorts by max sales and takes num_plots highest.
    Then for each generates a separate plot containing each product in the high sales set.

    PARAMETERS

    rs_dict_in [reqd]     : a dict of rulesets (i.e. the normal core output from a projection)

    rsets                 : can pass a list of keys (eg 'biol_sec'), and will use only those,
                            rather than going through whole of rs_dict_in

    selection             : pass a list of product names instead of finding products with max sales

    num_plots             : number of plots for each ruleset

    agg_filename          : puts together in a single image file if pass a filename
                             - most useful wwhen passing a selection, to get a single file of output

                             [makes the code a bit complex so dev notes:]
                             - will then find instances of the selection elements across all the rulesets
                             - so need to extend selection names to include all instances, eg a name may occur in several.
                             - Do this with ext_selection.
                             - Also use the length of this to set the number of plots, and
                             - use ext_selection to hold the annotated names (identifying rset they were found it)
                             - also use an additional iterator, agg_i, which keeps incrementing across different rsets

    n_pers                 : number of periods to project for

    xlims                  : x axis limits for plots

    show_phases            : will make coloured areas showing uptake and launch phases

    file_suffix            : gets added onto output file names.  Useful if want to distinguish from prev plots

    out_folder             : destination folder
    '''
    if _debug: print("\nIN FUNCTION:  ".ljust(20), inspect.stack()[0][3])
    if _debug: print("..called by:  ".ljust(20), inspect.stack()[1][3], end="\n\n")

    rcParams.update({'font.size': 8})

    pad = 35

    # first select only passed rsets
    if rsets is not None:
        rs_dict = {x:rs_dict_in[x] for x in rsets}

    # otherwise get rid of fut
    else:
        rs_dict = {x:rs_dict_in[x] for x in rs_dict_in.keys() if 'fut' not in x}

    if _debug: print(rs_dict.keys())

    # set up to make a single figure if aggregating
    if agg_filename is not None:
        if selection is not None:

            # tricky.  Need to know how many across all rsets
            ext_selection = []
            for r in rs_dict_in:
                ext_selection.extend([x + " - " + r for x in rs_dict_in[r].past.index.get_level_values('molecule')
                                        for s in selection if s in x])

            if _debug: print('extended list\n', ext_selection)

            num_plots = len(ext_selection)
            if _debug: print('num_plots (selection)'.ljust(pad), num_plots)

        else:
            num_plots = len(rs_dict * num_plots)
            if _debug: print('num_plots (rulesets)'.ljust(pad), num_plots)

        # set up a single pdf output file (if not agg, will do one anew for each ruleset)
        pdf = PdfPages(out_folder + agg_filename + '.pdf')

    # make an iterator for if aggregating in a single fig
    agg_i = 0

    # iterate non future rsets
    for r in [x for x in rs_dict.keys() if not x.endswith('_fut')]:
        # select data by max sales
        print('\nin rset'.ljust(pad), r)

        selected = None

        # use selection if passed
        if selection is not None:
            selected = rs_dict[r].past.loc[selection]

        # otherwise take top by sales
        else:
            selected = rs_dict[r].past.loc[rs_dict[r].past.max(axis=1).sort_values(ascending=False).head(num_plots).index]

        if _debug: print('length of selection'.ljust(pad), len(selected))
        #get into £m annualised
        selected *=12/10**8

        # get the LIST OF dfs from r_trend with an _out_type='df' flag
        df_list = rf.r_trend(selected, n_pers=n_pers,
                                   shed = rs_dict[r].f_args['shed'],
                                   term_gr = rs_dict[r].f_args['term_gr'],
                                   loe_delay = rs_dict[r].f_args['loe_delay'],
                                   _out_type='df')

        plat_dur = rs_dict[r].f_args['shed'].plat_dur
        total_dur = rs_dict[r].f_args['shed'].uptake_dur + plat_dur
        if _debug: print('plat length is '.ljust(pad), plat_dur)
        if _debug: print('total length is '.ljust(pad), total_dur)

        # make pdf for this ruleset (remember in a loop here already), if not already made one for aggregate
        if agg_filename is None:
            if file_suffix is None: fs = ""
            else: fs = file_suffix
            pdf = PdfPages(out_folder + r + fs + '.pdf')

        # now loop through the returned dataframes - one per product (each with 3 cols / lines to plot)
        for i, df in enumerate(df_list):

            if selected.iloc[i].name[0].lower() == 'not applicable':
                print('found product named "not applicable" - ignoring')
                continue

            # first get rid of zeros for cleaner plotting
            df = df[df!=0]

            if _debug: print('\ndf number'.ljust(pad), i)
            if _debug: print('..corresponding product name'.ljust(pad), selected.iloc[i].name[0])

            loe_date = pd.Period(selected.iloc[i].name[6], freq='M')
            if _debug: print('..with loe'.ljust(pad), loe_date)

            # make the index, from the selected input dataframe, adding n_pers
            ind_start = selected.columns[0]
            if _debug: print('ind start'.ljust(pad), ind_start)
            ind = pd.PeriodIndex(start=ind_start, periods = len(selected.columns) + n_pers).to_timestamp()
            if _debug: print('ind end'.ljust(pad), ind[-1])
            if _debug: print('total periods'.ljust(pad), len(ind))

            # snip df to length of index - THERE IS A STRAY PERIOD COMING FROM SOMEWHERE

            if _debug: print('length of dfs'.ljust(pad), len(df))
            if len(df) > len(ind):
                if _debug: print('snipping df.. ..')
                df = df.iloc[:len(ind), :]
                if _debug: print("length now".ljust(pad), len(df))

            ind_end = pd.Period(ind[-1], freq='M')
            if _debug: print('index end'.ljust(pad), ind_end)

            # # make an axes iterator that works with case if single plot
            # ax_it = None
            # if num_plots == 1:  ax_it = ax
            # elif agg_filename:  ax_it = ax[agg_i]
            # else:               ax_it = ax[i]

            # make a figure for this df (remember, one product, 3 lines)
            fig, ax = plt.subplots(dpi=200)

            # and now loop through the actual columns in the dataframe for the product
            for col in df:
                line = ax.plot(ind, df[col], linewidth=1)
                if col == 'projected': line[0].set_color('darkorchid')

            plot_name = selected.iloc[i].name[0]
            if agg_filename: plot_name = ext_selection[agg_i]
            else: plot_name = selected.iloc[i].name[0]

            ax.set_title(plot_name + ", loe: " + str(loe_date))
            if i%4 == 0:
                ax.legend(['actual', 'mov ave.', 'projected'])
            pat_exp = pd.Period(selected.iloc[i].name[6], freq='M')
            lim_0 = max(ind_start, (pat_exp - total_dur)).to_timestamp()
            lim_1 = max(ind_start, (pat_exp - plat_dur)).to_timestamp()
            lim_2 = min(ind_end, max(ind_start, (pat_exp))).to_timestamp()
            lim_3 = None

            if lim_1 > ind_start.to_timestamp() and show_phases:
                ax.axvspan(lim_0, lim_1, facecolor='g', alpha=0.1)
                # only draw the line if in scope
                if lim_1 < ind_end.to_timestamp():
                    ax.axvline(x=lim_1, linestyle='--', linewidth=1, color='gray')

            if lim_2 > ind_start.to_timestamp():
                if show_phases: ax.axvspan(lim_1, lim_2, facecolor='r', alpha=0.1)

                # only draw the line if in scope
                if lim_2 < ind_end.to_timestamp():
                    ax.axvline(x=lim_2,  linewidth=2, linestyle='--', color='seagreen')

            ax.set_ylim(0)

            if xlims is not None:
                ax.set_xlim(xlims)

            agg_i +=1

            # save to the pdf, and clear the plot
            pdf.savefig()
            plt.close()

        if agg_filename is None:
            pdf.close()

    if agg_filename is not None:
        pdf.close()

    if _debug: print("\nLEAVING:  ", inspect.stack()[0][3])

##_________________________________________________________________________##

def plot_hi_lo_base1(in_dict, df=None,
                        trim=('1-2010', '12-2023'), figsize=(12,6), ybarlims=None,
                        outfile=None, fig=None, ax=None, bars=True, return_fig=False, _debug=False):

    '''Pass in a dictionary of scenarios with structure as follows:

    in_dict = { 'hi': {'params': <parameters for constructing rsets and getting projections>,
                       'sums':   <summed projections>,
                       'color':  <color to plot this scenario>,
                       'legend': <how this should be labelled in the legend>},

                'baseline': {'params'.. etc},

                'lo': {'params'.. etc}
                }

    Keys of in_dict can be whatever - used as the names of the scenarios.
      (If one has 'base' it gets a thick line.)

    Need either sums or (params plus a dataframe - which can calc sums).
    See Sensitivity Analysis notebook.

    '''

    pad = 30

    # make a df of sums
    sums_list = []

    for s in in_dict:

        this_sum = None

        if _debug: print('in rset'.ljust(pad), s)

        # check if missing sums, and get them if reqd.  Append to the df
        if in_dict[s].get('sums', None) is None:

            if _debug: print('no sum detected, so making one')

            if df is None:
                print('Need a df and params for ', s, ' so stopping here'); return

            elif in_dict[s]['params'] is None:
                print('Need params for ', s, ' so stopping here'); return

            else:
                this_sum = make_rsets(df, in_dict[s]['params'], return_sum=True, trim=trim)
                if _debug: print('got this\n', this_sum.head())

        else:
            if _debug: print('found sums for this, so just going to trim it')
            this_sum = in_dict[s]['sums'].loc[slice(pd.Period(trim[0], freq='M'), pd.Period(trim[1], freq='M'),None)]

        this_sum.name = s

        # at this point must have the sums
        sums_list.append(this_sum)

    # so we have a df of results
    res_df =  pd.concat(sums_list, axis=1)

    num_plots = 1
    if bars: num_plots = 2

    # FIRST THE LINE GRAPH
    if fig is None and ax is None:
        fig, ax = plt.subplots(num_plots, figsize=(figsize[0], figsize[1]*num_plots))

    ind = res_df.index.to_timestamp()

    ax_it = ax
    if bars: ax_it = ax[0]

    for i, s in enumerate(res_df):
        line = ax_it.plot(ind, res_df[s]*12/10**11, alpha=0.5)
        if in_dict[s].get('color', None) is not None:
            line[0].set_color(in_dict[s]['color'])
        if 'base' in s.lower(): line[0].set_linewidth(3)

    # do filling in
    # first get hold of the baseline
    base_out_name = [x for x in res_df.columns if 'base' in x.lower()]

    if len(base_out_name)==1:
        base_out_name = base_out_name[0]
        for s in res_df.drop(base_out_name, axis=1):
            fill_color = in_dict[s].get('color', 'grey')
            ax_it.fill_between(ind, res_df[s]*12/10**11, res_df[base_out_name]*12/10**11, color=fill_color, alpha=0.15)

    leg = [in_dict[s].get('legend',s) for s in in_dict]
    ax_it.legend(leg)

    if bars: ax_it.set_xticks([])

    #  NOW THE BAR CHART
    if bars:
        ann_df = res_df.groupby(res_df.index.year).sum()
        diffs_df = ann_df.drop('baseline', axis=1).subtract(ann_df['baseline'], axis=0)/10**11

        ax_it = ax[1]

        bar_w = 1; gap = 0.4

        for i,s in enumerate(diffs_df.columns):
            rect = ax_it.bar(diffs_df.index, diffs_df[s].values, width=bar_w-gap, color=in_dict[s]['color'], alpha=0.3)

        if ybarlims is not None: ax_it.set_ylim(ybarlims)

        ax_it.set_xticks([])
        ax_it.set_xlim(diffs_df.index[0]-0.5, diffs_df.index[-1]+0.5)
        # ax_it.legend([l for l in leg if 'base' not in l])

        tab = ax_it.table(colLabels=diffs_df.index,
                          cellText=[diffs_df[x].round(2).values for x in diffs_df],
                          rowLabels=diffs_df.columns)

        tab.set_fontsize(12)
        tab.scale(1,2)
        # tab.auto_set_font_size

        fig.subplots_adjust(hspace=0.05, wspace=0.3)

    if outfile: fig.savefig(outfile)

    if return_fig: return fig

##_________________________________________________________________________##

def find_opt(obs, start_vars, total_dur=158, term_gr = 0, drop = 0.85, incr=0.01, max_its=50, _debug=False):

    pad=35

    # initialise a log structure - will be filled by learning and returned
    log = dict(uptake_dur=dict(val=[start_vars['uptake_dur']],active=True),
               coh_gr=dict(val=[start_vars['coh_gr']],active=True),
               diffs=dict(val=[np.nan]))

    # dict for  parameters to vary - will make copies from log, change them and pass to `get_diffs`
    panel = dict(uptake_dur=None, coh_gr=None)

    i=0
    is_active = True

    while i < max_its and is_active:
        i+=1

        # for each var do the variation
        for k in panel.keys():
            if _debug: print('\nin var'.ljust(pad), k)

            # first load up a panel from log (NB, want all the keys)
            for j in panel:
                panel[j]=deepcopy(log[j]['val'][-1])

            # get the initial result
            base_res = get_diff(panel, obs, total_dur=total_dur, drop=drop, term_gr=term_gr)
            if _debug: print('panel[k] base'.ljust(pad), panel[k])

            # change the relevant var up and get up result
            if k.endswith('_dur'):
                # limit possible uptake_dur values
                if panel[k]>1 and panel[k]<total_dur-1:
                    panel[k] +=1
            else:
                panel[k] *= (1+incr)
            up_val = deepcopy(panel[k])
            if _debug: print('panel[k] up'.ljust(pad), panel[k])
            up_res = get_diff(panel, obs, total_dur=total_dur, drop=drop, term_gr=term_gr)

            # same for down
            panel[k]=deepcopy(log[k]['val'][-1])
            if k.endswith('_dur'):
                #can't allow this to be zero or bigger than max
                if panel[k]>1 and panel[k]<total_dur-1:
                    panel[k]  -=1
            else:
                panel[k] *= (1-incr)
            down_val = deepcopy(panel[k])
            if _debug: print('panel[k] down'.ljust(pad), panel[k])
            down_res = get_diff(panel, obs, total_dur=total_dur, drop=drop, term_gr=term_gr)

    #         if _debug: print("var ".ljust(pad), k)
            if _debug: print("\nbase".ljust(pad), base_res)
            if _debug: print("up".ljust(pad), up_res)
            if _debug: print("down".ljust(pad), down_res)

            if up_res < base_res and up_res < down_res:
                if _debug: print('--> UP WINS')
                log[k]['val'].append(up_val)
                log['diffs']['val'].append(up_res)
                log[k]['active'] = True

            elif down_res < base_res and down_res < up_res:
                if _debug: print('--> DOWN WINS')
                log[k]['val'].append(down_val)
                log['diffs']['val'].append(down_res)
                log[k]['active'] = True

            else:
                if _debug: print('--> no win')
                log[k]['val'].append(log[k]['val'][-1])
                log['diffs']['val'].append(base_res)
                log[k]['active'] = False

        if log['uptake_dur']['active'] or log['coh_gr']['active']:
            is_active = True
            if _debug: print('still active')

        else:
            is_active = False
            if _debug: print('found inactive')

    return(log)

##_________________________________________________________________________##

def get_diff(panel, obs, total_dur = 158, drop=0.85, term_gr=0):
    '''for an input 'panel' dict, with keys 'uptake_dur' and 'coh_gr', and a set of observations `obs`,
    return the least squares difficulty of the difference between the observed curve and a scaled synthetic
    curve generated from the panel arguments.

    So currently this is explicitly set up only to handle those two parameters.
    '''
    sh = pt.Shed('x', panel['uptake_dur'], total_dur-panel['uptake_dur'], drop)
    shape = pt.make_shape1(shed=sh)
    unscaled = pf.get_forecast1(shape, term_gr=term_gr, coh_gr=panel['coh_gr'], n_pers=len(obs))

    scale_factor = obs.iloc[-12:].mean() / unscaled.iat[-6]
    scaled = unscaled * scale_factor

    return sum((scaled - obs.values)**2)


text:
<?php

declare(strict_types=1);

namespace BrianFaust\Basket\Formatters;

use BrianFaust\Basket\Contracts\Formatter;
use BrianFaust\Basket\Discounts\PercentageDiscount;
use BrianFaust\Basket\Discounts\ValueDiscount;

class QuantityFormatter implements Formatter
{
    /**
     * @param $value
     *
     * @return string
     */
    public function format($value): string
    {
        $value = $value->rate();

        if ($value instanceof PercentageDiscount) {
            return (new PercentFormatter($value))->format($value);
        }

        if ($value instanceof ValueDiscount) {
            return (new MoneyFormatter())->format($value);
        }
    }
}

redpajama_set_name: RedPajamaGithub


text:
package org.arquillian.cube.containerobject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import org.arquillian.cube.docker.impl.requirement.RequiresDocker;
import org.arquillian.cube.docker.impl.requirement.RequiresDockerMachine;
import org.arquillian.cube.requirement.ArquillianConditionalRunner;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;

import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

@Category({RequiresDockerMachine.class, RequiresDocker.class})
@RequiresDockerMachine(name = "dev")
@RunWith(ArquillianConditionalRunner.class)
public class PingPongIT {

    @Cube
    PingPongContainer pingPongContainer;

    @Test
    public void shouldReturnOkAsPong() throws IOException {
        String pong = ping();
        assertThat(pong, containsString("OK"));
        assertThat(pingPongContainer.getConnectionPort(), is(5000));
    }

    public String ping() throws IOException {
        URL url = new URL("http://" + pingPongContainer.getDockerHost() + ":" + pingPongContainer.getConnectionPort());
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setRequestMethod("GET");

        BufferedReader in = new BufferedReader(
            new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuilder response = new StringBuilder();

        while ((inputLine = in.readLine()) != null) {
            response.append(inputLine);
        }
        in.close();

        return response.toString();
    }
}


text:
from JumpScale import j
from JumpScale.portal.portal.auth import auth
from JumpScale.portal.portal import exceptions

class system_contentmanager(j.tools.code.classGetBase()):

    """
    this actor manages all content on the wiki
    can e.g. notify wiki/appserver of updates of content

    """

    def __init__(self):

        self._te = {}
        self.actorname = "contentmanager"
        self.appname = "system"
        self.dbmem = j.servers.kvs.getRedisStore('contentmanager')

    def getActors(self, **args):
        """
        result list(str)

        """
        return list(j.portal.server.active.actorsloader.actors.keys())

    def getActorsWithPaths(self, **args):
        """
        result list([name,path])

        """
        actors = []
        for actor in list(j.portal.server.active.actorsloader.id2object.keys()):
            actor = j.portal.server.active.actorsloader.id2object[actor]
            actors.append([actor.model.id, actor.model.path])
        return actors

    def getBuckets(self, **args):
        """
        result list(str)

        """
        return list(j.portal.server.active.bucketsloader.buckets.keys())

    def getBucketsWithPaths(self, **args):
        """
        result list([name,path])

        """
        buckets = []
        for bucket in list(j.portal.server.active.bucketsloader.id2object.keys()):
            bucket = j.portal.server.active.bucketsloader.id2object[bucket]
            buckets.append([bucket.model.id, bucket.model.path])
        return buckets

    def getContentDirsWithPaths(self, **args):
        """
        return root dirs of content (actors,buckets,spaces)
        result list([name,path])

        """
        objects = []
        for objectname in list(j.portal.server.active.contentdirs.keys()):
            objectpath = j.portal.server.active.contentdirs[objectname]
            objects.append([objectname, objectpath])
        return objects

    def getSpaces(self, **args):
        """
        result list(str)

        """
        return list(j.portal.server.active.spacesloader.spaces.keys())

    def getSpacesWithPaths(self, **args):
        """
        result list([name,path])

        """
        spaces = []
        for space in list(j.portal.server.active.spacesloader.spaces.keys()):
            space = j.portal.server.active.spacesloader.spaces[space]
            spaces.append([space.model.id, space.model.path])
        return spaces

    def modelobjectlist(self, namespace, category, key, **args):
        """
        TODO describe what the goal is of this method
        param:appname
        param:actorname
        param:modelname
        param:key
        """
        try:
            client = getattr(j.data.models, namespace)
            client = getattr(client, category.capitalize())
            dtext = j.portal.tools.datatables
            data = dtext.getData(namespace, category, key, **args)
            return data
        except:
            raise exceptions.NotFound("Couldn't get client for namespace %s and category %s" % (namespace, category))

    def modelobjectupdate(self, appname, actorname, key, **args):
        """
        post args with ref_$id which refer to the key which is stored per actor in the cache
        param:appname
        param:actorname
        param:key
        result html

        """
        if appname not in j.apps.__dict__:
            raise exceptions.NotFound("App with name %s does not exists" % appname)
        if actorname not in j.apps.__dict__[appname].__dict__:
            raise exceptions.NotFound("Actor with name %s does not exists" % actorname)

        actor = j.apps.__dict__[appname].__dict__[actorname]
        ctx = args["ctx"]
        data = actor.dbmem.get("form_%s" % key)
        for ref in [item for item in list(ctx.params.keys()) if item.find("ref") == 0]:
            ref0 = int(ref.replace("ref_", ""))
            key, refS = data[1][ref0]  # @ref is how to retrieve info from the object
            model = data[0][key]
            exec("model.%s=args[\"%s\"]" % (refS, ref))

        for modelkey in list(data[0].keys()):
            model = data[0][modelkey]
            exec("actor.model_%s_set(model)" % model._meta[2])
        if 'HTTP_REFERER' in ctx.env:
            headers = [('Location', ctx.env['HTTP_REFERER'])]
            ctx.start_response('302', headers)

    def notifyActorDelete(self, id, **args):
        """
        param:id id of space which changed
        result bool

        """
        self.reloadAll(id)

    def reloadAll(self, id):
        def reloadApp():
            print("RELOAD APP FOR ACTORS Delete")
            j.portal.server.active.reset()

        j.portal.server.active.actorsloader.id2object.pop(id)

        j.portal.server.active.scheduler.scheduleFromNow(2, 9, reloadApp)
        j.portal.server.active.scheduler.scheduleFromNow(10, 9, reloadApp)

    def notifyActorModification(self, id, **args):
        """
        param:id id of actor which changed
        result bool

        """
        loaders = j.portal.server.active.actorsloader
        loader = loaders.getLoaderFromId(id)
        loader.reset()

    def notifyActorNew(self, path, name, **args):
        """
        param:path path of content which got changed
        param:name name
        result bool

        """
        result = False
        key = name.strip().lower()
        # print "name:%s"%name
        if name.find("__") == -1:
            raise RuntimeError(
                "Cannot create actor with name which is not constructed as $appname__$actorname, here %s" %
                name)
        appname, actorname = name.split("__")
        path = path

        if key not in j.portal.server.active.actorsloader.actors:
            # actor does not exist yet, create required dirs in basedir
            if path == "":
                path = j.sal.fs.joinPaths(j.portal.server.active.basepath, "actors", key)
                j.sal.fs.createDir(path)
                j.sal.fs.createDir(j.sal.fs.joinPaths(path, ".actor"))
            else:
                j.sal.fs.createDir(path)
                j.sal.fs.createDir(j.sal.fs.joinPaths(path, ".actor"))

            print(("scan path:%s" % path))
            j.portal.server.active.actorsloader.scan(path)
            result = True
        else:
            result = False
        return result

    def notifyActorNewDir(self, actorname, actorpath, path, **args):
        """
        param:actorname
        param:actorpath
        param:path

        """
        # put your code here to implement this method
        raise NotImplementedError("not implemented method notifyActorNewDir")

    def notifyBucketDelete(self, id, **args):
        """
        param:id id of bucket which changed
        result bool

        """
        result = None

        # immediate remove
        loaders = j.portal.server.active.bucketsloader
        loaders.removeLoader(id)

        def reloadApp(id=None):
            j.portal.server.active.loadSpaces(reset=True)

        # loader.pop(id)
        # j.portal.server.active.scheduler.scheduleFromNow(1,9,reloadApp,id=id)
        j.portal.server.active.scheduler.scheduleFromNow(10, 9, reloadApp, id=id)
        return result

    def notifyBucketModification(self, id, **args):
        """
        param:id id of bucket which changed
        result bool

        """
        loaders = j.portal.server.active.bucketsloader
        loader = loaders.getLoaderFromId(id)
        loader.reset()

    def notifyBucketNew(self, path, name, **args):
        """
        param:path path of content which got changed
        param:name name
        result bool

        """
        result = False

        key = name.strip().lower()
        path = path

        loader = j.portal.server.active.bucketsloader

        if key not in loader.id2object:
            # does not exist yet, create required dirs in basedir
            if path == "":
                path = j.sal.fs.joinPaths(j.portal.server.active.basepath, "buckets", key)
                j.sal.fs.createDir(path)
                j.sal.fs.createDir(j.sal.fs.joinPaths(path, ".bucket"))
            else:
                j.sal.fs.createDir(path)
                j.sal.fs.createDir(j.sal.fs.joinPaths(path, ".bucket"))

            loader.scan(path)
            result = True
        else:
            result = False

        return result

    def notifyFiledir(self, path, **args):
        """
        param:path path of content which got changed
        result bool

        """
        # put your code here to implement this method
        raise NotImplementedError("not implemented method notifyFiledir")

    def notifySpaceDelete(self, id, **args):
        """
        param:id id of space which changed
        result bool

        """

        # immediate remove
        loaders = j.portal.server.active.spacesloader
        loaders.removeLoader(id)

        def reloadApp():
            print("RELOAD APP SPACE DELETE")
            j.portal.server.active.loadSpaces(reset=True)

        # loader=j.portal.server.active.spacesloader.id2object
        # loader.pop(id)

        j.portal.server.active.addSchedule1MinPeriod(name="reloadportal", method=reloadApp)

    def notifySpaceModification(self, id, **args):
        """
        param:id id of space which changed
        result bool

        """
        id = id.lower()
        loaders = j.portal.server.active.spacesloader
        loader = loaders.getLoaderFromId(id)
        loader.reset()

        ctx = args["ctx"]

        if "payload" in ctx.params:

            payload = j.data.serializer.json.loads(ctx.params["payload"])

            owner = payload["repository"]["owner"]
            name = payload["repository"]["name"]

            cmd = "cd %s/%s/%s;hg pull;hg update -C" % (j.dirs.codeDir, owner, name)
            print(("execute %s" % cmd))
            j.system.process.execute(cmd)

    def notifySpaceNew(self, path, name, **args):
        """
        param:path path of content which got changed
        param:name name
        result bool

        """
        result = False

        key = name.strip().lower()

        path = path

        loader = j.portal.server.active.spacesloader

        if key not in loader.id2object:
            # does not exist yet, create required dirs in basedir
            if path == "":
                path = j.sal.fs.joinPaths(j.portal.server.active.basepath, "spaces", name)
            else:
                j.sal.fs.createDir(path)

            # create default content
            mddir = j.sal.fs.joinPaths(path, ".space")
            dest = j.sal.fs.joinPaths(path, "%s.wiki" % name)
            j.sal.fs.createDir(mddir)
            loader.scan(path)
            source = j.sal.fs.joinPaths(mddir, "template.wiki")
            j.sal.fs.copyFile(source, dest)
            result = True
        else:
            result = False
        return result

    def notifySpaceNewDir(self, spacename, spacepath, path, **args):
        """
        param:spacename
        param:spacepath
        param:path

        """
        args = {}
        args["spacename"] = spacename
        args["spacepath"] = spacepath
        args["path"] = path
        return self._te["notifySpaceNewDir"].execute4method(args, params={}, actor=self)

    def prepareActorSpecs(self, app, actor, **args):
        """
        compress specs for specific actor and targz in appropriate download location
        param:app name of app
        param:actor name of actor
        result bool

        """
        result = None

        actorname = actor
        appname = app

        filesroot = j.portal.server.active.filesroot
        actorloader = j.portal.server.active.actorsloader.id2object["%s__%s" % (appname, actorname)]

        path = j.sal.fs.joinPaths(actorloader.model.path, "specs")

        pathdest = j.sal.fs.joinPaths(filesroot, "specs", "%s_%s.tgz" % (appname, actorname))
        j.sal.fs.remove(pathdest)
        # j.sal.fs.createDir(j.sal.fs.joinPaths("files","specs"))

        if not j.sal.fs.exists(path):
            return {"error": "could not find spec path for app %s actor %s" % (appname, actorname)}
        else:
            j.sal.fs.targzCompress(path, pathdest)

        return result

    @auth(['admin'])
    def wikisave(self, cachekey, text, **args):
        """
        param:cachekey key to the doc
        param:text content of file to edit
        result bool

        """
        contents = j.apps.system.contentmanager.dbmem.get(cachekey)
        contents = {k.decode():v.decode() for k, v in contents.items()}
        j.sal.fs.writeFile(contents['path'], text)
        returnpath = "/%s/%s" % (contents['space'], contents['page'])
        if contents['querystr']:
            returnpath += "?%s" % contents['querystr']
        raise exceptions.Redirect(returnpath)


text:
package net.argius.stew.ui.window;

import static java.awt.event.InputEvent.SHIFT_DOWN_MASK;
import static java.awt.event.KeyEvent.*;
import static java.awt.event.MouseEvent.MOUSE_DRAGGED;
import static java.awt.event.MouseEvent.MOUSE_PRESSED;
import static javax.swing.KeyStroke.getKeyStroke;
import static net.argius.stew.ui.window.AnyActionKey.*;
import static net.argius.stew.ui.window.ResultSetTable.ActionKey.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.io.*;
import java.sql.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import javax.swing.text.*;
import net.argius.stew.*;
import net.argius.stew.io.*;
import net.argius.stew.text.*;

/**
 * Table for Result Set.
 */
final class ResultSetTable extends JTable implements AnyActionListener, TextSearch {

    enum ActionKey {
        copyWithEscape,
        clearSelectedCellValue,
        setCurrentTimeValue,
        copyColumnName,
        findColumnName,
        addEmptyRow,
        insertFromClipboard,
        duplicateRows,
        linkRowsToDatabase,
        deleteRows,
        sort,
        jumpToColumn,
        doNothing,
    }

    static final String TAB = "\t";

    private static final Logger log = Logger.getLogger(ResultSetTable.class);
    private static final TableCellRenderer nullRenderer = new NullValueRenderer();
    private static final boolean[] booleans = {false, true};

    private final AnyActionListener anyActionListener;
    private final ColumnHeaderCellRenderer columnHeaderRenderer;
    private final RowHeader rowHeader;
    private final Point mousePositionForColumnHeader = new Point();

    private int lastSortedIndex;
    private boolean lastSortedIsReverse;
    private String autoAdjustMode;

    // It is used by the process that has no key-event.
    private volatile KeyEvent lastKeyEvent;

    /**
     * Constructor.
     */
    ResultSetTable(AnyActionListener anyActionListener) {
        this.anyActionListener = anyActionListener;
        JTableHeader columnHeader = getTableHeader();
        TableCellRenderer columnHeaderDefaultRenderer = columnHeader.getDefaultRenderer();
        final RowHeader rowHeader = new RowHeader(this);
        this.columnHeaderRenderer = new ColumnHeaderCellRenderer(columnHeaderDefaultRenderer);
        this.rowHeader = rowHeader;
        setColumnSelectionAllowed(true);
        setAutoResizeMode(AUTO_RESIZE_OFF);
        columnHeader.setDefaultRenderer(columnHeaderRenderer);
        columnHeader.setReorderingAllowed(false);
        // [Events]
        // column header
        MouseInputListener colHeaderMouseListener = new ColumnHeaderMouseInputListener();
        columnHeader.addMouseListener(colHeaderMouseListener);
        columnHeader.addMouseMotionListener(colHeaderMouseListener);
        // row header
        MouseInputListener rowHeaderMouseListener = new RowHeaderMouseInputListener(rowHeader);
        rowHeader.addMouseListener(rowHeaderMouseListener);
        rowHeader.addMouseMotionListener(rowHeaderMouseListener);
        // cursor
        for (final boolean withSelect : booleans) {
            bindJumpAction("home", VK_HOME, withSelect);
            bindJumpAction("end", VK_END, withSelect);
            bindJumpAction("top", VK_UP, withSelect);
            bindJumpAction("bottom", VK_DOWN, withSelect);
            bindJumpAction("leftmost", VK_LEFT, withSelect);
            bindJumpAction("rightmost", VK_RIGHT, withSelect);
        }
        // key binds
        final int shortcutKey = Utilities.getMenuShortcutKeyMask();
        AnyAction aa = new AnyAction(this);
        aa.bindSelf(copyWithEscape, getKeyStroke(VK_C, shortcutKey | InputEvent.SHIFT_DOWN_MASK));
        aa.bindSelf(paste, getKeyStroke(VK_V, shortcutKey));
        aa.bindSelf(clearSelectedCellValue, getKeyStroke(VK_DELETE, 0));
        aa.bindSelf(deleteRows, getKeyStroke(VK_MINUS, shortcutKey | InputEvent.SHIFT_DOWN_MASK));
        aa.bindKeyStroke(true, adjustColumnWidth, getKeyStroke(VK_SLASH, shortcutKey));
        aa.bindKeyStroke(false, doNothing, getKeyStroke(VK_ESCAPE, 0));
    }

    private final class RowHeaderMouseInputListener extends MouseInputAdapter {

        @SuppressWarnings("hiding")
        private final RowHeader rowHeader;
        private int dragStartRow;

        RowHeaderMouseInputListener(RowHeader rowHeader) {
            this.rowHeader = rowHeader;
        }

        @Override
        public void mousePressed(MouseEvent e) {
            changeSelection(e);
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            changeSelection(e);
        }

        private void changeSelection(MouseEvent e) {
            Point p = new Point(e.getX(), e.getY());
            if (SwingUtilities.isLeftMouseButton(e)) {
                int id = e.getID();
                boolean isMousePressed = (id == MOUSE_PRESSED);
                boolean isMouseDragged = (id == MOUSE_DRAGGED);
                if (isMousePressed || isMouseDragged) {
                    if (p.y >= rowHeader.getBounds().height) {
                        return;
                    }
                    if (!e.isControlDown() && !e.isShiftDown()) {
                        clearSelection();
                    }
                    int rowIndex = rowAtPoint(p);
                    if (rowIndex < 0 || getRowCount() < rowIndex) {
                        return;
                    }
                    final int index0;
                    final int index1;
                    if (isMousePressed) {
                        if (e.isShiftDown()) {
                            index0 = dragStartRow;
                            index1 = rowIndex;
                        } else {
                            dragStartRow = rowIndex;
                            index0 = rowIndex;
                            index1 = rowIndex;
                        }
                    } else if (isMouseDragged) {
                        index0 = dragStartRow;
                        index1 = rowIndex;
                    } else {
                        return;
                    }
                    addRowSelectionInterval(index0, index1);
                    addColumnSelectionInterval(getColumnCount() - 1, 0);
                    requestFocus();
                    // justify a position between table and its row header
                    JViewport tableView = (JViewport)getParent();
                    Point viewPosition = tableView.getViewPosition();
                    viewPosition.y = ((JViewport)rowHeader.getParent()).getViewPosition().y;
                    tableView.setViewPosition(viewPosition);
                }
            }
        }
    }

    private final class ColumnHeaderMouseInputListener extends MouseInputAdapter {

        private int dragStartColumn;

        ColumnHeaderMouseInputListener() {
        } // empty

        @SuppressWarnings("synthetic-access")
        @Override
        public void mousePressed(MouseEvent e) {
            if (SwingUtilities.isLeftMouseButton(e)) {
                changeSelection(e);
            }
            mousePositionForColumnHeader.setLocation(e.getPoint());
        }

        @Override
        public void mouseDragged(MouseEvent e) {
            if (SwingUtilities.isLeftMouseButton(e)) {
                changeSelection(e);
            }
        }

        private void changeSelection(MouseEvent e) {
            final Point p = e.getPoint();
            int id = e.getID();
            boolean isMousePressed = (id == MOUSE_PRESSED);
            boolean isMouseDragged = (id == MOUSE_DRAGGED);
            if (isMousePressed || isMouseDragged) {
                if (!e.isControlDown() && !e.isShiftDown()) {
                    clearSelection();
                }
                int columnIndex = columnAtPoint(p);
                if (columnIndex < 0 || getColumnCount() <= columnIndex) {
                    return;
                }
                final int index0;
                final int index1;
                if (isMousePressed) {
                    if (e.isShiftDown()) {
                        index0 = dragStartColumn;
                        index1 = columnIndex;
                    } else {
                        dragStartColumn = columnIndex;
                        index0 = columnIndex;
                        index1 = columnIndex;
                    }
                } else if (isMouseDragged) {
                    index0 = dragStartColumn;
                    index1 = columnIndex;
                } else {
                    return;
                }
                selectColumn(index0, index1);
                requestFocus();
            }
        }
    }

    @Override
    protected void processKeyEvent(KeyEvent e) {
        super.processKeyEvent(e);
        lastKeyEvent = e;
    }

    @Override
    public void anyActionPerformed(AnyActionEvent ev) {
        try {
            processAnyActionEvent(ev);
        } catch (Exception ex) {
            WindowOutputProcessor.showErrorDialog(this, ex);
        }
    }

    public void processAnyActionEvent(AnyActionEvent ev) throws Exception {
        if (ev.isAnyOf(copy, selectAll)) {
            final String cmd = ev.getActionCommand();
            getActionMap().get(cmd).actionPerformed(new ActionEvent(this, 0, cmd));
        } else if (ev.isAnyOf(copyWithEscape)) {
            List<String> rows = new ArrayList<String>();
            for (int rowIndex : getSelectedRows()) {
                List<Object> row = new ArrayList<Object>();
                for (int columnIndex : getSelectedColumns()) {
                    final Object o = getValueAt(rowIndex, columnIndex);
                    row.add(CsvFormatter.AUTO.format(o == null ? "" : String.valueOf(o)));
                }
                rows.add(TextUtilities.join(TAB, row));
            }
            ClipboardHelper.setStrings(rows);
        } else if (ev.isAnyOf(paste)) {
            try {
                InputStream is = new ByteArrayInputStream(ClipboardHelper.getString().getBytes());
                Importer importer = new SmartImporter(is, TAB);
                try {
                    int[] selectedColumns = getSelectedColumns();
                    for (int rowIndex : getSelectedRows()) {
                        Object[] values = importer.nextRow();
                        final int limit = Math.min(selectedColumns.length, values.length);
                        for (int x = 0; x < limit; x++) {
                            setValueAt(values[x], rowIndex, selectedColumns[x]);
                        }
                    }
                } finally {
                    importer.close();
                }
                repaint();
            } finally {
                editingCanceled(new ChangeEvent(ev.getSource()));
            }
        } else if (ev.isAnyOf(clearSelectedCellValue)) {
            try {
                setValueAtSelectedCells(null);
                repaint();
            } finally {
                editingCanceled(new ChangeEvent(ev.getSource()));
            }
        } else if (ev.isAnyOf(setCurrentTimeValue)) {
            try {
                setValueAtSelectedCells(new Timestamp(System.currentTimeMillis()));
                repaint();
            } finally {
                editingCanceled(new ChangeEvent(ev.getSource()));
            }
        } else if (ev.isAnyOf(copyColumnName)) {
            List<String> a = new ArrayList<String>();
            ResultSetTableModel m = getResultSetTableModel();
            if (ev.getModifiers() == 0) {
                for (int i = 0, n = m.getColumnCount(); i < n; i++) {
                    a.add(m.getColumnName(i));
                }
            } else {
                for (final int i : getSelectedColumns()) {
                    a.add(m.getColumnName(i));
                }
            }
            ClipboardHelper.setString(TextUtilities.join(TAB, a));
        } else if (ev.isAnyOf(findColumnName)) {
            anyActionListener.anyActionPerformed(ev);
        } else if (ev.isAnyOf(addEmptyRow)) {
            ResultSetTableModel m = getResultSetTableModel();
            int[] selectedRows = getSelectedRows();
            if (selectedRows.length > 0) {
                final int nextRow = selectedRows[selectedRows.length - 1] + 1;
                for (int i = 0; i < selectedRows.length; i++) {
                    m.insertUnlinkedRow(nextRow, new Object[m.getColumnCount()]);
                }
            } else {
                m.addUnlinkedRow(new Object[m.getColumnCount()]);
            }
        } else if (ev.isAnyOf(insertFromClipboard)) {
            try {
                Importer importer = new SmartImporter(ClipboardHelper.getReaderForText(), TAB);
                try {
                    ResultSetTableModel m = getResultSetTableModel();
                    while (true) {
                        Object[] row = importer.nextRow();
                        if (row.length == 0) {
                            break;
                        }
                        m.addUnlinkedRow(row);
                        m.linkRow(m.getRowCount() - 1);
                    }
                    repaintRowHeader("model");
                } finally {
                    importer.close();
                }
            } finally {
                editingCanceled(new ChangeEvent(ev.getSource()));
            }
        } else if (ev.isAnyOf(duplicateRows)) {
            ResultSetTableModel m = getResultSetTableModel();
            List<?> rows = m.getDataVector();
            int[] selectedRows = getSelectedRows();
            int index = selectedRows[selectedRows.length - 1];
            for (int rowIndex : selectedRows) {
                m.insertUnlinkedRow(++index, (Vector<?>)((Vector<?>)rows.get(rowIndex)).clone());
            }
            repaint();
            repaintRowHeader("model");
        } else if (ev.isAnyOf(linkRowsToDatabase)) {
            ResultSetTableModel m = getResultSetTableModel();
            try {
                for (int rowIndex : getSelectedRows()) {
                    m.linkRow(rowIndex);
                }
            } finally {
                repaintRowHeader("unlinkedRowStatus");
            }
        } else if (ev.isAnyOf(deleteRows)) {
            try {
                ResultSetTableModel m = getResultSetTableModel();
                while (true) {
                    final int selectedRow = getSelectedRow();
                    if (selectedRow < 0) {
                        break;
                    }
                    if (m.isLinkedRow(selectedRow)) {
                        final boolean removed = m.removeLinkedRow(selectedRow);
                        assert removed;
                    } else {
                        m.removeRow(selectedRow);
                    }
                }
            } finally {
                repaintRowHeader("model");
            }
        } else if (ev.isAnyOf(adjustColumnWidth)) {
            adjustColumnWidth();
        } else if (ev.isAnyOf(widenColumnWidth)) {
            changeTableColumnWidth(1.5f);
        } else if (ev.isAnyOf(narrowColumnWidth)) {
            changeTableColumnWidth(1 / 1.5f);
        } else if (ev.isAnyOf(sort)) {
            doSort(getTableHeader().columnAtPoint(mousePositionForColumnHeader));
        } else if (ev.isAnyOf(jumpToColumn)) {
            Object[] args = ev.getArgs();
            if (args != null && args.length > 0) {
                jumpToColumn(String.valueOf(args[0]));
            }
        } else if (ev.isAnyOf(showColumnNumber)) {
            setShowColumnNumber(!columnHeaderRenderer.fixesColumnNumber);
            updateUI();
        } else {
            log.warn("not expected: Event=%s", ev);
        }
    }

    @Override
    public void editingStopped(ChangeEvent e) {
        try {
            super.editingStopped(e);
        } catch (Exception ex) {
            WindowOutputProcessor.showErrorDialog(getParent(), ex);
        }
    }

    @Override
    public boolean editCellAt(int row, int column, EventObject e) {
        boolean succeeded = super.editCellAt(row, column, e);
        if (succeeded) {
            if (editorComp instanceof JTextField) {
                // make it selected when starting edit-mode
                if (lastKeyEvent != null && lastKeyEvent.getKeyCode() != VK_F2) {
                    JTextField editor = (JTextField)editorComp;
                    initializeEditorComponent(editor);
                    editor.requestFocus();
                    editor.selectAll();
                }
            }
        }
        return succeeded;
    }

    @Override
    public TableCellEditor getCellEditor() {
        TableCellEditor editor = super.getCellEditor();
        if (editor instanceof DefaultCellEditor) {
            DefaultCellEditor d = (DefaultCellEditor)editor;
            initializeEditorComponent(d.getComponent());
        }
        return editor;
    }

    private void initializeEditorComponent(Component c) {
        final Color bgColor = Color.ORANGE;
        if (c != null && c.getBackground() != bgColor) {
            // determines initialized state by bgcolor
            if (!c.isEnabled()) {
                c.setEnabled(true);
            }
            c.setFont(getFont());
            c.setBackground(bgColor);
            if (c instanceof JTextComponent) {
                final JTextComponent text = (JTextComponent)c;
                AnyAction aa = new AnyAction(text);
                aa.setUndoAction();
                class EditCanceledOnCellFocusLost extends FocusAdapter {
                    @Override
                    public void focusLost(FocusEvent e) {
                        editingCanceled(new ChangeEvent(e.getSource()));
                    }
                }
                c.addFocusListener(new EditCanceledOnCellFocusLost());
            }
        }
    }

    @Override
    public TableCellRenderer getCellRenderer(int row, int column) {
        final Object v = getValueAt(row, column);
        if (v == null) {
            return nullRenderer;
        }
        return super.getCellRenderer(row, column);
    }

    @Override
    public void updateUI() {
        super.updateUI();
        adjustRowHeight(this);
    }

    static void adjustRowHeight(JTable table) {
        Component c = new JLabel("0");
        final int height = c.getPreferredSize().height;
        if (height > 0) {
            table.setRowHeight(height);
        }
    }

    @Override
    protected void configureEnclosingScrollPane() {
        super.configureEnclosingScrollPane();
        Container p = getParent();
        if (p instanceof JViewport) {
            Container gp = p.getParent();
            if (gp instanceof JScrollPane) {
                JScrollPane scrollPane = (JScrollPane)gp;
                JViewport viewport = scrollPane.getViewport();
                if (viewport == null || viewport.getView() != this) {
                    return;
                }
                scrollPane.setRowHeaderView(rowHeader);
            }
        }
    }

    @Override
    public void setModel(TableModel dataModel) {
        dataModel.addTableModelListener(rowHeader);
        super.setModel(dataModel);
    }

    /**
     * Selects colomns.
     * @param index0 index of start
     * @param index1 index of end
     */
    void selectColumn(int index0, int index1) {
        if (getRowCount() > 0) {
            addColumnSelectionInterval(index0, index1);
            addRowSelectionInterval(getRowCount() - 1, 0);
        }
    }

    /**
     * Jumps to specified column.
     * @param index
     * @return whether the column exists or not
     */
    boolean jumpToColumn(int index) {
        final int columnCount = getColumnCount();
        if (0 <= index && index < columnCount) {
            if (getSelectedRowCount() == 0) {
                changeSelection(-1, index, false, false);
            } else {
                int[] selectedRows = getSelectedRows();
                changeSelection(getSelectedRow(), index, false, false);
                for (final int selectedRow : selectedRows) {
                    changeSelection(selectedRow, index, false, true);
                }
            }
            return true;
        }
        return false;
    }

    /**
     * Jumps to specified column.
     * @param name
     * @return whether the column exists or not
     */
    boolean jumpToColumn(String name) {
        TableColumnModel columnModel = getColumnModel();
        for (int i = 0, n = columnModel.getColumnCount(); i < n; i++) {
            if (name.equals(String.valueOf(columnModel.getColumn(i).getHeaderValue()))) {
                jumpToColumn(i);
                return true;
            }
        }
        return false;
    }

    RowHeader getRowHeader() {
        return rowHeader;
    }

    ResultSetTableModel getResultSetTableModel() {
        return (ResultSetTableModel)getModel();
    }

    boolean isShowColumnNumber() {
        return columnHeaderRenderer.fixesColumnNumber;
    }

    void setShowColumnNumber(boolean showColumnNumber) {
        final boolean oldValue = this.columnHeaderRenderer.fixesColumnNumber;
        this.columnHeaderRenderer.fixesColumnNumber = showColumnNumber;
        firePropertyChange("showNumber", oldValue, showColumnNumber);
    }

    void repaintRowHeader(String propName) {
        if (rowHeader != null) {
            rowHeader.propertyChange(new PropertyChangeEvent(this, propName, null, null));
        }
    }

    String getAutoAdjustMode() {
        return autoAdjustMode;
    }

    void setAutoAdjustMode(String autoAdjustMode) {
        final String oldValue = this.autoAdjustMode;
        this.autoAdjustMode = autoAdjustMode;
        firePropertyChange("autoAdjustMode", oldValue, autoAdjustMode);
    }

    private void bindJumpAction(String suffix, int key, boolean withSelect) {
        final String actionKey = String.format("%s-to-%s", (withSelect) ? "select" : "jump", suffix);
        final CellCursor c = new CellCursor(this, withSelect);
        final int modifiers = Utilities.getMenuShortcutKeyMask()
                              | (withSelect ? SHIFT_DOWN_MASK : 0);
        KeyStroke[] keyStrokes = {getKeyStroke(key, modifiers)};
        c.putValue(Action.ACTION_COMMAND_KEY, actionKey);
        InputMap im = this.getInputMap();
        for (KeyStroke ks : keyStrokes) {
            im.put(ks, actionKey);
        }
        this.getActionMap().put(actionKey, c);
    }

    private static final class CellCursor extends AbstractAction {

        private final JTable table;
        private final boolean extend;

        CellCursor(JTable table, boolean extend) {
            this.table = table;
            this.extend = extend;
        }

        int getColumnPosition() {
            int[] a = table.getSelectedColumns();
            if (a == null || a.length == 0) {
                return -1;
            }
            ListSelectionModel csm = table.getColumnModel().getSelectionModel();
            return (a[0] == csm.getAnchorSelectionIndex()) ? a[a.length - 1] : a[0];
        }

        int getRowPosition() {
            int[] a = table.getSelectedRows();
            if (a == null || a.length == 0) {
                return -1;
            }
            ListSelectionModel rsm = table.getSelectionModel();
            return (a[0] == rsm.getAnchorSelectionIndex()) ? a[a.length - 1] : a[0];
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            final String cmd = e.getActionCommand();
            final int ri;
            final int ci;
            if (cmd == null) {
                assert false : "command is null";
                return;
            } else if (cmd.endsWith("-to-home")) {
                ri = 0;
                ci = 0;
            } else if (cmd.endsWith("-to-end")) {
                ri = table.getRowCount() - 1;
                ci = table.getColumnCount() - 1;
            } else if (cmd.endsWith("-to-top")) {
                ri = 0;
                ci = getColumnPosition();
            } else if (cmd.endsWith("-to-bottom")) {
                ri = table.getRowCount() - 1;
                ci = getColumnPosition();
            } else if (cmd.endsWith("-to-leftmost")) {
                ri = getRowPosition();
                ci = 0;
            } else if (cmd.endsWith("-to-rightmost")) {
                ri = getRowPosition();
                ci = table.getColumnCount() - 1;
            } else {
                assert false : "unknown command: " + cmd;
                return;
            }
            table.changeSelection(ri, ci, false, extend);
        }

    }

    private static final class NullValueRenderer extends DefaultTableCellRenderer {

        NullValueRenderer() {
        } // empty

        @Override
        public Component getTableCellRendererComponent(JTable table,
                                                       Object value,
                                                       boolean isSelected,
                                                       boolean hasFocus,
                                                       int row,
                                                       int column) {
            Component c = super.getTableCellRendererComponent(table,
                                                              "NULL",
                                                              isSelected,
                                                              hasFocus,
                                                              row,
                                                              column);
            c.setForeground(new Color(63, 63, 192, 192));
            Font font = c.getFont();
            c.setFont(font.deriveFont(font.getSize() * 0.8f));
            return c;
        }

    }

    private static final class ColumnHeaderCellRenderer implements TableCellRenderer {

        TableCellRenderer renderer;
        boolean fixesColumnNumber;

        ColumnHeaderCellRenderer(TableCellRenderer renderer) {
            this.renderer = renderer;
            this.fixesColumnNumber = false;
        }

        @Override
        public Component getTableCellRendererComponent(JTable table,
                                                       Object value,
                                                       boolean isSelected,
                                                       boolean hasFocus,
                                                       int row,
                                                       int column) {
            Object o = fixesColumnNumber ? String.format("%d %s", column + 1, value) : value;
            return renderer.getTableCellRendererComponent(table,
                                                          o,
                                                          isSelected,
                                                          hasFocus,
                                                          row,
                                                          column);
        }

    }

    private static final class RowHeader extends JTable implements PropertyChangeListener {

        private static final int DEFAULT_WIDTH = 40;

        private DefaultTableModel model;
        private TableCellRenderer renderer;

        RowHeader(JTable table) {
            this.model = new DefaultTableModel(0, 1) {
                @Override
                public boolean isCellEditable(int row, int column) {
                    return false;
                }
            };
            final ResultSetTable t = (ResultSetTable)table;
            this.renderer = new DefaultTableCellRenderer() {
                @Override
                public Component getTableCellRendererComponent(JTable table,
                                                               Object value,
                                                               boolean isSelected,
                                                               boolean hasFocus,
                                                               int row,
                                                               int column) {
                    assert t.getModel() instanceof ResultSetTableModel;
                    final boolean rowLinked = t.getResultSetTableModel().isLinkedRow(row);
                    JLabel label = new JLabel(String.format("%s ", rowLinked ? row + 1 : "+"));
                    label.setHorizontalAlignment(RIGHT);
                    label.setFont(table.getFont());
                    label.setOpaque(true);
                    return label;
                }
            };
            setModel(model);
            setWidth(table);
            setFocusable(false);
            table.addPropertyChangeListener(this);
        }

        @Override
        public void propertyChange(PropertyChangeEvent e) {
            JTable table = (JTable)e.getSource();
            if (table == null) {
                return;
            }
            String propertyName = e.getPropertyName();
            if (propertyName.equals("enabled")) {
                boolean isEnabled = table.isEnabled();
                setVisible(isEnabled);
                if (isEnabled) {
                    setWidth(table);
                    resetViewPosition(table);
                }
            } else if (propertyName.equals("font")) {
                setFont(table.getFont());
            } else if (propertyName.equals("rowHeight")) {
                setRowHeight(table.getRowHeight());
            } else if (propertyName.equals("model")) {
                model.setRowCount(table.getRowCount());
            } else if (propertyName.equals("unlinkedRowStatus")) {
                repaint();
            } else if (propertyName.equals("ancestor")) {
                // empty
            } else {
                // empty
            }
            validate();
        }

        @Override
        public void tableChanged(TableModelEvent e) {
            Object src = e.getSource();
            if (model != null && src != null) {
                model.setRowCount(((TableModel)src).getRowCount());
            }
            super.tableChanged(e);
        }

        @Override
        public void updateUI() {
            super.updateUI();
            adjustRowHeight(this);
        }

        void setWidth(JTable table) {
            // XXX unstable
            final int rowCount = table.getRowCount();
            model.setRowCount(rowCount);
            JLabel label = new JLabel(String.valueOf(rowCount * 1000L));
            Dimension d = getSize();
            d.width = Math.max(label.getPreferredSize().width, DEFAULT_WIDTH);
            setPreferredScrollableViewportSize(d);
        }

        private void resetViewPosition(JTable table) {
            // forces to reset its view position to table's view position
            Container p1 = table.getParent();
            Container p2 = getParent();
            if (p1 instanceof JViewport && p2 instanceof JViewport) {
                JViewport v1 = (JViewport)p1;
                JViewport v2 = (JViewport)p2;
                v2.setViewPosition(v1.getViewPosition());
            }
        }

        @Override
        public boolean isCellEditable(int row, int column) {
            return false;
        }

        @Override
        public TableCellRenderer getCellRenderer(int row, int column) {
            return renderer;
        }

    }

    // text search

    @Override
    public boolean search(Matcher matcher) {
        final int rowCount = getRowCount();
        if (rowCount <= 0) {
            return false;
        }
        final int columnCount = getColumnCount();
        final boolean backward = matcher.isBackward();
        final int amount = backward ? -1 : 1;
        final int rowStart = backward ? rowCount - 1 : 0;
        final int rowEnd = backward ? 0 : rowCount - 1;
        final int columnStart = backward ? columnCount - 1 : 0;
        final int columnEnd = backward ? 0 : columnCount - 1;
        int row = rowStart;
        int column = columnStart;
        if (getSelectedColumnCount() > 0) {
            column = getSelectedColumn();
            row = getSelectedRow() + amount;
            if (backward) {
                if (row < 0) {
                    --column;
                    if (column < 0) {
                        return false;
                    }
                    row = rowStart;
                }
            } else {
                if (row >= rowCount) {
                    ++column;
                    if (column >= columnCount) {
                        return false;
                    }
                    row = rowStart;
                }
            }
        }
        final TableModel m = getModel();
        for (; backward ? column >= columnEnd : column <= columnEnd; column += amount) {
            for (; backward ? row >= rowEnd : row <= rowEnd; row += amount) {
                if (matcher.find(String.valueOf(m.getValueAt(row, column)))) {
                    changeSelection(row, column, false, false);
                    return true;
                }
            }
            row = rowStart;
        }
        return false;
    }

    @Override
    public void reset() {
        // empty
    }

    void resetSortState() {
        lastSortedIndex = -1;
        lastSortedIsReverse = false;
    }

    static final class TableHeaderTextSearch implements TextSearch {
        private ResultSetTable rstable;
        private JTableHeader tableHeader;
        TableHeaderTextSearch(ResultSetTable rstable, JTableHeader tableHeader) {
            this.rstable = rstable;
            this.tableHeader = tableHeader;
        }
        @Override
        public boolean search(Matcher matcher) {
            TableColumnModel m = tableHeader.getColumnModel();
            int columnCount = m.getColumnCount();
            if (columnCount < 1) {
                return false;
            }
            final boolean backward = matcher.isBackward();
            final int amount = backward ? -1 : 1;
            final int columnStart = backward ? columnCount - 1 : 0;
            final int columnEnd = backward ? 0 : columnCount - 1;
            int column = columnStart;
            if (rstable.getSelectedColumnCount() > 0) {
                column = rstable.getSelectedColumn() + amount;
            }
            for (; backward ? column >= columnEnd : column <= columnEnd; column += amount) {
                if (matcher.find(String.valueOf(m.getColumn(column).getHeaderValue()))) {
                    rstable.jumpToColumn(column);
                    return true;
                }
            }
            return false;
        }
        @Override
        public void reset() {
            // empty
        }
    }

    // event-handlers

    private void setValueAtSelectedCells(Object value) {
        int[] selectedColumns = getSelectedColumns();
        for (int rowIndex : getSelectedRows()) {
            for (int colIndex : selectedColumns) {
                setValueAt(value, rowIndex, colIndex);
            }
        }
    }

    private void adjustColumnWidth() {
        final int rowCount = getRowCount();
        final boolean byHeader;
        final boolean byValue;
        switch (AnyActionKey.of(autoAdjustMode)) {
            case autoAdjustModeNone:
                byHeader = false;
                byValue = false;
                break;
            case autoAdjustModeHeader:
                byHeader = true;
                byValue = false;
                break;
            case autoAdjustModeValue:
                byHeader = false;
                byValue = true;
                break;
            case autoAdjustModeHeaderAndValue:
                byHeader = true;
                byValue = true;
                break;
            default:
                log.warn("autoAdjustMode=%s", autoAdjustMode);
                return;
        }
        if (!byHeader && rowCount == 0) {
            return;
        }
        final float max = getParent().getWidth() * 0.8f;
        TableColumnModel columnModel = getColumnModel();
        JTableHeader header = getTableHeader();
        for (int columnIndex = 0, n = getColumnCount(); columnIndex < n; columnIndex++) {
            float size = 0f;
            if (byHeader) {
                TableColumn column = columnModel.getColumn(columnIndex);
                TableCellRenderer renderer = column.getHeaderRenderer();
                if (renderer == null) {
                    renderer = header.getDefaultRenderer();
                }
                if (renderer != null) {
                    Component c = renderer.getTableCellRendererComponent(this,
                                                                         column.getHeaderValue(),
                                                                         false,
                                                                         false,
                                                                         0,
                                                                         columnIndex);
                    size = c.getPreferredSize().width * 1.5f;
                }
            }
            if (byValue) {
                for (int rowIndex = 0; rowIndex < rowCount; rowIndex++) {
                    TableCellRenderer renderer = getCellRenderer(rowIndex,
                                                                                columnIndex);
                    if (renderer == null) {
                        continue;
                    }
                    Object value = getValueAt(rowIndex, columnIndex);
                    Component c = renderer.getTableCellRendererComponent(this,
                                                                         value,
                                                                         false,
                                                                         false,
                                                                         rowIndex,
                                                                         columnIndex);
                    size = Math.max(size, c.getPreferredSize().width);
                    if (size >= max) {
                        break;
                    }
                }
            }
            int width = Math.round(size > max ? max : size) + 1;
            columnModel.getColumn(columnIndex).setPreferredWidth(width);
        }
    }

    void doSort(int columnIndex) {
        if (getColumnCount() == 0) {
            return;
        }
        final boolean reverse;
        if (lastSortedIndex == columnIndex) {
            reverse = (lastSortedIsReverse == false);
        } else {
            lastSortedIndex = columnIndex;
            reverse = false;
        }
        lastSortedIsReverse = reverse;
        getResultSetTableModel().sort(columnIndex, reverse);
        repaint();
        repaintRowHeader("unlinkedRowStatus");
    }

    void changeTableColumnWidth(double rate) {
        for (TableColumn column : Collections.list(getColumnModel().getColumns())) {
            column.setPreferredWidth((int)(column.getWidth() * rate));
        }
    }

}


text:
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    tools:context=".activities.LibraryActivity"
    tools:ignore="AlwaysShowAction"
    tools:targetApi="o">
    <item
        android:id="@+id/action_edit_name"
        android:icon="@drawable/ic_edit"
        android:title="@string/menu_edit_name"
        android:tooltipText="@string/menu_edit_name"
        app:showAsAction="always" />
    <item
        android:id="@+id/action_delete"
        android:icon="@drawable/ic_action_delete_forever"
        android:title="@string/menu_delete_single"
        android:tooltipText="@string/menu_delete_single"
        app:showAsAction="always" />
    <item
        android:id="@+id/action_completed"
        android:icon="@drawable/ic_completed_single"
        android:title="@string/menu_completed_single"
        android:tooltipText="@string/menu_completed_single"
        app:showAsAction="always" />
    <item
        android:id="@+id/action_archive"
        android:icon="@drawable/ic_archive"
        android:title="@string/menu_archive"
        android:tooltipText="@string/menu_archive_help"
        app:showAsAction="ifRoom" />
    <item
        android:id="@+id/action_share"
        android:icon="@drawable/ic_action_share"
        android:title="@string/menu_share"
        android:tooltipText="@string/menu_share_help"
        app:showAsAction="ifRoom" />
    <item
        android:id="@+id/action_change_group"
        android:icon="@drawable/ic_folder_edit"
        android:title="@string/menu_change_group"
        android:tooltipText="@string/menu_change_group"
        app:showAsAction="ifRoom" />
    <item
        android:id="@+id/action_open_folder"
        android:icon="@drawable/ic_folder"
        android:title="@string/menu_open_folder"
        android:tooltipText="@string/menu_open_folder"
        app:showAsAction="ifRoom" />
    <!-- For downloaded books only -->
    <item
        android:id="@+id/action_redownload"
        android:icon="@drawable/ic_action_download_scratch"
        android:title="@string/menu_redownload_scratch"
        android:tooltipText="@string/menu_redownload_scratch"
        app:showAsAction="ifRoom" />
    <item
        android:id="@+id/action_stream"
        android:icon="@drawable/ic_action_download_stream"
        android:title="@string/menu_stream"
        android:tooltipText="@string/menu_stream"
        app:showAsAction="ifRoom" />
    <!-- For online books only -->
    <item
        android:id="@+id/action_download"
        android:icon="@drawable/ic_action_download_scratch"
        android:title="@string/menu_download_streamed"
        android:tooltipText="@string/menu_download_streamed"
        app:showAsAction="ifRoom" />
    <item
        android:id="@+id/action_set_cover"
        android:icon="@drawable/ic_action_make_cover"
        android:title="@string/group_make_cover"
        android:tooltipText="@string/group_make_cover"
        app:showAsAction="ifRoom" />
    <item
        android:id="@+id/action_merge"
        android:icon="@drawable/ic_action_merge"
        android:title="@string/merge_merge"
        android:tooltipText="@string/merge_merge"
        app:showAsAction="ifRoom" />
    <item
        android:id="@+id/action_split"
        android:icon="@drawable/ic_action_split"
        android:title="@string/merge_split"
        android:tooltipText="@string/merge_split"
        app:showAsAction="ifRoom" />
    <item
        android:id="@+id/action_selectAll"
        android:icon="@drawable/ic_action_select_all"
        android:title="@string/menu_select_all"
        android:tooltipText="@string/menu_select_all"
        app:showAsAction="ifRoom" />
</menu>