text:
import chalk from "chalk";
import * as path from "path";
import { TSBuffer } from 'tsbuffer';
import { Counter, Flow, getCustomObjectIdTypes, MsgHandlerManager, MsgService, ParsedServerInput, ServiceMap, ServiceMapUtil, TransportDataUtil } from 'tsrpc-base-client';
import { ApiReturn, ApiServiceDef, BaseServiceType, Logger, LogLevel, ServerInputData, ServiceProto, setLogLevel, TsrpcError, TsrpcErrorType } from 'tsrpc-proto';
import { getClassObjectId } from "../../models/getClassObjectId";
import { HttpConnection } from "../http/HttpConnection";
import { ApiCallInner } from "../inner/ApiCallInner";
import { InnerConnection } from "../inner/InnerConnection";
import { TerminalColorLogger } from '../models/TerminalColorLogger';
import { ApiCall } from './ApiCall';
import { BaseConnection } from './BaseConnection';
import { MsgCall } from './MsgCall';

/**
 * Abstract base class for TSRPC Server.
 * Implement on a transportation protocol (like HTTP WebSocket) by extend it.
 * @typeParam ServiceType - `ServiceType` from generated `proto.ts`
 */
export abstract class BaseServer<ServiceType extends BaseServiceType = BaseServiceType>{
    /**
     * Start the server
     * @throws
     */
    abstract start(): Promise<void>;

    /**
     * Stop server immediately, not waiting for the requests ending.
     */
    abstract stop(): Promise<void>;

    protected _status: ServerStatus = ServerStatus.Closed;
    get status(): ServerStatus {
        return this._status;
    }

    // 配置及其衍生项
    readonly proto: ServiceProto<ServiceType>;
    readonly options: BaseServerOptions<ServiceType>;
    readonly tsbuffer: TSBuffer;
    readonly serviceMap: ServiceMap;
    readonly logger: Logger;

    protected _connIdCounter = new Counter(1);

    /**
     * Flow is a specific concept created by TSRPC family.
     * All pre-flow can interrupt latter behaviours.
     * All post-flow can NOT interrupt latter behaviours.
     */
    readonly flows = {
        // Conn Flows
        /** After the connection is created */
        postConnectFlow: new Flow<BaseConnection<ServiceType>>(),
        /** After the connection is disconnected */
        postDisconnectFlow: new Flow<{ conn: BaseConnection<ServiceType>, reason?: string }>(),

        // Buffer Flows
        /**
         * Before processing the received data, usually be used to encryption / decryption.
         * Return `null | undefined` would ignore the buffer.
         */
        preRecvDataFlow: new Flow<{
            conn: BaseConnection<ServiceType>,
            data: string | Uint8Array | object,
            /**
             * @deprecated use `serviceId` instead
             */
            serviceName?: string,
            /**
             * Parsed service id, you can get this by `this.serviceMap.apiName2Service[serviceName].id`
             */
            serviceId?: number
        }>(),
        /**
         * Before send out data to network, usually be used to encryption / decryption.
         * Return `null | undefined` would not send the buffer.
         */
        preSendDataFlow: new Flow<{ conn: BaseConnection<ServiceType>, data: string | Uint8Array | object, call?: ApiCall }>(),
        /**
         * @deprecated Use `preRecvDataFlow` instead.
         */
        preRecvBufferFlow: new Flow<{ conn: BaseConnection<ServiceType>, buf: Uint8Array }>(),
        /**
         * @deprecated Use `preSendDataFlow` instead.
         */
        preSendBufferFlow: new Flow<{ conn: BaseConnection<ServiceType>, buf: Uint8Array, call?: ApiCall }>(),

        // ApiCall Flows
        /**
         * Before a API request is send.
         * Return `null | undefined` would cancel the request.
         */
        preApiCallFlow: new Flow<ApiCall>(),
        /**
         * Before return the `ApiReturn` to the client.
         * It may be used to change the return value, or return `null | undefined` to abort the request.
         */
        preApiReturnFlow: new Flow<{ call: ApiCall, return: ApiReturn<any> }>(),
        /**
         * After the `ApiReturn` is send.
         * return `null | undefined` would NOT interrupt latter behaviours.
         */
        postApiReturnFlow: new Flow<{ call: ApiCall, return: ApiReturn<any> }>(),
        /**
         * After the api handler is executed.
         * return `null | undefined` would NOT interrupt latter behaviours.
         */
        postApiCallFlow: new Flow<ApiCall>(),

        // MsgCall Flows
        /**
         * Before handle a `MsgCall`
         */
        preMsgCallFlow: new Flow<MsgCall>(),
        /**
         * After handlers of a `MsgCall` are executed.
         * return `null | undefined` would NOT interrupt latter behaviours.
         */
        postMsgCallFlow: new Flow<MsgCall>(),
        /**
         * Before send out a message.
         * return `null | undefined` would NOT interrupt latter behaviours.
         */
        preSendMsgFlow: new Flow<{ conn: BaseConnection<ServiceType>, service: MsgService, msg: any }>(),
        /**
         * After send out a message.
         * return `null | undefined` would NOT interrupt latter behaviours.
         */
        postSendMsgFlow: new Flow<{ conn: BaseConnection<ServiceType>, service: MsgService, msg: any }>(),
    } as const;

    // Handlers
    private _apiHandlers: { [apiName: string]: ApiHandler<any> | undefined } = {};
    // 多个Handler将异步并行执行
    private _msgHandlers: MsgHandlerManager = new MsgHandlerManager();

    private static _isUncaughtExceptionProcessed = false;
    /**
     * It makes the `uncaughtException` and `unhandledRejection` not lead to the server stopping.
     * @param logger
     * @returns
     */
    static processUncaughtException(logger: Logger) {
        if (this._isUncaughtExceptionProcessed) {
            return;
        }
        this._isUncaughtExceptionProcessed = true;

        process.on('uncaughtException', e => {
            logger.error('[uncaughtException]', e);
        });

        process.on('unhandledRejection', e => {
            logger.error('[unhandledRejection]', e);
        });
    }

    constructor(proto: ServiceProto<ServiceType>, options: BaseServerOptions<ServiceType>) {
        this.proto = proto;
        this.options = options;

        // @deprecated jsonEnabled
        if (this.options.json) {
            this.options.jsonEnabled = true;
        }

        this.tsbuffer = new TSBuffer({
            ...proto.types,
            // Support mongodb/ObjectId
            ...getCustomObjectIdTypes(getClassObjectId())
        }, {
            strictNullChecks: this.options.strictNullChecks
        });
        this.serviceMap = ServiceMapUtil.getServiceMap(proto);
        this.logger = this.options.logger;
        if (this.logger) {
            this.logger = setLogLevel(this.logger, this.options.logLevel);
        }

        // Process uncaught exception, so that Node.js process would not exit easily
        BaseServer.processUncaughtException(this.logger);

        // default flows onError handler
        this._setDefaultFlowOnError();
    }

    protected _setDefaultFlowOnError() {
        // API Flow Error: return [InternalServerError]
        this.flows.preApiCallFlow.onError = (e, call) => {
            if (e instanceof TsrpcError) {
                call.error(e)
            }
            else {
                this.onInternalServerError(e, call)
            }
        };
        this.flows.postApiCallFlow.onError = (e, call) => {
            if (!call.return) {
                if (e instanceof TsrpcError) {
                    call.error(e)
                }
                else {
                    this.onInternalServerError(e, call)
                }
            }
            else {
                call.logger.error('postApiCallFlow Error:', e);
            }
        };
        this.flows.preApiReturnFlow.onError = (e, last) => {
            last.call['_return'] = undefined;
            if (e instanceof TsrpcError) {
                last.call.error(e)
            }
            else {
                this.onInternalServerError(e, last.call)
            }
        }
        this.flows.postApiReturnFlow.onError = (e, last) => {
            if (!last.call.return) {
                if (e instanceof TsrpcError) {
                    last.call.error(e)
                }
                else {
                    this.onInternalServerError(e, last.call)
                }
            }
        }
    }

    protected _pendingApiCallNum = 0;

    // #region receive buffer process flow
    /**
     * Process the buffer, after the `preRecvBufferFlow`.
     */
    async _onRecvData(conn: BaseConnection<ServiceType>, data: string | Uint8Array | object, serviceId?: number) {
        // 非 OPENED 状态 停止接受新的请求
        if (!(conn instanceof InnerConnection) && this.status !== ServerStatus.Opened) {
            return;
        }

        // debugBuf log
        if (this.options.debugBuf) {
            if (typeof data === 'string') {
                conn.logger?.debug(`[RecvText] length=${data.length}`, data);
            }
            else if (data instanceof Uint8Array) {
                conn.logger?.debug(`[RecvBuf] length=${data.length}`, data);
            }
            else {
                conn.logger?.debug('[RecvJSON]', data);
            }
        }

        // jsonEnabled 未启用,不支持文本请求
        if (typeof data === 'string' && !this.options.jsonEnabled) {
            this.onInputDataError('JSON mode is not enabled, please use binary instead.', conn, data);
            return;
        }

        // Pre flow
        const preServiceName = serviceId ? this.serviceMap.id2Service[serviceId].name : undefined;
        let pre = await this.flows.preRecvDataFlow.exec({
            conn: conn,
            data: data,
            serviceId: serviceId,
            serviceName: preServiceName
        }, conn.logger);
        if (!pre) {
            conn.logger.debug('[preRecvDataFlow] Canceled');
            return;
        }
        data = pre.data;
        serviceId = pre.serviceId
        // @deprecated 兼容
        if (pre.serviceName && pre.serviceName !== preServiceName) {
            serviceId = this.serviceMap.apiName2Service[pre.serviceName]?.id;
        }

        // @deprecated preRecvBuffer
        if (data instanceof Uint8Array) {
            let preBuf = await this.flows.preRecvBufferFlow.exec({ conn: conn, buf: data }, conn.logger);
            if (!preBuf) {
                conn.logger.debug('[preRecvBufferFlow] Canceled');
                return;
            }
            data = preBuf.buf;
        }

        if (serviceId === undefined && conn instanceof HttpConnection && typeof data === 'string') {
            this.onInputDataError(`Invalid URL path: ${conn.httpReq.url}`, conn, data)
            return;
        }

        // Parse Call
        let opInput = this._parseServerInput(this.tsbuffer, this.serviceMap, data, serviceId);
        if (!opInput.isSucc) {
            this.onInputDataError(opInput.errMsg, conn, data);
            return;
        }
        let call = conn.makeCall(opInput.result);

        if (call.type === 'api') {
            await this._handleApiCall(call);
        }
        else {
            await this._onMsgCall(call);
        }
    }

    protected async _handleApiCall(call: ApiCall) {
        ++this._pendingApiCallNum;
        await this._onApiCall(call);
        if (--this._pendingApiCallNum === 0) {
            this._gracefulStop?.rs();
        }
    }

    protected async _onApiCall(call: ApiCall) {
        let timeoutTimer = this.options.apiTimeout ? setTimeout(() => {
            if (!call.return) {
                call.error('Server Timeout', {
                    code: 'SERVER_TIMEOUT',
                    type: TsrpcErrorType.ServerError
                })
            }
            timeoutTimer = undefined;
        }, this.options.apiTimeout) : undefined;

        // Pre Flow
        let preFlow = await this.flows.preApiCallFlow.exec(call, call.logger);
        if (!preFlow) {
            if (timeoutTimer) {
                clearTimeout(timeoutTimer);
                timeoutTimer = undefined;
            }
            call.logger.debug('[preApiCallFlow] Canceled');
            return;
        }
        call = preFlow;

        // exec ApiCall
        call.logger.log(chalk.green('[ApiReq]'), this.options.logReqBody ? call.req : '');
        let { handler } = await this.getApiHandler(call.service, this._delayImplementApiPath, call.logger);
        // exec API handler
        if (handler) {
            try {
                await handler(call);
            }
            catch (e: any) {
                if (e instanceof TsrpcError) {
                    call.error(e);
                }
                else {
                    this.onInternalServerError(e, call);
                }
            }
        }
        // 未找到ApiHandler,且未进行任何输出
        else {
            call.error(`Unhandled API: ${call.service.name}`, { code: 'UNHANDLED_API', type: TsrpcErrorType.ServerError });
        }

        // Post Flow
        await this.flows.postApiCallFlow.exec(call, call.logger);

        if (timeoutTimer) {
            clearTimeout(timeoutTimer);
            timeoutTimer = undefined;
        }

        // Destroy call
        // if (!call.return) {
        //     this.onInternalServerError({ message: 'API not return anything' }, call);
        // }
    }

    protected async _onMsgCall(call: MsgCall) {
        // 收到Msg即可断开连接(短连接)
        if (call.conn.type === 'SHORT') {
            call.conn.close();
        }

        // Pre Flow
        let preFlow = await this.flows.preMsgCallFlow.exec(call, call.logger);
        if (!preFlow) {
            call.logger.debug('[preMsgCallFlow]', 'Canceled')
            return;
        }
        call = preFlow;

        // MsgHandler
        this.options.logMsg && call.logger.log(chalk.green('[RecvMsg]'), call.msg);
        let promises = [
            // Conn Handlers
            ...(call.conn['_msgHandlers']?.forEachHandler(call.service.name, call.logger, call) ?? []),
            // Server Handlers
            this._msgHandlers.forEachHandler(call.service.name, call.logger, call)
        ];
        if (!promises.length) {
            this.logger.debug('[UNHANDLED_MSG]', call.service.name);
        }
        else {
            await Promise.all(promises);
        }

        // Post Flow
        await this.flows.postMsgCallFlow.exec(call, call.logger);
    }
    // #endregion

    // #region Api/Msg handler register
    /**
     * Associate a `ApiHandler` to a specific `apiName`.
     * So that when `ApiCall` is receiving, it can be handled correctly.
     * @param apiName
     * @param handler
     */
    implementApi<Api extends string & keyof ServiceType['api'], Call extends ApiCall<ServiceType['api'][Api]['req'], ServiceType['api'][Api]['res']>>(apiName: Api, handler: ApiHandler<Call>): void {
        if (this._apiHandlers[apiName as string]) {
            throw new Error('Already exist handler for API: ' + apiName);
        }
        this._apiHandlers[apiName as string] = handler;
        this.logger.log(`API implemented succ: [${apiName}]`);
    };

    /** 用于延迟注册 API */
    protected _delayImplementApiPath?: string;

    /**
     * Auto call `imeplementApi` by traverse the `apiPath` and find all matched `PtlXXX` and `ApiXXX`.
     * It is matched by checking whether the relative path and name of an API is consistent to the service name in `serviceProto`.
     * Notice that the name prefix of protocol is `Ptl`, of API is `Api`.
     * For example, `protocols/a/b/c/PtlTest` is matched to `api/a/b/c/ApiTest`.
     * @param apiPath Absolute path or relative path to `process.cwd()`.
     * @returns
     */
    async autoImplementApi(apiPath: string, delay?: boolean): Promise<{ succ: string[], fail: string[] }> {
        let apiServices = Object.values(this.serviceMap.apiName2Service) as ApiServiceDef[];
        let output: { succ: string[], fail: string[] } = { succ: [], fail: [] };

        if (delay) {
            this._delayImplementApiPath = apiPath;
            return output;
        }

        for (let svc of apiServices) {
            //get api handler
            let { handler } = await this.getApiHandler(svc, apiPath, this.logger)

            if (!handler) {
                output.fail.push(svc.name);
                continue;
            }

            this.implementApi(svc.name, handler);
            output.succ.push(svc.name);
        }

        if (output.fail.length) {
            this.logger.error(chalk.red(`${output.fail.length} API implemented failed: ` + output.fail.map(v => chalk.cyan.underline(v)).join(' ')))
        }

        return output;
    }

    async getApiHandler(svc: ApiServiceDef, apiPath?: string, logger?: Logger): Promise<{ handler: ApiHandler, errMsg?: undefined } | { handler?: undefined, errMsg: string }> {
        if (this._apiHandlers[svc.name]) {
            return { handler: this._apiHandlers[svc.name]! };
        }

        if (!apiPath) {
            return { errMsg: `Api not implemented: ${svc.name}` };
        }

        // get api last name
        let match = svc.name.match(/^(.+\/)*(.+)$/);
        if (!match) {
            logger?.error('Invalid apiName: ' + svc.name);
            return { errMsg: `Invalid api name: ${svc.name}` };
        }
        let handlerPath = match[1] || '';
        let handlerName = match[2];

        // try import
        let modulePath = path.resolve(apiPath, handlerPath, 'Api' + handlerName);
        try {
            var handlerModule = await import(modulePath);
        }
        catch (e: unknown) {
            this.logger.error(chalk.red(`Implement API ${chalk.cyan.underline(`${svc.name}`)} failed:`), e);
            return { errMsg: (e as Error).message };
        }

        // 优先 default,其次 ApiName 同名
        let handler = handlerModule.default ?? handlerModule['Api' + handlerName];
        if (handler) {
            return { handler: handler };
        }
        else {
            return { errMsg: `Missing 'export Api${handlerName}' or 'export default' in: ${modulePath}` }
        }
    }

    /**
     * Add a message handler,
     * duplicate handlers to the same `msgName` would be ignored.
     * @param msgName
     * @param handler
     * @returns
     */
    listenMsg<T extends keyof ServiceType['msg'], Call extends MsgCall<ServiceType['msg'][T]>>(msgName: T | RegExp, handler: MsgHandler<Call>): MsgHandler<Call> {
        if (msgName instanceof RegExp) {
            Object.keys(this.serviceMap.msgName2Service).filter(k => msgName.test(k)).forEach(k => {
                this._msgHandlers.addHandler(k, handler)
            })
        }
        else {
            this._msgHandlers.addHandler(msgName as string, handler)
        }

        return handler;
    }
    /**
     * Remove a message handler
     */
    unlistenMsg<T extends keyof ServiceType['msg']>(msgName: T | RegExp, handler: Function) {
        if (msgName instanceof RegExp) {
            Object.keys(this.serviceMap.msgName2Service).filter(k => msgName.test(k)).forEach(k => {
                this._msgHandlers.removeHandler(k, handler)
            })
        }
        else {
            this._msgHandlers.removeHandler(msgName as string, handler)
        }
    }
    /**
     * Remove all handlers from a message
     */
    unlistenMsgAll<T extends keyof ServiceType['msg']>(msgName: T | RegExp) {
        if (msgName instanceof RegExp) {
            Object.keys(this.serviceMap.msgName2Service).filter(k => msgName.test(k)).forEach(k => {
                this._msgHandlers.removeAllHandlers(k)
            })
        }
        else {
            this._msgHandlers.removeAllHandlers(msgName as string)
        }
    }
    // #endregion

    /**
     * Event when the server cannot parse input buffer to api/msg call.
     * By default, it will return "Input Data Error" .
     */
    async onInputDataError(errMsg: string, conn: BaseConnection<ServiceType>, data: string | Uint8Array | object) {
        if (this.options.debugBuf) {
            if (typeof data === 'string') {
                conn.logger.error(`[InputDataError] ${errMsg} length = ${data.length}`, data)
            }
            else if (data instanceof Uint8Array) {
                conn.logger.error(`[InputBufferError] ${errMsg} length = ${data.length}`, data.subarray(0, 16))
            }
            else {
                conn.logger.error(`[InputJsonError] ${errMsg} `, data)
            }
        }

        const message = data instanceof Uint8Array ? `Invalid request buffer, please check the version of service proto.` : errMsg;

        // Short conn, send apiReturn with error
        if (conn.type === 'SHORT') {
            // Return API Error
            let opEncode = ApiCall.encodeApiReturn(this.tsbuffer, {
                type: 'api',
                name: '?',
                id: 0,
                reqSchemaId: '?',
                resSchemaId: '?'
            }, {
                isSucc: false,
                err: new TsrpcError({
                    message: message,
                    type: TsrpcErrorType.ServerError,
                    code: 'INPUT_DATA_ERR'
                })
            }, conn.dataType)
            if (opEncode.isSucc) {
                let opSend = await conn.sendData(opEncode.output);
                if (opSend.isSucc) {
                    return;
                }
            }
        }

        conn.close(message);
    }

    /**
     * Event when a uncaught error (except `TsrpcError`) is throwed.
     * By default, it will return a `TsrpcError` with message "Internal server error".
     * If `returnInnerError` is `true`, the original error would be returned as `innerErr` property.
     */
    onInternalServerError(err: { message: string, stack?: string, name?: string }, call: ApiCall) {
        call.logger.error(err);
        call.error('Internal Server Error', {
            code: 'INTERNAL_ERR',
            type: TsrpcErrorType.ServerError,
            innerErr: call.conn.server.options.returnInnerError ? err.message : undefined
        });
    }

    protected _gracefulStop?: {
        rs: () => void
    };
    /**
     * Stop the server gracefully.
     * Wait all API requests finished and then stop the server.
     * @param maxWaitTime - The max time(ms) to wait before force stop the server.
     * `undefined` and `0` means unlimited time.
     */
    async gracefulStop(maxWaitTime?: number) {
        if (this._status !== ServerStatus.Opened) {
            throw new Error(`Cannot gracefulStop when server status is '${this._status}'.`);
        }

        this.logger.log('[GracefulStop] Start graceful stop, waiting all ApiCall finished...')
        this._status = ServerStatus.Closing;
        let promiseWaitApi = new Promise<void>(rs => {
            this._gracefulStop = {
                rs: rs
            };
        });

        return new Promise<void>(rs => {
            let maxWaitTimer: ReturnType<typeof setTimeout> | undefined;
            if (maxWaitTime) {
                maxWaitTimer = setTimeout(() => {
                    maxWaitTimer = undefined;
                    if (this._gracefulStop) {
                        this._gracefulStop = undefined;
                        this.logger.log('Graceful stop timeout, stop the server directly.');
                        this.stop().then(() => { rs() });
                    }
                }, maxWaitTime);
            }

            promiseWaitApi.then(() => {
                this.logger.log('All ApiCall finished, continue stop server.');
                if (maxWaitTimer) {
                    clearTimeout(maxWaitTimer);
                    maxWaitTimer = undefined;
                }
                if (this._gracefulStop) {
                    this._gracefulStop = undefined;
                    this.stop().then(() => { rs() });
                }
            })
        })
    }

    /**
     * Execute API function through the inner connection, which is useful for unit test.
     *
     * **NOTICE**
     * The `req` and return value is native JavaScript object which is not compatible to JSON. (etc. ArrayBuffer, Date, ObjectId)
     * If you are using pure JSON as transfering, you may need use `callApiByJSON`.
     * @param apiName
     * @param req
     * @param options
     */
    callApi<T extends string & keyof ServiceType['api']>(apiName: T, req: ServiceType['api'][T]['req']): Promise<ApiReturn<ServiceType['api'][T]['res']>> {
        return new Promise(rs => {
            // 确认是哪个Service
            let service = this.serviceMap.apiName2Service[apiName as string];
            if (!service) {
                let errMsg = `Cannot find service: ${apiName}`;
                this.logger.warn(`[callApi]`, errMsg);
                rs({ isSucc: false, err: new TsrpcError(errMsg, { type: TsrpcErrorType.ServerError, code: 'ERR_API_NAME' }) });
                return;
            }

            let conn = new InnerConnection({
                dataType: 'json',
                server: this,
                id: '' + this._connIdCounter.getNext(),
                ip: '',
                return: {
                    type: 'raw',
                    rs: rs
                }
            });
            let call = new ApiCallInner({
                conn: conn,
                req: req,
                service: service
            });
            this._handleApiCall(call);
        })
    }

    /**
     * Like `server.callApi`, but both input and output are pure JSON object,
     * which can be `JSON.stringify()` and `JSON.parse()` directly.
     * Types that not compatible to JSON, would be encoded and decoded automatically.
     * @param apiName - The same with `server.callApi`, may be parsed from the URL.
     * @param jsonReq - Request data in pure JSON
     * @returns Encoded `ApiReturn<Res>` in pure JSON
     */

    /**
     * Process JSON request by inner proxy, this is useful when you are porting to cloud function services.
     * Both the input and output is pure JSON, ArrayBuffer/Date/ObjectId are encoded to string automatically.
     * @param apiName - Parsed from URL
     * @param req - Pure JSON
     * @returns - Pure JSON
     */
    async inputJSON(apiName: string, req: object): Promise<ApiReturn<object>> {
        if (apiName.startsWith('/')) {
            apiName = apiName.slice(1);
        }
        const service = this.serviceMap.apiName2Service[apiName];
        if (!service) {
            return {
                isSucc: false,
                err: new TsrpcError(`Invalid service name: ${apiName}`, {
                    type: TsrpcErrorType.ServerError,
                    code: 'INPUT_DATA_ERR'
                })
            };
        }

        return new Promise(rs => {
            let conn = new InnerConnection({
                dataType: 'json',
                server: this,
                id: '' + this._connIdCounter.getNext(),
                ip: '',
                return: {
                    type: 'json',
                    rs: rs
                }
            });

            this._onRecvData(conn, req, service.id);
        })
    }

    /**
     * Process input buffer by inner proxy, this is useful when you are porting to cloud function services.
     * @param buf Input buffer (may be sent by TSRPC client)
     * @returns Response buffer
     */
    inputBuffer(buf: Uint8Array): Promise<Uint8Array> {
        return new Promise(rs => {
            let conn = new InnerConnection({
                dataType: 'buffer',
                server: this,
                id: '' + this._connIdCounter.getNext(),
                ip: '',
                return: {
                    type: 'buffer',
                    rs: rs
                }
            });

            this._onRecvData(conn, buf);
        })
    }

    protected _parseServerInput(tsbuffer: TSBuffer, serviceMap: ServiceMap, data: string | Uint8Array | object, serviceId?: number): { isSucc: true, result: ParsedServerInput } | { isSucc: false, errMsg: string } {
        if (data instanceof Uint8Array) {
            let opServerInputData = TransportDataUtil.tsbuffer.decode(data, 'ServerInputData');

            if (!opServerInputData.isSucc) {
                return opServerInputData;
            }
            let serverInput = opServerInputData.value as ServerInputData;

            // 确认是哪个Service
            let service = serviceMap.id2Service[serverInput.serviceId];
            if (!service) {
                return { isSucc: false, errMsg: `Cannot find service ID: ${serverInput.serviceId}` }
            }

            // 解码Body
            if (service.type === 'api') {
                let opReq = tsbuffer.decode(serverInput.buffer, service.reqSchemaId);
                return opReq.isSucc ? {
                    isSucc: true,
                    result: {
                        type: 'api',
                        service: service,
                        req: opReq.value,
                        sn: serverInput.sn
                    }
                } : opReq
            }
            else {
                let opMsg = tsbuffer.decode(serverInput.buffer, service.msgSchemaId);
                return opMsg.isSucc ? {
                    isSucc: true,
                    result: {
                        type: 'msg',
                        service: service,
                        msg: opMsg.value
                    }
                } : opMsg;
            }
        }
        else {
            let json: object;
            if (typeof data === 'string') {
                try {
                    json = JSON.parse(data);
                }
                catch (e: any) {
                    return { isSucc: false, errMsg: `Input is not a valid JSON string: ${e.message}` };
                }
            }
            else {
                json = data;
            }

            let body: any;
            let sn: number | undefined;

            // Parse serviceId / body / sn
            if (serviceId == undefined) {
                // 未指定 serviceId,必然是长连接,传入格式应该是数组 [serviceName, body, sn?]
                if (!Array.isArray(json)) {
                    return { isSucc: false, errMsg: `Invalid request format: unresolved service name.` };
                }

                body = json[1];
                sn = json[2];

                // Parse serviceId
                const serviceName = json[0] as string;
                const isMsg = sn === undefined;
                serviceId = (isMsg ? serviceMap.msgName2Service : serviceMap.apiName2Service)[serviceName]?.id;
                if (serviceId === undefined) {
                    return { isSucc: false, errMsg: `Invalid ${isMsg ? 'msg' : 'api'} path: ${serviceName}` };
                }
            }
            else {
                body = json;
            }

            // Get Service
            let service = serviceMap.id2Service[serviceId];
            if (!service) {
                let errMsg = `Invalid service id: ${chalk.cyan.underline(serviceId)}`;
                return { isSucc: false, errMsg: errMsg };
            }

            // Decode
            if (service.type === 'api') {
                let op = tsbuffer.decodeJSON(body, service.reqSchemaId);
                if (!op.isSucc) {
                    return op;
                }
                return {
                    isSucc: true,
                    result: {
                        type: 'api',
                        service: service,
                        sn: sn,
                        req: op.value
                    }
                };
            }
            else {
                let op = tsbuffer.decodeJSON(body, service.msgSchemaId);
                if (!op.isSucc) {
                    return op;
                }
                return {
                    isSucc: true,
                    result: {
                        type: 'msg',
                        service: service,
                        msg: op.value
                    }
                }
            }
        }
    }
}

export interface BaseServerOptions<ServiceType extends BaseServiceType> {
    /**
     * Whether to enable JSON compatible mode.
     * When it is true, it can be compatible with typical HTTP JSON request (like RESTful API).
     *
     * @remarks
     * The JSON request methods are:
     *
     * 1. Add `Content-type: application/json` to request header.
     * 2. HTTP request is: `POST /{jsonUrlPath}/{apiName}`.
     * 3. POST body is JSON string.
     * 4. The response body is JSON string also.
     *
     * NOTICE: Buffer type are not supported due to JSON not support them.
     * For security and efficient reason, we strongly recommend you use binary encoded transportation.
     *
     * @defaultValue `false`
     */
    json: boolean,
    /** @deprecated Use `json` instead. */
    jsonEnabled?: boolean,

    // TSBuffer相关
    /**
     * Whether to strictly distinguish between `null` and `undefined` when encoding, decoding, and type checking.
     * @defaultValue false
     */
    strictNullChecks: boolean,

    /**
     * Timeout for processing an `ApiCall`(ms)
     * `0` and `undefined` means unlimited time
     * @defaultValue 30000
     */
    apiTimeout: number | undefined,

    // LOG相关
    /**
     * Logger for processing log
     * @defaultValue `new TerminalColorLogger()` (print to console with color)
     */
    logger: Logger;
    /**
     * The minimum log level of `logger`
     * @defaultValue `debug`
     */
    logLevel: LogLevel;
    /**
     * Whether to print API request body into log (may increase log size)
     * @defaultValue `true`
     */
    logReqBody: boolean;
    /**
     * Whether to print API response body into log (may increase log size)
     * @defaultValue `true`
     */
    logResBody: boolean;
    /**
     * Whether to print `[SendMsg]` and `[RecvMsg]` log into log
     * @defaultValue `true`
     */
    logMsg: boolean;

    /**
     * If `true`, all sent and received raw buffer would be print into the log.
     * It may be useful when you do something for buffer encryption/decryption, and want to debug them.
     */
    debugBuf?: boolean;

    /**
     * When uncaught error throwed,
     * whether to return the original error as a property `innerErr`.
     * (May include some sensitive information, suggests set to `false` in production environment.)
     * @defaultValue It depends on environment variable `NODE_ENV`.
     * If `NODE_ENV` equals to `production`, the default value is `false`, otherwise is `true`.
     */
    returnInnerError: boolean;
}

export const defaultBaseServerOptions: BaseServerOptions<any> = {
    json: false,
    strictNullChecks: false,
    apiTimeout: 30000,
    logger: new TerminalColorLogger,
    logLevel: 'debug',
    logReqBody: true,
    logResBody: true,
    logMsg: true,
    returnInnerError: process.env['NODE_ENV'] !== 'production'
}

export type ApiHandler<Call extends ApiCall = ApiCall> = (call: Call) => void | Promise<void>;
export type MsgHandler<Call extends MsgCall = MsgCall> = (call: Call) => void | Promise<void>;

export enum ServerStatus {
    Opening = 'OPENING',
    Opened = 'OPENED',
    Closing = 'CLOSING',
    Closed = 'CLOSED',
}


text:
package com.hazelcast.logging;

import com.hazelcast.test.HazelcastParallelClassRunner;
import com.hazelcast.test.annotation.ParallelJVMTest;
import com.hazelcast.test.annotation.QuickTest;
import org.apache.logging.log4j.spi.ExtendedLogger;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;

import java.util.logging.Level;

import static org.apache.logging.log4j.Level.DEBUG;
import static org.apache.logging.log4j.Level.ERROR;
import static org.apache.logging.log4j.Level.INFO;
import static org.apache.logging.log4j.Level.OFF;
import static org.apache.logging.log4j.Level.TRACE;
import static org.apache.logging.log4j.Level.WARN;
import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

@RunWith(HazelcastParallelClassRunner.class)
@Category({QuickTest.class, ParallelJVMTest.class})
public class Log4j2LoggerTest extends AbstractLoggerTest {

    private static final String LOGGER_NAME = Log4j2Factory.Log4j2Logger.class.getName();

    private ExtendedLogger mockLogger;
    private ILogger hazelcastLogger;

    @Before
    public void setUp() {
        mockLogger = mock(ExtendedLogger.class);
        hazelcastLogger = new Log4j2Factory.Log4j2Logger(mockLogger);
    }

    @Test
    public void isLoggable_whenLevelOff_shouldReturnFalse() {
        assertFalse(hazelcastLogger.isLoggable(Level.OFF));
    }

    @Test
    public void logWithThrowable_shouldCallLogWithThrowable() {
        hazelcastLogger.warning(MESSAGE, THROWABLE);
        verify(mockLogger, times(1)).logIfEnabled(LOGGER_NAME, WARN, null, MESSAGE, THROWABLE);
    }

    @Test
    public void logAtLevelOff_shouldLogAtLevelOff() {
        hazelcastLogger.log(Level.OFF, MESSAGE);
        verify(mockLogger, times(1)).logIfEnabled(LOGGER_NAME, OFF, null, MESSAGE);
    }

    @Test
    public void logFinest_shouldLogTrace() {
        hazelcastLogger.finest(MESSAGE);
        verify(mockLogger, times(1)).logIfEnabled(LOGGER_NAME, TRACE, null, MESSAGE);
    }

    @Test
    public void logFiner_shouldLogDebug() {
        hazelcastLogger.log(Level.FINER, MESSAGE);
        verify(mockLogger, times(1)).logIfEnabled(LOGGER_NAME, DEBUG, null, MESSAGE);
    }

    @Test
    public void logFine_shouldLogDebug() {
        hazelcastLogger.fine(MESSAGE);
        verify(mockLogger, times(1)).logIfEnabled(LOGGER_NAME, DEBUG, null, MESSAGE);
    }

    @Test
    public void logInfo_shouldLogInfo() {
        hazelcastLogger.info(MESSAGE);
        verify(mockLogger, times(1)).logIfEnabled(LOGGER_NAME, INFO, null, MESSAGE);
    }

    @Test
    public void logWarning_shouldLogWarn() {
        hazelcastLogger.warning(MESSAGE);
        verify(mockLogger, times(1)).logIfEnabled(LOGGER_NAME, WARN, null, MESSAGE);
    }

    @Test
    public void logSevere_shouldLogError() {
        hazelcastLogger.severe(MESSAGE);
        verify(mockLogger, times(1)).logIfEnabled(LOGGER_NAME, ERROR, null, MESSAGE);
    }

    @Test
    public void logEvent_shouldLogWithCorrectLevel() {
        hazelcastLogger.log(LOG_EVENT);
        verify(mockLogger, times(1)).logIfEnabled(LOGGER_NAME, WARN, null, MESSAGE, THROWABLE);
    }
}

redpajama_set_name: RedPajamaGithub


text:
package org.apache.falcon.entity;

import org.apache.commons.lang3.StringUtils;
import org.apache.falcon.entity.v0.cluster.Cluster;
import org.apache.falcon.security.SecurityUtil;

import java.util.Properties;

/**
 * Hive Utilities.
 */
public final class HiveUtil {
    public static final String METASTOREURIS = "hive.metastore.uris";
    public static final String METASTORE_URI = "hcat.metastore.uri";
    public static final String NODE = "hcatNode";
    public static final String METASTORE_UGI = "hive.metastore.execute.setugi";

    private HiveUtil() {

    }

    public static Properties getHiveCredentials(Cluster cluster) {
        String metaStoreUrl = ClusterHelper.getRegistryEndPoint(cluster);
        if (StringUtils.isBlank(metaStoreUrl)) {
            throw new IllegalStateException(
                    "Registry interface is not defined in cluster: " + cluster.getName());
        }

        Properties hiveCredentials = new Properties();
        hiveCredentials.put(METASTOREURIS, metaStoreUrl);
        hiveCredentials.put(METASTORE_UGI, "true");
        hiveCredentials.put(NODE, metaStoreUrl.replace("thrift", "hcat"));
        hiveCredentials.put(METASTORE_URI, metaStoreUrl);

        if (SecurityUtil.isSecurityEnabled()) {
            String principal = ClusterHelper
                    .getPropertyValue(cluster, SecurityUtil.HIVE_METASTORE_KERBEROS_PRINCIPAL);
            hiveCredentials.put(SecurityUtil.HIVE_METASTORE_KERBEROS_PRINCIPAL, principal);
            hiveCredentials.put(SecurityUtil.METASTORE_PRINCIPAL, principal);
            hiveCredentials.put(SecurityUtil.METASTORE_USE_THRIFT_SASL, "true");
        }
        return hiveCredentials;
    }
}


text:
singlerot-smooth
================
"Single Rotation" cellular automaton demonstration with Lanczos smoothing.

![Sample picture](./images/singlerot-smooth.png)

[See it live](http://dmishin.github.io/singlerot-smooth/singlerot-smooth.html)

See also 3D version!
![Sample 3D screenshot](./images/singlerot-3d.png)
Also you can see [demonstration video](http://youtu.be/XX4igr8ufeA).

[Single Rotation](http://dmishin.blogspot.ru/2013/11/the-single-rotation-rule-remarkably.html)
is a very simple yet rich reversible cellular automaton.
Unlike the famous [Game of Life](http://en.wikipedia.org/wiki/Conway%27s_Game_of_Life), it preserves total number of alive cells.
This allows to track the trajectory of each cell as it moves through the field during its evolution.

This program demonstrates these trajectories, using HTML5 Canvas for GUI and JavaScript for computations.

Compilation
-----------

The code is written in CoffeeScript, and uses _browserify_ and _minify_ tools. To compile, type:

  $ make

Running
-------
Open the HTML file in the browser.

Browser support
---------------

Thr program uses following features:
 * Canvas
 * Typed arrays
 * Animation with requestAnimationFrame

All these features should be supported in the most desktop and many mobile browsers.


text:
/*
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */
/*
 * This code was generated by https://github.com/googleapis/google-api-java-client-services/
 * Modify at your own risk.
 */

package com.google.api.services.speech.v1p1beta1.model;

/**
 * Message sent by the client to refresh data in a existing dataset.
 *
 *
 This is the Java data model class that specifies how to parse/serialize into the JSON that is
 * transmitted over HTTP when working with the Cloud Speech API. For a detailed explanation see:
 * https://developers.google.com/api-client-library/java/google-http-java-client/json
 *

 *
 * @author Google, Inc.
 */
@SuppressWarnings("javadoc")
public final class RefreshDataRequest extends com.google.api.client.json.GenericJson {

  /**
   * URI that points to a file in csv file where each row has following format. ,, label can be
   * HUMAN_TRANSCRIBED or MACHINE_TRANSCRIBED. Few rules for a row to be considered valid are :- 1.
   * Each row must have at least a label and  2. If a row is marked HUMAN_TRANSCRIBED, then both
   * and  needs to be specified. 3. There has to be minimum 500 number of rows labelled
   * HUMAN_TRANSCRIBED if evaluation stats are required. 4. If use_logged_data_for_training is set
   * to true, then we ignore the rows labelled as MACHINE_TRANSCRIBED. 5. There has to be minimum
   * 100,000 words in the transcripts in order to provide sufficient textual training data for the
   * language model. Currently, only Google Cloud Storage URIs are supported, which must be
   * specified in the following format: `gs://bucket_name/object_name` (other URI formats will be
   * ignored). For more information, see [Request URIs](https://cloud.google.com/storage/docs
   * /reference-uris).
   * The value may be {@code null}.
   */
  @com.google.api.client.util.Key
  private java.lang.String uri;

  /**
   * URI that points to a file in csv file where each row has following format. ,, label can be
   * HUMAN_TRANSCRIBED or MACHINE_TRANSCRIBED. Few rules for a row to be considered valid are :- 1.
   * Each row must have at least a label and  2. If a row is marked HUMAN_TRANSCRIBED, then both
   * and  needs to be specified. 3. There has to be minimum 500 number of rows labelled
   * HUMAN_TRANSCRIBED if evaluation stats are required. 4. If use_logged_data_for_training is set
   * to true, then we ignore the rows labelled as MACHINE_TRANSCRIBED. 5. There has to be minimum
   * 100,000 words in the transcripts in order to provide sufficient textual training data for the
   * language model. Currently, only Google Cloud Storage URIs are supported, which must be
   * specified in the following format: `gs://bucket_name/object_name` (other URI formats will be
   * ignored). For more information, see [Request URIs](https://cloud.google.com/storage/docs
   * /reference-uris).
   * @return value or {@code null} for none
   */
  public java.lang.String getUri() {
    return uri;
  }

  /**
   * URI that points to a file in csv file where each row has following format. ,, label can be
   * HUMAN_TRANSCRIBED or MACHINE_TRANSCRIBED. Few rules for a row to be considered valid are :- 1.
   * Each row must have at least a label and  2. If a row is marked HUMAN_TRANSCRIBED, then both
   * and  needs to be specified. 3. There has to be minimum 500 number of rows labelled
   * HUMAN_TRANSCRIBED if evaluation stats are required. 4. If use_logged_data_for_training is set
   * to true, then we ignore the rows labelled as MACHINE_TRANSCRIBED. 5. There has to be minimum
   * 100,000 words in the transcripts in order to provide sufficient textual training data for the
   * language model. Currently, only Google Cloud Storage URIs are supported, which must be
   * specified in the following format: `gs://bucket_name/object_name` (other URI formats will be
   * ignored). For more information, see [Request URIs](https://cloud.google.com/storage/docs
   * /reference-uris).
   * @param uri uri or {@code null} for none
   */
  public RefreshDataRequest setUri(java.lang.String uri) {
    this.uri = uri;
    return this;
  }

  @Override
  public RefreshDataRequest set(String fieldName, Object value) {
    return (RefreshDataRequest) super.set(fieldName, value);
  }

  @Override
  public RefreshDataRequest clone() {
    return (RefreshDataRequest) super.clone();
  }

}


text:
/**
* @author Boris V. Kuznetsov
* @version $Revision$
*/

package java.security;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.apache.harmony.luni.util.TwoKeyHashMap;
import org.apache.harmony.security.fortress.Services;
import org.apache.harmony.security.internal.nls.Messages;

public abstract class Provider extends Properties {
    private static final long serialVersionUID = -4298000515446427739L;

    private String name;

    private double version;

    // String representation of the provider version number.
    private transient String versionString;

    private String info;

    //The provider preference order number.
    // Equals -1 for non registered provider.
    private transient int providerNumber = -1;

    // Contains "Service.Algorithm" and Provider.Service classes added using
    // putService()
    private transient TwoKeyHashMap<String, String, Service> serviceTable;

    // Contains "Service.Alias" and Provider.Service classes added using
    // putService()
    private transient TwoKeyHashMap<String, String, Service> aliasTable;

    // Contains "Service.Algorithm" and Provider.Service classes added using
    // put()
    private transient TwoKeyHashMap<String, String, Service> propertyServiceTable;

    // Contains "Service.Alias" and Provider.Service classes added using put()
    private transient TwoKeyHashMap<String, String, Service> propertyAliasTable;

    // The properties changed via put()
    private transient Properties changedProperties;

    // For getService(String type, String algorithm) optimization:
    // previous result
    private transient Provider.Service returnedService;
    // previous parameters
    private transient String lastAlgorithm;
    // last name
    private transient String lastServiceName;

    // For getServices() optimization:
    private transient Set<Service> lastServicesSet;

    // For getService(String type) optimization:
    private transient String lastType;
    // last Service found by type
    private transient Provider.Service lastServicesByType;

    protected Provider(String name, double version, String info) {
        this.name = name;
        this.version = version;
        this.info = info;
        versionString = String.valueOf(version);
        putProviderInfo();
    }

	/**
	 * Returns the name of this provider.
	 *
	 *
	 *
	 * @return String name of the provider
	 */
    public String getName() {
        return name;
    }

	/**
	 * Returns the version number for the services being provided
	 *
	 *
	 *
	 * @return double version number for the services being provided
	 */
    public double getVersion() {
        return version;
    }

	/**
	 * Returns the generic information about the services being provided.
	 *
	 *
	 *
	 * @return String generic description of the services being provided
	 */
    public String getInfo() {
        return info;
    }

	/**
	 * Answers a string containing a concise, human-readable description of the
	 * receiver.
	 *
	 *
	 * @return a printable representation for the receiver.
	 */
    public String toString() {
        return name + " provider, Ver. " + version + " " + info; //$NON-NLS-1$ //$NON-NLS-2$
    }

    public synchronized void clear() {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkSecurityAccess("clearProviderProperties." + name); //$NON-NLS-1$
        }
        super.clear();
        if (serviceTable != null) {
            serviceTable.clear();
        }
        if (propertyServiceTable != null) {
            propertyServiceTable.clear();
        }
        if (aliasTable != null) {
            aliasTable.clear();
        }
        if (propertyAliasTable != null) {
            propertyAliasTable.clear();
        }
        if (changedProperties != null) {
            changedProperties.clear();
        }
        putProviderInfo();
        if (providerNumber != -1) {
            // if registered then refresh Services
            Services.setNeedRefresh();
        }
        servicesChanged();
    }

    public synchronized void load(InputStream inStream) throws IOException {
        Properties tmp = new Properties();
        tmp.load(inStream);
        myPutAll(tmp);
    }

    public synchronized void putAll(Map<?,?> t) {

        // Implementation note:
        // checkSecurityAccess method call is NOT specified
        // Do it as in put(Object key, Object value).

        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkSecurityAccess("putProviderProperty." + name); //$NON-NLS-1$
        }
        myPutAll(t);
    }

    private void myPutAll(Map<?,?> t) {
        if (changedProperties == null) {
            changedProperties = new Properties();
        }
        Iterator<? extends Map.Entry<?, ?>> it = t.entrySet().iterator();
        Object key;
        Object value;
        while (it.hasNext()) {
            Map.Entry<?, ?> entry = it.next();
            key = entry.getKey();
            if (key instanceof String && ((String) key).startsWith("Provider.")) { //$NON-NLS-1$
                // Provider service type is reserved
                continue;
            }
            value = entry.getValue();
            super.put(key, value);
            if (changedProperties.remove(key) == null) {
                removeFromPropertyServiceTable(key);
            }
            changedProperties.put(key, value);
        }
        if (providerNumber != -1) {
            // if registered then refresh Services
            Services.setNeedRefresh();
        }
    }

    public synchronized Set<Map.Entry<Object,Object>> entrySet() {
        return Collections.unmodifiableSet(super.entrySet());
    }

    public Set<Object> keySet() {
        return Collections.unmodifiableSet(super.keySet());
    }

    public Collection<Object> values() {
        return Collections.unmodifiableCollection(super.values());
    }

    public synchronized Object put(Object key, Object value) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkSecurityAccess("putProviderProperty." + name); //$NON-NLS-1$
        }
        if (key instanceof String && ((String) key).startsWith("Provider.")) { //$NON-NLS-1$
            // Provider service type is reserved
            return null;
        }
        if (providerNumber != -1) {
            // if registered then refresh Services
            Services.setNeedRefresh();
        }
        if (changedProperties != null && changedProperties.remove(key) == null) {
            removeFromPropertyServiceTable(key);
        }
        if (changedProperties == null) {
            changedProperties = new Properties();
        }
        changedProperties.put(key, value);
        return super.put(key, value);
    }

    public synchronized Object remove(Object key) {
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkSecurityAccess("removeProviderProperty." + name); //$NON-NLS-1$
        }
        if (key instanceof String && ((String) key).startsWith("Provider.")) { //$NON-NLS-1$
            // Provider service type is reserved
            return null;
        }
        if (providerNumber != -1) {
            // if registered then refresh Services
            Services.setNeedRefresh();
        }
        if (changedProperties != null && changedProperties.remove(key) == null) {
            removeFromPropertyServiceTable(key);
        }
        return super.remove(key);
    }

    /**
     *
     * returns true if the provider implements the specified algorithm.  Caller must
     * specify the cryptographic service and specify constraints via the
     * attribute name the attribute value
     *
     * @param serv
     *            Crypto service
     * @param alg
     *            Algorithm or type
     * @param attribute
     *            The attribute name or null
     * @param val
     *            The attribute value
     * @return
     */
    boolean implementsAlg(String serv, String alg, String attribute, String val) {
        String servAlg = serv + "." + alg; //$NON-NLS-1$
        String prop = getPropertyIgnoreCase(servAlg);
        if (prop == null) {
            alg = getPropertyIgnoreCase("Alg.Alias." + servAlg); //$NON-NLS-1$
            if (alg != null) {
                servAlg = serv + "." + alg; //$NON-NLS-1$
                prop = getPropertyIgnoreCase(serv + "." + alg); //$NON-NLS-1$
            }
        }
        if (prop != null) {
            if (attribute == null) {
                return true;
            } else {
                return checkAttribute(serv + "." + alg, attribute, val); //$NON-NLS-1$
            }
        }
        return false;
    }

    // returns true if the implementation meets the constraint expressed by the
    // specified attribute name/value pair.
    private boolean checkAttribute(String servAlg, String attribute, String val) {

        String attributeValue = getPropertyIgnoreCase(servAlg + ' ' + attribute);
        if (attributeValue != null) {
            if (attribute.equalsIgnoreCase("KeySize")) { //$NON-NLS-1$
                if (Integer.valueOf(attributeValue).compareTo(
                        Integer.valueOf(val)) >= 0) {
                    return true;
                }
            } else { // other attributes
                if (attributeValue.equalsIgnoreCase(val)) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     *
     * Set the provider preference order number.
     *
     * @param n
     */
    void setProviderNumber(int n) {
        providerNumber = n;
    }

    /**
     *
     * Get the provider preference order number.
     *
     * @return
     */
    int getProviderNumber() {
        return providerNumber;
    }

    /**
     * Get the service of the specified type
     *
     */
    synchronized Provider.Service getService(String type) {
        updatePropertyServiceTable();
        if (lastServicesByType != null && type.equals(lastType)) {
            return lastServicesByType;
        }
        Provider.Service service;
        for (Iterator<Service> it = getServices().iterator(); it.hasNext();) {
            service = it.next();
            if (type.equals(service.type)) {
                lastType = type;
                lastServicesByType = service;
                return service;
            }
        }
        return null;
    }

    public synchronized Provider.Service getService(String type,
            String algorithm) {
        if (type == null || algorithm == null) {
            throw new NullPointerException();
        }

        if (type.equals(lastServiceName)
                && algorithm.equalsIgnoreCase(lastAlgorithm)) {
            return returnedService;
        }

        String alg = algorithm.toUpperCase();
        Object o = null;
        if (serviceTable != null) {
            o = serviceTable.get(type, alg);
        }
        if (o == null && aliasTable != null) {
            o = aliasTable.get(type, alg);
        }
        if (o == null) {
            updatePropertyServiceTable();
        }
        if (o == null && propertyServiceTable != null) {
            o = propertyServiceTable.get(type, alg);
        }
        if (o == null && propertyAliasTable != null) {
            o = propertyAliasTable.get(type, alg);
        }

        if (o != null) {
            lastServiceName = type;
            lastAlgorithm = algorithm;
            returnedService = (Provider.Service) o;
            return returnedService;
        }
        return null;
    }

    public synchronized Set<Provider.Service> getServices() {
        updatePropertyServiceTable();
        if (lastServicesSet != null) {
            return lastServicesSet;
        }
        if (serviceTable != null) {
            lastServicesSet = new HashSet<Service>(serviceTable.values());
        } else {
            lastServicesSet = new HashSet<Service>();
        }
        if (propertyServiceTable != null) {
            lastServicesSet.addAll(propertyServiceTable.values());
        }
        lastServicesSet = Collections.unmodifiableSet(lastServicesSet);
        return lastServicesSet;
    }

    protected synchronized void putService(Provider.Service s) {
        if (s == null) {
            throw new NullPointerException();
        }
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkSecurityAccess("putProviderProperty." + name); //$NON-NLS-1$
        }
        if ("Provider".equals(s.getType())) { // Provider service type cannot be //$NON-NLS-1$
                                              // added
            return;
        }
        servicesChanged();
        if (serviceTable == null) {
            serviceTable = new TwoKeyHashMap<String, String, Service>(128);
        }
        serviceTable.put(s.type, s.algorithm.toUpperCase(), s);
        if (s.aliases != null) {
            if (aliasTable == null) {
                aliasTable = new TwoKeyHashMap<String, String, Service>(256);
            }
            for (Iterator<String> it = s.getAliases(); it.hasNext();) {
                aliasTable.put(s.type, (it.next()).toUpperCase(), s);
            }
        }
        serviceInfoToProperties(s);
    }

    protected synchronized void removeService(Provider.Service s) {
        if (s == null) {
            throw new NullPointerException();
        }
        SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            sm.checkSecurityAccess("removeProviderProperty." + name); //$NON-NLS-1$
        }
        servicesChanged();
        if (serviceTable != null) {
            serviceTable.remove(s.type, s.algorithm.toUpperCase());
        }
        if (aliasTable != null && s.aliases != null) {
            for (Iterator<String> it = s.getAliases(); it.hasNext();) {
                aliasTable.remove(s.type, (it.next()).toUpperCase());
            }
        }
        serviceInfoFromProperties(s);
    }

    // Add Service information to the provider's properties.
    private void serviceInfoToProperties(Provider.Service s) {
        super.put(s.type + "." + s.algorithm, s.className); //$NON-NLS-1$
        if (s.aliases != null) {
            for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) {
                super.put("Alg.Alias." + s.type + "." + i.next(), s.algorithm); //$NON-NLS-1$ //$NON-NLS-2$
            }
        }
        if (s.attributes != null) {
            for (Iterator<Map.Entry<String, String>> i = s.attributes.entrySet().iterator(); i.hasNext();) {
                Map.Entry<String, String> entry = i.next();
                super.put(s.type + "." + s.algorithm + " " + entry.getKey(), //$NON-NLS-1$ //$NON-NLS-2$
                        entry.getValue());
            }
        }
        if (providerNumber != -1) {
            // if registered then refresh Services
            Services.setNeedRefresh();
        }
    }

    // Remove Service information from the provider's properties.
    private void serviceInfoFromProperties(Provider.Service s) {
        super.remove(s.type + "." + s.algorithm); //$NON-NLS-1$
        if (s.aliases != null) {
            for (Iterator<String> i = s.aliases.iterator(); i.hasNext();) {
                super.remove("Alg.Alias." + s.type + "." + i.next()); //$NON-NLS-1$ //$NON-NLS-2$
            }
        }
        if (s.attributes != null) {
            for (Iterator<Map.Entry<String, String>> i = s.attributes.entrySet().iterator(); i.hasNext();) {
                Map.Entry<String, String> entry = i.next();
                super.remove(s.type + "." + s.algorithm + " " + entry.getKey()); //$NON-NLS-1$ //$NON-NLS-2$
            }
        }
        if (providerNumber != -1) {
            // if registered then refresh Services
            Services.setNeedRefresh();
        }
    }

    // Remove property information from provider Services
    private void removeFromPropertyServiceTable(Object key) {
        if (key == null || !(key instanceof String)) {
            return;
        }
        String k = (String) key;
        if (k.startsWith("Provider.")) { // Provider service type is reserved //$NON-NLS-1$
            return;
        }
        Provider.Service s;
        String serviceName;
        String algorithm = null;
        String attribute = null;
        int i;
        if (k.startsWith("Alg.Alias.")) { // Alg.Alias.<crypto_service>.<aliasName>=<stanbdardName> //$NON-NLS-1$
            String aliasName;
            String service_alias = k.substring(10);
            i = service_alias.indexOf("."); //$NON-NLS-1$
            serviceName = service_alias.substring(0, i);
            aliasName = service_alias.substring(i + 1);
            if (propertyAliasTable != null) {
                propertyAliasTable.remove(serviceName, aliasName.toUpperCase());
            }
            if (propertyServiceTable != null) {
                for (Iterator<Service> it = propertyServiceTable.values().iterator(); it
                        .hasNext();) {
                    s = it.next();
                    if (s.aliases.contains(aliasName)) {
                        s.aliases.remove(aliasName);
                        return;
                    }
                }
            }
            return;
        }
        int j = k.indexOf("."); //$NON-NLS-1$
        if (j == -1) { // unknown format
            return;
        }

        i = k.indexOf(" "); //$NON-NLS-1$
        if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className>
            serviceName = k.substring(0, j);
            algorithm = k.substring(j + 1);
            if (propertyServiceTable != null) {
                Provider.Service ser = propertyServiceTable.remove(serviceName, algorithm.toUpperCase());
                if (ser != null && propertyAliasTable != null
                        && ser.aliases != null) {
                    for (Iterator<String> it = ser.aliases.iterator(); it.hasNext();) {
                        propertyAliasTable.remove(serviceName, (it
                                .next()).toUpperCase());
                    }
                }
            }
        } else { // <crypto_service>.<algorithm_or_type>
                 // <attribute_name>=<attrValue>
            attribute = k.substring(i + 1);
            serviceName = k.substring(0, j);
            algorithm = k.substring(j + 1, i);
            if (propertyServiceTable != null) {
                Object o = propertyServiceTable.get(serviceName, algorithm
                        .toUpperCase());
                if (o != null) {
                    s = (Provider.Service) o;
                    s.attributes.remove(attribute);
                }
            }
        }
    }

    // Update provider Services if the properties was changed
    private void updatePropertyServiceTable() {
        Object _key;
        Object _value;
        Provider.Service s;
        String serviceName;
        String algorithm;
        if (changedProperties == null || changedProperties.isEmpty()) {
            return;
        }
        for (Iterator<Map.Entry<Object, Object>> it = changedProperties.entrySet().iterator(); it
                .hasNext();) {
            Map.Entry<Object, Object> entry = it.next();
            _key = entry.getKey();
            _value = entry.getValue();
            if (_key == null || _value == null || !(_key instanceof String)
                    || !(_value instanceof String)) {
                continue;
            }
            String key = (String) _key;
            String value = (String) _value;
            if (key.startsWith("Provider")) { // Provider service type is reserved //$NON-NLS-1$
                continue;
            }
            int i;
            if (key.startsWith("Alg.Alias.")) { // Alg.Alias.<crypto_service>.<aliasName>=<stanbdardName> //$NON-NLS-1$
                String aliasName;
                String service_alias = key.substring(10);
                i = service_alias.indexOf("."); //$NON-NLS-1$
                serviceName = service_alias.substring(0, i);
                aliasName = service_alias.substring(i + 1);
                algorithm = value;
                String algUp = algorithm.toUpperCase();
                Object o = null;
                if (propertyServiceTable == null) {
                    propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128);
                } else {
                    o = propertyServiceTable.get(serviceName, algUp);
                }
                if (o != null) {
                    s = (Provider.Service) o;
                    s.aliases.add(aliasName);
                    if (propertyAliasTable == null) {
                        propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256);
                    }
                    propertyAliasTable.put(serviceName,
                            aliasName.toUpperCase(), s);
                } else {
                    String className = (String) changedProperties
                            .get(serviceName + "." + algorithm); //$NON-NLS-1$
                    if (className != null) {
                        List<String> l = new ArrayList<String>();
                        l.add(aliasName);
                        s = new Provider.Service(this, serviceName, algorithm,
                                className, l, new HashMap<String, String>());
                        propertyServiceTable.put(serviceName, algUp, s);
                        if (propertyAliasTable == null) {
                            propertyAliasTable = new TwoKeyHashMap<String, String, Service>(256);
                        }
                        propertyAliasTable.put(serviceName, aliasName
                                .toUpperCase(), s);
                    }
                }
                continue;
            }
            int j = key.indexOf("."); //$NON-NLS-1$
            if (j == -1) { // unknown format
                continue;
            }
            i = key.indexOf(" "); //$NON-NLS-1$
            if (i == -1) { // <crypto_service>.<algorithm_or_type>=<className>
                serviceName = key.substring(0, j);
                algorithm = key.substring(j + 1);
                String alg = algorithm.toUpperCase();
                Object o = null;
                if (propertyServiceTable != null) {
                    o = propertyServiceTable.get(serviceName, alg);
                }
                if (o != null) {
                    s = (Provider.Service) o;
                    s.className = value;
                } else {
                    s = new Provider.Service(this, serviceName, algorithm,
                            value, new ArrayList<String>(), new HashMap<String, String>());
                    if (propertyServiceTable == null) {
                        propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128);
                    }
                    propertyServiceTable.put(serviceName, alg, s);

                }
            } else { // <crypto_service>.<algorithm_or_type>
                     // <attribute_name>=<attrValue>
                serviceName = key.substring(0, j);
                algorithm = key.substring(j + 1, i);
                String attribute = key.substring(i + 1);
                String alg = algorithm.toUpperCase();
                Object o = null;
                if (propertyServiceTable != null) {
                    o = propertyServiceTable.get(serviceName, alg);
                }
                if (o != null) {
                    s = (Provider.Service) o;
                    s.attributes.put(attribute, value);
                } else {
                    String className = (String) changedProperties
                            .get(serviceName + "." + algorithm); //$NON-NLS-1$
                    if (className != null) {
                        Map<String, String> m = new HashMap<String, String>();
                        m.put(attribute, value);
                        s = new Provider.Service(this, serviceName, algorithm,
                                className, new ArrayList<String>(), m);
                        if (propertyServiceTable == null) {
                            propertyServiceTable = new TwoKeyHashMap<String, String, Service>(128);
                        }
                        propertyServiceTable.put(serviceName, alg, s);
                    }
                }
            }
        }
        servicesChanged();
        changedProperties.clear();
    }

    private void servicesChanged() {
        lastServicesByType = null;
        lastServiceName = null;
        lastServicesSet = null;
    }

    // These attributes should be placed in each Provider object:
    // Provider.id name, Provider.id version, Provider.id info,
    // Provider.id className
    private void putProviderInfo() {
        super.put("Provider.id name", null != name ? name : "null"); //$NON-NLS-1$
		super.put("Provider.id version", versionString); //$NON-NLS-1$
		super.put("Provider.id info", null != info ? info : "null"); //$NON-NLS-1$
        super.put("Provider.id className", this.getClass().getName()); //$NON-NLS-1$
    }

    // Searches for the property with the specified key in the provider
    // properties. Key is not case-sensitive.
    //
    // @param prop
    // @return the property value with the specified key value.
    private String getPropertyIgnoreCase(String key) {
        String res = getProperty(key);
        if (res != null) {
            return res;
        }
        for (Enumeration<?> e = propertyNames(); e.hasMoreElements();) {
            String pname = (String) e.nextElement();
            if (key.equalsIgnoreCase(pname)) {
                return getProperty(pname);
            }
        }
        return null;
    }

    public static class Service {
        // The provider
        private Provider provider;

        // The type of this service
        private String type;

        // The algorithm name
        private String algorithm;

        // The class implementing this service
        private String className;

        // The aliases
        private List<String> aliases;

        // The attributes
        private Map<String,String> attributes;

        // Service implementation
        private Class<?> implementation;

        // For newInstance() optimization
        private String lastClassName;

        public Service(Provider provider, String type, String algorithm,
                String className, List<String> aliases, Map<String, String> attributes) {
            if (provider == null || type == null || algorithm == null
                    || className == null) {
                throw new NullPointerException();
            }
            this.provider = provider;
            this.type = type;
            this.algorithm = algorithm;
            this.className = className;
            this.aliases = aliases;
            this.attributes = attributes;
        }

        public final String getType() {
            return type;
        }

        public final String getAlgorithm() {
            return algorithm;
        }

        public final Provider getProvider() {
            return provider;
        }

        public final String getClassName() {
            return className;
        }

        public final String getAttribute(String name) {
            if (name == null) {
                throw new NullPointerException();
            }
            if (attributes == null) {
                return null;
            }
            return attributes.get(name);
        }

        Iterator<String> getAliases() {
            return aliases.iterator();
        }

        public Object newInstance(Object constructorParameter)
                throws NoSuchAlgorithmException {
            if (implementation == null || !className.equals(lastClassName)) {
                NoSuchAlgorithmException result = AccessController
                        .doPrivileged(new PrivilegedAction<NoSuchAlgorithmException>() {
                            public NoSuchAlgorithmException run() {
                                ClassLoader cl = provider.getClass()
                                        .getClassLoader();
                                if (cl == null) {
                                    cl = ClassLoader.getSystemClassLoader();
                                }
                                try {
                                    implementation = Class.forName(className,
                                            true, cl);
                                } catch (Exception e) {
                                    return new NoSuchAlgorithmException(
                                            Messages.getString("security.11",  //$NON-NLS-1$
                                                    new Object[]{type, algorithm, e}));
                                }
                                lastClassName = className;
                                return null;
                            }
                        });
                if (result != null) {
                    throw result;
                }
            }
            if (constructorParameter == null) {
                try {
                    return implementation.newInstance();
                } catch (Exception e) {
                    throw new NoSuchAlgorithmException(Messages.getString("security.199", //$NON-NLS-1$
                            type, algorithm), e);
                }
            } else {
                if (!supportsParameter(constructorParameter)) {
                    throw new InvalidParameterException(
                            Messages.getString("security.12", type)); //$NON-NLS-1$
                }

                Class[] parameterTypes = new Class[1];
                Object[] initargs = { constructorParameter };
                try {
                    if (type.equalsIgnoreCase("CertStore")) { //$NON-NLS-1$
                        parameterTypes[0] = Class
                                .forName("java.security.cert.CertStoreParameters"); //$NON-NLS-1$
                    } else {
                        parameterTypes[0] = constructorParameter.getClass();
                    }
                    return implementation.getConstructor(parameterTypes)
                            .newInstance(initargs);
                } catch (Exception e) {
                    throw new NoSuchAlgorithmException(Messages.getString("security.199", //$NON-NLS-1$
                            type, algorithm), e);
                }
            }
        }

        public boolean supportsParameter(Object parameter) {
            return true;
        }

	/**
	 * Answers a string containing a concise, human-readable
	 * description of the receiver.
	 *
	 *
	 * @return a printable representation for the receiver.
	 */
        public String toString() {
            String result = "Provider " + provider.getName() + " Service " //$NON-NLS-1$ //$NON-NLS-2$
                    + type + "." + algorithm + " " + className; //$NON-NLS-1$ //$NON-NLS-2$
            if (aliases != null) {
                result = result + "\nAliases " + aliases.toString(); //$NON-NLS-1$
            }
            if (attributes != null) {
                result = result + "\nAttributes " + attributes.toString(); //$NON-NLS-1$
            }
            return result;
        }
    }
}


text:
A thin layer over the ruby driver that allows you to quickly grab hold of your data (pluck it!).

Used as a query selector inside of MongoMapper.

## Install

```
gem install plucky
```

## Examples

See `examples/query.rb`.

## Help

https://groups.google.com/forum/#!forum/mongomapper

## Contributing

* Fork the project.
* Make your feature addition or bug fix.
* Add tests for it. This is important so I don't break it in a future version unintentionally.
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
* Send me a pull request. Bonus points for topic branches.

## Copyright

Copyright (c) 2010-2020 MongoMapper. See LICENSE for details.

## Contributors

MongoMapper/Plucky is:

* John Nunemaker
* Chris Heald
* Scott Taylor

With contributions from:

* Frederick Cheung


text:
package vpc

//Licensed under the Apache License, Version 2.0 (the "License");
//you may not use this file except in compliance with the License.
//You may obtain a copy of the License at
//
//http://www.apache.org/licenses/LICENSE-2.0
//
//Unless required by applicable law or agreed to in writing, software
//distributed under the License is distributed on an "AS IS" BASIS,
//WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//See the License for the specific language governing permissions and
//limitations under the License.
//
// Code generated by Alibaba Cloud SDK Code Generator.
// Changes may cause incorrect behavior and will be lost if the code is regenerated.

// SslVpnServer is a nested struct in vpc response
type SslVpnServer struct {
	InternetIp            string `json:"InternetIp" xml:"InternetIp"`
	IDaaSInstanceId       string `json:"IDaaSInstanceId" xml:"IDaaSInstanceId"`
	CreateTime            int64  `json:"CreateTime" xml:"CreateTime"`
	VpnGatewayId          string `json:"VpnGatewayId" xml:"VpnGatewayId"`
	IDaaSRegionId         string `json:"IDaaSRegionId" xml:"IDaaSRegionId"`
	Compress              bool   `json:"Compress" xml:"Compress"`
	Port                  int    `json:"Port" xml:"Port"`
	LocalSubnet           string `json:"LocalSubnet" xml:"LocalSubnet"`
	RegionId              string `json:"RegionId" xml:"RegionId"`
	Cipher                string `json:"Cipher" xml:"Cipher"`
	Connections           int    `json:"Connections" xml:"Connections"`
	SslVpnServerId        string `json:"SslVpnServerId" xml:"SslVpnServerId"`
	MaxConnections        int    `json:"MaxConnections" xml:"MaxConnections"`
	Name                  string `json:"Name" xml:"Name"`
	EnableMultiFactorAuth bool   `json:"EnableMultiFactorAuth" xml:"EnableMultiFactorAuth"`
	ClientIpPool          string `json:"ClientIpPool" xml:"ClientIpPool"`
	Proto                 string `json:"Proto" xml:"Proto"`
}


text:
using System;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
using SFA.DAS.Notifications.Api.Client;
using SFA.DAS.Notifications.Api.Types;

namespace SFA.DAS.EmployerUsers.Infrastructure.Notification
{
    public class StubNotificationsApi : INotificationsApi
    {
        private readonly string _directory;

        public StubNotificationsApi()
        {
            _directory = Path.Combine((string)AppDomain.CurrentDomain.GetData("DataDirectory"), "Emails");
        }

        public async Task SendEmail(Email email)
        {
            var id = string.IsNullOrEmpty(email.SystemId) ? Guid.NewGuid().ToString() : email.SystemId;
            var path = Path.Combine(_directory, id + ".json");

            if (!Directory.Exists(_directory))
            {
                Directory.CreateDirectory(_directory);
            }

            using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write))
            using (var writer = new StreamWriter(stream))
            {
                await writer.WriteAsync(JsonConvert.SerializeObject(email));
            }
        }
    }
}


text:
using System;
using System.Linq;
using System.Reflection;

namespace GoldenEye.Utils.Assemblies;

public static class TypeProvider
{
    public static Type GetTypeFromAnyReferencingAssembly(string typeName)
    {
        var referencedAssemblies = Assembly.GetEntryAssembly()
            .GetReferencedAssemblies()
            .Select(a => a.FullName);

        return AppDomain.CurrentDomain.GetAssemblies()
            .Where(a => referencedAssemblies.Contains(a.FullName))
            .SelectMany(a => a.GetTypes().Where(x => x.Name == typeName))
            .FirstOrDefault();
    }
}


text:
package org.keycloak.testsuite.adapter.example.authorization;

import org.jboss.arquillian.container.test.api.Deployer;
import org.jboss.arquillian.graphene.page.Page;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.junit.Before;
import org.junit.BeforeClass;
import org.keycloak.admin.client.resource.AuthorizationResource;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.admin.client.resource.ClientsResource;
import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.authorization.PolicyRepresentation;
import org.keycloak.representations.idm.authorization.ResourceServerRepresentation;
import org.keycloak.representations.idm.authorization.UserPolicyRepresentation;
import org.keycloak.testsuite.ProfileAssume;
import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
import org.keycloak.testsuite.pages.InfoPage;
import org.keycloak.testsuite.pages.LogoutConfirmPage;
import org.keycloak.testsuite.util.UIUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

import javax.ws.rs.core.Response;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.List;

import static org.junit.Assert.assertFalse;
import static org.keycloak.common.Profile.Feature.AUTHORIZATION;
import static org.keycloak.testsuite.util.WaitUtils.waitForPageToLoad;
import static org.keycloak.testsuite.utils.io.IOUtil.loadJson;
import static org.keycloak.testsuite.utils.io.IOUtil.loadRealm;
import static org.keycloak.testsuite.util.WaitUtils.waitUntilElement;

/**
 * @author Pedro Igor
 */
public abstract class AbstractBaseServletAuthzAdapterTest extends AbstractExampleAdapterTest {

    protected static final String REALM_NAME = "servlet-authz";
    protected static final String RESOURCE_SERVER_ID = "servlet-authz-app";

    @ArquillianResource
    private Deployer deployer;

    @Page
    protected LogoutConfirmPage logoutConfirmPage;

    @Page
    protected InfoPage infoPage;

    @BeforeClass
    public static void enabled() {
        ProfileAssume.assumeFeatureEnabled(AUTHORIZATION);
    }

    @Override
    public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
        testRealms.add(
                loadRealm(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-realm.json")));
    }

    protected void performTests(ExceptionRunnable assertion) {
        performTests(() -> importResourceServerSettings(), assertion);
    }

    protected void performTests(ExceptionRunnable beforeDeploy, ExceptionRunnable assertion) {
        try {
            beforeDeploy.run();
            deployer.deploy(RESOURCE_SERVER_ID);
            assertion.run();
        } catch (FileNotFoundException cause) {
            throw new RuntimeException("Failed to import authorization settings", cause);
        } catch (Exception cause) {
            throw new RuntimeException("Error while executing tests", cause);
        } finally {
            deployer.undeploy(RESOURCE_SERVER_ID);
        }
    }

    protected boolean hasLink(String text) {
        return getLink(text) != null;
    }

    protected boolean hasText(String text) {
        return this.driver.getPageSource().contains(text);
    }

    protected WebElement getLink(String text) {
        return this.driver.findElement(By.xpath("//a[text() = '" + text + "']"));
    }

    protected void importResourceServerSettings() throws FileNotFoundException {
        getAuthorizationResource().importSettings(loadJson(new FileInputStream(new File(TEST_APPS_HOME_DIR + "/servlet-authz-app/servlet-authz-app-authz-service.json")), ResourceServerRepresentation.class));
    }

    protected AuthorizationResource getAuthorizationResource() {
        return getClientResource(RESOURCE_SERVER_ID).authorization();
    }

    protected ClientResource getClientResource(String clientId) {
        ClientsResource clients = this.realmsResouce().realm(REALM_NAME).clients();
        ClientRepresentation resourceServer = clients.findByClientId(clientId).get(0);
        return clients.get(resourceServer.getId());
    }

    private void logOut() {
        navigateTo();
        UIUtils.clickLink(driver.findElement(By.xpath("//a[text() = 'Sign Out']")));

        logoutConfirmPage.assertCurrent();
        logoutConfirmPage.confirmLogout();
        infoPage.assertCurrent();
    }

    protected void login(String username, String password) {
        try {
            navigateTo();
            if (this.driver.getCurrentUrl().startsWith(getResourceServerUrl().toString())) {
                logOut();
                navigateTo();
            }

            this.loginPage.form().login(username, password);
        } catch (Exception cause) {
            throw new RuntimeException("Login failed", cause);
        }
    }

    protected void navigateTo() {
        this.driver.navigate().to(getResourceServerUrl() + "/");
        waitForPageToLoad();
    }

    protected void assertWasDenied() {
        waitUntilElement(By.tagName("body")).text().contains("You can not access this resource.");
    }

    protected void assertWasNotDenied() {
        waitUntilElement(By.tagName("body")).text().not().contains("You can not access this resource.");
    }

    protected URL getResourceServerUrl() {
        try {
            return new URL(this.appServerContextRootPage + "/" + RESOURCE_SERVER_ID);
        } catch (MalformedURLException e) {
            throw new RuntimeException("Could not obtain resource server url.", e);
        }
    }

    protected void navigateToDynamicMenuPage() {
        navigateTo();
        UIUtils.clickLink(getLink("Dynamic Menu"));
    }

    protected void navigateToUserPremiumPage() {
        navigateTo();
        UIUtils.clickLink(getLink("User Premium"));
    }

    protected void navigateToAdminPage() {
        navigateTo();
        UIUtils.clickLink(getLink("Administration"));
    }

    protected void updatePermissionPolicies(String permissionName, String... policyNames) {
        for (PolicyRepresentation policy : getAuthorizationResource().policies().policies()) {
            if (permissionName.equalsIgnoreCase(policy.getName())) {
                StringBuilder policies = new StringBuilder("[");

                for (String policyName : policyNames) {
                    if (policies.length() > 1) {
                        policies.append(",");
                    }
                    policies.append("\"").append(policyName).append("\"");

                }

                policies.append("]");

                policy.getConfig().put("applyPolicies", policies.toString());
                getAuthorizationResource().policies().policy(policy.getId()).update(policy);
            }
        }
    }

    protected void createUserPolicy(String name, String... userNames) {
        UserPolicyRepresentation policy = new UserPolicyRepresentation();

        policy.setName(name);

        for (String userName : userNames) {
            policy.addUser(userName);
        }

        assertFalse(policy.getUsers().isEmpty());

        Response response = getAuthorizationResource().policies().user().create(policy);
        response.close();
    }

    protected interface ExceptionRunnable {
        void run() throws Exception;
    }
}


text:
import React, { useState, useEffect } from 'react';
import createPlotlyComponent from "react-plotly.js/factory";

let Plot;

const Chart = (props) => {
    const [plotCreated, setPlotCreated] = useState(0) //0: false, 1: true

    useEffect(() => {
        import(`plotly.js-basic-dist`).then(Plotly => { //import Plotly dist when Page has been generated
            Plot = createPlotlyComponent(Plotly);
            setPlotCreated(1)
        });
    }, [])

    if (!plotCreated) {
        return (
Loading...
)
    }

    return (

            <Plot {...props.spec}
                layout={{ autosize: true }}
                style={{ width: "100%", height: "100%" }}
                useResizeHandler={true}
            />

    )
}

export default Chart


text:
+--------------+-------------------+
| WomenQueue   | R Documentation   |
+--------------+-------------------+

Women in Queues
---------------

Description
~~~~~~~~~~~

Data from Jinkinson \\& Slater (1981) and Hoaglin \\& Tukey (1985)
reporting the frequency distribution of females in 100 queues of length
10 in a London Underground station.

Usage
~~~~~

::

    data("WomenQueue")

Format
~~~~~~

A 1-way table giving the number of women in 100 queues of length 10. The
variable and its levels are

No

Name

Levels

1

nWomen

0, 1, ..., 10

Source
~~~~~~

M. Friendly (2000), Visualizing Categorical Data, pages 19–20.

References
~~~~~~~~~~

D. C. Hoaglin \\& J. W. Tukey (1985), Checking the shape of discrete
distributions. In D. C. Hoaglin, F. Mosteller, J. W. Tukey (eds.),
*Exploring Data Tables, Trends and Shapes*, chapter 9. John Wiley \\&
Sons, New York.

R. A. Jinkinson \\& M. Slater (1981), Critical discussion of a graphical
method for identifying discrete distributions, *The Statistician*,
**30**, 239–248.

M. Friendly (2000), *Visualizing Categorical Data*. SAS Institute, Cary,
NC.

Examples
~~~~~~~~

::

    data("WomenQueue")
    gf <- goodfit(WomenQueue, type = "binomial")
    summary(gf)
    plot(gf)


text:
const LessPassFingerprint = require("lesspass-fingerprint");
const LessPassEntropy = require("lesspass-entropy");
const LessPassRenderPassword = require("lesspass-render-password");

function generatePassword(site, login, masterPassword, passwordProfile) {
  return LessPassEntropy.calcEntropy(
    {
      site,
      login,
      options: {
        counter: passwordProfile.counter
      }
    },
    masterPassword
  ).then(entropy => {
    const options = {
      length: passwordProfile.length,
      lowercase: passwordProfile.lowercase,
      uppercase: passwordProfile.uppercase,
      digits: passwordProfile.numbers,
      symbols: passwordProfile.symbols
    };
    const generatedPassword = LessPassRenderPassword.renderPassword(
      entropy,
      options
    );
    return Promise.resolve(generatedPassword);
  });
}

function createFingerprint(string) {
  return LessPassFingerprint.createHmac("sha256", string).then(hmac => {
    const fingerprint = LessPassFingerprint.createFingerprint(hmac);
    return Promise.resolve(fingerprint);
  });
}

module.exports = {
  generatePassword,
  createFingerprint
};