182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir/*
282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Copyright (C) 2017 The Android Open Source Project
382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *
482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Licensed under the Apache License, Version 2.0 (the "License");
582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * you may not use this file except in compliance with the License.
682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * You may obtain a copy of the License at
782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *
882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *      http://www.apache.org/licenses/LICENSE-2.0
982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir *
1082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Unless required by applicable law or agreed to in writing, software
1182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * distributed under the License is distributed on an "AS IS" BASIS,
1282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * See the License for the specific language governing permissions and
1482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * limitations under the License.
1582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */
1682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirpackage android.support.text.emoji;
1782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
1882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport static android.support.annotation.RestrictTo.Scope.LIBRARY_GROUP;
1982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
2082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.content.res.AssetManager;
2182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.graphics.Typeface;
2282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.support.annotation.AnyThread;
2382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.support.annotation.NonNull;
2477b5c5b734f9f665577d1e3d178615db43ae1d4fSiyamed Sinirimport android.support.annotation.RequiresApi;
2582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.support.annotation.RestrictTo;
2682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.support.annotation.VisibleForTesting;
2782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.support.text.emoji.flatbuffer.MetadataList;
2882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.support.v4.util.Preconditions;
2982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport android.util.SparseArray;
3082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
3182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport java.io.IOException;
3282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport java.io.InputStream;
3382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirimport java.nio.ByteBuffer;
3482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
3582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir/**
3682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir * Class to hold the emoji metadata required to process and draw emojis.
3782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir */
3882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir@AnyThread
3977b5c5b734f9f665577d1e3d178615db43ae1d4fSiyamed Sinir@RequiresApi(19)
4082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinirpublic final class MetadataRepo {
4182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
4282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * The default children size of the root node.
4382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
4482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private static final int DEFAULT_ROOT_SIZE = 1024;
4582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
4682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
4782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * MetadataList that contains the emoji metadata.
4882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
4982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private final MetadataList mMetadataList;
5082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
5182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
5282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * char presentation of all EmojiMetadata's in a single array.
5382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
5482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private final char[] mEmojiCharArray;
5582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
5682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
5782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Empty root node of the trie.
5882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
5982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private final Node mRootNode;
6082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
6182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
6282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Typeface to be used to render emojis.
6382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
6482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private final Typeface mTypeface;
6582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
6682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
6782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Constructor used for tests.
6882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *
6982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @hide
7082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
7182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
7282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    MetadataRepo() {
7382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        mTypeface = null;
7482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        mMetadataList = null;
7582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        mRootNode = new Node(DEFAULT_ROOT_SIZE);
7682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        mEmojiCharArray = new char[0];
7782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
7882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
7982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
8082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Private constructor that is called by one of {@code create} methods.
8182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *
8282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @param typeface Typeface to be used to render emojis
8382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @param metadataList MetadataList that contains the emoji metadata
8482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
8582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private MetadataRepo(@NonNull final Typeface typeface,
8682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            @NonNull final MetadataList metadataList) {
8782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        mTypeface = typeface;
8882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        mMetadataList = metadataList;
8982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        mRootNode = new Node(DEFAULT_ROOT_SIZE);
9082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        mEmojiCharArray = new char[mMetadataList.listLength() * 2];
9182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        constructIndex(mMetadataList);
9282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
9382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
9482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
9582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Construct MetadataRepo from an input stream. The library does not close the given
9682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * InputStream, therefore it is caller's responsibility to properly close the stream.
9782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *
9882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @param typeface Typeface to be used to render emojis
9982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @param inputStream InputStream to read emoji metadata from
10082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
10182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static MetadataRepo create(@NonNull final Typeface typeface,
10282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            @NonNull final InputStream inputStream) throws IOException {
10382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new MetadataRepo(typeface, MetadataListReader.read(inputStream));
10482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
10582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
10682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
10782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Construct MetadataRepo from a byte buffer. The position of the ByteBuffer will change, it is
10882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * caller's responsibility to reposition the buffer if required.
10982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *
11082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @param typeface Typeface to be used to render emojis
11182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @param byteBuffer ByteBuffer to read emoji metadata from
11282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
11382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static MetadataRepo create(@NonNull final Typeface typeface,
11482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            @NonNull final ByteBuffer byteBuffer) throws IOException {
11582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new MetadataRepo(typeface, MetadataListReader.read(byteBuffer));
11682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
11782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
11882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
11982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Construct MetadataRepo from an asset.
12082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *
12182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @param assetManager AssetManager instance
12282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @param assetPath asset manager path of the file that the Typeface and metadata will be
12382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *                  created from
12482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
12582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public static MetadataRepo create(@NonNull final AssetManager assetManager,
12682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            final String assetPath) throws IOException {
12782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        final Typeface typeface = Typeface.createFromAsset(assetManager, assetPath);
12882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return new MetadataRepo(typeface, MetadataListReader.read(assetManager, assetPath));
12982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
13082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
13182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
13282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Read emoji metadata list and construct the trie.
13382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
13482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    private void constructIndex(final MetadataList metadataList) {
13582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        int length = metadataList.listLength();
13682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        for (int i = 0; i < length; i++) {
13782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            final EmojiMetadata metadata = new EmojiMetadata(this, i);
13882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            Character.toChars(metadata.getId(), mEmojiCharArray, i * 2);
13982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            put(metadata);
14082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
14182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
14282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
14382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
14482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @hide
14582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
14682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
14782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    Typeface getTypeface() {
14882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return mTypeface;
14982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
15082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
15182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
15282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @hide
15382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
15482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
15582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    int getMetadataVersion() {
15682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return mMetadataList.version();
15782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
15882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
15982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
16082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @hide
16182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
16282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
16382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    Node getRootNode() {
16482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return mRootNode;
16582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
16682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
16782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
16882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @hide
16982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
17082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
17182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public char[] getEmojiCharArray() {
17282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return mEmojiCharArray;
17382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
17482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
17582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
17682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @hide
17782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
17882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
17982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    public MetadataList getMetadataList() {
18082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        return mMetadataList;
18182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
18282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
18382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
18482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Add an EmojiMetadata to the index.
18582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *
18682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @hide
18782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
18882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
18982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    @VisibleForTesting
19082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    void put(@NonNull final EmojiMetadata data) {
19182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        Preconditions.checkNotNull(data, "emoji metadata cannot be null");
19282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        Preconditions.checkArgument(data.getCodepointsLength() > 0,
19382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                "invalid metadata codepoint length");
19482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
19582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        mRootNode.put(data, 0, data.getCodepointsLength() - 1);
19682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
19782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
19882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    /**
19982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * Trie node that holds mapping from emoji codepoint(s) to EmojiMetadata. A single codepoint
20082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * emoji is represented by a child of the root node.
20182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     *
20282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     * @hide
20382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir     */
20482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    @RestrictTo(LIBRARY_GROUP)
20582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    static class Node {
20682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private SparseArray<Node> mChildren;
20782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private EmojiMetadata mData;
20882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
20982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private Node() {
21082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
21182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
21282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private Node(final int defaultChildrenSize) {
21382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            mChildren = new SparseArray<>(defaultChildrenSize);
21482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
21582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
21682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        Node get(final int key) {
21782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return mChildren == null ? null : mChildren.get(key);
21882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
21982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
22082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        final EmojiMetadata getData() {
22182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            return mData;
22282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
22382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
22482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        private void put(@NonNull final EmojiMetadata data, final int start, final int end) {
22582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            Node node = get(data.getCodepointAt(start));
22682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (node == null) {
22782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                if (mChildren == null) {
22882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                    mChildren = new SparseArray<>(1);
22982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                }
23082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                node = new Node();
23182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                mChildren.put(data.getCodepointAt(start), node);
23282d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
23382d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir
23482d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            if (end > start) {
23582d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                node.put(data, start + 1, end);
23682d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            } else {
23782d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir                node.mData = data;
23882d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir            }
23982d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir        }
24082d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir    }
24182d2cc1cf0c2bfdd5121e6d6913dfe9fcaacf439Siyamed Sinir}
242