text:
package androidx.slice.builders.impl;

import static android.app.slice.Slice.HINT_ACTIONS;
import static android.app.slice.Slice.HINT_ERROR;
import static android.app.slice.Slice.HINT_KEYWORDS;
import static android.app.slice.Slice.HINT_LAST_UPDATED;
import static android.app.slice.Slice.HINT_LIST_ITEM;
import static android.app.slice.Slice.HINT_PARTIAL;
import static android.app.slice.Slice.HINT_SEE_MORE;
import static android.app.slice.Slice.HINT_SHORTCUT;
import static android.app.slice.Slice.HINT_SUMMARY;
import static android.app.slice.Slice.HINT_TITLE;
import static android.app.slice.Slice.HINT_TTL;
import static android.app.slice.Slice.SUBTYPE_COLOR;
import static android.app.slice.Slice.SUBTYPE_CONTENT_DESCRIPTION;
import static android.app.slice.Slice.SUBTYPE_LAYOUT_DIRECTION;
import static android.app.slice.Slice.SUBTYPE_MAX;
import static android.app.slice.Slice.SUBTYPE_RANGE;
import static android.app.slice.Slice.SUBTYPE_VALUE;
import static android.app.slice.SliceItem.FORMAT_ACTION;
import static android.app.slice.SliceItem.FORMAT_BUNDLE;
import static android.app.slice.SliceItem.FORMAT_SLICE;
import static android.app.slice.SliceItem.FORMAT_TEXT;

import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.slice.Slice.SUBTYPE_RANGE_MODE;
import static androidx.slice.builders.ListBuilder.INFINITY;
import static androidx.slice.builders.ListBuilder.RANGE_MODE_DETERMINATE;
import static androidx.slice.builders.ListBuilder.RANGE_MODE_STAR_RATING;
import static androidx.slice.core.SliceHints.HINT_END_OF_SECTION;
import static androidx.slice.core.SliceHints.SUBTYPE_HOST_EXTRAS;
import static androidx.slice.core.SliceHints.SUBTYPE_MILLIS;
import static androidx.slice.core.SliceHints.SUBTYPE_MIN;
import static androidx.slice.core.SliceHints.SUBTYPE_SELECTION;

import android.app.PendingIntent;
import android.os.Bundle;
import android.os.PersistableBundle;

import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.graphics.drawable.IconCompat;
import androidx.core.util.Pair;
import androidx.slice.Clock;
import androidx.slice.Slice;
import androidx.slice.SliceItem;
import androidx.slice.SliceSpec;
import androidx.slice.SliceSpecs;
import androidx.slice.SystemClock;
import androidx.slice.builders.GridRowBuilder;
import androidx.slice.builders.ListBuilder.HeaderBuilder;
import androidx.slice.builders.ListBuilder.InputRangeBuilder;
import androidx.slice.builders.ListBuilder.RangeBuilder;
import androidx.slice.builders.ListBuilder.RatingBuilder;
import androidx.slice.builders.ListBuilder.RowBuilder;
import androidx.slice.builders.SelectionBuilder;
import androidx.slice.builders.SliceAction;
import androidx.slice.core.SliceQuery;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * @hide
 */
@RestrictTo(LIBRARY)
@RequiresApi(19)
public class ListBuilderImpl extends TemplateBuilderImpl implements ListBuilder {
    private List<Slice> mSliceActions;
    private Set<String> mKeywords;
    private Slice mSliceHeader;
    private boolean mIsError;
    private boolean mFirstRowChecked;
    private boolean mIsFirstRowTypeValid;
    private boolean mFirstRowHasText;
    private Bundle mHostExtras;

    public ListBuilderImpl(@Nullable final Slice.Builder b, @Nullable final SliceSpec spec) {
        this(b, spec, new SystemClock());
    }

    /**
     */
    public ListBuilderImpl(@Nullable final Slice.Builder b, @Nullable final SliceSpec spec,
            @NonNull final Clock clock) {
        super(b, spec, clock);
    }

    /**
     */
    @Override
    public void apply(@NonNull final Slice.Builder builder) {
        builder.addLong(getClock().currentTimeMillis(), SUBTYPE_MILLIS, HINT_LAST_UPDATED);
        if (mSliceHeader != null) {
            builder.addSubSlice(mSliceHeader);
        }
        if (mSliceActions != null) {
            Slice.Builder sb = new Slice.Builder(builder);
            for (int i = 0; i < mSliceActions.size(); i++) {
                sb.addSubSlice(mSliceActions.get(i));
            }
            builder.addSubSlice(sb.addHints(HINT_ACTIONS).build());
        }
        if (mIsError) {
            builder.addHints(HINT_ERROR);
        }
        if (mKeywords != null) {
            Slice.Builder sb = new Slice.Builder(getBuilder());
            for (String keyword : mKeywords) {
                sb.addText(keyword, null);
            }
            getBuilder().addSubSlice(sb.addHints(HINT_KEYWORDS).build());
        }
        if (mHostExtras != null) {
            builder.addItem(new SliceItem(mHostExtras, FORMAT_BUNDLE, SUBTYPE_HOST_EXTRAS,
                    new String[0]));
        }
    }

    /**
     * Construct the slice.
     */
    @Override
    @NonNull
    public Slice build() {
        Slice slice = super.build();
        boolean isLoading = SliceQuery.find(slice, null, HINT_PARTIAL, null) != null;
        boolean isEmpty = SliceQuery.find(slice, FORMAT_SLICE, HINT_LIST_ITEM, null) == null;
        String[] hints = new String[] {HINT_SHORTCUT, HINT_TITLE};
        SliceItem action = SliceQuery.find(slice, FORMAT_ACTION, hints, null);
        List<SliceItem> possiblePrimaries = SliceQuery.findAll(slice, FORMAT_SLICE, hints, null);
        if (!isLoading && !isEmpty && action == null
                && (possiblePrimaries == null || possiblePrimaries.isEmpty())) {
            throw new IllegalStateException("A slice requires a primary action; ensure one of your "
                    + "builders has called #setPrimaryAction with a valid SliceAction.");
        }
        if (mFirstRowChecked && !mIsFirstRowTypeValid) {
            throw new IllegalStateException("A slice cannot have the first row be"
                    + " constructed from a GridRowBuilder, consider using #setHeader.");
        }
        if (mFirstRowChecked && !mFirstRowHasText) {
            throw new IllegalStateException("A slice requires the first row to have some text.");
        }
        return slice;
    }

    /**
     * Add a row to list builder.
     */
    @Override
    public void addRow(@NonNull RowBuilder builder) {
        RowBuilderImpl impl = new RowBuilderImpl(createChildBuilder());
        impl.fillFrom(builder);
        checkRow(true, impl.hasText());
        addRow(impl);
    }

    /**
     * Add a row to list builder.
     */
    public void addRow(@NonNull RowBuilderImpl builder) {
        checkRow(true, builder.hasText());
        builder.getBuilder().addHints(HINT_LIST_ITEM);
        if (builder.isEndOfSection()) {
            builder.getBuilder().addHints(HINT_END_OF_SECTION);
        }
        getBuilder().addSubSlice(builder.build());
    }

    /**
     */
    @Override
    public void addGridRow(@NonNull GridRowBuilder builder) {
        checkRow(false, false);
        GridRowBuilderListV1Impl impl = new GridRowBuilderListV1Impl(this, builder);
        impl.getBuilder().addHints(HINT_LIST_ITEM);
        getBuilder().addSubSlice(impl.build());
    }

    /**
     */
    @Override
    public void setHeader(@NonNull HeaderBuilder builder) {
        mIsFirstRowTypeValid = true;
        mFirstRowHasText = true;
        mFirstRowChecked = true;
        HeaderBuilderImpl impl = new HeaderBuilderImpl(this);
        impl.fillFrom(builder);
        mSliceHeader = impl.build();
    }

    /**
     */
    @Override
    public void addAction(@NonNull SliceAction action) {
        if (mSliceActions == null) {
            mSliceActions = new ArrayList<>();
        }
        Slice.Builder b = new Slice.Builder(getBuilder()).addHints(HINT_ACTIONS);
        mSliceActions.add(action.buildSlice(b));
    }

    @Override
    public void addRating(@NonNull final RatingBuilder builder) {
        RatingBuilderImpl impl = new RatingBuilderImpl(createChildBuilder(), builder);
        checkRow(true, impl.hasText());
        getBuilder().addSubSlice(impl.build(), SUBTYPE_RANGE);
    }

    @Override
    public void addInputRange(@NonNull final InputRangeBuilder builder) {
        InputRangeBuilderImpl impl = new InputRangeBuilderImpl(createChildBuilder(), builder);
        checkRow(true, impl.hasText());
        getBuilder().addSubSlice(impl.build(), SUBTYPE_RANGE);
    }

    @Override
    public void addRange(@NonNull final RangeBuilder builder) {
        RangeBuilderImpl impl = new RangeBuilderImpl(createChildBuilder(), builder);
        checkRow(true, impl.hasText());
        getBuilder().addSubSlice(impl.build(), SUBTYPE_RANGE);
    }

    @Override
    public void addSelection(@NonNull final SelectionBuilder builder) {
        if (getSpec().canRender(SliceSpecs.LIST_V2)) {
            getBuilder().addSubSlice(
                    new SelectionBuilderListV2Impl(createChildBuilder(), builder).build(),
                    SUBTYPE_SELECTION);
        } else {
            getBuilder().addSubSlice(
                    new SelectionBuilderBasicImpl(createChildBuilder(), builder).build());
        }
    }

    /**
     */
    @Override
    public void setSeeMoreRow(@NonNull final RowBuilder builder) {
        RowBuilderImpl impl = new RowBuilderImpl(createChildBuilder());
        impl.fillFrom(builder);
        impl.getBuilder().addHints(HINT_SEE_MORE);
        getBuilder().addSubSlice(impl.build());
    }

    /**
     */
    @Override
    public void setSeeMoreAction(@NonNull final PendingIntent intent) {
        getBuilder().addSubSlice(
                new Slice.Builder(getBuilder())
                        .addHints(HINT_SEE_MORE)
                        .addAction(intent, new Slice.Builder(getBuilder())
                                .addHints(HINT_SEE_MORE).build(), null)
                        .build());
    }

    /**
     */
    @Override
    public void setColor(@ColorInt int color) {
        getBuilder().addInt(color, SUBTYPE_COLOR);
    }

    /**
     */
    @Override
    public void setKeywords(@NonNull Set<String> keywords) {
        mKeywords = keywords;
    }

    /**
     */
    @Override
    public void setTtl(long ttl) {
        long expiry = ttl == INFINITY ? INFINITY : getClock().currentTimeMillis() + ttl;
        getBuilder().addTimestamp(expiry, SUBTYPE_MILLIS, HINT_TTL);
    }

    @Override
    @RequiresApi(26)
    public void setTtl(@Nullable Duration ttl) {
        setTtl(ttl == null ? INFINITY : ttl.toMillis());
    }

    @Override
    public void setIsError(boolean isError) {
        mIsError = isError;
    }

    @Override
    public void setLayoutDirection(int layoutDirection) {
        getBuilder().addInt(layoutDirection, SUBTYPE_LAYOUT_DIRECTION);
    }

    @Override
    @RequiresApi(21)
    public void setHostExtras(@NonNull PersistableBundle extras) {
        mHostExtras = ConvertPersistableBundleApi21Impl.toBundle(extras);
    }

    @RequiresApi(21)
    private static class ConvertPersistableBundleApi21Impl {
        private ConvertPersistableBundleApi21Impl() {}
        static Bundle toBundle(@NonNull PersistableBundle extras) {
            return new Bundle(extras);
        }
    }

    /**
     * There are some requirements that first row of a list is not a grid row and has some text.
     * This method helps check whether first row fulfils these requirements.
     */
    private void checkRow(boolean isTypeValid, boolean hasText) {
        if (!mFirstRowChecked) {
            mFirstRowChecked = true;
            mIsFirstRowTypeValid = isTypeValid;
            mFirstRowHasText = hasText;
        }
    }

    /**
     * Builder to construct an input row.
     */
    public static class RangeBuilderImpl extends TemplateBuilderImpl {
        protected int mMin = 0;
        protected int mMax = 100;
        protected int mValue = 0;
        protected boolean mValueSet = false;
        @Nullable
        protected CharSequence mTitle;
        @Nullable
        protected CharSequence mSubtitle;
        @Nullable
        protected CharSequence mContentDescr;
        @Nullable
        protected SliceAction mPrimaryAction;
        protected int mLayoutDir = -1;
        private int mMode = RANGE_MODE_DETERMINATE;
        private Slice mStartItem;

        RangeBuilderImpl(Slice.Builder sb, RangeBuilder builder) {
            super(sb, null);
            if (builder != null) {
                mValueSet = builder.isValueSet();
                mMax = builder.getMax();
                mValue = builder.getValue();
                mTitle = builder.getTitle();
                mSubtitle = builder.getSubtitle();
                mContentDescr = builder.getContentDescription();
                mPrimaryAction = builder.getPrimaryAction();
                mLayoutDir = builder.getLayoutDirection();
                mMode = builder.getMode();
                if (builder.getTitleIcon() != null) {
                    setTitleItem(builder.getTitleIcon(), builder.getTitleImageMode(),
                            builder.isTitleItemLoading());
                }
            }
        }

        void setTitleItem(IconCompat icon, int imageMode, boolean isLoading) {
            Slice.Builder sb = new Slice.Builder(getBuilder())
                    .addIcon(icon, null, parseImageMode(imageMode, isLoading));
            if (isLoading) {
                sb.addHints(HINT_PARTIAL);
            }
            mStartItem = sb.addHints(HINT_TITLE).build();
        }

        @Override
        public void apply(@NonNull Slice.Builder builder) {
            if (!mValueSet) {
                // Unset, make it whatever min is
                mValue = mMin;
            }
            if (!(mMin <= mValue && mValue <= mMax && mMin < mMax))  {
                throw new IllegalArgumentException(
                        "Invalid range values, min=" + mMin + ", value=" + mValue + ", max=" + mMax
                + " ensure value falls within (min, max) and min < max.");
            }

            if (mStartItem != null) {
                builder.addSubSlice(mStartItem);
            }
            if (mTitle != null) {
                builder.addText(mTitle, null, HINT_TITLE);
            }
            if (mSubtitle != null) {
                builder.addText(mSubtitle, null);
            }
            if (mContentDescr != null) {
                builder.addText(mContentDescr, SUBTYPE_CONTENT_DESCRIPTION);
            }
            if (mPrimaryAction != null) {
                mPrimaryAction.setPrimaryAction(builder);
            }
            if (mLayoutDir != -1) {
                builder.addInt(mLayoutDir, SUBTYPE_LAYOUT_DIRECTION);
            }
            builder.addHints(HINT_LIST_ITEM)
                    .addInt(mMin, SUBTYPE_MIN)
                    .addInt(mMax, SUBTYPE_MAX)
                    .addInt(mValue, SUBTYPE_VALUE)
                    .addInt(mMode, SUBTYPE_RANGE_MODE);
        }
        boolean hasText() {
            return mTitle != null || mSubtitle != null;
        }
    }

    /**
     * Builder to construct an input range row.
     */
    public static class InputRangeBuilderImpl extends RangeBuilderImpl {
        private final PendingIntent mAction;
        private final IconCompat mThumb;
        private Slice mStartItem;
        private final ArrayList<Slice> mEndItems = new ArrayList<>();

        InputRangeBuilderImpl(Slice.Builder sb, InputRangeBuilder builder) {
            super(sb, null);
            mValueSet = builder.isValueSet();
            mMin = builder.getMin();
            mMax = builder.getMax();
            mValue = builder.getValue();
            mTitle = builder.getTitle();
            mSubtitle = builder.getSubtitle();
            mContentDescr = builder.getContentDescription();
            mPrimaryAction = builder.getPrimaryAction();
            mLayoutDir = builder.getLayoutDirection();
            mAction = builder.getInputAction();
            mThumb = builder.getThumb();
            if (builder.getTitleIcon() != null) {
                setTitleItem(builder.getTitleIcon(), builder.getTitleImageMode(),
                        builder.isTitleItemLoading());
            }
            List<Object> endItems = builder.getEndItems();
            List<Integer> endTypes = builder.getEndTypes();
            List<Boolean> endLoads = builder.getEndLoads();
            for (int i = 0; i < endItems.size(); i++) {
                if (endTypes.get(i) == InputRangeBuilder.TYPE_ACTION) {
                    addEndItem((SliceAction) endItems.get(i), endLoads.get(i));
                }
            }
        }

        @Override
        void setTitleItem(IconCompat icon, int imageMode, boolean isLoading) {
            Slice.Builder sb = new Slice.Builder(getBuilder())
                    .addIcon(icon, null, parseImageMode(imageMode, isLoading));
            if (isLoading) {
                sb.addHints(HINT_PARTIAL);
            }
            mStartItem = sb.addHints(HINT_TITLE).build();
        }

        private void addEndItem(@NonNull SliceAction action, boolean isLoading) {
            Slice.Builder sb = new Slice.Builder(getBuilder());
            if (isLoading) {
                sb.addHints(HINT_PARTIAL);
            }
            mEndItems.add(action.buildSlice(sb));
        }

        @Override
        public void apply(@NonNull Slice.Builder builder) {
            if (mAction == null) {
                throw new IllegalStateException("Input ranges must have an associated action.");
            }
            Slice.Builder sb = new Slice.Builder(builder);
            super.apply(sb);
            if (mThumb != null) {
                sb.addIcon(mThumb, null);
            }
            builder.addAction(mAction, sb.build(), SUBTYPE_RANGE).addHints(HINT_LIST_ITEM);
            if (mStartItem != null) {
                builder.addSubSlice(mStartItem);
            }
            for (int i = 0; i < mEndItems.size(); i++) {
                builder.addSubSlice(mEndItems.get(i));
            }
        }
    }

    /**
     * Builder to construct an input range row.
     */
    public static class RatingBuilderImpl extends TemplateBuilderImpl {
        private final PendingIntent mAction;
        protected int mMin = 0;
        protected int mMax = 100;
        protected int mValue = 0;
        @Nullable
        protected CharSequence mTitle;
        @Nullable
        protected CharSequence mSubtitle;
        @Nullable
        protected CharSequence mContentDescr;
        @Nullable
        protected SliceAction mPrimaryAction;
        protected boolean mValueSet = false;
        private Slice mStartItem;

        RatingBuilderImpl(Slice.Builder sb, RatingBuilder builder) {
            super(sb, null);
            mValueSet = builder.isValueSet();
            mMin = builder.getMin();
            mMax = builder.getMax();
            mValue = (int) builder.getValue();
            mTitle = builder.getTitle();
            mSubtitle = builder.getSubtitle();
            mContentDescr = builder.getContentDescription();
            mPrimaryAction = builder.getPrimaryAction();
            mAction = builder.getInputAction();
            if (builder.getTitleIcon() != null) {
                setTitleItem(builder.getTitleIcon(), builder.getTitleImageMode(),
                        builder.isTitleItemLoading());
            }
        }

        void setTitleItem(IconCompat icon, int imageMode, boolean isLoading) {
            Slice.Builder sb = new Slice.Builder(getBuilder())
                    .addIcon(icon, null, parseImageMode(imageMode, isLoading));
            if (isLoading) {
                sb.addHints(HINT_PARTIAL);
            }
            mStartItem = sb.addHints(HINT_TITLE).build();
        }

        @Override
        public void apply(@NonNull Slice.Builder builder) {
            if (mAction == null) {
                throw new IllegalStateException("Star rating must have an associated action.");
            }

            if (!mValueSet) {
                // Unset, make it outside whatever min is, to represent an unset value
                mValue = mMin - 1;
            }
            if (mTitle != null) {
                builder.addText(mTitle, null, HINT_TITLE);
            }
            if (mSubtitle != null) {
                builder.addText(mSubtitle, null);
            }
            if (mContentDescr != null) {
                builder.addText(mContentDescr, SUBTYPE_CONTENT_DESCRIPTION);
            }
            if (mPrimaryAction != null) {
                mPrimaryAction.setPrimaryAction(builder);
            }
            if (mStartItem != null) {
                builder.addSubSlice(mStartItem);
            }
            Slice.Builder sb = new Slice.Builder(builder);

            sb.addHints(HINT_LIST_ITEM)
                    .addInt(mMin, SUBTYPE_MIN)
                    .addInt(mMax, SUBTYPE_MAX)
                    .addInt(mValue, SUBTYPE_VALUE)
                    .addInt(RANGE_MODE_STAR_RATING, SUBTYPE_RANGE_MODE);
            builder.addAction(mAction, sb.build(), SUBTYPE_RANGE).addHints(HINT_LIST_ITEM);
        }

        boolean hasText() {
            return mTitle != null || mSubtitle != null;
        }
    }

    /**
     *
     */
    public static class RowBuilderImpl extends TemplateBuilderImpl {

        private boolean mIsEndOfSection;
        private SliceAction mPrimaryAction;
        private SliceItem mTitleItem;
        private SliceItem mSubtitleItem;
        private Slice mStartItem;
        private final ArrayList<Slice> mEndItems = new ArrayList<>();
        private CharSequence mContentDescr;

        /**
         */
        RowBuilderImpl(Slice.Builder builder) {
            super(builder, null);
        }

        @SuppressWarnings("unchecked")
        void fillFrom(RowBuilder builder) {
            if (builder.getUri() != null) {
                setBuilder(new Slice.Builder(builder.getUri()));
            }
            setPrimaryAction(builder.getPrimaryAction());
            mIsEndOfSection = builder.isEndOfSection();
            if (builder.getLayoutDirection() != -1) {
                setLayoutDirection(builder.getLayoutDirection());
            }
            if (builder.getTitleAction() != null || builder.isTitleActionLoading()) {
                setTitleItem(builder.getTitleAction(), builder.isTitleActionLoading());
            } else if (builder.getTitleIcon() != null || builder.isTitleItemLoading()) {
                setTitleItem(builder.getTitleIcon(), builder.getTitleImageMode(),
                        builder.isTitleItemLoading());
            } else if (builder.getTimeStamp() != -1L) {
                setTitleItem(builder.getTimeStamp());
            }
            if (builder.getTitle() != null || builder.isTitleLoading()) {
                setTitle(builder.getTitle(), builder.isTitleLoading());
            }
            if (builder.getSubtitle() != null || builder.isSubtitleLoading()) {
                setSubtitle(builder.getSubtitle(), builder.isSubtitleLoading());
            }
            if (builder.getContentDescription() != null) {
                setContentDescription(builder.getContentDescription());
            }
            List<Object> endItems = builder.getEndItems();
            List<Integer> endTypes = builder.getEndTypes();
            List<Boolean> endLoads = builder.getEndLoads();
            for (int i = 0; i < endItems.size(); i++) {
                switch (endTypes.get(i)) {
                    case RowBuilder.TYPE_TIMESTAMP:
                        addEndItem((Long) endItems.get(i));
                        break;
                    case RowBuilder.TYPE_ACTION:
                        addEndItem((SliceAction) endItems.get(i), endLoads.get(i));
                        break;
                    case RowBuilder.TYPE_ICON:
                        Pair<IconCompat, Integer> pair =
                                (Pair<IconCompat, Integer>) endItems.get(i);
                        addEndItem(pair.first, pair.second, endLoads.get(i));
                        break;
                }
            }
        }

        /**
         */
        private void setTitleItem(long timeStamp) {
            mStartItem = new Slice.Builder(getBuilder())
                    .addTimestamp(timeStamp, null).addHints(HINT_TITLE).build();
        }

        /**
         */
        protected void setTitleItem(@NonNull final IconCompat icon, final int imageMode) {
            setTitleItem(icon, imageMode, false /* isLoading */);
        }

        /**
         */
        private void setTitleItem(@NonNull final IconCompat icon, final int imageMode,
                final boolean isLoading) {
            Slice.Builder sb = new Slice.Builder(getBuilder())
                    .addIcon(icon, null, parseImageMode(imageMode, isLoading));
            if (isLoading) {
                sb.addHints(HINT_PARTIAL);
            }
            mStartItem = sb.addHints(HINT_TITLE).build();
        }

        /**
         */
        private void setTitleItem(@NonNull final SliceAction action, final boolean isLoading) {
            Slice.Builder sb = new Slice.Builder(getBuilder()).addHints(HINT_TITLE);
            if (isLoading) {
                sb.addHints(HINT_PARTIAL);
            }
            mStartItem = action.buildSlice(sb);
        }

        /**
         */
        private void setPrimaryAction(@NonNull SliceAction action) {
            mPrimaryAction = action;
        }

        /**
         */
        private void setTitle(@NonNull final CharSequence title, final boolean isLoading) {
            mTitleItem = new SliceItem(title, FORMAT_TEXT, null, new String[] {HINT_TITLE});
            if (isLoading) {
                mTitleItem.addHint(HINT_PARTIAL);
            }
        }

        /**
         */
        protected void setSubtitle(@NonNull final CharSequence subtitle) {
            setSubtitle(subtitle, false /* isLoading */);
        }

        /**
         */
        private void setSubtitle(@NonNull final CharSequence subtitle, final boolean isLoading) {
            mSubtitleItem = new SliceItem(subtitle, FORMAT_TEXT, null, new String[0]);
            if (isLoading) {
                mSubtitleItem.addHint(HINT_PARTIAL);
            }
        }

        /**
         */
        protected void addEndItem(long timeStamp) {
            mEndItems.add(new Slice.Builder(getBuilder()).addTimestamp(timeStamp,
                    null).build());
        }

        /**
         */
        private void addEndItem(@NonNull final IconCompat icon, final int imageMode,
                final boolean isLoading) {
            Slice.Builder sb = new Slice.Builder(getBuilder())
                    .addIcon(icon, null, parseImageMode(imageMode, isLoading));
            if (isLoading) {
                sb.addHints(HINT_PARTIAL);
            }
            mEndItems.add(sb.build());
        }

        /**
         */
        private void addEndItem(@NonNull final SliceAction action, final boolean isLoading) {
            Slice.Builder sb = new Slice.Builder(getBuilder());
            if (isLoading) {
                sb.addHints(HINT_PARTIAL);
            }
            mEndItems.add(action.buildSlice(sb));
        }

        private void setContentDescription(CharSequence description) {
            mContentDescr = description;
        }

        private void setLayoutDirection(int layoutDirection) {
            getBuilder().addInt(layoutDirection, SUBTYPE_LAYOUT_DIRECTION);
        }

        /**
         */
        public boolean isEndOfSection() {
            return mIsEndOfSection;
        }

        boolean hasText() {
            return mTitleItem != null || mSubtitleItem != null;
        }

        /**
         */
        @Override
        public void apply(@NonNull Slice.Builder b) {
            if (mStartItem != null) {
                b.addSubSlice(mStartItem);
            }
            if (mTitleItem != null) {
                b.addItem(mTitleItem);
            }
            if (mSubtitleItem != null) {
                b.addItem(mSubtitleItem);
            }
            for (int i = 0; i < mEndItems.size(); i++) {
                Slice item = mEndItems.get(i);
                b.addSubSlice(item);
            }
            if (mContentDescr != null) {
                b.addText(mContentDescr, SUBTYPE_CONTENT_DESCRIPTION);
            }
            if (mPrimaryAction != null) {
                mPrimaryAction.setPrimaryAction(b);
            }
        }
    }

    /**
     */
    public static class HeaderBuilderImpl extends TemplateBuilderImpl {

        private SliceItem mTitleItem;
        private SliceItem mSubtitleItem;
        private SliceItem mSummaryItem;
        private SliceAction mPrimaryAction;
        private CharSequence mContentDescr;

        /**
         */
        HeaderBuilderImpl(@NonNull ListBuilderImpl parent) {
            super(parent.createChildBuilder(), null);
        }
        void fillFrom(HeaderBuilder builder) {
            if (builder.getUri() != null) {
                setBuilder(new Slice.Builder(builder.getUri()));
            }
            setPrimaryAction(builder.getPrimaryAction());
            if (builder.getLayoutDirection() != -1) {
                setLayoutDirection(builder.getLayoutDirection());
            }
            if (builder.getTitle() != null || builder.isTitleLoading()) {
                setTitle(builder.getTitle(), builder.isTitleLoading());
            }
            if (builder.getSubtitle() != null || builder.isSubtitleLoading()) {
                setSubtitle(builder.getSubtitle(), builder.isSubtitleLoading());
            }
            if (builder.getSummary() != null || builder.isSummaryLoading()) {
                setSummary(builder.getSummary(), builder.isSummaryLoading());
            }
            if (builder.getContentDescription() != null) {
                setContentDescription(builder.getContentDescription());
            }
        }

        /**
         */
        @Override
        public void apply(@NonNull Slice.Builder b) {
            if (mTitleItem != null) {
                b.addItem(mTitleItem);
            }
            if (mSubtitleItem != null) {
                b.addItem(mSubtitleItem);
            }
            if (mSummaryItem != null) {
                b.addItem(mSummaryItem);
            }
            if (mContentDescr != null) {
                b.addText(mContentDescr, SUBTYPE_CONTENT_DESCRIPTION);
            }
            if (mPrimaryAction != null) {
                mPrimaryAction.setPrimaryAction(b);
            }
            if (mSubtitleItem == null && mTitleItem == null) {
                throw new IllegalStateException("Header requires a title or subtitle to be set.");
            }
        }

        /**
         */
        private void setTitle(CharSequence title, boolean isLoading) {
            mTitleItem = new SliceItem(title, FORMAT_TEXT, null, new String[] {HINT_TITLE});
            if (isLoading) {
                mTitleItem.addHint(HINT_PARTIAL);
            }
        }

        /**
         */
        private void setSubtitle(CharSequence subtitle, boolean isLoading) {
            mSubtitleItem = new SliceItem(subtitle, FORMAT_TEXT, null, new String[0]);
            if (isLoading) {
                mSubtitleItem.addHint(HINT_PARTIAL);
            }
        }

        /**
         */
        private void setSummary(CharSequence summarySubtitle, boolean isLoading) {
            mSummaryItem = new SliceItem(summarySubtitle, FORMAT_TEXT, null,
                    new String[] {HINT_SUMMARY});
            if (isLoading) {
                mSummaryItem.addHint(HINT_PARTIAL);
            }
        }

        /**
         */
        private void setPrimaryAction(SliceAction action) {
            mPrimaryAction = action;
        }

        /**
         */
        private void setContentDescription(CharSequence description) {
            mContentDescr = description;
        }

        private void setLayoutDirection(int layoutDirection) {
            getBuilder().addInt(layoutDirection, SUBTYPE_LAYOUT_DIRECTION);
        }
    }
}


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

package net.groboclown.p4.server.impl.connection.impl;

import com.perforce.p4java.core.file.IExtendedFileSpec;
import com.perforce.p4java.exception.RequestException;
import com.perforce.p4java.server.IServerMessage;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class OpenFileStatus {
    private final Set<IExtendedFileSpec> add = new HashSet<>();
    private final Set<IExtendedFileSpec> delete = new HashSet<>();
    private final Set<IExtendedFileSpec> edit = new HashSet<>();
    private final Set<IExtendedFileSpec> filesWithMessage = new HashSet<>();
    private final List<IServerMessage> messages = new ArrayList<>();
    private final List<IExtendedFileSpec> skipped = new ArrayList<>();

    // Note: everything in move is in one of the other collections.
    private final Map<IExtendedFileSpec, IExtendedFileSpec> moveMap = new HashMap<>();

    public OpenFileStatus(Collection<IExtendedFileSpec> status) {
        final Map<String, IExtendedFileSpec> integrateMap = new HashMap<>();

        for (IExtendedFileSpec spec : status) {
            if (spec == null) {
                continue;
            }
            if (spec.getStatusMessage() != null) {
                if (spec.getDepotPath() != null || spec.getClientPath() != null
                        || spec.getOriginalPathString() != null) {
                    filesWithMessage.add(spec);
                }
                messages.add(spec.getStatusMessage());
            } else if (spec.getAction() == null) {
                skipped.add(spec);
            } else {
                switch (spec.getAction()) {
                    case ADD:
                    case ADDED:
                    case ADD_EDIT:
                        add.add(spec);
                        break;

                    case EDIT:
                        edit.add(spec);
                        break;

                    case DELETE:
                    case DELETED:
                        delete.add(spec);
                        break;

                    case BRANCH:
                        add.add(spec);
                        if (spec.getFromFile() != null) {
                            integrateMap.put(spec.getFromFile(), spec);
                        }
                        break;

                    case INTEGRATE:
                        edit.add(spec);
                        if (spec.getFromFile() != null) {
                            integrateMap.put(spec.getFromFile(), spec);
                        }
                        break;

                    // See #194 - move add/delete need special handling.
                    case MOVE_ADD:
                        add.add(spec);
                        if (spec.getMovedFile() != null) {
                            integrateMap.put(spec.getMovedFile(), spec);
                        }
                        break;
                    case MOVE_DELETE:
                        delete.add(spec);
                        break;

                    default:
                        skipped.add(spec);
                        break;
                }
            }
        }

        for (IExtendedFileSpec spec : delete) {
            if (integrateMap.containsKey(spec.getDepotPath().getPathString())) {
                moveMap.put(spec, integrateMap.get(spec.getDepotPath().getPathString()));
            }
        }
    }

    public Set<IExtendedFileSpec> getAdd() {
        return add;
    }

    public boolean hasAdd() {
        return !add.isEmpty();
    }

    public Set<IExtendedFileSpec> getDelete() {
        return delete;
    }

    public boolean hasDelete() {
        return !delete.isEmpty();
    }

    public Set<IExtendedFileSpec> getEdit() {
        return edit;
    }

    public boolean hasEdit() {
        return !edit.isEmpty();
    }

    public boolean hasAddEdit() {
        return !edit.isEmpty() || !add.isEmpty();
    }

    public int getOpenedCount() {
        return add.size() + delete.size() + edit.size();
    }

    public boolean hasOpen() {
        return getOpenedCount() > 0;
    }

    public List<IExtendedFileSpec> getOpen() {
        List<IExtendedFileSpec> ret = new ArrayList<>();
        ret.addAll(add);
        ret.addAll(delete);
        ret.addAll(edit);
        return ret;
    }

    public List<IServerMessage> getMessages() {
        return messages;
    }

    public boolean hasMessages() {
        return !messages.isEmpty();
    }

    public boolean isNotOnServer() {
        // TODO this isn't 100% accurate, but a message is usually "blah - no such file(s)."
        // It takes the form of no skipped (means known by the server, but has no open status)
        // and no open files.
        return !hasOpen() && !hasSkipped() && hasMessages();
    }

    /**
     *
     * @return files not categorized because they had an info/warning/error associated with them.
     */
    public Set<IExtendedFileSpec> getFilesWithMessages() {
        return filesWithMessage;
    }

    public List<IExtendedFileSpec> getSkipped() {
        return skipped;
    }

    public boolean hasSkipped() {
        return !skipped.isEmpty();
    }

    /**
     * If the messages contain an error, then throw an exception.
     */
    public void throwIfError()
            throws RequestException {
        // TODO look at calling out to MessageStatusUtil
        for (IServerMessage msg : messages) {
            if (msg != null && msg.isError()) {
                throw new RequestException(msg);
            }
        }
    }

    /**
     *
     * @return mapping of from (deleted) spec to new location (added/edited).
     */
    public Map<IExtendedFileSpec, IExtendedFileSpec> getMoveMap() {
        return moveMap;
    }

    @Override
    public String toString() {
        return "Status{ add: " + add + "; edit: " + edit + "; delete: " + delete +
                "; skipped: " + skipped + "; messages: " + messages + " }";
    }
}

redpajama_set_name: RedPajamaGithub


text:
require "spec_helper"
require "chef/node/attribute_collections"

describe Chef::Node::VividMash do
  let(:root) { instance_double(Chef::Node::Attribute) }

  let(:vivid) do
    Chef::Node::VividMash.new(
      { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil },
      root
    )
  end

  context "without a root node" do
    let(:vivid) do
      Chef::Node::VividMash.new(
        { "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil }
      )
    end

    it "sets the root to the root object" do
      expect(vivid["one"]["two"].__root__).to eql(vivid)
    end

    it "does not send reset cache" do
      # if we setup the expectation here then the object winds up responding to :reset_cache and then it fails...
      #   expect(vivid).not_to receive(:reset_cache)
      # but even so we expect to blow up here with NoMethodError if we screw up and send :reset_cache to a root VividMash
      vivid["one"]["foo"] = "bar"
    end
  end

  context "#[]" do
    it "works with array slices" do
      expect(vivid["array"][1, 2]).to eql([1, 2])
    end
  end

  context "#[]=" do
    it "works with array slices" do
      vivid["array"][3, 2] = [ 3, 4 ]
      expect(vivid["array"]).to eql([0, 1, 2, 3, 4])
    end

    it "deep converts values through arrays" do
      expect(root).to receive(:reset_cache).with("foo")
      vivid["foo"] = [ { :bar => true } ]
      expect(vivid["foo"].class).to eql(Chef::Node::AttrArray)
      expect(vivid["foo"][0].class).to eql(Chef::Node::VividMash)
      expect(vivid["foo"][0]["bar"]).to be true
    end

    it "deep converts values through nested arrays" do
      expect(root).to receive(:reset_cache).with("foo")
      vivid["foo"] = [ [ { :bar => true } ] ]
      expect(vivid["foo"].class).to eql(Chef::Node::AttrArray)
      expect(vivid["foo"][0].class).to eql(Chef::Node::AttrArray)
      expect(vivid["foo"][0][0].class).to eql(Chef::Node::VividMash)
      expect(vivid["foo"][0][0]["bar"]).to be true
    end

    it "deep converts values through hashes" do
      expect(root).to receive(:reset_cache).with("foo")
      vivid["foo"] = { baz: { :bar => true } }
      expect(vivid["foo"]).to be_an_instance_of(Chef::Node::VividMash)
      expect(vivid["foo"]["baz"]).to be_an_instance_of(Chef::Node::VividMash)
      expect(vivid["foo"]["baz"]["bar"]).to be true
    end
  end

  context "#read" do
    before do
      expect(root).not_to receive(:reset_cache)
    end

    it "reads hashes deeply" do
      expect(vivid.read("one", "two", "three")).to eql("four")
    end

    it "does not trainwreck when hitting hash keys that do not exist" do
      expect(vivid.read("one", "five", "six")).to eql(nil)
    end

    it "does not trainwreck when hitting an array with an out of bounds index" do
      expect(vivid.read("array", 5, "one")).to eql(nil)
    end

    it "does not trainwreck when hitting an array with a string key" do
      expect(vivid.read("array", "one", "two")).to eql(nil)
    end

    it "does not trainwreck when traversing a nil" do
      expect(vivid.read("nil", "one", "two")).to eql(nil)
    end
  end

  context "#exist?" do
    before do
      expect(root).not_to receive(:reset_cache)
    end

    it "true if there's a hash key there" do
      expect(vivid.exist?("one", "two", "three")).to be true
    end

    it "true for intermediate hashes" do
      expect(vivid.exist?("one")).to be true
    end

    it "true for arrays that exist" do
      expect(vivid.exist?("array", 1)).to be true
    end

    it "true when the value of the key is nil" do
      expect(vivid.exist?("nil")).to be true
    end

    it "false when attributes don't exist" do
      expect(vivid.exist?("one", "five", "six")).to be false
    end

    it "false when traversing a non-container" do
      expect(vivid.exist?("one", "two", "three", "four")).to be false
    end

    it "false when an array index does not exist" do
      expect(vivid.exist?("array", 3)).to be false
    end

    it "false when traversing a nil" do
      expect(vivid.exist?("nil", "foo", "bar")).to be false
    end
  end

  context "#read!" do
    before do
      expect(root).not_to receive(:reset_cache)
    end

    it "reads hashes deeply" do
      expect(vivid.read!("one", "two", "three")).to eql("four")
    end

    it "reads arrays deeply" do
      expect(vivid.read!("array", 1)).to eql(1)
    end

    it "throws an exception when attributes do not exist" do
      expect { vivid.read!("one", "five", "six") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
    end

    it "throws an exception when traversing a non-container" do
      expect { vivid.read!("one", "two", "three", "four") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
    end

    it "throws an exception when an array element does not exist" do
      expect { vivid.read!("array", 3) }.to raise_error(Chef::Exceptions::NoSuchAttribute)
    end
  end

  context "#write" do
    it "should write into hashes" do
      expect(root).to receive(:reset_cache).at_least(:once).with("one")
      vivid.write("one", "five", "six")
      expect(vivid["one"]["five"]).to eql("six")
    end

    it "should deeply autovivify" do
      expect(root).to receive(:reset_cache).at_least(:once).with("one")
      vivid.write("one", "five", "six", "seven", "eight", "nine", "ten")
      expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
    end

    it "should raise an exception if you overwrite an array with a hash" do
      expect(root).to receive(:reset_cache).at_least(:once).with("array")
      vivid.write("array", "five", "six")
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => "six" }, "nil" => nil })
    end

    it "should raise an exception if you traverse through an array with a hash" do
      expect(root).to receive(:reset_cache).at_least(:once).with("array")
      vivid.write("array", "five", "six", "seven")
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => { "five" => { "six" => "seven" } }, "nil" => nil })
    end

    it "should raise an exception if you overwrite a string with a hash" do
      expect(root).to receive(:reset_cache).at_least(:once).with("one")
      vivid.write("one", "two", "three", "four", "five")
      expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => "five" } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
    end

    it "should raise an exception if you traverse through a string with a hash" do
      expect(root).to receive(:reset_cache).at_least(:once).with("one")
      vivid.write("one", "two", "three", "four", "five", "six")
      expect(vivid).to eql({ "one" => { "two" => { "three" => { "four" => { "five" => "six" } } } }, "array" => [ 0, 1, 2 ], "nil" => nil })
    end

    it "should raise an exception if you overwrite a nil with a hash" do
      expect(root).to receive(:reset_cache).at_least(:once).with("nil")
      vivid.write("nil", "one", "two")
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => "two" } })
    end

    it "should raise an exception if you traverse through a nil with a hash" do
      expect(root).to receive(:reset_cache).at_least(:once).with("nil")
      vivid.write("nil", "one", "two", "three")
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => { "one" => { "two" => "three" } } })
    end

    it "writes with a block" do
      expect(root).to receive(:reset_cache).at_least(:once).with("one")
      vivid.write("one", "five") { "six" }
      expect(vivid["one"]["five"]).to eql("six")
    end
  end

  context "#write!" do
    it "should write into hashes" do
      expect(root).to receive(:reset_cache).at_least(:once).with("one")
      vivid.write!("one", "five", "six")
      expect(vivid["one"]["five"]).to eql("six")
    end

    it "should deeply autovivify" do
      expect(root).to receive(:reset_cache).at_least(:once).with("one")
      vivid.write!("one", "five", "six", "seven", "eight", "nine", "ten")
      expect(vivid["one"]["five"]["six"]["seven"]["eight"]["nine"]).to eql("ten")
    end

    it "should raise an exception if you overwrite an array with a hash" do
      expect(root).not_to receive(:reset_cache)
      expect { vivid.write!("array", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
    end

    it "should raise an exception if you traverse through an array with a hash" do
      expect(root).not_to receive(:reset_cache)
      expect { vivid.write!("array", "five", "six", "seven") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
    end

    it "should raise an exception if you overwrite a string with a hash" do
      expect(root).not_to receive(:reset_cache)
      expect { vivid.write!("one", "two", "three", "four", "five") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
    end

    it "should raise an exception if you traverse through a string with a hash" do
      expect(root).not_to receive(:reset_cache)
      expect { vivid.write!("one", "two", "three", "four", "five", "six") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
    end

    it "should raise an exception if you overwrite a nil with a hash" do
      expect(root).not_to receive(:reset_cache)
      expect { vivid.write!("nil", "one", "two") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
    end

    it "should raise an exception if you traverse through a nil with a hash" do
      expect(root).not_to receive(:reset_cache)
      expect { vivid.write!("nil", "one", "two", "three") }.to raise_error(Chef::Exceptions::AttributeTypeMismatch)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
    end

    it "writes with a block" do
      expect(root).to receive(:reset_cache).at_least(:once).with("one")
      vivid.write!("one", "five") { "six" }
      expect(vivid["one"]["five"]).to eql("six")
    end
  end

  context "#unlink" do
    it "should return nil if the keys don't already exist" do
      expect(root).not_to receive(:reset_cache)
      expect(vivid.unlink("five", "six", "seven", "eight")).to eql(nil)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
    end

    it "should unlink hashes" do
      expect(root).to receive(:reset_cache).at_least(:once).with("one")
      expect( vivid.unlink("one") ).to eql({ "two" => { "three" => "four" } })
      expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
    end

    it "should unlink array elements" do
      expect(root).to receive(:reset_cache).at_least(:once).with("array")
      expect(vivid.unlink("array", 2)).to eql(2)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
    end

    it "should unlink nil" do
      expect(root).to receive(:reset_cache).at_least(:once).with("nil")
      expect(vivid.unlink("nil")).to eql(nil)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
    end

    it "should traverse a nil and safely do nothing" do
      expect(root).not_to receive(:reset_cache)
      expect(vivid.unlink("nil", "foo")).to eql(nil)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
    end
  end

  context "#unlink!" do
    it "should raise an exception if the keys don't already exist" do
      expect(root).not_to receive(:reset_cache)
      expect { vivid.unlink!("five", "six", "seven", "eight") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
    end

    it "should unlink! hashes" do
      expect(root).to receive(:reset_cache).at_least(:once).with("one")
      expect( vivid.unlink!("one") ).to eql({ "two" => { "three" => "four" } })
      expect(vivid).to eql({ "array" => [ 0, 1, 2 ], "nil" => nil })
    end

    it "should unlink! array elements" do
      expect(root).to receive(:reset_cache).at_least(:once).with("array")
      expect(vivid.unlink!("array", 2)).to eql(2)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1 ], "nil" => nil })
    end

    it "should unlink! nil" do
      expect(root).to receive(:reset_cache).at_least(:once).with("nil")
      expect(vivid.unlink!("nil")).to eql(nil)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ] })
    end

    it "should raise an exception if it traverses a nil" do
      expect(root).not_to receive(:reset_cache)
      expect { vivid.unlink!("nil", "foo") }.to raise_error(Chef::Exceptions::NoSuchAttribute)
      expect(vivid).to eql({ "one" => { "two" => { "three" => "four" } }, "array" => [ 0, 1, 2 ], "nil" => nil })
    end
  end
end


text:
#include <string>

#include <gtest/gtest.h>

#include <osquery/utils/conversions/join.h>

namespace osquery{

class ConversionsTests : public testing::Test {};

TEST_F(ConversionsTests, test_join) {
  std::vector<std::string> content = {
      "one", "two", "three",
  };
  EXPECT_EQ(join(content, ", "), "one, two, three");
}

}


text:
import pytest

from dvc.fs.base import FileSystem, RemoteMissingDepsError

@pytest.mark.parametrize(
    "pkg, msg",
    [
        (None, "Please report this bug to"),
        ("pip", "pip install"),
        ("conda", "conda install"),
    ],
)
def test_missing_deps(pkg, msg, mocker):
    requires = {"missing": "missing"}
    mocker.patch.object(FileSystem, "REQUIRES", requires)
    mocker.patch("dvc.utils.pkg.PKG", pkg)
    with pytest.raises(RemoteMissingDepsError, match=msg):
        FileSystem()


text:
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder"
	xmlns:g="urn:import:com.google.gwt.user.client.ui"
	xmlns:m="urn:import:com.framework.client.widget">
	<ui:style>
		.important {
			font-weight: bold;
		}
	</ui:style>
	<m:HtmlComponent>
		Hello,DDDDDDDDDDDDDDDDDDDDDDDDDDDD
	</m:HtmlComponent>
</ui:UiBinder>


text:
import { create, collection, text } from 'ember-cli-page-object';
import table1 from 'ilios/tests/pages/components/curriculum-inventory/verification-preview-table1';
import table2 from 'ilios/tests/pages/components/curriculum-inventory/verification-preview-table2';
import table3a from 'ilios/tests/pages/components/curriculum-inventory/verification-preview-table3a';
import table3b from 'ilios/tests/pages/components/curriculum-inventory/verification-preview-table3b';
import table4 from 'ilios/tests/pages/components/curriculum-inventory/verification-preview-table4';
import table5 from 'ilios/tests/pages/components/curriculum-inventory/verification-preview-table5';
import table6 from 'ilios/tests/pages/components/curriculum-inventory/verification-preview-table6';
import table7 from 'ilios/tests/pages/components/curriculum-inventory/verification-preview-table7';
import table8 from 'ilios/tests/pages/components/curriculum-inventory/verification-preview-table8';

const definition = {
  scope: '[data-test-curriculum-inventory-verification-preview]',
  tableOfContents: {
    scope: '[data-test-table-of-contents]',
    items: collection('li', {
      text: text(),
    }),
  },
  table1,
  table2,
  table3a,
  table3b,
  table4,
  table5,
  table6,
  table7,
  table8,
};

export default definition;
export const component = create(definition);


text:
using System;
using CupCake.Chat;
using CupCake.Command.Source;
using CupCake.Core;
using CupCake.Core.Events;
using CupCake.Core.Log;
using CupCake.Permissions;
using CupCake.Players;

namespace CupCake.Command
{
    public sealed class CommandService : CupCakeService
    {
        public const string DefaultPrefix = "!";
        private const string UnknownCommandStr = "Unknown command.";
        private ChatService _chatService;
        public string CommandPrefix { get; set; }
        public Group ResponseMinGroup { get; set; }

        protected override void Enable()
        {
            this.CommandPrefix = DefaultPrefix;
            this.ResponseMinGroup = Group.Moderator;

            this.ServiceLoader.EnableComplete += this.ServiceLoader_EnableComplete;
            this.Events.Bind<SayPlayerEvent>(this.OnSay, EventPriority.High);
        }

        private void ServiceLoader_EnableComplete(object sender, EventArgs e)
        {
            this._chatService = this.ServiceLoader.Get<ChatService>();
        }

        private void OnSay(object sender, SayPlayerEvent e)
        {
            this.SynchronizePlatform.Do(() =>
            {
                if (e.Player.Say.StartsWith(this.CommandPrefix))
                {
                    string command = e.Player.Say.Substring(this.CommandPrefix.Length);
                    if (String.IsNullOrEmpty(command))
                        return;

                    this.InvokeFromPlayer(e.Player, new ParsedCommand(command));
                }
            });
        }

        public void InvokeFromPlayer(Player player, ParsedCommand message)
        {
            this.InvokeFromPlayer(player, player.GetGroup(), message);
        }

        public void InvokeFromPlayer(Player player, Group group, ParsedCommand message)
        {
            var source = new PlayerInvokeSource(this, group, player,
                (name, msg) =>
                    this._chatService.Reply(player.Username, name, msg));
            this.Invoke(source, message);
        }

        public void InvokeFromConsole(ParsedCommand message)
        {
            this.InvokeFromConsole(Group.Host, message);
        }

        public void InvokeFromConsole(Group group, ParsedCommand message)
        {
            var source = new ConsoleInvokeSource(this, group,
                (name, msg) =>
                    this.Logger.LogPlatform.Log(name, LogPriority.Message, msg));

            this.Invoke(source, message);
        }

        public void Invoke(IInvokeSource source, ParsedCommand message)
        {
            var e = new InvokeEvent(source, message);
            this.Events.Raise(e);

            if (!e.Handled && source.Group >= this.ResponseMinGroup)
                source.Reply(UnknownCommandStr);
        }
    }
}


text:
<?hh
namespace Hackdinium\Http;

use GuzzleHttp\Command\Event\ProcessEvent;
use GuzzleHttp\Event\SubscriberInterface;
use Hackdinium\Game;
use Hackdinium\Hero;
use Hackdinium\Input;

class Processor implements SubscriberInterface {

    public function getEvents() : array {
        $m = Map { 'process' => Vector { 'onProcess' } };
        return $m->toArray();
    }

    public function onProcess(ProcessEvent $event) : void {
        $model = $event->getResult();

        $event->setResult(new Input(
            $this->toModel($model['game'], Game::class),
            $this->toModel($model['hero'], Hero::class),
            $model['token'],
            urldecode($model['viewUrl']),
            urldecode($model['playUrl'])
        ));
    }

    private function toModel(array $data, string $name) {
        // Bypass auto-instantiation for the Board
        if (Game\Board::class === $name) {
            return new Game\Board($data['size'], $data['tiles']);
        }

        $class      = new \ReflectionClass($name);
        $invoked    = $class->getConstructor();
        $args       = Vector {};

        if (! $invoked) {
            throw new \RuntimeException(sprintf('Class "%s"  has no constructor.', $name));
        }

        foreach ($invoked->getParameters() as $p) {
            $key = $p->name;

            // Overload heroes array with an ID indexed ImmMap
            if ('heroes' === $key) {
                $args[] = $this->generateHeroesMap($data[$key]);
                continue;
            }

            $klass = $this->getParameterType($p);
            if (null === $klass && array_key_exists($key, $data)) {
                $args[] = $data[$key];
            } elseif(!array_key_exists($key, $data)) {
                $args[] = null;
            } elseif(null !== $klass) {
                $args[] = $this->toModel($data[$key], $klass);
            } else {
                throw new \InvalidArgumentException('Model class could not be determined.');
            }
        }

        return $class->newInstanceArgs($args);
    }

    private function generateHeroesMap(array $data) : ImmMap<int, Hero> {
        $getPairs = ($heroes) ==> {
            foreach ($heroes as $hero) {
                $hero = $this->toModel($hero, Hero::class);
                yield Pair { $hero->id, $hero };
            }
        };

        return ImmMap::fromItems($getPairs($data));
    }

    private function getParameterType(\ReflectionParameter $p) : ?string {
        $type = null;
        try {
            $class = $p->getClass();
            if (!$class) {
                $type = null;
            } elseif ($class->implementsInterface('\\OutputCollection')) {
                $type = null;
            } else {
                $type = $class->name;
            }
        }
        catch(\ReflectionException $ex) {}
        return $type;
    }
}


text:
import Ember from 'ember';

export default Ember.Route.extend({
	routing: Ember.inject.service('-routing'),
	transitionHistory: [],
	transitioningToBack: false,

	actions: {

		willTransition: function(transition) {
			if (!this.get('transitioningToBack')) {
				let history = this.get('transitionHistory');

				let routing = this.get('routing');
				let params = Object.values(transition.params).filter(param => {
					return Object.values(param).length;
				});

				let urlStr = routing.generateURL(transition.targetName, params);
				let url = new URL(window.location.origin + urlStr);

				if(url.pathname !== window.location.pathname) {
					history.push(window.location.pathname + window.location.search);
				}

			}
			this.set('transitioningToBack', false);
		},

		back: function() {
			var last = this.get('transitionHistory').pop();
			last = last ? last : '/runs';
			this.set('transitioningToBack', true);
			this.transitionTo(last);
		}
	}
});


text:
<!doctype html>
<title>Location hash - multiple set hash during inline execution</title>


text:
#ifndef WTF_Complex_h
#define WTF_Complex_h

#include <complex>
#include "wtf/MathExtras.h"

namespace WTF {

typedef std::complex<double> Complex;

inline Complex complexFromMagnitudePhase(double magnitude, double phase)
{
    return Complex(magnitude * cos(phase), magnitude * sin(phase));
}

} // namespace WTF

using WTF::Complex;
using WTF::complexFromMagnitudePhase;

#endif // WTF_Complex_h


text:
* Announcements: filesender-announce@filesender.org
* Support and development discussion: filesender-dev@filesender.org
* Bugs: [GitHub Issue Tracker](https://github.com/filesender/filesender/issues)
* Track: [Github commits](https://github.com/filesender/filesender/commits/master)

## Announcements

The FileSender project uses a mailinglist to announce new releases and important news:

filesender-announce@filesender.org

To subscribe send an email to sympa@filesender.org with 'subscribe
filesender-announce' in the subject or the body of the mail.
Alternatively you can go to the Sympa mailinglist server web interface
and subscribe yourself there.

[The mailinglist archive can be found
here](https://postlister.uninett.no/sympa_filesender/arc/filesender-announce)

## Support and development discussion

The FileSender project uses one mailinglist to coordinate the
development and provide support to the user community:

     filesender-dev@filesender.org

To subscribe send an email to sympa@filesender.org with 'subscribe
filesender-dev' in the subject or the body of the mail. Alternatively
you can go to the [Sympa mailinglist server web interface](https://postlister.uninett.no/sympa_filesender/info/filesender-dev) and subscribe
yourself there.

Non-subscribers can also send emails to the list - note that those
emails are moderated by one of three list-owners, so it can take a
little while before your email gets through.

[The mailinglist archive can be found
here](https://postlister.uninett.no/sympa_filesender/arc/filesender-dev)

## Bugs

Please inspect and report bugs on the [GitHub Issue
Tracker](https://github.com/filesender/filesender/issues)


text:
package com.amazonaws.services.servicediscovery.model;

import javax.annotation.Generated;

/**
 *
 */
@Generated("com.amazonaws:aws-java-sdk-code-generator")
public enum HealthCheckType {

    HTTP("HTTP"),
    HTTPS("HTTPS"),
    TCP("TCP");

    private String value;

    private HealthCheckType(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return this.value;
    }

    /**
     * Use this in place of valueOf.
     *
     * @param value
     *        real value
     * @return HealthCheckType corresponding to the value
     *
     * @throws IllegalArgumentException
     *         If the specified value does not map to one of the known values in this enum.
     */
    public static HealthCheckType fromValue(String value) {
        if (value == null || "".equals(value)) {
            throw new IllegalArgumentException("Value cannot be null or empty!");
        }

        for (HealthCheckType enumEntry : HealthCheckType.values()) {
            if (enumEntry.toString().equals(value)) {
                return enumEntry;
            }
        }

        throw new IllegalArgumentException("Cannot create enum from " + value + " value!");
    }
}


text:
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

using AutoMapper;
using Microsoft.Azure.Commands.Network.Models;
using Microsoft.Azure.Commands.ResourceManager.Common.ArgumentCompleters;
using Microsoft.Azure.Management.Network;
using System.Management.Automation;
using MNM = Microsoft.Azure.Management.Network.Models;

namespace Microsoft.Azure.Commands.Network
{
    [Cmdlet("Set", ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "VirtualNetworkGatewayConnectionSharedKey", SupportsShouldProcess = true),OutputType(typeof(string))]
    public class NewAzureVirtualNetworkGatewayConnectionSharedKeyCommand : VirtualNetworkGatewayConnectionBaseCmdlet
    {
        [Alias("ResourceName")]
        [Parameter(
            Mandatory = true,
            ValueFromPipelineByPropertyName = true,
            HelpMessage = "The resource name.")]
        [ResourceNameCompleter("Microsoft.Network/connections", "ResourceGroupName")]
        [ValidateNotNullOrEmpty]
        public virtual string Name { get; set; }

        [Parameter(
            Mandatory = true,
            ValueFromPipelineByPropertyName = true,
            HelpMessage = "The resource group name.")]
        [ResourceGroupCompleter]
        [ValidateNotNullOrEmpty]
        public virtual string ResourceGroupName { get; set; }

        [Parameter(
         Mandatory = true,
         ValueFromPipelineByPropertyName = true,
         HelpMessage = "The virtual network connection shared key value.")]
        [ValidateNotNullOrEmpty]
        public string Value { get; set; }

        [Parameter(
            Mandatory = false,
            HelpMessage = "Do not ask for confirmation if you want to overrite a resource")]
        public SwitchParameter Force { get; set; }

        public override void Execute()
        {
            base.Execute();
            var present = this.IsVirtualNetworkGatewayConnectionSharedKeyPresent(this.ResourceGroupName, this.Name);

            ConfirmAction(
                Force.IsPresent,
                string.Format(Properties.Resources.OverwritingResource, Name),
                Properties.Resources.SettingResourceMessage,
                Name,
                () =>
                {
                    var virtualNetworkGatewayConnectionSharedKey = SetVirtualNetworkGatewayConnectionSharedKey();
                    WriteObject(virtualNetworkGatewayConnectionSharedKey);
                },
                () => present);
        }

        private string SetVirtualNetworkGatewayConnectionSharedKey()
        {
            var vnetGatewayConnectionSharedKey = new PSConnectionSharedKey();
            vnetGatewayConnectionSharedKey.Value = Value;

            // Map to the sdk object
            var vnetGatewayConnectionSharedKeyModel = NetworkResourceManagerProfile.Mapper.Map<MNM.ConnectionSharedKey>(vnetGatewayConnectionSharedKey);

            // Execute the Set VirtualNetworkConnectionSharedKey call
            this.VirtualNetworkGatewayConnectionClient.SetSharedKey(this.ResourceGroupName, this.Name, vnetGatewayConnectionSharedKeyModel);

            var getVirtualNetworkGatewayConnectionSharedKey = this.GetVirtualNetworkGatewayConnectionSharedKey(this.ResourceGroupName, this.Name);

            return getVirtualNetworkGatewayConnectionSharedKey;
        }
    }
}


text:
using System;
using System.Threading.Tasks;
using tomware.Microwf.Core;

namespace tomware.Microwf.Domain
{
  public interface IWorkflowRepository : IAsyncRepository<Workflow>
  {
    IWorkflow Find(int id, Type type);

    Task ApplyChangesAsync();

    void AddAutoTrigger(AutoTrigger autoTrigger, IWorkflowInstanceEntity entity);
  }
}


text:
<?php

namespace Store\BackendBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class MainController extends Controller
{
    public function indexAction()
    {
        return $this->render('StoreBackendBundle:Main:index.html.twig');
    }
}


text:
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

namespace Telegram.Bot.Types
{
    /// <summary>
    /// This object represents a venue.
    /// </summary>
    [JsonObject(MemberSerialization.OptIn, NamingStrategyType = typeof(SnakeCaseNamingStrategy))]
    public class Venue
    {
        /// <summary>
        /// Venue location
        /// </summary>
        [JsonProperty(Required = Required.Always)]
        public Location Location { get; set; } = default!;

        /// <summary>
        /// Name of the venue
        /// </summary>
        [JsonProperty(Required = Required.Always)]
        public string Title { get; set; } = default!;

        /// <summary>
        /// Address of the venue
        /// </summary>
        [JsonProperty(Required = Required.Always)]
        public string Address { get; set; } = default!;

        /// <summary>
        /// Optional. Foursquare identifier of the venue
        /// </summary>
        [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
        public string? FoursquareId { get; set; }

        /// <summary>
        /// Optional. Foursquare type of the venue. (For example, "arts_entertainment/default",
        /// "arts_entertainment/aquarium" or "food/icecream".)
        /// </summary>
        [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
        public string? FoursquareType { get; set; }

        /// <summary>
        /// Optional. Google Places identifier of the venue
        /// </summary>
        [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
        public string? GooglePlaceId { get; set; }

        /// <summary>
        /// Optional. Google Places type of the venue. (See
        /// <see href="https://developers.google.com/places/web-service/supported_types">supported types</see>.)
        /// </summary>
        [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)]
        public string? GooglePlaceType { get; set; }
    }
}


text:
<?php

class Ffuenf_Common_Block_Adminhtml_Log_View extends Mage_Adminhtml_Block_Widget_Container
{

    /**
     * Instance of the log model
     *
     * @var Varien_Object $_model
     */
    protected $_model = null;

    public function __construct()
    {
        parent::__construct();
        $this->_addButton(
            'back',
            array(
                'label'   => Mage::helper('adminhtml')->__('Back'),
                'onclick' => 'window.location.href=\''.$this->getUrl('*/*/').'\'',
                'class'   => 'back',
            )
        );
    }

    /**
     * Returns log model instance
     *
     * @return Varien_Object
     */
    protected function _getLog()
    {
        return $this->_model;
    }

    /**
     * Converts field names for geters
     *
     * @param string $name
     * @return string
     */
    protected function _underscore($name)
    {
        return strtolower(preg_replace('/(.)([A-Z])/', "$1_$2", $name));
    }

    /**
     * @return string
     */
    public function __call($method, $args)
    {
        if (null !== $this->_getLog()) {
            if (substr($method, 0, 3) == 'get') {
                $key = $this->_underscore(substr($method, 3));
                return $this->_getLog()->getData($key);
            }
        }
    }

    /**
     * @return null|string
     */
    public function getTimestamp()
    {
        if (null !== $this->_getLog()) {
            return Mage::getSingleton('core/date')->date($this->_getLog()->getTimestamp());
        }
        return null;
    }

    /**
     * @return Ffuenf_Common_Block_Adminhtml_Log_View
     */
    public function setLog($model)
    {
        $this->_model = $model;
        return $this;
    }

    /**
     * @return string
     */
    public function getHeaderCssClass()
    {
        return 'icon-head head-ffuenf-log '.parent::getHeaderCssClass();
    }
}


text:
namespace ui {

// Translates the key value from an X11 key value to key value string
// visible from javascript.
UI_BASE_EXPORT std::string FromXKeycodeToKeyValue(int keyval);

}  // namespace ui

#endif  // UI_BASE_IME_CHROMEOS_IME_KEYMAP_H_