public void setColorSchemeResources(int... colorResIds) {
        final Resources res = getResources();
        int[] colorRes = new int[colorResIds.length];
        for (int i = 0; i < colorResIds.length; i++) {
            colorRes[i] = res.getColor(colorResIds[i]);
        }
        setColorSchemeColors(colorRes);
    }

package com.xlf.nrl;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.net.Uri;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.animation.Animation;
import android.widget.ImageView;

/**
 * The author is lsjwzh(MaterialLoadingProgressBar) link{http://example.com},
 * I did some slight modifications
 */
public class CircleProgressBar extends ImageView {

    private static final int KEY_SHADOW_COLOR = 0x1E000000;

    private static final int FILL_SHADOW_COLOR = 0x3D000000;

    // PX
    private static final float X_OFFSET = 0f;

    private static final float Y_OFFSET = 1.75f;

    private static final float SHADOW_RADIUS = 3.5f;

    private static final int SHADOW_ELEVATION = 4;

    public static final int DEFAULT_CIRCLE_BG_LIGHT = 0xFFFAFAFA;

    public static final int DEFAULT_CIRCLE_COLOR = 0xFFF00000;

    private static final int DEFAULT_CIRCLE_DIAMETER = 40;

    private static final int STROKE_WIDTH_LARGE = 3;

    public static final int DEFAULT_TEXT_SIZE = 9;

    private Animation.AnimationListener mListener;

    private int mShadowRadius;

    private int mBackGroundColor;

    private int mProgressColor;

    private int mProgressStokeWidth;

    private int mArrowWidth;

    private int mArrowHeight;

    private int mProgress;

    private int mMax;

    private int mDiameter;

    private int mInnerRadius;

    private Paint mTextPaint;

    private int mTextColor;

    private int mTextSize;

    private boolean mIfDrawText;

    private boolean mShowArrow;

    public MaterialProgressDrawable mProgressDrawable;

    private ShapeDrawable mBgCircle;

    private boolean mCircleBackgroundEnabled;

    private int[] mColors = new int[] { Color.BLACK };

    public CircleProgressBar(Context context) {
        super(context);
        init(context, null, 0);
    }

    public CircleProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public CircleProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressBar, defStyleAttr, 0);
        final float density = getContext().getResources().getDisplayMetrics().density;
        mBackGroundColor = a.getColor(R.styleable.CircleProgressBar_background_color, DEFAULT_CIRCLE_BG_LIGHT);
        mProgressColor = a.getColor(R.styleable.CircleProgressBar_progress_color, DEFAULT_CIRCLE_COLOR);
        mColors = new int[] { mProgressColor };
        mInnerRadius = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_inner_radius, -1);
        mProgressStokeWidth = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_progress_stoke_width, (int) (STROKE_WIDTH_LARGE * density));
        mArrowWidth = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_arrow_width, -1);
        mArrowHeight = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_arrow_height, -1);
        mTextSize = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_progress_text_size, (int) (DEFAULT_TEXT_SIZE * density));
        mTextColor = a.getColor(R.styleable.CircleProgressBar_progress_text_color, Color.BLACK);
        mShowArrow = a.getBoolean(R.styleable.CircleProgressBar_show_arrow, true);
        mCircleBackgroundEnabled = a.getBoolean(R.styleable.CircleProgressBar_enable_circle_background, true);
        mProgress = a.getInt(R.styleable.CircleProgressBar_progress, 0);
        mMax = a.getInt(R.styleable.CircleProgressBar_max, 100);
        int textVisible = a.getInt(R.styleable.CircleProgressBar_progress_text_visibility, 1);
        if (textVisible != 1) {
            mIfDrawText = true;
        }
        mTextPaint = new Paint();
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setAntiAlias(true);
        a.recycle();
        mProgressDrawable = new MaterialProgressDrawable(getContext(), this);
        super.setImageDrawable(mProgressDrawable);
    }

    public void setProgressBackGroundColor(int color) {
        this.mBackGroundColor = color;
    }

    public void setTextColor(int color) {
        this.mTextColor = color;
    }

    private boolean elevationSupported() {
        return android.os.Build.VERSION.SDK_INT >= 21;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (!elevationSupported()) {
            setMeasuredDimension(getMeasuredWidth() + mShadowRadius * 2, getMeasuredHeight() + mShadowRadius * 2);
        }
    }

    public int getProgressStokeWidth() {
        return mProgressStokeWidth;
    }

    public void setProgressStokeWidth(int mProgressStokeWidth) {
        final float density = getContext().getResources().getDisplayMetrics().density;
        this.mProgressStokeWidth = (int) (mProgressStokeWidth * density);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        final float density = getContext().getResources().getDisplayMetrics().density;
        mDiameter = Math.min(getMeasuredWidth(), getMeasuredHeight());
        if (mDiameter <= 0) {
            mDiameter = (int) density * DEFAULT_CIRCLE_DIAMETER;
        }
        if (getBackground() == null && mCircleBackgroundEnabled) {
            final int shadowYOffset = (int) (density * Y_OFFSET);
            final int shadowXOffset = (int) (density * X_OFFSET);
            mShadowRadius = (int) (density * SHADOW_RADIUS);
            if (elevationSupported()) {
                mBgCircle = new ShapeDrawable(new OvalShape());
                ViewCompat.setElevation(this, SHADOW_ELEVATION * density);
            } else {
                OvalShape oval = new OvalShadow(mShadowRadius, mDiameter - mShadowRadius * 2);
                mBgCircle = new ShapeDrawable(oval);
                ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, mBgCircle.getPaint());
                mBgCircle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset, KEY_SHADOW_COLOR);
                final int padding = (int) mShadowRadius;
                // set padding so the inner image sits correctly within the shadow.
                setPadding(padding, padding, padding, padding);
            }
            mBgCircle.getPaint().setColor(mBackGroundColor);
            setBackgroundDrawable(mBgCircle);
        }
        mProgressDrawable.setBackgroundColor(mBackGroundColor);
        mProgressDrawable.setColorSchemeColors(mColors);
        mProgressDrawable.setSizeParameters(mDiameter, mDiameter, mInnerRadius <= 0 ? (mDiameter - mProgressStokeWidth * 2) / 4 : mInnerRadius, mProgressStokeWidth, mArrowWidth < 0 ? mProgressStokeWidth * 4 : mArrowWidth, mArrowHeight < 0 ? mProgressStokeWidth * 2 : mArrowHeight);
        if (isShowArrow()) {
            mProgressDrawable.showArrowOnFirstStart(true);
            mProgressDrawable.setArrowScale(1f);
            mProgressDrawable.showArrow(true);
        }
        super.setImageDrawable(null);
        super.setImageDrawable(mProgressDrawable);
        mProgressDrawable.setAlpha(255);
        if (getVisibility() == VISIBLE) {
            mProgressDrawable.setStartEndTrim(0, (float) 0.8);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mIfDrawText) {
            String text = String.format("%s%%", mProgress);
            int x = getWidth() / 2 - text.length() * mTextSize / 4;
            int y = getHeight() / 2 + mTextSize / 4;
            canvas.drawText(text, x, y, mTextPaint);
        }
    }

    @Override
    final public void setImageResource(int resId) {
    }

    public boolean isShowArrow() {
        return mShowArrow;
    }

    public void setShowArrow(boolean showArrow) {
        this.mShowArrow = showArrow;
    }

    @Override
    final public void setImageURI(Uri uri) {
        super.setImageURI(uri);
    }

    @Override
    final public void setImageDrawable(Drawable drawable) {
    }

    public void setAnimationListener(Animation.AnimationListener listener) {
        mListener = listener;
    }

    @Override
    public void onAnimationStart() {
        super.onAnimationStart();
        if (mListener != null) {
            mListener.onAnimationStart(getAnimation());
        }
    }

    @Override
    public void onAnimationEnd() {
        super.onAnimationEnd();
        if (mListener != null) {
            mListener.onAnimationEnd(getAnimation());
        }
    }

    /**
     * Set the color resources used in the progress animation from color resources.
     * The first color will also be the color of the bar that grows in response
     * to a user swipe gesture.
     *
     * @param colorResIds
     */

    /**
     * Set the colors used in the progress animation. The first
     * color will also be the color of the bar that grows in response to a user
     * swipe gesture.
     *
     * @param colors
     */
    public void setColorSchemeColors(int... colors) {
        mColors = colors;
        if (mProgressDrawable != null) {
            mProgressDrawable.setColorSchemeColors(colors);
        }
    }

    /**
     * Update the background color of the mBgCircle image view.
     */
    public void setBackgroundColorResource(int colorRes) {
        if (getBackground() instanceof ShapeDrawable) {
            final Resources res = getResources();
            ((ShapeDrawable) getBackground()).getPaint().setColor(res.getColor(colorRes));
        }
    }

    public void setBackgroundColor(int color) {
        if (getBackground() instanceof ShapeDrawable) {
            final Resources res = getResources();
            ((ShapeDrawable) getBackground()).getPaint().setColor(color);
        }
    }

    public boolean isShowProgressText() {
        return mIfDrawText;
    }

    public void setShowProgressText(boolean mIfDrawText) {
        this.mIfDrawText = mIfDrawText;
    }

    public int getMax() {
        return mMax;
    }

    public void setMax(int max) {
        mMax = max;
    }

    public int getProgress() {
        return mProgress;
    }

    public void setProgress(int progress) {
        if (getMax() > 0) {
            mProgress = progress;
        }
        invalidate();
        Log.i("cjj_log", "progress------->>>>" + progress);
    }

    public boolean circleBackgroundEnabled() {
        return mCircleBackgroundEnabled;
    }

    public void setCircleBackgroundEnabled(boolean enableCircleBackground) {
        this.mCircleBackgroundEnabled = enableCircleBackground;
    }

    @Override
    public int getVisibility() {
        return super.getVisibility();
    }

    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mProgressDrawable != null) {
            mProgressDrawable.stop();
            mProgressDrawable.setVisible(getVisibility() == VISIBLE, false);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mProgressDrawable != null) {
            mProgressDrawable.stop();
            mProgressDrawable.setVisible(false, false);
        }
    }

    private class OvalShadow extends OvalShape {

        private RadialGradient mRadialGradient;

        private int mShadowRadius;

        private Paint mShadowPaint;

        private int mCircleDiameter;

        public OvalShadow(int shadowRadius, int circleDiameter) {
            super();
            mShadowPaint = new Paint();
            mShadowRadius = shadowRadius;
            mCircleDiameter = circleDiameter;
            mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2, mShadowRadius, new int[] { FILL_SHADOW_COLOR, Color.TRANSPARENT }, null, Shader.TileMode.CLAMP);
            mShadowPaint.setShader(mRadialGradient);
        }

        @Override
        public void draw(Canvas canvas, Paint paint) {
            final int viewWidth = CircleProgressBar.this.getWidth();
            final int viewHeight = CircleProgressBar.this.getHeight();
            canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius), mShadowPaint);
            canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint);
        }
    }

    public void start() {
        mProgressDrawable.start();
    }

    public void setStartEndTrim(float startAngle, float endAngle) {
        mProgressDrawable.setStartEndTrim(startAngle, endAngle);
    }

    public void stop() {
        mProgressDrawable.stop();
    }

    public void setProgressRotation(float rotation) {
        mProgressDrawable.setProgressRotation(rotation);
    }
}


if (mListener != null) {
            mListener.onAnimationStart(getAnimation());
        }

package com.xlf.nrl;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.net.Uri;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.animation.Animation;
import android.widget.ImageView;

/**
 * The author is lsjwzh(MaterialLoadingProgressBar) link{http://example.com},
 * I did some slight modifications
 */
public class CircleProgressBar extends ImageView {

    private static final int KEY_SHADOW_COLOR = 0x1E000000;

    private static final int FILL_SHADOW_COLOR = 0x3D000000;

    // PX
    private static final float X_OFFSET = 0f;

    private static final float Y_OFFSET = 1.75f;

    private static final float SHADOW_RADIUS = 3.5f;

    private static final int SHADOW_ELEVATION = 4;

    public static final int DEFAULT_CIRCLE_BG_LIGHT = 0xFFFAFAFA;

    public static final int DEFAULT_CIRCLE_COLOR = 0xFFF00000;

    private static final int DEFAULT_CIRCLE_DIAMETER = 40;

    private static final int STROKE_WIDTH_LARGE = 3;

    public static final int DEFAULT_TEXT_SIZE = 9;

    private Animation.AnimationListener mListener;

    private int mShadowRadius;

    private int mBackGroundColor;

    private int mProgressColor;

    private int mProgressStokeWidth;

    private int mArrowWidth;

    private int mArrowHeight;

    private int mProgress;

    private int mMax;

    private int mDiameter;

    private int mInnerRadius;

    private Paint mTextPaint;

    private int mTextColor;

    private int mTextSize;

    private boolean mIfDrawText;

    private boolean mShowArrow;

    public MaterialProgressDrawable mProgressDrawable;

    private ShapeDrawable mBgCircle;

    private boolean mCircleBackgroundEnabled;

    private int[] mColors = new int[] { Color.BLACK };

    public CircleProgressBar(Context context) {
        super(context);
        init(context, null, 0);
    }

    public CircleProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public CircleProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressBar, defStyleAttr, 0);
        final float density = getContext().getResources().getDisplayMetrics().density;
        mBackGroundColor = a.getColor(R.styleable.CircleProgressBar_background_color, DEFAULT_CIRCLE_BG_LIGHT);
        mProgressColor = a.getColor(R.styleable.CircleProgressBar_progress_color, DEFAULT_CIRCLE_COLOR);
        mColors = new int[] { mProgressColor };
        mInnerRadius = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_inner_radius, -1);
        mProgressStokeWidth = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_progress_stoke_width, (int) (STROKE_WIDTH_LARGE * density));
        mArrowWidth = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_arrow_width, -1);
        mArrowHeight = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_arrow_height, -1);
        mTextSize = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_progress_text_size, (int) (DEFAULT_TEXT_SIZE * density));
        mTextColor = a.getColor(R.styleable.CircleProgressBar_progress_text_color, Color.BLACK);
        mShowArrow = a.getBoolean(R.styleable.CircleProgressBar_show_arrow, true);
        mCircleBackgroundEnabled = a.getBoolean(R.styleable.CircleProgressBar_enable_circle_background, true);
        mProgress = a.getInt(R.styleable.CircleProgressBar_progress, 0);
        mMax = a.getInt(R.styleable.CircleProgressBar_max, 100);
        int textVisible = a.getInt(R.styleable.CircleProgressBar_progress_text_visibility, 1);
        if (textVisible != 1) {
            mIfDrawText = true;
        }
        mTextPaint = new Paint();
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setAntiAlias(true);
        a.recycle();
        mProgressDrawable = new MaterialProgressDrawable(getContext(), this);
        super.setImageDrawable(mProgressDrawable);
    }

    public void setProgressBackGroundColor(int color) {
        this.mBackGroundColor = color;
    }

    public void setTextColor(int color) {
        this.mTextColor = color;
    }

    private boolean elevationSupported() {
        return android.os.Build.VERSION.SDK_INT >= 21;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (!elevationSupported()) {
            setMeasuredDimension(getMeasuredWidth() + mShadowRadius * 2, getMeasuredHeight() + mShadowRadius * 2);
        }
    }

    public int getProgressStokeWidth() {
        return mProgressStokeWidth;
    }

    public void setProgressStokeWidth(int mProgressStokeWidth) {
        final float density = getContext().getResources().getDisplayMetrics().density;
        this.mProgressStokeWidth = (int) (mProgressStokeWidth * density);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        final float density = getContext().getResources().getDisplayMetrics().density;
        mDiameter = Math.min(getMeasuredWidth(), getMeasuredHeight());
        if (mDiameter <= 0) {
            mDiameter = (int) density * DEFAULT_CIRCLE_DIAMETER;
        }
        if (getBackground() == null && mCircleBackgroundEnabled) {
            final int shadowYOffset = (int) (density * Y_OFFSET);
            final int shadowXOffset = (int) (density * X_OFFSET);
            mShadowRadius = (int) (density * SHADOW_RADIUS);
            if (elevationSupported()) {
                mBgCircle = new ShapeDrawable(new OvalShape());
                ViewCompat.setElevation(this, SHADOW_ELEVATION * density);
            } else {
                OvalShape oval = new OvalShadow(mShadowRadius, mDiameter - mShadowRadius * 2);
                mBgCircle = new ShapeDrawable(oval);
                ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, mBgCircle.getPaint());
                mBgCircle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset, KEY_SHADOW_COLOR);
                final int padding = (int) mShadowRadius;
                // set padding so the inner image sits correctly within the shadow.
                setPadding(padding, padding, padding, padding);
            }
            mBgCircle.getPaint().setColor(mBackGroundColor);
            setBackgroundDrawable(mBgCircle);
        }
        mProgressDrawable.setBackgroundColor(mBackGroundColor);
        mProgressDrawable.setColorSchemeColors(mColors);
        mProgressDrawable.setSizeParameters(mDiameter, mDiameter, mInnerRadius <= 0 ? (mDiameter - mProgressStokeWidth * 2) / 4 : mInnerRadius, mProgressStokeWidth, mArrowWidth < 0 ? mProgressStokeWidth * 4 : mArrowWidth, mArrowHeight < 0 ? mProgressStokeWidth * 2 : mArrowHeight);
        if (isShowArrow()) {
            mProgressDrawable.showArrowOnFirstStart(true);
            mProgressDrawable.setArrowScale(1f);
            mProgressDrawable.showArrow(true);
        }
        super.setImageDrawable(null);
        super.setImageDrawable(mProgressDrawable);
        mProgressDrawable.setAlpha(255);
        if (getVisibility() == VISIBLE) {
            mProgressDrawable.setStartEndTrim(0, (float) 0.8);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mIfDrawText) {
            String text = String.format("%s%%", mProgress);
            int x = getWidth() / 2 - text.length() * mTextSize / 4;
            int y = getHeight() / 2 + mTextSize / 4;
            canvas.drawText(text, x, y, mTextPaint);
        }
    }

    @Override
    final public void setImageResource(int resId) {
    }

    public boolean isShowArrow() {
        return mShowArrow;
    }

    public void setShowArrow(boolean showArrow) {
        this.mShowArrow = showArrow;
    }

    @Override
    final public void setImageURI(Uri uri) {
        super.setImageURI(uri);
    }

    @Override
    final public void setImageDrawable(Drawable drawable) {
    }

    public void setAnimationListener(Animation.AnimationListener listener) {
        mListener = listener;
    }

    @Override
    public void onAnimationStart() {
        super.onAnimationStart();

    }

    @Override
    public void onAnimationEnd() {
        super.onAnimationEnd();
        if (mListener != null) {
            mListener.onAnimationEnd(getAnimation());
        }
    }

    /**
     * Set the color resources used in the progress animation from color resources.
     * The first color will also be the color of the bar that grows in response
     * to a user swipe gesture.
     *
     * @param colorResIds
     */
    public void setColorSchemeResources(int... colorResIds) {
        final Resources res = getResources();
        int[] colorRes = new int[colorResIds.length];
        for (int i = 0; i < colorResIds.length; i++) {
            colorRes[i] = res.getColor(colorResIds[i]);
        }
        setColorSchemeColors(colorRes);
    }

    /**
     * Set the colors used in the progress animation. The first
     * color will also be the color of the bar that grows in response to a user
     * swipe gesture.
     *
     * @param colors
     */
    public void setColorSchemeColors(int... colors) {
        mColors = colors;
        if (mProgressDrawable != null) {
            mProgressDrawable.setColorSchemeColors(colors);
        }
    }

    /**
     * Update the background color of the mBgCircle image view.
     */
    public void setBackgroundColorResource(int colorRes) {
        if (getBackground() instanceof ShapeDrawable) {
            final Resources res = getResources();
            ((ShapeDrawable) getBackground()).getPaint().setColor(res.getColor(colorRes));
        }
    }

    public void setBackgroundColor(int color) {
        if (getBackground() instanceof ShapeDrawable) {
            final Resources res = getResources();
            ((ShapeDrawable) getBackground()).getPaint().setColor(color);
        }
    }

    public boolean isShowProgressText() {
        return mIfDrawText;
    }

    public void setShowProgressText(boolean mIfDrawText) {
        this.mIfDrawText = mIfDrawText;
    }

    public int getMax() {
        return mMax;
    }

    public void setMax(int max) {
        mMax = max;
    }

    public int getProgress() {
        return mProgress;
    }

    public void setProgress(int progress) {
        if (getMax() > 0) {
            mProgress = progress;
        }
        invalidate();
        Log.i("cjj_log", "progress------->>>>" + progress);
    }

    public boolean circleBackgroundEnabled() {
        return mCircleBackgroundEnabled;
    }

    public void setCircleBackgroundEnabled(boolean enableCircleBackground) {
        this.mCircleBackgroundEnabled = enableCircleBackground;
    }

    @Override
    public int getVisibility() {
        return super.getVisibility();
    }

    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mProgressDrawable != null) {
            mProgressDrawable.stop();
            mProgressDrawable.setVisible(getVisibility() == VISIBLE, false);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mProgressDrawable != null) {
            mProgressDrawable.stop();
            mProgressDrawable.setVisible(false, false);
        }
    }

    private class OvalShadow extends OvalShape {

        private RadialGradient mRadialGradient;

        private int mShadowRadius;

        private Paint mShadowPaint;

        private int mCircleDiameter;

        public OvalShadow(int shadowRadius, int circleDiameter) {
            super();
            mShadowPaint = new Paint();
            mShadowRadius = shadowRadius;
            mCircleDiameter = circleDiameter;
            mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2, mShadowRadius, new int[] { FILL_SHADOW_COLOR, Color.TRANSPARENT }, null, Shader.TileMode.CLAMP);
            mShadowPaint.setShader(mRadialGradient);
        }

        @Override
        public void draw(Canvas canvas, Paint paint) {
            final int viewWidth = CircleProgressBar.this.getWidth();
            final int viewHeight = CircleProgressBar.this.getHeight();
            canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius), mShadowPaint);
            canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint);
        }
    }

    public void start() {
        mProgressDrawable.start();
    }

    public void setStartEndTrim(float startAngle, float endAngle) {
        mProgressDrawable.setStartEndTrim(startAngle, endAngle);
    }

    public void stop() {
        mProgressDrawable.stop();
    }

    public void setProgressRotation(float rotation) {
        mProgressDrawable.setProgressRotation(rotation);
    }
}


for (int i = 0; i < colorResIds.length; i++) {
            colorRes[i] = res.getColor(colorResIds[i]);
        }

package com.xlf.nrl;

import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.OvalShape;
import android.net.Uri;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.animation.Animation;
import android.widget.ImageView;

/**
 * The author is lsjwzh(MaterialLoadingProgressBar) link{http://example.com},
 * I did some slight modifications
 */
public class CircleProgressBar extends ImageView {

    private static final int KEY_SHADOW_COLOR = 0x1E000000;

    private static final int FILL_SHADOW_COLOR = 0x3D000000;

    // PX
    private static final float X_OFFSET = 0f;

    private static final float Y_OFFSET = 1.75f;

    private static final float SHADOW_RADIUS = 3.5f;

    private static final int SHADOW_ELEVATION = 4;

    public static final int DEFAULT_CIRCLE_BG_LIGHT = 0xFFFAFAFA;

    public static final int DEFAULT_CIRCLE_COLOR = 0xFFF00000;

    private static final int DEFAULT_CIRCLE_DIAMETER = 40;

    private static final int STROKE_WIDTH_LARGE = 3;

    public static final int DEFAULT_TEXT_SIZE = 9;

    private Animation.AnimationListener mListener;

    private int mShadowRadius;

    private int mBackGroundColor;

    private int mProgressColor;

    private int mProgressStokeWidth;

    private int mArrowWidth;

    private int mArrowHeight;

    private int mProgress;

    private int mMax;

    private int mDiameter;

    private int mInnerRadius;

    private Paint mTextPaint;

    private int mTextColor;

    private int mTextSize;

    private boolean mIfDrawText;

    private boolean mShowArrow;

    public MaterialProgressDrawable mProgressDrawable;

    private ShapeDrawable mBgCircle;

    private boolean mCircleBackgroundEnabled;

    private int[] mColors = new int[] { Color.BLACK };

    public CircleProgressBar(Context context) {
        super(context);
        init(context, null, 0);
    }

    public CircleProgressBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs, 0);
    }

    public CircleProgressBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs, defStyleAttr);
    }

    private void init(Context context, AttributeSet attrs, int defStyleAttr) {
        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleProgressBar, defStyleAttr, 0);
        final float density = getContext().getResources().getDisplayMetrics().density;
        mBackGroundColor = a.getColor(R.styleable.CircleProgressBar_background_color, DEFAULT_CIRCLE_BG_LIGHT);
        mProgressColor = a.getColor(R.styleable.CircleProgressBar_progress_color, DEFAULT_CIRCLE_COLOR);
        mColors = new int[] { mProgressColor };
        mInnerRadius = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_inner_radius, -1);
        mProgressStokeWidth = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_progress_stoke_width, (int) (STROKE_WIDTH_LARGE * density));
        mArrowWidth = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_arrow_width, -1);
        mArrowHeight = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_arrow_height, -1);
        mTextSize = a.getDimensionPixelOffset(R.styleable.CircleProgressBar_progress_text_size, (int) (DEFAULT_TEXT_SIZE * density));
        mTextColor = a.getColor(R.styleable.CircleProgressBar_progress_text_color, Color.BLACK);
        mShowArrow = a.getBoolean(R.styleable.CircleProgressBar_show_arrow, true);
        mCircleBackgroundEnabled = a.getBoolean(R.styleable.CircleProgressBar_enable_circle_background, true);
        mProgress = a.getInt(R.styleable.CircleProgressBar_progress, 0);
        mMax = a.getInt(R.styleable.CircleProgressBar_max, 100);
        int textVisible = a.getInt(R.styleable.CircleProgressBar_progress_text_visibility, 1);
        if (textVisible != 1) {
            mIfDrawText = true;
        }
        mTextPaint = new Paint();
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setAntiAlias(true);
        a.recycle();
        mProgressDrawable = new MaterialProgressDrawable(getContext(), this);
        super.setImageDrawable(mProgressDrawable);
    }

    public void setProgressBackGroundColor(int color) {
        this.mBackGroundColor = color;
    }

    public void setTextColor(int color) {
        this.mTextColor = color;
    }

    private boolean elevationSupported() {
        return android.os.Build.VERSION.SDK_INT >= 21;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (!elevationSupported()) {
            setMeasuredDimension(getMeasuredWidth() + mShadowRadius * 2, getMeasuredHeight() + mShadowRadius * 2);
        }
    }

    public int getProgressStokeWidth() {
        return mProgressStokeWidth;
    }

    public void setProgressStokeWidth(int mProgressStokeWidth) {
        final float density = getContext().getResources().getDisplayMetrics().density;
        this.mProgressStokeWidth = (int) (mProgressStokeWidth * density);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        final float density = getContext().getResources().getDisplayMetrics().density;
        mDiameter = Math.min(getMeasuredWidth(), getMeasuredHeight());
        if (mDiameter <= 0) {
            mDiameter = (int) density * DEFAULT_CIRCLE_DIAMETER;
        }
        if (getBackground() == null && mCircleBackgroundEnabled) {
            final int shadowYOffset = (int) (density * Y_OFFSET);
            final int shadowXOffset = (int) (density * X_OFFSET);
            mShadowRadius = (int) (density * SHADOW_RADIUS);
            if (elevationSupported()) {
                mBgCircle = new ShapeDrawable(new OvalShape());
                ViewCompat.setElevation(this, SHADOW_ELEVATION * density);
            } else {
                OvalShape oval = new OvalShadow(mShadowRadius, mDiameter - mShadowRadius * 2);
                mBgCircle = new ShapeDrawable(oval);
                ViewCompat.setLayerType(this, ViewCompat.LAYER_TYPE_SOFTWARE, mBgCircle.getPaint());
                mBgCircle.getPaint().setShadowLayer(mShadowRadius, shadowXOffset, shadowYOffset, KEY_SHADOW_COLOR);
                final int padding = (int) mShadowRadius;
                // set padding so the inner image sits correctly within the shadow.
                setPadding(padding, padding, padding, padding);
            }
            mBgCircle.getPaint().setColor(mBackGroundColor);
            setBackgroundDrawable(mBgCircle);
        }
        mProgressDrawable.setBackgroundColor(mBackGroundColor);
        mProgressDrawable.setColorSchemeColors(mColors);
        mProgressDrawable.setSizeParameters(mDiameter, mDiameter, mInnerRadius <= 0 ? (mDiameter - mProgressStokeWidth * 2) / 4 : mInnerRadius, mProgressStokeWidth, mArrowWidth < 0 ? mProgressStokeWidth * 4 : mArrowWidth, mArrowHeight < 0 ? mProgressStokeWidth * 2 : mArrowHeight);
        if (isShowArrow()) {
            mProgressDrawable.showArrowOnFirstStart(true);
            mProgressDrawable.setArrowScale(1f);
            mProgressDrawable.showArrow(true);
        }
        super.setImageDrawable(null);
        super.setImageDrawable(mProgressDrawable);
        mProgressDrawable.setAlpha(255);
        if (getVisibility() == VISIBLE) {
            mProgressDrawable.setStartEndTrim(0, (float) 0.8);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mIfDrawText) {
            String text = String.format("%s%%", mProgress);
            int x = getWidth() / 2 - text.length() * mTextSize / 4;
            int y = getHeight() / 2 + mTextSize / 4;
            canvas.drawText(text, x, y, mTextPaint);
        }
    }

    @Override
    final public void setImageResource(int resId) {
    }

    public boolean isShowArrow() {
        return mShowArrow;
    }

    public void setShowArrow(boolean showArrow) {
        this.mShowArrow = showArrow;
    }

    @Override
    final public void setImageURI(Uri uri) {
        super.setImageURI(uri);
    }

    @Override
    final public void setImageDrawable(Drawable drawable) {
    }

    public void setAnimationListener(Animation.AnimationListener listener) {
        mListener = listener;
    }

    @Override
    public void onAnimationStart() {
        super.onAnimationStart();
        if (mListener != null) {
            mListener.onAnimationStart(getAnimation());
        }
    }

    @Override
    public void onAnimationEnd() {
        super.onAnimationEnd();
        if (mListener != null) {
            mListener.onAnimationEnd(getAnimation());
        }
    }

    /**
     * Set the color resources used in the progress animation from color resources.
     * The first color will also be the color of the bar that grows in response
     * to a user swipe gesture.
     *
     * @param colorResIds
     */
    public void setColorSchemeResources(int... colorResIds) {
        final Resources res = getResources();
        int[] colorRes = new int[colorResIds.length];

        setColorSchemeColors(colorRes);
    }

    /**
     * Set the colors used in the progress animation. The first
     * color will also be the color of the bar that grows in response to a user
     * swipe gesture.
     *
     * @param colors
     */
    public void setColorSchemeColors(int... colors) {
        mColors = colors;
        if (mProgressDrawable != null) {
            mProgressDrawable.setColorSchemeColors(colors);
        }
    }

    /**
     * Update the background color of the mBgCircle image view.
     */
    public void setBackgroundColorResource(int colorRes) {
        if (getBackground() instanceof ShapeDrawable) {
            final Resources res = getResources();
            ((ShapeDrawable) getBackground()).getPaint().setColor(res.getColor(colorRes));
        }
    }

    public void setBackgroundColor(int color) {
        if (getBackground() instanceof ShapeDrawable) {
            final Resources res = getResources();
            ((ShapeDrawable) getBackground()).getPaint().setColor(color);
        }
    }

    public boolean isShowProgressText() {
        return mIfDrawText;
    }

    public void setShowProgressText(boolean mIfDrawText) {
        this.mIfDrawText = mIfDrawText;
    }

    public int getMax() {
        return mMax;
    }

    public void setMax(int max) {
        mMax = max;
    }

    public int getProgress() {
        return mProgress;
    }

    public void setProgress(int progress) {
        if (getMax() > 0) {
            mProgress = progress;
        }
        invalidate();
        Log.i("cjj_log", "progress------->>>>" + progress);
    }

    public boolean circleBackgroundEnabled() {
        return mCircleBackgroundEnabled;
    }

    public void setCircleBackgroundEnabled(boolean enableCircleBackground) {
        this.mCircleBackgroundEnabled = enableCircleBackground;
    }

    @Override
    public int getVisibility() {
        return super.getVisibility();
    }

    @Override
    public void setVisibility(int visibility) {
        super.setVisibility(visibility);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        if (mProgressDrawable != null) {
            mProgressDrawable.stop();
            mProgressDrawable.setVisible(getVisibility() == VISIBLE, false);
        }
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        if (mProgressDrawable != null) {
            mProgressDrawable.stop();
            mProgressDrawable.setVisible(false, false);
        }
    }

    private class OvalShadow extends OvalShape {

        private RadialGradient mRadialGradient;

        private int mShadowRadius;

        private Paint mShadowPaint;

        private int mCircleDiameter;

        public OvalShadow(int shadowRadius, int circleDiameter) {
            super();
            mShadowPaint = new Paint();
            mShadowRadius = shadowRadius;
            mCircleDiameter = circleDiameter;
            mRadialGradient = new RadialGradient(mCircleDiameter / 2, mCircleDiameter / 2, mShadowRadius, new int[] { FILL_SHADOW_COLOR, Color.TRANSPARENT }, null, Shader.TileMode.CLAMP);
            mShadowPaint.setShader(mRadialGradient);
        }

        @Override
        public void draw(Canvas canvas, Paint paint) {
            final int viewWidth = CircleProgressBar.this.getWidth();
            final int viewHeight = CircleProgressBar.this.getHeight();
            canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2 + mShadowRadius), mShadowPaint);
            canvas.drawCircle(viewWidth / 2, viewHeight / 2, (mCircleDiameter / 2), paint);
        }
    }

    public void start() {
        mProgressDrawable.start();
    }

    public void setStartEndTrim(float startAngle, float endAngle) {
        mProgressDrawable.setStartEndTrim(startAngle, endAngle);
    }

    public void stop() {
        mProgressDrawable.stop();
    }

    public void setProgressRotation(float rotation) {
        mProgressDrawable.setProgressRotation(rotation);
    }
}


package com.sun.org.apache.xerces.internal.impl.xs.util;

/**
 * @xerces.internal
 *
 * @author Henry Zongaro, IBM
 */
public final class XIntPool {

    private static final short POOL_SIZE = 10;

    private static final XInt[] fXIntPool = new XInt[POOL_SIZE];

    static {
        for (int i = 0; i < POOL_SIZE; i++) fXIntPool[i] = new XInt(i);
    }

{
        if (value >= 0 && value < fXIntPool.length)
            return fXIntPool[value];
        else
            return new XInt(value);
    }
}


{
        if (value >= 0 && value < fXIntPool.length)
            return fXIntPool[value];
        else
            return new XInt(value);
    }

package com.sun.org.apache.xerces.internal.impl.xs.util;

/**
 * @xerces.internal
 *
 * @author Henry Zongaro, IBM
 */
public final class XIntPool {

    private static final short POOL_SIZE = 10;

    private static final XInt[] fXIntPool = new XInt[POOL_SIZE];

    static {
        for (int i = 0; i < POOL_SIZE; i++) fXIntPool[i] = new XInt(i);
    }

    public final XInt getXInt(int value)


if (value >= 0 && value < fXIntPool.length)
            return fXIntPool[value];
        else
            return new XInt(value);

package com.sun.org.apache.xerces.internal.impl.xs.util;

/**
 * @xerces.internal
 *
 * @author Henry Zongaro, IBM
 */
public final class XIntPool {

    private static final short POOL_SIZE = 10;

    private static final XInt[] fXIntPool = new XInt[POOL_SIZE];

    static {
        for (int i = 0; i < POOL_SIZE; i++) fXIntPool[i] = new XInt(i);
    }

    public final XInt getXInt(int value) {


for (int i = 0; i < POOL_SIZE; i++) fXIntPool[i] = new XInt(i);

package com.sun.org.apache.xerces.internal.impl.xs.util;

/**
 * @xerces.internal
 *
 * @author Henry Zongaro, IBM
 */
public final class XIntPool {

    private static final short POOL_SIZE = 10;

    private static final XInt[] fXIntPool = new XInt[POOL_SIZE];

    static {

    }

    public final XInt getXInt(int value) {
        if (value >= 0 && value < fXIntPool.length)
            return fXIntPool[value];
        else
            return new XInt(value);
    }
}


package com.chen.fakevibrato.widget.emojiview;

import android.content.Context;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * emoji 可以显示emoji的textview
 * @author Created by CHEN on 2019/7/5
 * @email example@nnthink.com
 */
public class EmojiconTextView extends TextView {

    private int mEmojiconSize;

    private int mEmojiconTextSize;

    private int mTextStart = 0;

    private int mTextLength = -1;

    private boolean mUseSystemDefault = false;

    public EmojiconTextView(Context context) {
        super(context);
        init(null);
    }

    public EmojiconTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public EmojiconTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        mEmojiconTextSize = (int) getTextSize();
        mEmojiconSize = (int) getTextSize();
        setText(getText());
    }

    public void setTextWithWidth(CharSequence text, int limitedWidth) {
        if (TextUtils.isEmpty(text)) {
            super.setText(text);
            return;
        }
        if (limitedWidth < 0) {
            limitedWidth = this.getMeasuredWidth() - getPaddingRight() - getPaddingLeft();
        }
        SpannableStringBuilder builder = new SpannableStringBuilder(text);
        EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
        CharSequence trucatedText = TextUtils.ellipsize(builder, getPaint(), limitedWidth, getEllipsize());
        super.setText(trucatedText, BufferType.SPANNABLE);
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.setText(text, type);
    }

    @Override
    public void append(CharSequence text, int start, int end) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.append(text, start, end);
    }

    /**
     * Set the size of emojicon in pixels.
     */

{
        mEmojiconSize = pixels;
        super.setText(getText());
    }

    /**
     * Set whether to use system default emojicon
     */
    public void setUseSystemDefault(boolean useSystemDefault) {
        mUseSystemDefault = useSystemDefault;
    }
}


{
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.setText(text, type);
    }

package com.chen.fakevibrato.widget.emojiview;

import android.content.Context;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * emoji 可以显示emoji的textview
 * @author Created by CHEN on 2019/7/5
 * @email example@nnthink.com
 */
public class EmojiconTextView extends TextView {

    private int mEmojiconSize;

    private int mEmojiconTextSize;

    private int mTextStart = 0;

    private int mTextLength = -1;

    private boolean mUseSystemDefault = false;

    public EmojiconTextView(Context context) {
        super(context);
        init(null);
    }

    public EmojiconTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public EmojiconTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        mEmojiconTextSize = (int) getTextSize();
        mEmojiconSize = (int) getTextSize();
        setText(getText());
    }

    public void setTextWithWidth(CharSequence text, int limitedWidth) {
        if (TextUtils.isEmpty(text)) {
            super.setText(text);
            return;
        }
        if (limitedWidth < 0) {
            limitedWidth = this.getMeasuredWidth() - getPaddingRight() - getPaddingLeft();
        }
        SpannableStringBuilder builder = new SpannableStringBuilder(text);
        EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
        CharSequence trucatedText = TextUtils.ellipsize(builder, getPaint(), limitedWidth, getEllipsize());
        super.setText(trucatedText, BufferType.SPANNABLE);
    }

    @Override
    public void setText(CharSequence text, BufferType type)

    @Override
    public void append(CharSequence text, int start, int end) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.append(text, start, end);
    }

    /**
     * Set the size of emojicon in pixels.
     */
    public void setEmojiconSize(int pixels) {
        mEmojiconSize = pixels;
        super.setText(getText());
    }

    /**
     * Set whether to use system default emojicon
     */
    public void setUseSystemDefault(boolean useSystemDefault) {
        mUseSystemDefault = useSystemDefault;
    }
}


package com.chen.fakevibrato.widget.emojiview;

import android.content.Context;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * emoji 可以显示emoji的textview
 * @author Created by CHEN on 2019/7/5
 * @email example@nnthink.com
 */
public class EmojiconTextView extends TextView {

    private int mEmojiconSize;

    private int mEmojiconTextSize;

    private int mTextStart = 0;

    private int mTextLength = -1;

    private boolean mUseSystemDefault = false;

    public EmojiconTextView(Context context) {
        super(context);
        init(null);
    }

    public EmojiconTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public EmojiconTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        mEmojiconTextSize = (int) getTextSize();
        mEmojiconSize = (int) getTextSize();
        setText(getText());
    }

    public void setTextWithWidth(CharSequence text, int limitedWidth) {
        if (TextUtils.isEmpty(text)) {
            super.setText(text);
            return;
        }
        if (limitedWidth < 0) {
            limitedWidth = this.getMeasuredWidth() - getPaddingRight() - getPaddingLeft();
        }
        SpannableStringBuilder builder = new SpannableStringBuilder(text);
        EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
        CharSequence trucatedText = TextUtils.ellipsize(builder, getPaint(), limitedWidth, getEllipsize());
        super.setText(trucatedText, BufferType.SPANNABLE);
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.setText(text, type);
    }

    @Override
    public void append(CharSequence text, int start, int end) {

        super.append(text, start, end);
    }

    /**
     * Set the size of emojicon in pixels.
     */
    public void setEmojiconSize(int pixels) {
        mEmojiconSize = pixels;
        super.setText(getText());
    }

    /**
     * Set whether to use system default emojicon
     */
    public void setUseSystemDefault(boolean useSystemDefault) {
        mUseSystemDefault = useSystemDefault;
    }
}


package com.chen.fakevibrato.widget.emojiview;

import android.content.Context;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * emoji 可以显示emoji的textview
 * @author Created by CHEN on 2019/7/5
 * @email example@nnthink.com
 */
public class EmojiconTextView extends TextView {

    private int mEmojiconSize;

    private int mEmojiconTextSize;

    private int mTextStart = 0;

    private int mTextLength = -1;

    private boolean mUseSystemDefault = false;

    public EmojiconTextView(Context context) {
        super(context);
        init(null);
    }

    public EmojiconTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public EmojiconTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        mEmojiconTextSize = (int) getTextSize();
        mEmojiconSize = (int) getTextSize();
        setText(getText());
    }

    public void setTextWithWidth(CharSequence text, int limitedWidth) {
        if (TextUtils.isEmpty(text)) {
            super.setText(text);
            return;
        }
        if (limitedWidth < 0) {
            limitedWidth = this.getMeasuredWidth() - getPaddingRight() - getPaddingLeft();
        }
        SpannableStringBuilder builder = new SpannableStringBuilder(text);
        EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
        CharSequence trucatedText = TextUtils.ellipsize(builder, getPaint(), limitedWidth, getEllipsize());
        super.setText(trucatedText, BufferType.SPANNABLE);
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.setText(text, type);
    }

    @Override
    public void append(CharSequence text, int start, int end) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }

    }

    /**
     * Set the size of emojicon in pixels.
     */
    public void setEmojiconSize(int pixels) {
        mEmojiconSize = pixels;
        super.setText(getText());
    }

    /**
     * Set whether to use system default emojicon
     */
    public void setUseSystemDefault(boolean useSystemDefault) {
        mUseSystemDefault = useSystemDefault;
    }
}


package com.chen.fakevibrato.widget.emojiview;

import android.content.Context;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * emoji 可以显示emoji的textview
 * @author Created by CHEN on 2019/7/5
 * @email example@nnthink.com
 */
public class EmojiconTextView extends TextView {

    private int mEmojiconSize;

    private int mEmojiconTextSize;

    private int mTextStart = 0;

    private int mTextLength = -1;

    private boolean mUseSystemDefault = false;

    public EmojiconTextView(Context context) {
        super(context);
        init(null);
    }

    public EmojiconTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public EmojiconTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        mEmojiconTextSize = (int) getTextSize();
        mEmojiconSize = (int) getTextSize();
        setText(getText());
    }

    public void setTextWithWidth(CharSequence text, int limitedWidth) {
        if (TextUtils.isEmpty(text)) {
            super.setText(text);
            return;
        }
        if (limitedWidth < 0) {
            limitedWidth = this.getMeasuredWidth() - getPaddingRight() - getPaddingLeft();
        }
        SpannableStringBuilder builder = new SpannableStringBuilder(text);
        EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
        CharSequence trucatedText = TextUtils.ellipsize(builder, getPaint(), limitedWidth, getEllipsize());
        super.setText(trucatedText, BufferType.SPANNABLE);
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.setText(text, type);
    }

    @Override
    public void append(CharSequence text, int start, int end) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.append(text, start, end);

    /**
     * Set the size of emojicon in pixels.
     */
    public void setEmojiconSize(int pixels) {
        mEmojiconSize = pixels;
        super.setText(getText());
    }

    /**
     * Set whether to use system default emojicon
     */
    public void setUseSystemDefault(boolean useSystemDefault) {
        mUseSystemDefault = useSystemDefault;
    }
}


package com.chen.fakevibrato.widget.emojiview;

import android.content.Context;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * emoji 可以显示emoji的textview
 * @author Created by CHEN on 2019/7/5
 * @email example@nnthink.com
 */
public class EmojiconTextView extends TextView {

    private int mEmojiconSize;

    private int mEmojiconTextSize;

    private int mTextStart = 0;

    private int mTextLength = -1;

    private boolean mUseSystemDefault = false;

    public EmojiconTextView(Context context) {
        super(context);
        init(null);
    }

    public EmojiconTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public EmojiconTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        mEmojiconTextSize = (int) getTextSize();
        mEmojiconSize = (int) getTextSize();
        setText(getText());
    }

    public void setTextWithWidth(CharSequence text, int limitedWidth) {
        if (TextUtils.isEmpty(text)) {
            super.setText(text);
            return;
        }
        if (limitedWidth < 0) {
            limitedWidth = this.getMeasuredWidth() - getPaddingRight() - getPaddingLeft();
        }
        SpannableStringBuilder builder = new SpannableStringBuilder(text);
        EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
        CharSequence trucatedText = TextUtils.ellipsize(builder, getPaint(), limitedWidth, getEllipsize());
        super.setText(trucatedText, BufferType.SPANNABLE);
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.setText(text, type);
    }

    @Override
    public void append(CharSequence text, int start, int end) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.append(te


public void setUseSystemDefault(boolean useSystemDefault) {
        mUseSystemDefault = useSystemDefault;
    }

package com.chen.fakevibrato.widget.emojiview;

import android.content.Context;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * emoji 可以显示emoji的textview
 * @author Created by CHEN on 2019/7/5
 * @email example@nnthink.com
 */
public class EmojiconTextView extends TextView {

    private int mEmojiconSize;

    private int mEmojiconTextSize;

    private int mTextStart = 0;

    private int mTextLength = -1;

    private boolean mUseSystemDefault = false;

    public EmojiconTextView(Context context) {
        super(context);
        init(null);
    }

    public EmojiconTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public EmojiconTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        mEmojiconTextSize = (int) getTextSize();
        mEmojiconSize = (int) getTextSize();
        setText(getText());
    }

    public void setTextWithWidth(CharSequence text, int limitedWidth) {
        if (TextUtils.isEmpty(text)) {
            super.setText(text);
            return;
        }
        if (limitedWidth < 0) {
            limitedWidth = this.getMeasuredWidth() - getPaddingRight() - getPaddingLeft();
        }
        SpannableStringBuilder builder = new SpannableStringBuilder(text);
        EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
        CharSequence trucatedText = TextUtils.ellipsize(builder, getPaint(), limitedWidth, getEllipsize());
        super.setText(trucatedText, BufferType.SPANNABLE);
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.setText(text, type);
    }

    @Override
    public void append(CharSequence text, int start, int end) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.append(text, start, end);
    }

    /**
     * Set the size of emojicon in pixels.
     */
    public void setEmojiconSize(int pixels) {
        mEmojiconSize = pixels;
        super.setText(getText());
    }

    /**
     * Set whether to use system default emojicon
     */


if (TextUtils.isEmpty(text)) {
            super.setText(text);
            return;
        }

package com.chen.fakevibrato.widget.emojiview;

import android.content.Context;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.widget.TextView;

/**
 * emoji 可以显示emoji的textview
 * @author Created by CHEN on 2019/7/5
 * @email example@nnthink.com
 */
public class EmojiconTextView extends TextView {

    private int mEmojiconSize;

    private int mEmojiconTextSize;

    private int mTextStart = 0;

    private int mTextLength = -1;

    private boolean mUseSystemDefault = false;

    public EmojiconTextView(Context context) {
        super(context);
        init(null);
    }

    public EmojiconTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public EmojiconTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs);
    }

    private void init(AttributeSet attrs) {
        mEmojiconTextSize = (int) getTextSize();
        mEmojiconSize = (int) getTextSize();
        setText(getText());
    }

    public void setTextWithWidth(CharSequence text, int limitedWidth) {

        if (limitedWidth < 0) {
            limitedWidth = this.getMeasuredWidth() - getPaddingRight() - getPaddingLeft();
        }
        SpannableStringBuilder builder = new SpannableStringBuilder(text);
        EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
        CharSequence trucatedText = TextUtils.ellipsize(builder, getPaint(), limitedWidth, getEllipsize());
        super.setText(trucatedText, BufferType.SPANNABLE);
    }

    @Override
    public void setText(CharSequence text, BufferType type) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.setText(text, type);
    }

    @Override
    public void append(CharSequence text, int start, int end) {
        if (!TextUtils.isEmpty(text)) {
            SpannableStringBuilder builder = new SpannableStringBuilder(text);
            EmojiconHandler.addEmojis(getContext(), builder, mEmojiconSize, mEmojiconTextSize, mTextStart, mTextLength, mUseSystemDefault);
            text = builder;
        }
        super.append(text, start, end);
    }

    /**
     * Set the size of emojicon in pixels.
     */
    public void setEmojiconSize(int pixels) {
        mEmojiconSize = pixels;
        super.setText(getText());
    }

    /**
     * Set whether to use system default emojicon
     */
    public void setUseSystemDefault(boolean useSystemDefault) {
        mUseSystemDefault = useSystemDefault;
    }
}


package org.seaborne.delta.link;

import java.util.concurrent.atomic.LongAdder;
import org.seaborne.delta.Id;
import org.seaborne.delta.Version;
import org.seaborne.patch.RDFPatch;

/**
 * {@link DeltaLink} listener that counts operations.
 */
public class DeltaLinkCounter implements DeltaLinkListener {

    private LongAdder cNew = new LongAdder();

    private LongAdder cCopied = new LongAdder();

    private LongAdder cRename = new LongAdder();

    private LongAdder cRemove = new LongAdder();

    private LongAdder cFetch = new LongAdder();

    private LongAdder cAppend = new LongAdder();

{
        return cNew.longValue();
    }

    public long countCopied() {
        return cCopied.longValue();
    }

    public long countRenamed() {
        return cRename.longValue();
    }

    public long countRemoved() {
        return cRemove.longValue();
    }

    public long countFetch() {
        return cFetch.longValue();
    }

    public long countAppend() {
        return cAppend.longValue();
    }

    @Override
    public void newDataSource(Id dsRef, String name) {
        cNew.increment();
    }

    @Override
    public void copyDataSource(Id dsRef, Id dsRef2, String oldName, String newName) {
        cCopied.increment();
    }

    @Override
    public void renameDataSource(Id dsRef, Id dsRef2, String oldName, String newName) {
        cRename.increment();
    }

    @Override
    public void removeDataSource(Id dsRef) {
        cRemove.increment();
    }

    @Override
    public void fetchById(Id dsRef, Id patchId, RDFPatch patch) {
        cFetch.increment();
    }

    @Override
    public void fetchByVersion(Id dsRef, Version version, RDFPatch patch) {
        cFetch.increment();
    }

    @Override
    public void append(Id dsRef, Version version, RDFPatch patch) {
        cAppend.increment();
    }
}


package org.seaborne.delta.link;

import java.util.concurrent.atomic.LongAdder;
import org.seaborne.delta.Id;
import org.seaborne.delta.Version;
import org.seaborne.patch.RDFPatch;

/**
 * {@link DeltaLink} listener that counts operations.
 */
public class DeltaLinkCounter implements DeltaLinkListener {

    private LongAdder cNew = new LongAdder();

    private LongAdder cCopied = new LongAdder();

    private LongAdder cRename = new LongAdder();

    private LongAdder cRemove = new LongAdder();

    private LongAdder cFetch = new LongAdder();

    private LongAdder cAppend = new LongAdder();

    public long countCreated() {
        return cNew.longValue();
    }

    public long countCopied() {
        return cCopied.longValue();
    }

    public long countRenamed() {
        return cRename.longValue();
    }

    public long countRemoved() {
        return cRemove.longValue();
    }

    public long countFetch() {
        return cFetch.longValue();
    }

    public long countAppend() {
        return cAppend.longValue();
    }

    @Override
    public void newDataSource(Id dsRef, String name) {
        cNew.increment();
    }

    @Override
    public void copyDataSource(Id dsRef, Id dsRef2, String oldName, String newName) {
        cCopied.increment();
    }

    @Override
    public void renameDataSource(Id dsRef, Id dsRef2, String oldName, String newName) {
        cRename.increment();
    }

    @Override
    public void removeDataSource(Id dsRef) {
        cRemove.increment();
    }

    @Override
    public void fetchById(Id dsRef, Id patchId, RDFPatch patch) {
        cFetch.increment();
    }

    @Override
    public void fetchByVersion(Id dsRef, Version version, RDFPatch patch)

    @Override
    public void append(Id dsRef, Version version, RDFPatch patch) {
        cAppend.increment();
    }
}


package org.seaborne.delta.link;

import java.util.concurrent.atomic.LongAdder;
import org.seaborne.delta.Id;
import org.seaborne.delta.Version;
import org.seaborne.patch.RDFPatch;

/**
 * {@link DeltaLink} listener that counts operations.
 */
public class DeltaLinkCounter implements DeltaLinkListener {

    private LongAdder cNew = new LongAdder();

    private LongAdder cCopied = new LongAdder();

    private LongAdder cRename = new LongAdder();

    private LongAdder cRemove = new LongAdder();

    private LongAdder cFetch = new LongAdder();

    private LongAdder cAppend = new LongAdder();

    public long countCreated() {
        return cNew.longValue();
    }

    public long countCopied() {
        return cCopied.longValue();
    }

    public long countRenamed() {
        return cRename.longValue();
    }

    public long countRemoved() {
        return cRemove.longValue();
    }

    public long countFetch() {
        return cFetch.longValue();
    }

    public long countAppend() {
        return cAppend.longValue();
    }

    @Override
    public void newDataSource(Id dsRef, String name) {
        cNew.increment();
    }

    @Override
    public void copyDataSource(Id dsRef, Id dsRef2, String oldName, String newName) {
        cCopied.increment();
    }

    @Override
    public void renameDataSource(Id dsRef, Id dsRef2, String oldName, String newName) {
        cRename.increment();
    }

    @Override
    public void removeDataSource(Id dsRef) {
        cRemove.increment();
    }

    @Override
    public void fetchById(Id dsRef, Id patchId, RDFPatch patch) {
        cFetch.increment();
    }

    @Override
    public void fetchByVersion(Id dsRef, Version version, RDFPatch patch) {
        cFetch.increment();
    }

    @Override
    public void append(Id dsRef, Version version, RDFPatch patch) {
        cAppend.increment()


@Override
        public void skipTo(long pos)

package net.amygdalum.stringsearchalgorithms.search.chars;

import static java.lang.Math.max;
import static java.util.Arrays.fill;
import net.amygdalum.stringsearchalgorithms.search.AbstractStringFinder;
import net.amygdalum.stringsearchalgorithms.search.StringFinder;
import net.amygdalum.stringsearchalgorithms.search.StringFinderOption;
import net.amygdalum.stringsearchalgorithms.search.StringMatch;
import net.amygdalum.util.io.CharProvider;
import net.amygdalum.util.map.CharLongMap;
import net.amygdalum.util.map.CharObjectMap;
import net.amygdalum.util.text.CharAlphabet;
import net.amygdalum.util.text.CharMapping;

/**
 * An implementation of the String Search Algorithm Shift-Or (or Baeza-Yates–Gonnet).
 *
 * This algorithm takes a single pattern as input and generates a finder which can find this pattern in documents
 */
public class ShiftOr implements StringSearchAlgorithm {

    private int patternLength;

    private BitMapStates states;

    public ShiftOr(String pattern) {
        this(pattern, CharMapping.IDENTITY);
    }

    public ShiftOr(String pattern, CharMapping mapping) {
        this.patternLength = pattern.length();
        this.states = computeStates(pattern.toCharArray(), mapping);
    }

    private static BitMapStates computeStates(char[] pattern, CharMapping mapping) {
        CharAlphabet alphabet = CharAlphabet.ranged(pattern, mapping);
        int compactSize = max(256, pattern.length * 2);
        if (alphabet.getRange() < compactSize) {
            if (pattern.length > 64) {
                return new QuickMultiLongStates(pattern, alphabet, mapping);
            } else {
                return new QuickSingleLongStates(pattern, alphabet, mapping);
            }
        } else {
            if (pattern.length > 64) {
                return new SmartMultiLongStates(pattern, mapping);
            } else {
                return new SmartSingleLongStates(pattern, mapping);
            }
        }
    }

    @Override
    public int getPatternLength() {
        return patternLength;
    }

    @Override
    public StringFinder createFinder(CharProvider chars, StringFinderOption... options) {
        if (states.supportsSingle()) {
            return new LongFinder(chars, options);
        } else {
            return new MultiLongFinder(chars, options);
        }
    }

    @Override
    public String toString() {
        return getClass().getSimpleName();
    }

    private abstract class Finder extends AbstractStringFinder {

        protected final long finalstate;

        protected CharProvider chars;

        public Finder(CharProvider chars, StringFinderOption... options) {
            super(options);
            this.finalstate = ~(1l << ((patternLength - 1) % 64));
            this.chars = chars;
        }

        protected StringMatch createMatch() {
            long end = chars.current();
            long start = end - patternLength;
            String s = chars.slice(start, end);
            return new StringMatch(start, end, s);
        }
    }

    private class LongFinder extends Finder {

        private long state;

        public LongFinder(CharProvider chars, StringFinderOption... options) {
            super(chars, options);
            this.state = BitMapStates.ALLBITS;
        }

{
            if (pos > chars.current()) {
                chars.move(pos);
            }
            state = BitMapStates.ALLBITS;
        }

        @Override
        public StringMatch findNext() {
            while (!chars.finished()) {
                char nextChar = chars.next();
                long bits = states.single(nextChar);
                state = (state << 1) | bits;
                if ((state | finalstate) != BitMapStates.ALLBITS) {
                    return createMatch();
                }
            }
            return null;
        }
    }

    private class MultiLongFinder extends Finder {

        private long[] state;

        public MultiLongFinder(CharProvider chars, StringFinderOption... options) {
            super(chars, options);
            this.state = new long[((patternLength - 1) / 64) + 1];
            fill(state, BitMapStates.ALLBITS);
        }

        @Override
        public void skipTo(long pos) {
            if (pos > chars.current()) {
                chars.move(pos);
            }
            fill(state, BitMapStates.ALLBITS);
        }

        @Override
        public StringMatch findNext() {
            while (!chars.finished()) {
                char nextChar = chars.next();
                long[] bits = states.all(nextChar);
                state = next(state, bits);
                if ((state[0] | finalstate) != BitMapStates.ALLBITS) {
                    return createMatch();
                }
            }
            return null;
        }

        private long[] next(long[] state, long[] bits) {
            for (int i = 0; i < state.length; i++) {
                int j = i + 1;
                long leastBit = j < state.length ? state[j] >>> 63 : 0l;
                state[i] = (state[i] << 1 | leastBit) | bits[i];
            }
            return state;
        }
    }

    public static class Factory implements StringSearchAlgorithmFactory, SupportsCharClasses {

        private CharMapping mapping;

        @Override
        public void enableCharClasses(CharMapping mapping) {
            this.mapping = mapping;
        }

        @Override
        public StringSearchAlgorithm of(String pattern) {
            if (mapping == null) {
                return new ShiftOr(pattern);
            } else {
                return new ShiftOr(pattern, mapping);
            }
        }
    }

    public interface BitMapStates {

        public static final long ALLBITS = ~0l;

        boolean supportsSingle();

        long single(char c);

        long[] all(char c);
    }

    private abstract static class SingleLongBitMapStates implements BitMapStates {

        @Override
        public boolean supportsSingle() {
            return true;
        }

        @Override
        public long[] all(char c) {
            return new long[] { single(c) };
        }
    }

    private static class QuickSingleLongStates extends SingleLongBitMapStates {

        private char minChar;

        private char maxChar;

        private long[] characters;

        public QuickSingleLongStates(char[] pattern, CharAlphabet alphabet, CharMapping mapping) {
            this.minChar = alphabet.minChar();
            this.maxChar = alphabet.maxChar();
            this.characters = computeStates(pattern, mapping, this.minChar, this.maxChar);
        }

        private static long[] computeStates(char[] pattern, CharMapping mapping, char min, char max) {
            long[] characters = new long[max - min + 1];
            fill(characters, ALLBITS);
            for (int i = 0; i < pattern.length; i++) {
                for (char c : mapping.map(pattern[i])) {
                    characters[c - min] &= ~(1l << i);
                }
            }
            return characters;
        }

        @Override
        public long single(char c) {
            if (c < minChar || c > maxChar) {
                return ALLBITS;
            }
            return characters[c - minChar];
        }
    }

    private static class SmartSingleLongStates extends SingleLongBitMapStates {

        private CharLongMap states;

        public SmartSingleLongStates(char[] pattern, CharMapping mapping) {
            this.states = computeStates(pattern, mapping);
        }

        private static CharLongMap computeStates(char[] pattern, CharMapping mapping) {
            CharLongMap map = new CharLongMap(ALLBITS);
            for (int i = 0; i < pattern.length; i++) {
                for (char c : mapping.map(pattern[i])) {
                    long newState = map.get(c) & ~(1l << i);
                    map.put(c, newState);
                }
            }
            return map;
        }

        @Override
        public long single(char c) {
            return states.get(c);
        }
    }

    private abstract static class MultiLongBitMapStates implements BitMapStates {

        public static long[] computeZero(int length) {
            long[] zero = new long[((length - 1) / 64) + 1];
            fill(zero, ALLBITS);
            return zero;
        }

        @Override
        public boolean supportsSingle() {
            return false;
        }

        @Override
        public long single(char c) {
            throw new UnsupportedOperationException();
        }
    }

    private static class QuickMultiLongStates extends MultiLongBitMapStates {

        private char minChar;

        private char maxChar;

        private long[][] characters;

        private long[] zero;

        public QuickMultiLongStates(char[] pattern, CharAlphabet alphabet, CharMapping mapping) {
            this.minChar = alphabet.minChar();
            this.maxChar = alphabet.maxChar();
            this.characters = computeStates(pattern, mapping, this.minChar, this.maxChar);
            this.zero = computeZero(pattern.length);
        }

        private static long[][] computeStates(char[] pattern, CharMapping mapping, char min, char max) {
            long[][] characters = new long[max - min + 1][];
            for (int c = min; c <= max; c++) {
                characters[c - min] = computeZero(pattern.length);
            }
            for (int i = 0; i < pattern.length; i++) {
                int slot = ((pattern.length - 1) / 64) - i / 64;
                int offset = i % 64;
                for (char c : mapping.map(pattern[i])) {
                    characters[c - min][slot] &= ~(1l << offset);
                }
            }
            return characters;
        }

        @Override
        public long[] all(char c) {
            if (c < minChar || c > maxChar) {
                return zero;
            }
            return characters[c - minChar];
        }
    }

    private static class SmartMultiLongStates extends MultiLongBitMapStates {

        private CharObjectMap<long[]> states;

        public SmartMultiLongStates(char[] pattern, CharMapping mapping) {
            this.states = computeStates(pattern, mapping);
        }

        private static CharObjectMap<long[]> computeStates(char[] pattern, CharMapping mapping) {
            long[] zero = computeZero(pattern.length);
            CharObjectMap<long[]> map = new CharObjectMap<>(zero);
            for (int i = 0; i < pattern.length; i++) {
                int slot = ((pattern.length - 1) / 64) - i / 64;
                int offset = i % 64;
                for (char c : mapping.map(pattern[i])) {
                    long[] newState = map.get(c);
                    if (newState == zero) {
                        newState = computeZero(pattern.length);
                    }
                    newState[slot] &= ~(1l << offset);
                    map.put(c, newState);
                }
            }
            return map;
        }

        @Override
        public long[] all(char c) {
            return states.get(c);
        }
    }
}


{
            return new long[] { single(c) };
        }

package net.amygdalum.stringsearchalgorithms.search.chars;

import static java.lang.Math.max;
import static java.util.Arrays.fill;
import net.amygdalum.stringsearchalgorithms.search.AbstractStringFinder;
import net.amygdalum.stringsearchalgorithms.search.StringFinder;
import net.amygdalum.stringsearchalgorithms.search.StringFinderOption;
import net.amygdalum.stringsearchalgorithms.search.StringMatch;
import net.amygdalum.util.io.CharProvider;
import net.amygdalum.util.map.CharLongMap;
import net.amygdalum.util.map.CharObjectMap;
import net.amygdalum.util.text.CharAlphabet;
import net.amygdalum.util.text.CharMapping;

/**
 * An implementation of the String Search Algorithm Shift-Or (or Baeza-Yates–Gonnet).
 *
 * This algorithm takes a single pattern as input and generates a finder which can find this pattern in documents
 */
public class ShiftOr implements StringSearchAlgorithm {

    private int patternLength;

    private BitMapStates states;

    public ShiftOr(String pattern) {
        this(pattern, CharMapping.IDENTITY);
    }

    public ShiftOr(String pattern, CharMapping mapping) {
        this.patternLength = pattern.length();
        this.states = computeStates(pattern.toCharArray(), mapping);
    }

    private static BitMapStates computeStates(char[] pattern, CharMapping mapping) {
        CharAlphabet alphabet = CharAlphabet.ranged(pattern, mapping);
        int compactSize = max(256, pattern.length * 2);
        if (alphabet.getRange() < compactSize) {
            if (pattern.length > 64) {
                return new QuickMultiLongStates(pattern, alphabet, mapping);
            } else {
                return new QuickSingleLongStates(pattern, alphabet, mapping);
            }
        } else {
            if (pattern.length > 64) {
                return new SmartMultiLongStates(pattern, mapping);
            } else {
                return new SmartSingleLongStates(pattern, mapping);
            }
        }
    }

    @Override
    public int getPatternLength() {
        return patternLength;
    }

    @Override
    public StringFinder createFinder(CharProvider chars, StringFinderOption... options) {
        if (states.supportsSingle()) {
            return new LongFinder(chars, options);
        } else {
            return new MultiLongFinder(chars, options);
        }
    }

    @Override
    public String toString() {
        return getClass().getSimpleName();
    }

    private abstract class Finder extends AbstractStringFinder {

        protected final long finalstate;

        protected CharProvider chars;

        public Finder(CharProvider chars, StringFinderOption... options) {
            super(options);
            this.finalstate = ~(1l << ((patternLength - 1) % 64));
            this.chars = chars;
        }

        protected StringMatch createMatch() {
            long end = chars.current();
            long start = end - patternLength;
            String s = chars.slice(start, end);
            return new StringMatch(start, end, s);
        }
    }

    private class LongFinder extends Finder {

        private long state;

        public LongFinder(CharProvider chars, StringFinderOption... options) {
            super(chars, options);
            this.state = BitMapStates.ALLBITS;
        }

        @Override
        public void skipTo(long pos) {
            if (pos > chars.current()) {
                chars.move(pos);
            }
            state = BitMapStates.ALLBITS;
        }

        @Override
        public StringMatch findNext() {
            while (!chars.finished()) {
                char nextChar = chars.next();
                long bits = states.single(nextChar);
                state = (state << 1) | bits;
                if ((state | finalstate) != BitMapStates.ALLBITS) {
                    return createMatch();
                }
            }
            return null;
        }
    }

    private class MultiLongFinder extends Finder {

        private long[] state;

        public MultiLongFinder(CharProvider chars, StringFinderOption... options) {
            super(chars, options);
            this.state = new long[((patternLength - 1) / 64) + 1];
            fill(state, BitMapStates.ALLBITS);
        }

        @Override
        public void skipTo(long pos) {
            if (pos > chars.current()) {
                chars.move(pos);
            }
            fill(state, BitMapStates.ALLBITS);
        }

        @Override
        public StringMatch findNext() {
            while (!chars.finished()) {
                char nextChar = chars.next();
                long[] bits = states.all(nextChar);
                state = next(state, bits);
                if ((state[0] | finalstate) != BitMapStates.ALLBITS) {
                    return createMatch();
                }
            }
            return null;
        }

        private long[] next(long[] state, long[] bits) {
            for (int i = 0; i < state.length; i++) {
                int j = i + 1;
                long leastBit = j < state.length ? state[j] >>> 63 : 0l;
                state[i] = (state[i] << 1 | leastBit) | bits[i];
            }
            return state;
        }
    }

    public static class Factory implements StringSearchAlgorithmFactory, SupportsCharClasses {

        private CharMapping mapping;

        @Override
        public void enableCharClasses(CharMapping mapping) {
            this.mapping = mapping;
        }

        @Override
        public StringSearchAlgorithm of(String pattern) {
            if (mapping == null) {
                return new ShiftOr(pattern);
            } else {
                return new ShiftOr(pattern, mapping);
            }
        }
    }

    public interface BitMapStates {

        public static final long ALLBITS = ~0l;

        boolean supportsSingle();

        long single(char c);

        long[] all(char c);
    }

    private abstract static class SingleLongBitMapStates implements BitMapStates {

        @Override
        public boolean supportsSingle() {
            return true;
        }

        @Override
        public long[] all(char c)

    }

    private static class QuickSingleLongStates extends SingleLongBitMapStates {

        private char minChar;

        private char maxChar;

        private long[] characters;

        public QuickSingleLongStates(char[] pattern, CharAlphabet alphabet, CharMapping mapping) {
            this.minChar = alphabet.minChar();
            this.maxChar = alphabet.maxChar();
            this.characters = computeStates(pattern, mapping, this.minChar, this.maxChar);
        }

        private static long[] computeStates(char[] pattern, CharMapping mapping, char min, char max) {
            long[] characters = new long[max - min + 1];
            fill(characters, ALLBITS);
            for (int i = 0; i < pattern.length; i++) {
                for (char c : mapping.map(pattern[i])) {
                    characters[c - min] &= ~(1l << i);
                }
            }
            return characters;
        }

        @Override
        public long single(char c) {
            if (c < minChar || c > maxChar) {
                return ALLBITS;
            }
            return characters[c - minChar];
        }
    }

    private static class SmartSingleLongStates extends SingleLongBitMapStates {

        private CharLongMap states;

        public SmartSingleLongStates(char[] pattern, CharMapping mapping) {
            this.states = computeStates(pattern, mapping);
        }

        private static CharLongMap computeStates(char[] pattern, CharMapping mapping) {
            CharLongMap map = new CharLongMap(ALLBITS);
            for (int i = 0; i < pattern.length; i++) {
                for (char c : mapping.map(pattern[i])) {
                    long newState = map.get(c) & ~(1l << i);
                    map.put(c, newState);
                }
            }
            return map;
        }

        @Override
        public long single(char c) {
            return states.get(c);
        }
    }

    private abstract static class MultiLongBitMapStates implements BitMapStates {

        public static long[] computeZero(int length) {
            long[] zero = new long[((length - 1) / 64) + 1];
            fill(zero, ALLBITS);
            return zero;
        }

        @Override
        public boolean supportsSingle() {
            return false;
        }

        @Override
        public long single(char c) {
            throw new UnsupportedOperationException();
        }
    }

    private static class QuickMultiLongStates extends MultiLongBitMapStates {

        private char minChar;

        private char maxChar;

        private long[][] characters;

        private long[] zero;

        public QuickMultiLongStates(char[] pattern, CharAlphabet alphabet, CharMapping mapping) {
            this.minChar = alphabet.minChar();
            this.maxChar = alphabet.maxChar();
            this.characters = computeStates(pattern, mapping, this.minChar, this.maxChar);
            this.zero = computeZero(pattern.length);
        }

        private static long[][] computeStates(char[] pattern, CharMapping mapping, char min, char max) {
            long[][] characters = new long[max - min + 1][];
            for (int c = min; c <= max; c++) {
                characters[c - min] = computeZero(pattern.length);
            }
            for (int i = 0; i < pattern.length; i++) {
                int slot = ((pattern.length - 1) / 64) - i / 64;
                int offset = i % 64;
                for (char c : mapping.map(pattern[i])) {
                    characters[c - min][slot] &= ~(1l << offset);
                }
            }
            return characters;
        }

        @Override
        public long[] all(char c) {
            if (c < minChar || c > maxChar) {
                return zero;
            }
            return characters[c - minChar];
        }
    }

    private static class SmartMultiLongStates extends MultiLongBitMapStates {

        private CharObjectMap<long[]> states;

        public SmartMultiLongStates(char[] pattern, CharMapping mapping) {
            this.states = computeStates(pattern, mapping);
        }

        private static CharObjectMap<long[]> computeStates(char[] pattern, CharMapping mapping) {
            long[] zero = computeZero(pattern.length);
            CharObjectMap<long[]> map = new CharObjectMap<>(zero);
            for (int i = 0; i < pattern.length; i++) {
                int slot = ((pattern.length - 1) / 64) - i / 64;
                int offset = i % 64;
                for (char c : mapping.map(pattern[i])) {
                    long[] newState = map.get(c);
                    if (newState == zero) {
                        newState = computeZero(pattern.length);
                    }
                    newState[slot] &= ~(1l << offset);
                    map.put(c, newState);
                }
            }
            return map;
        }

        @Override
        public long[] all(char c) {
            return states.get(c);
        }
    }
}