text:
package com.chaos.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.ColorStateList;
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.Path;
import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.text.InputFilter;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.MovementMethod;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ActionMode;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.view.inputmethod.EditorInfo;

import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.Nullable;
import androidx.annotation.Px;
import androidx.appcompat.widget.AppCompatEditText;
import androidx.core.content.res.ResourcesCompat;
import androidx.core.view.ViewCompat;

/**
 * Provides a widget for enter PIN/OTP/password etc.
 *
 * @author Chaos Leong
 * 01/04/2017
 */
public class PinView extends AppCompatEditText {

    private static final String TAG = "PinView";

    private static final boolean DBG = false;

    private static final int BLINK = 500;

    private static final int DEFAULT_COUNT = 4;

    private static final InputFilter[] NO_FILTERS = new InputFilter[0];

    private static final int[] HIGHLIGHT_STATES = new int[]{
            android.R.attr.state_selected};

    private static final int VIEW_TYPE_RECTANGLE = 0;
    private static final int VIEW_TYPE_LINE = 1;
    private static final int VIEW_TYPE_NONE = 2;

    private int mViewType;

    private int mPinItemCount;

    private int mPinItemWidth;
    private int mPinItemHeight;
    private int mPinItemRadius;
    private int mPinItemSpacing;

    private final Paint mPaint;
    private final TextPaint mAnimatorTextPaint = new TextPaint();

    private ColorStateList mLineColor;
    private int mCurLineColor = Color.BLACK;
    private int mLineWidth;

    private final Rect mTextRect = new Rect();
    private final RectF mItemBorderRect = new RectF();
    private final RectF mItemLineRect = new RectF();
    private final Path mPath = new Path();
    private final PointF mItemCenterPoint = new PointF();

    private ValueAnimator mDefaultAddAnimator;
    private boolean isAnimationEnable = false;

    private Blink mBlink;
    private boolean isCursorVisible;
    private boolean drawCursor;
    private float mCursorHeight;
    private int mCursorWidth;
    private int mCursorColor;

    private int mItemBackgroundResource;
    private Drawable mItemBackground;

    private boolean mHideLineWhenFilled;

    public PinView(Context context) {
        this(context, null);
    }

    public PinView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, R.attr.pinViewStyle);
    }

    public PinView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        final Resources res = getResources();

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);

        mAnimatorTextPaint.set(getPaint());

        final Resources.Theme theme = context.getTheme();

        TypedArray a = theme.obtainStyledAttributes(attrs, R.styleable.PinView, defStyleAttr, 0);

        mViewType = a.getInt(R.styleable.PinView_viewType, VIEW_TYPE_RECTANGLE);
        mPinItemCount = a.getInt(R.styleable.PinView_itemCount, DEFAULT_COUNT);
        mPinItemHeight = (int) a.getDimension(R.styleable.PinView_itemHeight,
                res.getDimensionPixelSize(R.dimen.pv_pin_view_item_size));
        mPinItemWidth = (int) a.getDimension(R.styleable.PinView_itemWidth,
                res.getDimensionPixelSize(R.dimen.pv_pin_view_item_size));
        mPinItemSpacing = a.getDimensionPixelSize(R.styleable.PinView_itemSpacing,
                res.getDimensionPixelSize(R.dimen.pv_pin_view_item_spacing));
        mPinItemRadius = (int) a.getDimension(R.styleable.PinView_itemRadius, 0);
        mLineWidth = (int) a.getDimension(R.styleable.PinView_lineWidth,
                res.getDimensionPixelSize(R.dimen.pv_pin_view_item_line_width));
        mLineColor = a.getColorStateList(R.styleable.PinView_lineColor);
        isCursorVisible = a.getBoolean(R.styleable.PinView_android_cursorVisible, true);
        mCursorColor = a.getColor(R.styleable.PinView_cursorColor, getCurrentTextColor());
        mCursorWidth = a.getDimensionPixelSize(R.styleable.PinView_cursorWidth,
                res.getDimensionPixelSize(R.dimen.pv_pin_view_cursor_width));

        mItemBackground = a.getDrawable(R.styleable.PinView_android_itemBackground);
        mHideLineWhenFilled = a.getBoolean(R.styleable.PinView_hideLineWhenFilled, false);

        a.recycle();

        if (mLineColor != null) {
            mCurLineColor = mLineColor.getDefaultColor();
        }
        updateCursorHeight();

        checkItemRadius();

        setMaxLength(mPinItemCount);
        mPaint.setStrokeWidth(mLineWidth);
        setupAnimator();

        super.setCursorVisible(false);
        disableSelectionMenu();
    }

    @Override
    public void setTypeface(Typeface tf, int style) {
        super.setTypeface(tf, style);
    }

    @Override
    public void setTypeface(Typeface tf) {
        super.setTypeface(tf);
        if (mAnimatorTextPaint != null) {
            mAnimatorTextPaint.set(getPaint());
        }
    }

    private void setMaxLength(int maxLength) {
        if (maxLength >= 0) {
            setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
        } else {
            setFilters(NO_FILTERS);
        }
    }

    private void setupAnimator() {
        mDefaultAddAnimator = ValueAnimator.ofFloat(0.5f, 1f);
        mDefaultAddAnimator.setDuration(150);
        mDefaultAddAnimator.setInterpolator(new DecelerateInterpolator());
        mDefaultAddAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float scale = (float) animation.getAnimatedValue();
                int alpha = (int) (255 * scale);
                mAnimatorTextPaint.setTextSize(getTextSize() * scale);
                mAnimatorTextPaint.setAlpha(alpha);
                postInvalidate();
            }
        });
    }

    private void checkItemRadius() {
        if (mViewType == VIEW_TYPE_LINE) {
            float halfOfLineWidth = ((float) mLineWidth) / 2;
            if (mPinItemRadius > halfOfLineWidth) {
                throw new IllegalArgumentException("The itemRadius can not be greater than lineWidth when viewType is line");
            }
        } else if (mViewType == VIEW_TYPE_RECTANGLE) {
            float halfOfItemWidth = ((float) mPinItemWidth) / 2;
            if (mPinItemRadius > halfOfItemWidth) {
                throw new IllegalArgumentException("The itemRadius can not be greater than itemWidth");
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        int width;
        int height;

        int boxHeight = mPinItemHeight;

        if (widthMode == MeasureSpec.EXACTLY) {
            // Parent has told us how big to be. So be it.
            width = widthSize;
        } else {
            int boxesWidth = (mPinItemCount - 1) * mPinItemSpacing + mPinItemCount * mPinItemWidth;
            width = boxesWidth + ViewCompat.getPaddingEnd(this) + ViewCompat.getPaddingStart(this);
            if (mPinItemSpacing == 0) {
                width -= (mPinItemCount - 1) * mLineWidth;
            }
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            // Parent has told us how big to be. So be it.
            height = heightSize;
        } else {
            height = boxHeight + getPaddingTop() + getPaddingBottom();
        }

        setMeasuredDimension(width, height);
    }

    @Override
    protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
        if (start != text.length()) {
            moveSelectionToEnd();
        }

        makeBlink();

        if (isAnimationEnable) {
            final boolean isAdd = lengthAfter - lengthBefore > 0;
            if (isAdd) {
                if (mDefaultAddAnimator != null) {
                    mDefaultAddAnimator.end();
                    mDefaultAddAnimator.start();
                }
            }
        }
    }

    @Override
    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect);

        if (focused) {
            moveSelectionToEnd();
            makeBlink();
        }
    }

    @Override
    protected void onSelectionChanged(int selStart, int selEnd) {
        super.onSelectionChanged(selStart, selEnd);

        if (selEnd != getText().length()) {
            moveSelectionToEnd();
        }
    }

    private void moveSelectionToEnd() {
        setSelection(getText().length());
    }

    @Override
    protected void drawableStateChanged() {
        super.drawableStateChanged();

        if (mLineColor == null || mLineColor.isStateful()) {
            updateColors();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.save();

        updatePaints();
        drawPinView(canvas);

        canvas.restore();
    }

    private void updatePaints() {
        mPaint.setColor(mCurLineColor);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(mLineWidth);
        getPaint().setColor(getCurrentTextColor());
    }

    private void drawPinView(Canvas canvas) {
        int highlightIdx = getText().length();
        for (int i = 0; i < mPinItemCount; i++) {
            boolean highlight = isFocused() && highlightIdx == i;
            mPaint.setColor(highlight ? getLineColorForState(HIGHLIGHT_STATES) : mCurLineColor);

            updateItemRectF(i);
            updateCenterPoint();

            canvas.save();
            if (mViewType == VIEW_TYPE_RECTANGLE) {
                updatePinBoxPath(i);
                canvas.clipPath(mPath);
            }
            drawItemBackground(canvas, highlight);
            canvas.restore();

            if (highlight) {
                drawCursor(canvas);
            }

            if (mViewType == VIEW_TYPE_RECTANGLE) {
                drawPinBox(canvas, i);
            } else if (mViewType == VIEW_TYPE_LINE) {
                drawPinLine(canvas, i);
            }

            if (DBG) {
                drawAnchorLine(canvas);
            }

            if (getText().length() > i) {
                if (isPasswordInputType(getInputType())) {
                    drawCircle(canvas, i);
                } else {
                    drawText(canvas, i);
                }
            } else if (!TextUtils.isEmpty(getHint()) && getHint().length() == mPinItemCount) {
                drawHint(canvas, i);
            }
        }

        // highlight the next item
        if (isFocused() && getText().length() != mPinItemCount && mViewType == VIEW_TYPE_RECTANGLE) {
            int index = getText().length();
            updateItemRectF(index);
            updateCenterPoint();
            updatePinBoxPath(index);
            mPaint.setColor(getLineColorForState(HIGHLIGHT_STATES));
            drawPinBox(canvas, index);
        }
    }

    private int getLineColorForState(int... states) {
        return mLineColor != null ? mLineColor.getColorForState(states, mCurLineColor) : mCurLineColor;
    }

    private void drawItemBackground(Canvas canvas, boolean highlight) {
        if (mItemBackground == null) {
            return;
        }
        float delta = (float) mLineWidth / 2;
        int left = Math.round(mItemBorderRect.left - delta);
        int top = Math.round(mItemBorderRect.top - delta);
        int right = Math.round(mItemBorderRect.right + delta);
        int bottom = Math.round(mItemBorderRect.bottom + delta);

        mItemBackground.setBounds(left, top, right, bottom);
        mItemBackground.setState(highlight ? HIGHLIGHT_STATES : getDrawableState());
        mItemBackground.draw(canvas);
    }

    private void updatePinBoxPath(int i) {
        boolean drawRightCorner = false;
        boolean drawLeftCorner = false;
        if (mPinItemSpacing != 0) {
            drawLeftCorner = drawRightCorner = true;
        } else {
            if (i == 0 && i != mPinItemCount - 1) {
                drawLeftCorner = true;
            }
            if (i == mPinItemCount - 1 && i != 0) {
                drawRightCorner = true;
            }
        }
        updateRoundRectPath(mItemBorderRect, mPinItemRadius, mPinItemRadius, drawLeftCorner, drawRightCorner);
    }

    private void drawPinBox(Canvas canvas, int i) {
        if (mHideLineWhenFilled && i < getText().length()) {
            return;
        }
        canvas.drawPath(mPath, mPaint);
    }

    private void drawPinLine(Canvas canvas, int i) {
        if (mHideLineWhenFilled && i < getText().length()) {
            return;
        }
        boolean l, r;
        l = r = true;
        if (mPinItemSpacing == 0 && mPinItemCount > 1) {
            if (i == 0) {
                // draw only left round
                r = false;
            } else if (i == mPinItemCount - 1) {
                // draw only right round
                l = false;
            } else {
                // draw rect
                l = r = false;
            }
        }
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setStrokeWidth(((float) mLineWidth) / 10);
        float halfLineWidth = ((float) mLineWidth) / 2;
        mItemLineRect.set(
                mItemBorderRect.left - halfLineWidth,
                mItemBorderRect.bottom - halfLineWidth,
                mItemBorderRect.right + halfLineWidth,
                mItemBorderRect.bottom + halfLineWidth);

        updateRoundRectPath(mItemLineRect, mPinItemRadius, mPinItemRadius, l, r);
        canvas.drawPath(mPath, mPaint);
    }

    private void drawCursor(Canvas canvas) {
        if (drawCursor) {
            float cx = mItemCenterPoint.x;
            float cy = mItemCenterPoint.y;
            float x = cx;
            float y = cy - mCursorHeight / 2;

            int color = mPaint.getColor();
            float width = mPaint.getStrokeWidth();
            mPaint.setColor(mCursorColor);
            mPaint.setStrokeWidth(mCursorWidth);

            canvas.drawLine(x, y, x, y + mCursorHeight, mPaint);

            mPaint.setColor(color);
            mPaint.setStrokeWidth(width);
        }
    }

    private void updateRoundRectPath(RectF rectF, float rx, float ry, boolean l, boolean r) {
        updateRoundRectPath(rectF, rx, ry, l, r, r, l);
    }

    private void updateRoundRectPath(RectF rectF, float rx, float ry,
                                     boolean tl, boolean tr, boolean br, boolean bl) {
        mPath.reset();

        float l = rectF.left;
        float t = rectF.top;
        float r = rectF.right;
        float b = rectF.bottom;

        float w = r - l;
        float h = b - t;

        float lw = w - 2 * rx;// line width
        float lh = h - 2 * ry;// line height

        mPath.moveTo(l, t + ry);

        if (tl) {
            mPath.rQuadTo(0, -ry, rx, -ry);// top-left corner
        } else {
            mPath.rLineTo(0, -ry);
            mPath.rLineTo(rx, 0);
        }

        mPath.rLineTo(lw, 0);

        if (tr) {
            mPath.rQuadTo(rx, 0, rx, ry);// top-right corner
        } else {
            mPath.rLineTo(rx, 0);
            mPath.rLineTo(0, ry);
        }

        mPath.rLineTo(0, lh);

        if (br) {
            mPath.rQuadTo(0, ry, -rx, ry);// bottom-right corner
        } else {
            mPath.rLineTo(0, ry);
            mPath.rLineTo(-rx, 0);
        }

        mPath.rLineTo(-lw, 0);

        if (bl) {
            mPath.rQuadTo(-rx, 0, -rx, -ry);// bottom-left corner
        } else {
            mPath.rLineTo(-rx, 0);
            mPath.rLineTo(0, -ry);
        }

        mPath.rLineTo(0, -lh);

        mPath.close();
    }

    private void updateItemRectF(int i) {
        float halfLineWidth = ((float) mLineWidth) / 2;
        float left = getScrollX() + ViewCompat.getPaddingStart(this) + i * (mPinItemSpacing + mPinItemWidth) + halfLineWidth;
        if (mPinItemSpacing == 0 && i > 0) {
            left = left - (mLineWidth) * i;
        }
        float right = left + mPinItemWidth - mLineWidth;
        float top = getScrollY() + getPaddingTop() + halfLineWidth;
        float bottom = top + mPinItemHeight - mLineWidth;

        mItemBorderRect.set(left, top, right, bottom);
    }

    private void drawText(Canvas canvas, int i) {
        Paint paint = getPaintByIndex(i);
        // 1, Rect(4, -39, 20, 0)
        // 您, Rect(2, -47, 51, 3)
        // *, Rect(0, -39, 23, -16)
        // =, Rect(4, -26, 26, -10)
        // -, Rect(1, -19, 14, -14)
        // +, Rect(2, -32, 29, -3)
        drawTextAtBox(canvas, paint, getText(), i);
    }

    private void drawHint(Canvas canvas, int i) {
        Paint paint = getPaintByIndex(i);
        paint.setColor(getCurrentHintTextColor());
        drawTextAtBox(canvas, paint, getHint(), i);
    }

    private void drawTextAtBox(Canvas canvas, Paint paint, CharSequence text, int charAt) {
        paint.getTextBounds(text.toString(), charAt, charAt + 1, mTextRect);
        float cx = mItemCenterPoint.x;
        float cy = mItemCenterPoint.y;
        float x = cx - Math.abs((float) mTextRect.width()) / 2 - mTextRect.left;
        float y = cy + Math.abs((float) mTextRect.height()) / 2 - mTextRect.bottom;// always center vertical
        canvas.drawText(text, charAt, charAt + 1, x, y, paint);
    }

    private void drawCircle(Canvas canvas, int i) {
        Paint paint = getPaintByIndex(i);
        float cx = mItemCenterPoint.x;
        float cy = mItemCenterPoint.y;
        canvas.drawCircle(cx, cy, paint.getTextSize() / 2, paint);
    }

    private Paint getPaintByIndex(int i) {
        if (isAnimationEnable && i == getText().length() - 1) {
            mAnimatorTextPaint.setColor(getPaint().getColor());
            return mAnimatorTextPaint;
        } else {
            return getPaint();
        }
    }

    /**
     * For seeing the font position
     */
    private void drawAnchorLine(Canvas canvas) {
        float cx = mItemCenterPoint.x;
        float cy = mItemCenterPoint.y;
        mPaint.setStrokeWidth(1);
        cx -= mPaint.getStrokeWidth() / 2;
        cy -= mPaint.getStrokeWidth() / 2;

        mPath.reset();
        mPath.moveTo(cx, mItemBorderRect.top);
        mPath.lineTo(cx, mItemBorderRect.top + Math.abs(mItemBorderRect.height()));
        canvas.drawPath(mPath, mPaint);

        mPath.reset();
        mPath.moveTo(mItemBorderRect.left, cy);
        mPath.lineTo(mItemBorderRect.left + Math.abs(mItemBorderRect.width()), cy);
        canvas.drawPath(mPath, mPaint);

        mPath.reset();

        mPaint.setStrokeWidth(mLineWidth);
    }

    private void updateColors() {
        boolean inval = false;

        int color;
        if (mLineColor != null) {
            color = mLineColor.getColorForState(getDrawableState(), 0);
        } else {
            color = getCurrentTextColor();
        }

        if (color != mCurLineColor) {
            mCurLineColor = color;
            inval = true;
        }

        if (inval) {
            invalidate();
        }
    }

    private void updateCenterPoint() {
        float cx = mItemBorderRect.left + Math.abs(mItemBorderRect.width()) / 2;
        float cy = mItemBorderRect.top + Math.abs(mItemBorderRect.height()) / 2;
        mItemCenterPoint.set(cx, cy);
    }

    private static boolean isPasswordInputType(int inputType) {
        final int variation =
                inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION);
        return variation
                == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)
                || variation
                == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD)
                || variation
                == (EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD);
    }

    /*@Override
    protected MovementMethod getDefaultMovementMethod() {
        return DefaultMovementMethod.getInstance();
    }*/

    /**
     * Sets the line color for all the states (normal, selected,
     * focused) to be this color.
     *
     * @param color A color value in the form 0xAARRGGBB.
     *              Do not pass a resource ID. To get a color value from a resource ID, call
     *              {@link androidx.core.content.ContextCompat#getColor(Context, int) getColor}.
     * @attr ref R.styleable#PinView_lineColor
     * @see #setLineColor(ColorStateList)
     * @see #getLineColors()
     */
    public void setLineColor(@ColorInt int color) {
        mLineColor = ColorStateList.valueOf(color);
        updateColors();
    }

    /**
     * Sets the line color.
     *
     * @attr ref R.styleable#PinView_lineColor
     * @see #setLineColor(int)
     * @see #getLineColors()
     */
    public void setLineColor(ColorStateList colors) {
        if (colors == null) {
            throw new NullPointerException();
        }

        mLineColor = colors;
        updateColors();
    }

    /**
     * Gets the line colors for the different states (normal, selected, focused) of the PinView.
     *
     * @attr ref R.styleable#PinView_lineColor
     * @see #setLineColor(ColorStateList)
     * @see #setLineColor(int)
     */
    public ColorStateList getLineColors() {
        return mLineColor;
    }

    /**
     * <p>Return the current color selected for normal line.</p>
     *
     * @return Returns the current item's line color.
     */
    @ColorInt
    public int getCurrentLineColor() {
        return mCurLineColor;
    }

    /**
     * Sets the line width.
     *
     * @attr ref R.styleable#PinView_lineWidth
     * @see #getLineWidth()
     */
    public void setLineWidth(@Px int borderWidth) {
        mLineWidth = borderWidth;
        checkItemRadius();
        requestLayout();
    }

    /**
     * @return Returns the width of the item's line.
     * @see #setLineWidth(int)
     */
    public int getLineWidth() {
        return mLineWidth;
    }

    /**
     * Sets the count of items.
     *
     * @attr ref R.styleable#PinView_itemCount
     * @see #getItemCount()
     */
    public void setItemCount(int count) {
        mPinItemCount = count;
        setMaxLength(count);
        requestLayout();
    }

    /**
     * @return Returns the count of items.
     * @see #setItemCount(int)
     */
    public int getItemCount() {
        return mPinItemCount;
    }

    /**
     * Sets the radius of square.
     *
     * @attr ref R.styleable#PinView_itemRadius
     * @see #getItemRadius()
     */
    public void setItemRadius(@Px int itemRadius) {
        mPinItemRadius = itemRadius;
        checkItemRadius();
        requestLayout();
    }

    /**
     * @return Returns the radius of square.
     * @see #setItemRadius(int)
     */
    public int getItemRadius() {
        return mPinItemRadius;
    }

    /**
     * Specifies extra space between two items.
     *
     * @attr ref R.styleable#PinView_itemSpacing
     * @see #getItemSpacing()
     */
    public void setItemSpacing(@Px int itemSpacing) {
        mPinItemSpacing = itemSpacing;
        requestLayout();
    }

    /**
     * @return Returns the spacing between two items.
     * @see #setItemSpacing(int)
     */
    @Px
    public int getItemSpacing() {
        return mPinItemSpacing;
    }

    /**
     * Sets the height of item.
     *
     * @attr ref R.styleable#PinView_itemHeight
     * @see #getItemHeight()
     */
    public void setItemHeight(@Px int itemHeight) {
        mPinItemHeight = itemHeight;
        updateCursorHeight();
        requestLayout();
    }

    /**
     * @return Returns the height of item.
     * @see #setItemHeight(int)
     */
    public int getItemHeight() {
        return mPinItemHeight;
    }

    /**
     * Sets the width of item.
     *
     * @attr ref R.styleable#PinView_itemWidth
     * @see #getItemWidth()
     */
    public void setItemWidth(@Px int itemWidth) {
        mPinItemWidth = itemWidth;
        checkItemRadius();
        requestLayout();
    }

    /**
     * @return Returns the width of item.
     * @see #setItemWidth(int)
     */
    public int getItemWidth() {
        return mPinItemWidth;
    }

    /**
     * Specifies whether the text animation should be enabled or disabled.
     * By the default, the animation is disabled.
     *
     * @param enable True to start animation when adding text, false to transition immediately
     */
    public void setAnimationEnable(boolean enable) {
        isAnimationEnable = enable;
    }

    /**
     * Specifies whether the line (border) should be hidden or visible when text entered.
     * By the default, this flag is false and the line is always drawn.
     *
     * @param hideLineWhenFilled true to hide line on a position where text entered,
     *                           false to always show line
     * @attr ref R.styleable#PinView_hideLineWhenFilled
     */
    public void setHideLineWhenFilled(boolean hideLineWhenFilled) {
        this.mHideLineWhenFilled = hideLineWhenFilled;
    }

    @Override
    public void setTextSize(float size) {
        super.setTextSize(size);
        updateCursorHeight();
    }

    @Override
    public void setTextSize(int unit, float size) {
        super.setTextSize(unit, size);
        updateCursorHeight();
    }

    //region ItemBackground
    /**
     * Set the item background to a given resource. The resource should refer to
     * a Drawable object or 0 to remove the item background.
     *
     * @param resId The identifier of the resource.
     * @attr ref R.styleable#PinView_android_itemBackground
     */
    public void setItemBackgroundResources(@DrawableRes int resId) {
        if (resId != 0 && mItemBackgroundResource != resId) {
            return;
        }
        mItemBackground = ResourcesCompat.getDrawable(getResources(), resId, getContext().getTheme());
        setItemBackground(mItemBackground);
        mItemBackgroundResource = resId;
    }

    /**
     * Sets the item background color for this view.
     *
     * @param color the color of the item background
     */
    public void setItemBackgroundColor(@ColorInt int color) {
        if (mItemBackground instanceof ColorDrawable) {
            ((ColorDrawable) mItemBackground.mutate()).setColor(color);
            mItemBackgroundResource = 0;
        } else {
            setItemBackground(new ColorDrawable(color));
        }
    }

    /**
     * Set the item background to a given Drawable, or remove the background.
     *
     * @param background The Drawable to use as the item background, or null to remove the
     *                   item background
     */
    public void setItemBackground(Drawable background) {
        mItemBackgroundResource = 0;
        mItemBackground = background;
        invalidate();
    }
    //endregion

    //region Cursor

    /**
     * Sets the width (in pixels) of cursor.
     *
     * @attr ref R.styleable#PinView_cursorWidth
     * @see #getCursorWidth()
     */
    public void setCursorWidth(@Px int width) {
        mCursorWidth = width;
        if (isCursorVisible()) {
            invalidateCursor(true);
        }
    }

    /**
     * @return Returns the width (in pixels) of cursor.
     * @see #setCursorWidth(int)
     */
    public int getCursorWidth() {
        return mCursorWidth;
    }

    /**
     * Sets the cursor color.
     *
     * @param color A color value in the form 0xAARRGGBB.
     *              Do not pass a resource ID. To get a color value from a resource ID, call
     *              {@link androidx.core.content.ContextCompat#getColor(Context, int) getColor}.
     * @attr ref R.styleable#PinView_cursorColor
     * @see #getCursorColor()
     */
    public void setCursorColor(@ColorInt int color) {
        mCursorColor = color;
        if (isCursorVisible()) {
            invalidateCursor(true);
        }
    }

    /**
     * Gets the cursor color.
     *
     * @return Return current cursor color.
     * @see #setCursorColor(int)
     */
    public int getCursorColor() {
        return mCursorColor;
    }

    @Override
    public void setCursorVisible(boolean visible) {
        if (isCursorVisible != visible) {
            isCursorVisible = visible;
            invalidateCursor(isCursorVisible);
            makeBlink();
        }
    }

    @Override
    public boolean isCursorVisible() {
        return isCursorVisible;
    }

    @Override
    public void onScreenStateChanged(int screenState) {
        super.onScreenStateChanged(screenState);
        switch (screenState) {
            case View.SCREEN_STATE_ON:
                resumeBlink();
                break;
            case View.SCREEN_STATE_OFF:
                suspendBlink();
                break;
        }
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        resumeBlink();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        suspendBlink();
    }

    private boolean shouldBlink() {
        return isCursorVisible() && isFocused();
    }

    private void makeBlink() {
        if (shouldBlink()) {
            if (mBlink == null) {
                mBlink = new Blink();
            }
            removeCallbacks(mBlink);
            drawCursor = false;
            postDelayed(mBlink, BLINK);
        } else {
            if (mBlink != null) {
                removeCallbacks(mBlink);
            }
        }
    }

    private void suspendBlink() {
        if (mBlink != null) {
            mBlink.cancel();
            invalidateCursor(false);
        }
    }

    private void resumeBlink() {
        if (mBlink != null) {
            mBlink.uncancel();
            makeBlink();
        }
    }

    private void invalidateCursor(boolean showCursor) {
        if (drawCursor != showCursor) {
            drawCursor = showCursor;
            invalidate();
        }
    }

    private void updateCursorHeight() {
        int delta = 2 * dpToPx(2);
        mCursorHeight = mPinItemHeight - getTextSize() > delta ? getTextSize() + delta : getTextSize();
    }

    private class Blink implements Runnable {
        private boolean mCancelled;

        @Override
        public void run() {
            if (mCancelled) {
                return;
            }

            removeCallbacks(this);

            if (shouldBlink()) {
                invalidateCursor(!drawCursor);
                postDelayed(this, BLINK);
            }
        }

        private void cancel() {
            if (!mCancelled) {
                removeCallbacks(this);
                mCancelled = true;
            }
        }

        void uncancel() {
            mCancelled = false;
        }
    }
    //endregion

    //region Selection Menu
    private void disableSelectionMenu() {
        setCustomSelectionActionModeCallback(new ActionMode.Callback() {
            @Override
            public boolean onCreateActionMode(ActionMode mode, Menu menu) {
                return false;
            }

            @Override
            public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
                return false;
            }

            @Override
            public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
                return false;
            }

            @Override
            public void onDestroyActionMode(ActionMode mode) {
                // no-op
            }
        });
        setLongClickable(false);
    }

    @Override
    public boolean isSuggestionsEnabled() {
        return false;
    }
    //endregion

    private int dpToPx(float dp) {
        return (int) (dp * getResources().getDisplayMetrics().density + 0.5f);
    }
}


text:
/* automatically generated by rust-bindgen */

pub const FAAD2_VERSION: &'static [u8; 4usize] = b"2.7\x00";
pub const MAIN: ::std::os::raw::c_uint = 1;
pub const LC: ::std::os::raw::c_uint = 2;
pub const SSR: ::std::os::raw::c_uint = 3;
pub const LTP: ::std::os::raw::c_uint = 4;
pub const HE_AAC: ::std::os::raw::c_uint = 5;
pub const ER_LC: ::std::os::raw::c_uint = 17;
pub const ER_LTP: ::std::os::raw::c_uint = 19;
pub const LD: ::std::os::raw::c_uint = 23;
pub const DRM_ER_LC: ::std::os::raw::c_uint = 27;
pub const RAW: ::std::os::raw::c_uint = 0;
pub const ADIF: ::std::os::raw::c_uint = 1;
pub const ADTS: ::std::os::raw::c_uint = 2;
pub const LATM: ::std::os::raw::c_uint = 3;
pub const NO_SBR: ::std::os::raw::c_uint = 0;
pub const SBR_UPSAMPLED: ::std::os::raw::c_uint = 1;
pub const SBR_DOWNSAMPLED: ::std::os::raw::c_uint = 2;
pub const NO_SBR_UPSAMPLED: ::std::os::raw::c_uint = 3;
pub const FAAD_FMT_16BIT: ::std::os::raw::c_uint = 1;
pub const FAAD_FMT_24BIT: ::std::os::raw::c_uint = 2;
pub const FAAD_FMT_32BIT: ::std::os::raw::c_uint = 3;
pub const FAAD_FMT_FLOAT: ::std::os::raw::c_uint = 4;
pub const FAAD_FMT_FIXED: ::std::os::raw::c_uint = 4;
pub const FAAD_FMT_DOUBLE: ::std::os::raw::c_uint = 5;
pub const LC_DEC_CAP: ::std::os::raw::c_uint = 1;
pub const MAIN_DEC_CAP: ::std::os::raw::c_uint = 2;
pub const LTP_DEC_CAP: ::std::os::raw::c_uint = 4;
pub const LD_DEC_CAP: ::std::os::raw::c_uint = 8;
pub const ERROR_RESILIENCE_CAP: ::std::os::raw::c_uint = 16;
pub const FIXED_POINT_CAP: ::std::os::raw::c_uint = 32;
pub const FRONT_CHANNEL_CENTER: ::std::os::raw::c_uint = 1;
pub const FRONT_CHANNEL_LEFT: ::std::os::raw::c_uint = 2;
pub const FRONT_CHANNEL_RIGHT: ::std::os::raw::c_uint = 3;
pub const SIDE_CHANNEL_LEFT: ::std::os::raw::c_uint = 4;
pub const SIDE_CHANNEL_RIGHT: ::std::os::raw::c_uint = 5;
pub const BACK_CHANNEL_LEFT: ::std::os::raw::c_uint = 6;
pub const BACK_CHANNEL_RIGHT: ::std::os::raw::c_uint = 7;
pub const BACK_CHANNEL_CENTER: ::std::os::raw::c_uint = 8;
pub const LFE_CHANNEL: ::std::os::raw::c_uint = 9;
pub const UNKNOWN_CHANNEL: ::std::os::raw::c_uint = 0;
pub const DRMCH_MONO: ::std::os::raw::c_uint = 1;
pub const DRMCH_STEREO: ::std::os::raw::c_uint = 2;
pub const DRMCH_SBR_MONO: ::std::os::raw::c_uint = 3;
pub const DRMCH_SBR_STEREO: ::std::os::raw::c_uint = 4;
pub const DRMCH_SBR_PS_STEREO: ::std::os::raw::c_uint = 5;
pub const FAAD_MIN_STREAMSIZE: ::std::os::raw::c_uint = 768;
pub type NeAACDecHandle = *mut ::std::os::raw::c_void;
#[repr(C)]
#[derive(Debug, Copy)]
pub struct mp4AudioSpecificConfig {
    pub objectTypeIndex: ::std::os::raw::c_uchar,
    pub samplingFrequencyIndex: ::std::os::raw::c_uchar,
    pub samplingFrequency: ::std::os::raw::c_ulong,
    pub channelsConfiguration: ::std::os::raw::c_uchar,
    pub frameLengthFlag: ::std::os::raw::c_uchar,
    pub dependsOnCoreCoder: ::std::os::raw::c_uchar,
    pub coreCoderDelay: ::std::os::raw::c_ushort,
    pub extensionFlag: ::std::os::raw::c_uchar,
    pub aacSectionDataResilienceFlag: ::std::os::raw::c_uchar,
    pub aacScalefactorDataResilienceFlag: ::std::os::raw::c_uchar,
    pub aacSpectralDataResilienceFlag: ::std::os::raw::c_uchar,
    pub epConfig: ::std::os::raw::c_uchar,
    pub sbr_present_flag: ::std::os::raw::c_char,
    pub forceUpSampling: ::std::os::raw::c_char,
    pub downSampledSBR: ::std::os::raw::c_char,
}
#[test]
fn bindgen_test_layout_mp4AudioSpecificConfig() {
    assert_eq!(::std::mem::size_of::<mp4AudioSpecificConfig>() , 32usize ,
               concat ! ( "Size of: " , stringify ! ( mp4AudioSpecificConfig )
               ));
    assert_eq! (::std::mem::align_of::<mp4AudioSpecificConfig>() , 8usize ,
                concat ! (
                "Alignment of " , stringify ! ( mp4AudioSpecificConfig ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                objectTypeIndex as * const _ as usize } , 0usize , concat ! (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( objectTypeIndex ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                samplingFrequencyIndex as * const _ as usize } , 1usize ,
                concat ! (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( samplingFrequencyIndex ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                samplingFrequency as * const _ as usize } , 8usize , concat !
                (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( samplingFrequency ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                channelsConfiguration as * const _ as usize } , 16usize ,
                concat ! (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( channelsConfiguration ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                frameLengthFlag as * const _ as usize } , 17usize , concat ! (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( frameLengthFlag ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                dependsOnCoreCoder as * const _ as usize } , 18usize , concat
                ! (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( dependsOnCoreCoder ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                coreCoderDelay as * const _ as usize } , 20usize , concat ! (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( coreCoderDelay ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                extensionFlag as * const _ as usize } , 22usize , concat ! (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( extensionFlag ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                aacSectionDataResilienceFlag as * const _ as usize } , 23usize
                , concat ! (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( aacSectionDataResilienceFlag ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                aacScalefactorDataResilienceFlag as * const _ as usize } ,
                24usize , concat ! (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( aacScalefactorDataResilienceFlag )
                ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                aacSpectralDataResilienceFlag as * const _ as usize } ,
                25usize , concat ! (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( aacSpectralDataResilienceFlag ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) . epConfig as
                * const _ as usize } , 26usize , concat ! (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( epConfig ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                sbr_present_flag as * const _ as usize } , 27usize , concat !
                (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( sbr_present_flag ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                forceUpSampling as * const _ as usize } , 28usize , concat ! (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( forceUpSampling ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const mp4AudioSpecificConfig ) ) .
                downSampledSBR as * const _ as usize } , 29usize , concat ! (
                "Alignment of field: " , stringify ! ( mp4AudioSpecificConfig
                ) , "::" , stringify ! ( downSampledSBR ) ));
}
impl Clone for mp4AudioSpecificConfig {
    fn clone(&self) -> Self { *self }
}
#[repr(C)]
#[derive(Debug, Copy)]
pub struct NeAACDecConfiguration {
    pub defObjectType: ::std::os::raw::c_uchar,
    pub defSampleRate: ::std::os::raw::c_ulong,
    pub outputFormat: ::std::os::raw::c_uchar,
    pub downMatrix: ::std::os::raw::c_uchar,
    pub useOldADTSFormat: ::std::os::raw::c_uchar,
    pub dontUpSampleImplicitSBR: ::std::os::raw::c_uchar,
}
#[test]
fn bindgen_test_layout_NeAACDecConfiguration() {
    assert_eq!(::std::mem::size_of::<NeAACDecConfiguration>() , 24usize ,
               concat ! ( "Size of: " , stringify ! ( NeAACDecConfiguration )
               ));
    assert_eq! (::std::mem::align_of::<NeAACDecConfiguration>() , 8usize ,
                concat ! (
                "Alignment of " , stringify ! ( NeAACDecConfiguration ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecConfiguration ) ) . defObjectType
                as * const _ as usize } , 0usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecConfiguration )
                , "::" , stringify ! ( defObjectType ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecConfiguration ) ) . defSampleRate
                as * const _ as usize } , 8usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecConfiguration )
                , "::" , stringify ! ( defSampleRate ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecConfiguration ) ) . outputFormat
                as * const _ as usize } , 16usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecConfiguration )
                , "::" , stringify ! ( outputFormat ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecConfiguration ) ) . downMatrix as
                * const _ as usize } , 17usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecConfiguration )
                , "::" , stringify ! ( downMatrix ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecConfiguration ) ) .
                useOldADTSFormat as * const _ as usize } , 18usize , concat !
                (
                "Alignment of field: " , stringify ! ( NeAACDecConfiguration )
                , "::" , stringify ! ( useOldADTSFormat ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecConfiguration ) ) .
                dontUpSampleImplicitSBR as * const _ as usize } , 19usize ,
                concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecConfiguration )
                , "::" , stringify ! ( dontUpSampleImplicitSBR ) ));
}
impl Clone for NeAACDecConfiguration {
    fn clone(&self) -> Self { *self }
}
pub type NeAACDecConfigurationPtr = *mut NeAACDecConfiguration;
#[repr(C)]
pub struct NeAACDecFrameInfo {
    pub bytesconsumed: ::std::os::raw::c_ulong,
    pub samples: ::std::os::raw::c_ulong,
    pub channels: ::std::os::raw::c_uchar,
    pub error: ::std::os::raw::c_uchar,
    pub samplerate: ::std::os::raw::c_ulong,
    pub sbr: ::std::os::raw::c_uchar,
    pub object_type: ::std::os::raw::c_uchar,
    pub header_type: ::std::os::raw::c_uchar,
    pub num_front_channels: ::std::os::raw::c_uchar,
    pub num_side_channels: ::std::os::raw::c_uchar,
    pub num_back_channels: ::std::os::raw::c_uchar,
    pub num_lfe_channels: ::std::os::raw::c_uchar,
    pub channel_position: [::std::os::raw::c_uchar; 64usize],
    pub ps: ::std::os::raw::c_uchar,
}
#[test]
fn bindgen_test_layout_NeAACDecFrameInfo() {
    assert_eq!(::std::mem::size_of::<NeAACDecFrameInfo>() , 104usize , concat
               ! ( "Size of: " , stringify ! ( NeAACDecFrameInfo ) ));
    assert_eq! (::std::mem::align_of::<NeAACDecFrameInfo>() , 8usize , concat
                ! ( "Alignment of " , stringify ! ( NeAACDecFrameInfo ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) . bytesconsumed as
                * const _ as usize } , 0usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( bytesconsumed ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) . samples as *
                const _ as usize } , 8usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( samples ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) . channels as *
                const _ as usize } , 16usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( channels ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) . error as * const
                _ as usize } , 17usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( error ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) . samplerate as *
                const _ as usize } , 24usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( samplerate ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) . sbr as * const _
                as usize } , 32usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( sbr ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) . object_type as *
                const _ as usize } , 33usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( object_type ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) . header_type as *
                const _ as usize } , 34usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( header_type ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) .
                num_front_channels as * const _ as usize } , 35usize , concat
                ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( num_front_channels ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) . num_side_channels
                as * const _ as usize } , 36usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( num_side_channels ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) . num_back_channels
                as * const _ as usize } , 37usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( num_back_channels ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) . num_lfe_channels
                as * const _ as usize } , 38usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( num_lfe_channels ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) . channel_position
                as * const _ as usize } , 39usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( channel_position ) ));
    assert_eq! (unsafe {
                & ( * ( 0 as * const NeAACDecFrameInfo ) ) . ps as * const _
                as usize } , 103usize , concat ! (
                "Alignment of field: " , stringify ! ( NeAACDecFrameInfo ) ,
                "::" , stringify ! ( ps ) ));
}
#[link(name="faad")]
extern "C" {
    pub fn NeAACDecGetErrorMessage(errcode: ::std::os::raw::c_uchar)
     -> *mut ::std::os::raw::c_char;
}
#[link(name="faad")]
extern "C" {
    pub fn NeAACDecGetCapabilities() -> ::std::os::raw::c_ulong;
}
#[link(name="faad")]
extern "C" {
    pub fn NeAACDecOpen() -> NeAACDecHandle;
}
#[link(name="faad")]
extern "C" {
    pub fn NeAACDecGetCurrentConfiguration(hDecoder: NeAACDecHandle)
     -> NeAACDecConfigurationPtr;
}
#[link(name="faad")]
extern "C" {
    pub fn NeAACDecSetConfiguration(hDecoder: NeAACDecHandle,
                                    config: NeAACDecConfigurationPtr)
     -> ::std::os::raw::c_uchar;
}
#[link(name="faad")]
extern "C" {
    pub fn NeAACDecInit(hDecoder: NeAACDecHandle,
                        buffer: *mut ::std::os::raw::c_uchar,
                        buffer_size: ::std::os::raw::c_ulong,
                        samplerate: *mut ::std::os::raw::c_ulong,
                        channels: *mut ::std::os::raw::c_uchar)
     -> ::std::os::raw::c_long;
}
#[link(name="faad")]
extern "C" {
    pub fn NeAACDecInit2(hDecoder: NeAACDecHandle,
                         pBuffer: *mut ::std::os::raw::c_uchar,
                         SizeOfDecoderSpecificInfo: ::std::os::raw::c_ulong,
                         samplerate: *mut ::std::os::raw::c_ulong,
                         channels: *mut ::std::os::raw::c_uchar)
     -> ::std::os::raw::c_char;
}
#[link(name="faad")]
extern "C" {
    pub fn NeAACDecInitDRM(hDecoder: *mut NeAACDecHandle,
                           samplerate: ::std::os::raw::c_ulong,
                           channels: ::std::os::raw::c_uchar)
     -> ::std::os::raw::c_char;
}
#[link(name="faad")]
extern "C" {
    pub fn NeAACDecPostSeekReset(hDecoder: NeAACDecHandle,
                                 frame: ::std::os::raw::c_long);
}
#[link(name="faad")]
extern "C" {
    pub fn NeAACDecClose(hDecoder: NeAACDecHandle);
}
#[link(name="faad")]
extern "C" {
    pub fn NeAACDecDecode(hDecoder: NeAACDecHandle,
                          hInfo: *mut NeAACDecFrameInfo,
                          buffer: *mut ::std::os::raw::c_uchar,
                          buffer_size: ::std::os::raw::c_ulong)
     -> *mut ::std::os::raw::c_void;
}
#[link(name="faad")]
extern "C" {
    pub fn NeAACDecDecode2(hDecoder: NeAACDecHandle,
                           hInfo: *mut NeAACDecFrameInfo,
                           buffer: *mut ::std::os::raw::c_uchar,
                           buffer_size: ::std::os::raw::c_ulong,
                           sample_buffer: *mut *mut ::std::os::raw::c_void,
                           sample_buffer_size: ::std::os::raw::c_ulong)
     -> *mut ::std::os::raw::c_void;
}
#[link(name="faad")]
extern "C" {
    pub fn NeAACDecAudioSpecificConfig(pBuffer: *mut ::std::os::raw::c_uchar,
                                       buffer_size: ::std::os::raw::c_ulong,
                                       mp4ASC: *mut mp4AudioSpecificConfig)
     -> ::std::os::raw::c_char;
}

redpajama_set_name: RedPajamaGithub


text:
package org.joyrest.transform.interceptor;

import org.joyrest.interceptor.Interceptor;
import org.joyrest.interceptor.InterceptorChain;
import org.joyrest.interceptor.InterceptorInternalOrders;
import org.joyrest.model.request.InternalRequest;
import org.joyrest.model.response.InternalResponse;
import org.joyrest.routing.InternalRoute;
import org.joyrest.transform.SerializationUtils;

/**
 * Aspect which marshalls and unmarshalls an entity object from/to Request/Response object.
 *
 * @author pbouda
 */
public class SerializationInterceptor implements Interceptor {

    @Override
    public InternalResponse<Object> around(InterceptorChain chain, InternalRequest<Object> req, InternalResponse<Object> resp)
            throws Exception{

        InternalRoute route = chain.getRoute();
        if (route.hasRequestBody()) {
            Object entity = SerializationUtils.readEntity(route, req);
            req.setEntity(entity);
        }

        chain.proceed(req, resp);
        SerializationUtils.writeEntity(route, req, resp);
        return resp;
    }

    @Override
    public int getOrder() {
        return InterceptorInternalOrders.SERIALIZATION;
    }

}


text:
package org.apache.jackrabbit.oak.plugins.blob.datastore;

import java.io.IOException;

import javax.annotation.Nonnull;

public interface TextWriter {

    void write(@Nonnull String blobId, @Nonnull String text) throws IOException;

    void markEmpty(String blobId);

    void markError(String blobId);

    boolean isProcessed(String blobId);
}


text:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface MainTabBarController : UITabBarController
{
}

@end


text:
package org.valkyriercp.form.binding.swing.text;

import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.PlainDocument;
import java.util.regex.Pattern;

/**
 * This DocumentFactory will create a Document which holds a {@link java.util.regex.Pattern}. This Pattern is then used
 * to verify each character that is inserted. This makes it possible to eg. allow only alfanumeric. Note
 * that if you need a specific sequence of characters, you probably need a formatted textField with formatter
 * instead.
 *
 * @author Lieven Doclo
 * @author Jan Hoskens
 */
public class RegExDocumentFactory implements DocumentFactory {

    /** Pattern to use in the RegExDocument. */
    private String characterPattern;

    /** Converting to uppercase letters? */
    private boolean convertToUppercase;

    /** Default constructor sets the pattern to use '.'. */
    public RegExDocumentFactory() {
        this(".");
    }

    /**
     * Construct a RegExDocumentFactory with the given pattern.
     *
     * @param characterPattern the pattern to use.
     */
    public RegExDocumentFactory(String characterPattern) {
        this(characterPattern, false);
    }

    /**
     * Construct a RegExDocumentFactory with the given pattern and convert to uppercase if requested.
     *
     * @param characterPattern   the pattern to use.
     * @param convertToUppercase if true convert all letters to uppercase.
     */
    public RegExDocumentFactory(String characterPattern, boolean convertToUppercase) {
        this.characterPattern = characterPattern;
        this.convertToUppercase = convertToUppercase;
    }

    /**
     * Returns the pattern that is used by this RegExDocumentFactory.
     *
     * @return the pattern.
     */
    public String getCharacterPattern() {
        return characterPattern;
    }

    /**
     * Set the pattern to be used by this RegExDocumentFactory.
     *
     * @param characterPattern the pattern.
     */
    public void setCharacterPattern(String characterPattern) {
        this.characterPattern = characterPattern;
    }

    /**
     * Returns true if the Document should convert all lowercase letters to uppercase.
     *
     * @return true if lowercase is converted to uppercase.
     */
    public boolean isConvertToUppercase() {
        return convertToUppercase;
    }

    /**
     * Set to true if all lowercase letters should be converted to uppercase.
     *
     * @param convertToUppercase set to true if conversion should happen.
     */
    public void setConvertToUppercase(boolean convertToUppercase) {
        this.convertToUppercase = convertToUppercase;
    }

    /** {@inheritDoc} */
    public Document createDocument() {
        return new RegExDocument(getCharacterPattern(), isConvertToUppercase());
    }

    private static class RegExDocument extends PlainDocument {

        private final Pattern pattern;

        private final boolean convertToUppercase;

        public RegExDocument(String stringPattern, boolean convertToUppercase) {
            this.pattern = Pattern.compile(stringPattern, Pattern.DOTALL);
            this.convertToUppercase = convertToUppercase;
        }

        public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
            if (str == null) {
                return;
            }
            char[] upper = str.toCharArray();
            StringBuilder builder = new StringBuilder();
            char character;
            for (int i = 0; i < upper.length; i++)
            {
                if (convertToUppercase)
                    character = Character.toUpperCase(upper[i]);
                else
                    character = upper[i];
                if (pattern.matcher(Character.toString(character)).find()) {
                    builder.append(character);
                }
            }
            super.insertString(offs, builder.toString(), a);
        }
    }
}


text:
#pragma once

//
// Macros used by other macros to create a standard const wchar_t* string
// from an arbritrary (unquoted) text .
//
#define CONCAT(x,y) x##y
#define WSTRINGIFY(x) CONCAT(L,#x)

//
// Macro to execute a COM API and to test its result.
// It will execute the call given as a parameter. If the resulting HRESULT
// indicates an error, an exception will be thrown (via GenerateVssException in
// Utils.h) with the code forming the call as the message.
//
#define CheckCom( Call ) CheckComError( Call, #Call )

//
// Utility macro assisting CheckCom, takes a HRESULT error code and a text
// describing the error. If the error code indicates an error,
// an appropriate exception will be thrown, using GenerateVssException,
// otherwise nothing will happen.
//
#define CheckComError( ErrorCode, Text )							\
{																	\
   HRESULT hrInternal = ErrorCode;									\
   if (FAILED(hrInternal))											\
   {																\
   ThrowException( ErrorCode );	\
   }																\
}

//
// Helper definitions for restricting strings managed by AutoPtr subclasses
// to not be allowed to be null.
//
#define NoNullAutoMStr(arg)  NoNullAutoStrImpl(arg, #arg, AutoMStr)
#define NoNullAutoMBStr(arg) NoNullAutoStrImpl(arg, #arg, AutoMBStr)
#define NoNullAutoStrImpl(arg, name, type) NoNull<type>(arg, L##name)

#define UnsupportedOs() throw gcnew UnsupportedOperatingSystemException(Alphaleonis::Win32::Vss::Resources::LocalizedStrings::RequestedOperationUnsupportedByOS)

//
// Defines an instance variable and accessor methods to an "Ex" interface
// of a class. The accessor methods will be named GetXXX and RequireXXX where
// XXX is the name of the interface. GetXXX may return NULL if the QueryInterface
// method fails, while RequireXXX will call UnsupportedOs() if the method fails.
//
#define DEFINE_EX_INTERFACE_ACCESSOR(interfaceName, baseInstance)		\
   interfaceName *m_##interfaceName;			\
   interfaceName *Get##interfaceName()			\
   {											\
   if (m_##interfaceName == 0)				\
      {										\
      void *ifc = 0;						\
      if (FAILED((baseInstance)->QueryInterface(IID_##interfaceName, &ifc))) \
      return 0;			    \
      m_##interfaceName = (interfaceName *)ifc; \
      }										\
      return m_##interfaceName;				\
   }											\
   \
   interfaceName *Require##interfaceName() \
   {\
   if (m_##interfaceName == 0)\
      {\
      if ( (m_##interfaceName = Get##interfaceName()) == 0) \
      UnsupportedOs();\
      }\
      return m_##interfaceName;\
   }


text:
<%
  var settings = require("ep_etherpad-lite/node/utils/Settings");
%>
<!doctype html>

        <title><%=settings.title%></title>

        <meta charset="utf-8">
        <meta name="referrer" content="no-referrer">
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0">
        <link rel="shortcut icon" href="<%=settings.favicon%>">

        <link rel="localizations" type="application/l10n+json" href="locales.json">

        <link href="static/skins/<%=encodeURI(settings.skinName)%>/index.css" rel="stylesheet">

        <% e.begin_block("indexWrapper"); %>

                <button id="button" onclick="go2Random()" data-l10n-id="index.newPad"></button>

                <form action="#" onsubmit="go2Name();return false;">
                    <input type="text" id="padname" maxlength="50" autofocus x-webkit-speech>
                    <button type="submit">OK</button>
                </form>

        <% e.end_block(); %>

JavaScript license information


text:
/// <reference path='services.ts' />

/* @internal */
module ts.NavigationBar {
    export function getNavigationBarItems(sourceFile: SourceFile): ts.NavigationBarItem[]  {
        // If the source file has any child items, then it included in the tree
        // and takes lexical ownership of all other top-level items.
        let hasGlobalNode = false;

        return getItemsWorker(getTopLevelNodes(sourceFile), createTopLevelItem);

        function getIndent(node: Node): number {
            // If we have a global node in the tree,
            // then it adds an extra layer of depth to all subnodes.
            let indent = hasGlobalNode ? 1 : 0;

            let current = node.parent;
            while (current) {
                switch (current.kind) {
                    case SyntaxKind.ModuleDeclaration:
                        // If we have a module declared as A.B.C, it is more "intuitive"
                        // to say it only has a single layer of depth
                        do {
                            current = current.parent;
                        }
                        while (current.kind === SyntaxKind.ModuleDeclaration);

                        // fall through
                    case SyntaxKind.ClassDeclaration:
                    case SyntaxKind.EnumDeclaration:
                    case SyntaxKind.InterfaceDeclaration:
                    case SyntaxKind.FunctionDeclaration:
                        indent++;
                }

                current = current.parent;
            }

            return indent;
        }

        function getChildNodes(nodes: Node[]): Node[] {
            let childNodes: Node[] = [];

            function visit(node: Node) {
                switch (node.kind) {
                    case SyntaxKind.VariableStatement:
                        forEach((<VariableStatement>node).declarationList.declarations, visit);
                        break;
                    case SyntaxKind.ObjectBindingPattern:
                    case SyntaxKind.ArrayBindingPattern:
                        forEach((<BindingPattern>node).elements, visit);
                        break;

                    case SyntaxKind.ExportDeclaration:
                        // Handle named exports case e.g.:
                        //    export {a, b as B} from "mod";
                        if ((<ExportDeclaration>node).exportClause) {
                            forEach((<ExportDeclaration>node).exportClause.elements, visit);
                        }
                        break;

                    case SyntaxKind.ImportDeclaration:
                        let importClause = (<ImportDeclaration>node).importClause;
                        if (importClause) {
                            // Handle default import case e.g.:
                            //    import d from "mod";
                            if (importClause.name) {
                                childNodes.push(importClause);
                            }

                            // Handle named bindings in imports e.g.:
                            //    import * as NS from "mod";
                            //    import {a, b as B} from "mod";
                            if (importClause.namedBindings) {
                                if (importClause.namedBindings.kind === SyntaxKind.NamespaceImport) {
                                    childNodes.push(importClause.namedBindings);
                                }
                                else {
                                    forEach((<NamedImports>importClause.namedBindings).elements, visit);
                                }
                            }
                        }
                        break;

                    case SyntaxKind.BindingElement:
                    case SyntaxKind.VariableDeclaration:
                        if (isBindingPattern((<VariableDeclaration>node).name)) {
                            visit((<VariableDeclaration>node).name);
                            break;
                        }
                        // Fall through
                    case SyntaxKind.ClassDeclaration:
                    case SyntaxKind.EnumDeclaration:
                    case SyntaxKind.InterfaceDeclaration:
                    case SyntaxKind.ModuleDeclaration:
                    case SyntaxKind.FunctionDeclaration:
                    case SyntaxKind.ImportEqualsDeclaration:
                    case SyntaxKind.ImportSpecifier:
                    case SyntaxKind.ExportSpecifier:
                        childNodes.push(node);
                        break;
                }
            }

            //for (let i = 0, n = nodes.length; i < n; i++) {
            //    let node = nodes[i];

            //    if (node.kind === SyntaxKind.ClassDeclaration ||
            //        node.kind === SyntaxKind.EnumDeclaration ||
            //        node.kind === SyntaxKind.InterfaceDeclaration ||
            //        node.kind === SyntaxKind.ModuleDeclaration ||
            //        node.kind === SyntaxKind.FunctionDeclaration) {

            //        childNodes.push(node);
            //    }
            //    else if (node.kind === SyntaxKind.VariableStatement) {
            //        childNodes.push.apply(childNodes, (<VariableStatement>node).declarations);
            //    }
            //}
            forEach(nodes, visit);
            return sortNodes(childNodes);
        }

        function getTopLevelNodes(node: SourceFile): Node[] {
            let topLevelNodes: Node[] = [];
            topLevelNodes.push(node);

            addTopLevelNodes(node.statements, topLevelNodes);

            return topLevelNodes;
        }

        function sortNodes(nodes: Node[]): Node[] {
            return nodes.slice(0).sort((n1: Declaration, n2: Declaration) => {
                if (n1.name && n2.name) {
                    return getPropertyNameForPropertyNameNode(n1.name).localeCompare(getPropertyNameForPropertyNameNode(n2.name));
                }
                else if (n1.name) {
                    return 1;
                }
                else if (n2.name) {
                    return -1;
                }
                else {
                    return n1.kind - n2.kind;
                }
            });
        }

        function addTopLevelNodes(nodes: Node[], topLevelNodes: Node[]): void {
            nodes = sortNodes(nodes);

            for (let node of nodes) {
                switch (node.kind) {
                    case SyntaxKind.ClassDeclaration:
                    case SyntaxKind.EnumDeclaration:
                    case SyntaxKind.InterfaceDeclaration:
                        topLevelNodes.push(node);
                        break;

                    case SyntaxKind.ModuleDeclaration:
                        let moduleDeclaration = <ModuleDeclaration>node;
                        topLevelNodes.push(node);
                        addTopLevelNodes((<Block>getInnermostModule(moduleDeclaration).body).statements, topLevelNodes);
                        break;

                    case SyntaxKind.FunctionDeclaration:
                        let functionDeclaration = <FunctionLikeDeclaration>node;
                        if (isTopLevelFunctionDeclaration(functionDeclaration)) {
                            topLevelNodes.push(node);
                            addTopLevelNodes((<Block>functionDeclaration.body).statements, topLevelNodes);
                        }
                        break;
                }
            }
        }

        function isTopLevelFunctionDeclaration(functionDeclaration: FunctionLikeDeclaration) {
            if (functionDeclaration.kind === SyntaxKind.FunctionDeclaration) {
                // A function declaration is 'top level' if it contains any function declarations
                // within it.
                if (functionDeclaration.body && functionDeclaration.body.kind === SyntaxKind.Block) {
                    // Proper function declarations can only have identifier names
                    if (forEach((<Block>functionDeclaration.body).statements,
                        s => s.kind === SyntaxKind.FunctionDeclaration && !isEmpty((<FunctionDeclaration>s).name.text))) {

                        return true;
                    }

                    // Or if it is not parented by another function.  i.e all functions
                    // at module scope are 'top level'.
                    if (!isFunctionBlock(functionDeclaration.parent)) {
                        return true;
                    }
                }
            }

            return false;
        }

        function getItemsWorker(nodes: Node[], createItem: (n: Node) => ts.NavigationBarItem): ts.NavigationBarItem[] {
            let items: ts.NavigationBarItem[] = [];

            let keyToItem: Map<NavigationBarItem> = {};

            for (let child of nodes) {
                let item = createItem(child);
                if (item !== undefined) {
                    if (item.text.length > 0) {
                        let key = item.text + "-" + item.kind + "-" + item.indent;

                        let itemWithSameName = keyToItem[key];
                        if (itemWithSameName) {
                            // We had an item with the same name.  Merge these items together.
                            merge(itemWithSameName, item);
                        }
                        else {
                            keyToItem[key] = item;
                            items.push(item);
                        }
                    }
                }
            }

            return items;
        }

        function merge(target: ts.NavigationBarItem, source: ts.NavigationBarItem) {
            // First, add any spans in the source to the target.
            target.spans.push.apply(target.spans, source.spans);

            if (source.childItems) {
                if (!target.childItems) {
                    target.childItems = [];
                }

                // Next, recursively merge or add any children in the source as appropriate.
                outer:
                for (let sourceChild of source.childItems) {
                    for (let targetChild of target.childItems) {
                        if (targetChild.text === sourceChild.text && targetChild.kind === sourceChild.kind) {
                            // Found a match.  merge them.
                            merge(targetChild, sourceChild);
                            continue outer;
                        }
                    }

                    // Didn't find a match, just add this child to the list.
                    target.childItems.push(sourceChild);
                }
            }
        }

        function createChildItem(node: Node): ts.NavigationBarItem {
            switch (node.kind) {
                case SyntaxKind.Parameter:
                    if (isBindingPattern((<ParameterDeclaration>node).name)) {
                        break;
                    }
                    if ((node.flags & NodeFlags.Modifier) === 0) {
                        return undefined;
                    }
                    return createItem(node, getTextOfNode((<ParameterDeclaration>node).name), ts.ScriptElementKind.memberVariableElement);

                case SyntaxKind.MethodDeclaration:
                case SyntaxKind.MethodSignature:
                    return createItem(node, getTextOfNode((<MethodDeclaration>node).name), ts.ScriptElementKind.memberFunctionElement);

                case SyntaxKind.GetAccessor:
                    return createItem(node, getTextOfNode((<AccessorDeclaration>node).name), ts.ScriptElementKind.memberGetAccessorElement);

                case SyntaxKind.SetAccessor:
                    return createItem(node, getTextOfNode((<AccessorDeclaration>node).name), ts.ScriptElementKind.memberSetAccessorElement);

                case SyntaxKind.IndexSignature:
                    return createItem(node, "[]", ts.ScriptElementKind.indexSignatureElement);

                case SyntaxKind.EnumMember:
                    return createItem(node, getTextOfNode((<EnumMember>node).name), ts.ScriptElementKind.memberVariableElement);

                case SyntaxKind.CallSignature:
                    return createItem(node, "()", ts.ScriptElementKind.callSignatureElement);

                case SyntaxKind.ConstructSignature:
                    return createItem(node, "new()", ts.ScriptElementKind.constructSignatureElement);

                case SyntaxKind.PropertyDeclaration:
                case SyntaxKind.PropertySignature:
                    return createItem(node, getTextOfNode((<PropertyDeclaration>node).name), ts.ScriptElementKind.memberVariableElement);

                case SyntaxKind.FunctionDeclaration:
                    return createItem(node, getTextOfNode((<FunctionLikeDeclaration>node).name), ts.ScriptElementKind.functionElement);

                case SyntaxKind.VariableDeclaration:
                case SyntaxKind.BindingElement:
                    let variableDeclarationNode: Node;
                    let name: Node;

                    if (node.kind === SyntaxKind.BindingElement) {
                        name = (<BindingElement>node).name;
                        variableDeclarationNode = node;
                        // binding elements are added only for variable declarations
                        // bubble up to the containing variable declaration
                        while (variableDeclarationNode && variableDeclarationNode.kind !== SyntaxKind.VariableDeclaration) {
                            variableDeclarationNode = variableDeclarationNode.parent;
                        }
                        Debug.assert(variableDeclarationNode !== undefined);
                    }
                    else {
                        Debug.assert(!isBindingPattern((<VariableDeclaration>node).name));
                        variableDeclarationNode = node;
                        name = (<VariableDeclaration>node).name;
                    }

                    if (isConst(variableDeclarationNode)) {
                        return createItem(node, getTextOfNode(name), ts.ScriptElementKind.constElement);
                    }
                    else if (isLet(variableDeclarationNode)) {
                        return createItem(node, getTextOfNode(name), ts.ScriptElementKind.letElement);
                    }
                    else {
                        return createItem(node, getTextOfNode(name), ts.ScriptElementKind.variableElement);
                    }

                case SyntaxKind.Constructor:
                    return createItem(node, "constructor", ts.ScriptElementKind.constructorImplementationElement);

                case SyntaxKind.ExportSpecifier:
                case SyntaxKind.ImportSpecifier:
                case SyntaxKind.ImportEqualsDeclaration:
                case SyntaxKind.ImportClause:
                case SyntaxKind.NamespaceImport:
                    return createItem(node, getTextOfNode((<Declaration>node).name), ts.ScriptElementKind.alias);
            }

            return undefined;

            function createItem(node: Node, name: string, scriptElementKind: string): NavigationBarItem {
                return getNavigationBarItem(name, scriptElementKind, getNodeModifiers(node), [getNodeSpan(node)]);
            }
        }

        function isEmpty(text: string) {
            return !text || text.trim() === "";
        }

        function getNavigationBarItem(text: string, kind: string, kindModifiers: string, spans: TextSpan[], childItems: NavigationBarItem[] = [], indent: number = 0): NavigationBarItem {
            if (isEmpty(text)) {
                return undefined;
            }

            return {
                text,
                kind,
                kindModifiers,
                spans,
                childItems,
                indent,
                bolded: false,
                grayed: false
            };
        }

        function createTopLevelItem(node: Node): ts.NavigationBarItem {
            switch (node.kind) {
                case SyntaxKind.SourceFile:
                    return createSourceFileItem(<SourceFile>node);

                case SyntaxKind.ClassDeclaration:
                    return createClassItem(<ClassDeclaration>node);

                case SyntaxKind.EnumDeclaration:
                    return createEnumItem(<EnumDeclaration>node);

                case SyntaxKind.InterfaceDeclaration:
                    return createIterfaceItem(<InterfaceDeclaration>node);

                case SyntaxKind.ModuleDeclaration:
                    return createModuleItem(<ModuleDeclaration>node);

                case SyntaxKind.FunctionDeclaration:
                    return createFunctionItem(<FunctionDeclaration>node);
            }

            return undefined;

            function getModuleName(moduleDeclaration: ModuleDeclaration): string {
                // We want to maintain quotation marks.
                if (moduleDeclaration.name.kind === SyntaxKind.StringLiteral) {
                    return getTextOfNode(moduleDeclaration.name);
                }

                // Otherwise, we need to aggregate each identifier to build up the qualified name.
                let result: string[] = [];

                result.push(moduleDeclaration.name.text);

                while (moduleDeclaration.body && moduleDeclaration.body.kind === SyntaxKind.ModuleDeclaration) {
                    moduleDeclaration = <ModuleDeclaration>moduleDeclaration.body;

                    result.push(moduleDeclaration.name.text);
                }

                return result.join(".");
            }

            function createModuleItem(node: ModuleDeclaration): NavigationBarItem {
                let moduleName = getModuleName(node);

                let childItems = getItemsWorker(getChildNodes((<Block>getInnermostModule(node).body).statements), createChildItem);

                return getNavigationBarItem(moduleName,
                    ts.ScriptElementKind.moduleElement,
                    getNodeModifiers(node),
                    [getNodeSpan(node)],
                    childItems,
                    getIndent(node));
            }

            function createFunctionItem(node: FunctionDeclaration) {
                if (node.body && node.body.kind === SyntaxKind.Block) {
                    let childItems = getItemsWorker(sortNodes((<Block>node.body).statements), createChildItem);

                    return getNavigationBarItem(!node.name ? "default": node.name.text ,
                        ts.ScriptElementKind.functionElement,
                        getNodeModifiers(node),
                        [getNodeSpan(node)],
                        childItems,
                        getIndent(node));
                }

                return undefined;
            }

            function createSourceFileItem(node: SourceFile): ts.NavigationBarItem {
                let childItems = getItemsWorker(getChildNodes(node.statements), createChildItem);

                if (childItems === undefined || childItems.length === 0) {
                    return undefined;
                }

                hasGlobalNode = true;
                let rootName = isExternalModule(node)
                    ? "\"" + escapeString(getBaseFileName(removeFileExtension(normalizePath(node.fileName)))) + "\""
                    : "<global>"

                return getNavigationBarItem(rootName,
                    ts.ScriptElementKind.moduleElement,
                    ts.ScriptElementKindModifier.none,
                    [getNodeSpan(node)],
                    childItems);
            }

            function createClassItem(node: ClassDeclaration): ts.NavigationBarItem {
                let childItems: NavigationBarItem[];

                if (node.members) {
                    let constructor = <ConstructorDeclaration>forEach(node.members, member => {
                        return member.kind === SyntaxKind.Constructor && member;
                    });

                    // Add the constructor parameters in as children of the class (for property parameters).
                    // Note that *all non-binding pattern named* parameters will be added to the nodes array, but parameters that
                    // are not properties will be filtered out later by createChildItem.
                    let nodes: Node[] = removeDynamicallyNamedProperties(node);
                    if (constructor) {
                        nodes.push.apply(nodes, filter(constructor.parameters, p => !isBindingPattern(p.name)));
                    }

                    childItems = getItemsWorker(sortNodes(nodes), createChildItem);
                }

                var nodeName = !node.name ? "default" : node.name.text;

                return getNavigationBarItem(
                    nodeName,
                    ts.ScriptElementKind.classElement,
                    getNodeModifiers(node),
                    [getNodeSpan(node)],
                    childItems,
                    getIndent(node));
            }

            function createEnumItem(node: EnumDeclaration): ts.NavigationBarItem {
                let childItems = getItemsWorker(sortNodes(removeComputedProperties(node)), createChildItem);
                return getNavigationBarItem(
                    node.name.text,
                    ts.ScriptElementKind.enumElement,
                    getNodeModifiers(node),
                    [getNodeSpan(node)],
                    childItems,
                    getIndent(node));
            }

            function createIterfaceItem(node: InterfaceDeclaration): ts.NavigationBarItem {
                let childItems = getItemsWorker(sortNodes(removeDynamicallyNamedProperties(node)), createChildItem);
                return getNavigationBarItem(
                    node.name.text,
                    ts.ScriptElementKind.interfaceElement,
                    getNodeModifiers(node),
                    [getNodeSpan(node)],
                    childItems,
                    getIndent(node));
            }
        }

        function removeComputedProperties(node: EnumDeclaration): Declaration[] {
            return filter<Declaration>(node.members, member => member.name === undefined || member.name.kind !== SyntaxKind.ComputedPropertyName);
        }

        /**
         * Like removeComputedProperties, but retains the properties with well known symbol names
         */
        function removeDynamicallyNamedProperties(node: ClassDeclaration | InterfaceDeclaration): Declaration[]{
            return filter<Declaration>(node.members, member => !hasDynamicName(member));
        }

        function getInnermostModule(node: ModuleDeclaration): ModuleDeclaration {
            while (node.body.kind === SyntaxKind.ModuleDeclaration) {
                node = <ModuleDeclaration>node.body;
            }

            return node;
        }

        function getNodeSpan(node: Node) {
            return node.kind === SyntaxKind.SourceFile
                ? createTextSpanFromBounds(node.getFullStart(), node.getEnd())
                : createTextSpanFromBounds(node.getStart(), node.getEnd());
        }

        function getTextOfNode(node: Node): string {
            return getTextOfNodeFromSourceText(sourceFile.text, node);
        }
    }
}