public final class

LinkifyCompat

extends java.lang.Object

 java.lang.Object

↳androidx.core.text.util.LinkifyCompat

Gradle dependencies

compile group: 'androidx.core', name: 'core', version: '1.15.0-alpha02'

  • groupId: androidx.core
  • artifactId: core
  • version: 1.15.0-alpha02

Artifact androidx.core:core:1.15.0-alpha02 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.core:core com.android.support:support-compat

Androidx class mapping:

androidx.core.text.util.LinkifyCompat android.support.v4.text.util.LinkifyCompat

Overview

LinkifyCompat brings in Linkify improvements for URLs and email addresses to older API levels.

Summary

Methods
public static booleanaddLinks(Spannable text, int mask)

Scans the text of the provided Spannable and turns all occurrences of the link types indicated in the mask into clickable links.

public static booleanaddLinks(Spannable text, java.util.regex.Pattern pattern, java.lang.String scheme)

Applies a regex to a Spannable turning the matches into links.

public static booleanaddLinks(Spannable spannable, java.util.regex.Pattern pattern, java.lang.String scheme, MatchFilter matchFilter, TransformFilter transformFilter)

Applies a regex to a Spannable turning the matches into links.

public static booleanaddLinks(Spannable spannable, java.util.regex.Pattern pattern, java.lang.String defaultScheme, java.lang.String schemes[], MatchFilter matchFilter, TransformFilter transformFilter)

Applies a regex to a Spannable turning the matches into links.

public static booleanaddLinks(TextView text, int mask)

Scans the text of the provided TextView and turns all occurrences of the link types indicated in the mask into clickable links.

public static voidaddLinks(TextView text, java.util.regex.Pattern pattern, java.lang.String scheme)

Applies a regex to the text of a TextView turning the matches into links.

public static voidaddLinks(TextView text, java.util.regex.Pattern pattern, java.lang.String scheme, MatchFilter matchFilter, TransformFilter transformFilter)

Applies a regex to the text of a TextView turning the matches into links.

public static voidaddLinks(TextView text, java.util.regex.Pattern pattern, java.lang.String defaultScheme, java.lang.String schemes[], MatchFilter matchFilter, TransformFilter transformFilter)

Applies a regex to the text of a TextView turning the matches into links.

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Methods

public static boolean addLinks(Spannable text, int mask)

Scans the text of the provided Spannable and turns all occurrences of the link types indicated in the mask into clickable links. If the mask is nonzero, it also removes any existing URLSpans attached to the Spannable, to avoid problems if you call it repeatedly on the same text.

Parameters:

text: Spannable whose text is to be marked-up with links
mask: Mask to define which kinds of links will be searched.

Returns:

True if at least one link is found and applied.

public static boolean addLinks(TextView text, int mask)

Scans the text of the provided TextView and turns all occurrences of the link types indicated in the mask into clickable links. If matches are found the movement method for the TextView is set to LinkMovementMethod.

Parameters:

text: TextView whose text is to be marked-up with links
mask: Mask to define which kinds of links will be searched.

Returns:

True if at least one link is found and applied.

public static void addLinks(TextView text, java.util.regex.Pattern pattern, java.lang.String scheme)

Applies a regex to the text of a TextView turning the matches into links. If links are found then UrlSpans are applied to the link text match areas, and the movement method for the text is changed to LinkMovementMethod.

Parameters:

text: TextView whose text is to be marked-up with links
pattern: Regex pattern to be used for finding links
scheme: URL scheme string (eg http://) to be prepended to the links that do not start with this scheme.

public static void addLinks(TextView text, java.util.regex.Pattern pattern, java.lang.String scheme, MatchFilter matchFilter, TransformFilter transformFilter)

Applies a regex to the text of a TextView turning the matches into links. If links are found then UrlSpans are applied to the link text match areas, and the movement method for the text is changed to LinkMovementMethod.

Parameters:

text: TextView whose text is to be marked-up with links
pattern: Regex pattern to be used for finding links
scheme: URL scheme string (eg http://) to be prepended to the links that do not start with this scheme.
matchFilter: The filter that is used to allow the client code additional control over which pattern matches are to be converted into links.
transformFilter: Filter to allow the client code to update the link found.

public static void addLinks(TextView text, java.util.regex.Pattern pattern, java.lang.String defaultScheme, java.lang.String schemes[], MatchFilter matchFilter, TransformFilter transformFilter)

Applies a regex to the text of a TextView turning the matches into links. If links are found then UrlSpans are applied to the link text match areas, and the movement method for the text is changed to LinkMovementMethod.

Parameters:

text: TextView whose text is to be marked-up with links.
pattern: Regex pattern to be used for finding links.
defaultScheme: The default scheme to be prepended to links if the link does not start with one of the schemes given.
schemes: Array of schemes (eg http://) to check if the link found contains a scheme. Passing a null or empty value means prepend defaultScheme to all links.
matchFilter: The filter that is used to allow the client code additional control over which pattern matches are to be converted into links.
transformFilter: Filter to allow the client code to update the link found.

public static boolean addLinks(Spannable text, java.util.regex.Pattern pattern, java.lang.String scheme)

Applies a regex to a Spannable turning the matches into links.

Parameters:

text: Spannable whose text is to be marked-up with links
pattern: Regex pattern to be used for finding links
scheme: URL scheme string (eg http://) to be prepended to the links that do not start with this scheme.

public static boolean addLinks(Spannable spannable, java.util.regex.Pattern pattern, java.lang.String scheme, MatchFilter matchFilter, TransformFilter transformFilter)

Applies a regex to a Spannable turning the matches into links.

Parameters:

spannable: Spannable whose text is to be marked-up with links
pattern: Regex pattern to be used for finding links
scheme: URL scheme string (eg http://) to be prepended to the links that do not start with this scheme.
matchFilter: The filter that is used to allow the client code additional control over which pattern matches are to be converted into links.
transformFilter: Filter to allow the client code to update the link found.

Returns:

True if at least one link is found and applied.

public static boolean addLinks(Spannable spannable, java.util.regex.Pattern pattern, java.lang.String defaultScheme, java.lang.String schemes[], MatchFilter matchFilter, TransformFilter transformFilter)

Applies a regex to a Spannable turning the matches into links.

Parameters:

spannable: Spannable whose text is to be marked-up with links.
pattern: Regex pattern to be used for finding links.
defaultScheme: The default scheme to be prepended to links if the link does not start with one of the schemes given.
schemes: Array of schemes (eg http://) to check if the link found contains a scheme. Passing a null or empty value means prepend defaultScheme to all links.
matchFilter: The filter that is used to allow the client code additional control over which pattern matches are to be converted into links.
transformFilter: Filter to allow the client code to update the link found.

Returns:

True if at least one link is found and applied.

Source

/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * 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 androidx.core.text.util;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;

import android.os.Build;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.method.LinkMovementMethod;
import android.text.method.MovementMethod;
import android.text.style.URLSpan;
import android.text.util.Linkify;
import android.text.util.Linkify.MatchFilter;
import android.text.util.Linkify.TransformFilter;
import android.webkit.WebView;
import android.widget.TextView;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.RestrictTo;
import androidx.core.util.PatternsCompat;

import java.io.UnsupportedEncodingException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * LinkifyCompat brings in {@code Linkify} improvements for URLs and email addresses to older API
 * levels.
 */
public final class LinkifyCompat {
    private static final String[] EMPTY_STRING = new String[0];

    private static final Comparator<LinkSpec>  COMPARATOR = (a, b) -> {
        if (a.start < b.start) {
            return -1;
        }

        if (a.start > b.start) {
            return 1;
        }

        return Integer.compare(b.end, a.end);
    };

    @RestrictTo(LIBRARY_GROUP_PREFIX)
    @IntDef(flag = true, value = { Linkify.WEB_URLS, Linkify.EMAIL_ADDRESSES, Linkify.PHONE_NUMBERS,
            Linkify.MAP_ADDRESSES, Linkify.ALL })
    @Retention(RetentionPolicy.SOURCE)
    public @interface LinkifyMask {}

    /**
     *  Scans the text of the provided Spannable and turns all occurrences
     *  of the link types indicated in the mask into clickable links.
     *  If the mask is nonzero, it also removes any existing URLSpans
     *  attached to the Spannable, to avoid problems if you call it
     *  repeatedly on the same text.
     *
     *  @param text Spannable whose text is to be marked-up with links
     *  @param mask Mask to define which kinds of links will be searched.
     *
     *  @return True if at least one link is found and applied.
     */
    @SuppressWarnings("deprecation")
    public static boolean addLinks(@NonNull Spannable text, @LinkifyMask int mask) {
        if (shouldAddLinksFallbackToFramework()) {
            return Linkify.addLinks(text, mask);
        }
        if (mask == 0) {
            return false;
        }

        URLSpan[] old = text.getSpans(0, text.length(), URLSpan.class);

        for (int i = old.length - 1; i >= 0; i--) {
            text.removeSpan(old[i]);
        }

        if ((mask & Linkify.PHONE_NUMBERS) != 0) {
            Linkify.addLinks(text, Linkify.PHONE_NUMBERS);
        }

        final ArrayList<LinkSpec> links = new ArrayList<>();

        if ((mask & Linkify.WEB_URLS) != 0) {
            gatherLinks(links, text, PatternsCompat.AUTOLINK_WEB_URL,
                    new String[] { "http://", "https://", "rtsp://" },
                    Linkify.sUrlMatchFilter, null);
        }

        if ((mask & Linkify.EMAIL_ADDRESSES) != 0) {
            gatherLinks(links, text, PatternsCompat.AUTOLINK_EMAIL_ADDRESS,
                    new String[] { "mailto:" },
                    null, null);
        }

        if ((mask & Linkify.MAP_ADDRESSES) != 0) {
            gatherMapLinks(links, text);
        }

        pruneOverlaps(links, text);

        if (links.size() == 0) {
            return false;
        }

        for (LinkSpec link: links) {
            if (link.frameworkAddedSpan == null) {
                applyLink(link.url, link.start, link.end, text);
            }
        }

        return true;
    }

    /**
     *  Scans the text of the provided TextView and turns all occurrences of
     *  the link types indicated in the mask into clickable links.  If matches
     *  are found the movement method for the TextView is set to
     *  LinkMovementMethod.
     *
     *  @param text TextView whose text is to be marked-up with links
     *  @param mask Mask to define which kinds of links will be searched.
     *
     *  @return True if at least one link is found and applied.
     */
    public static boolean addLinks(@NonNull TextView text, @LinkifyMask int mask) {
        if (shouldAddLinksFallbackToFramework()) {
            return Linkify.addLinks(text, mask);
        }
        if (mask == 0) {
            return false;
        }

        CharSequence t = text.getText();

        if (t instanceof Spannable) {
            if (addLinks((Spannable) t, mask)) {
                addLinkMovementMethod(text);
                return true;
            }

        } else {
            SpannableString s = SpannableString.valueOf(t);

            if (addLinks(s, mask)) {
                addLinkMovementMethod(text);
                text.setText(s);

                return true;
            }

        }
        return false;
    }

    /**
     *  Applies a regex to the text of a TextView turning the matches into
     *  links.  If links are found then UrlSpans are applied to the link
     *  text match areas, and the movement method for the text is changed
     *  to LinkMovementMethod.
     *
     *  @param text         TextView whose text is to be marked-up with links
     *  @param pattern      Regex pattern to be used for finding links
     *  @param scheme       URL scheme string (eg <code>http://</code>) to be
     *                      prepended to the links that do not start with this scheme.
     */
    public static void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
            @Nullable String scheme) {
        if (shouldAddLinksFallbackToFramework()) {
            Linkify.addLinks(text, pattern, scheme);
            return;
        }
        addLinks(text, pattern, scheme, null, null, null);
    }

    /**
     *  Applies a regex to the text of a TextView turning the matches into
     *  links.  If links are found then UrlSpans are applied to the link
     *  text match areas, and the movement method for the text is changed
     *  to LinkMovementMethod.
     *
     *  @param text         TextView whose text is to be marked-up with links
     *  @param pattern      Regex pattern to be used for finding links
     *  @param scheme       URL scheme string (eg <code>http://</code>) to be
     *                      prepended to the links that do not start with this scheme.
     *  @param matchFilter  The filter that is used to allow the client code
     *                      additional control over which pattern matches are
     *                      to be converted into links.
     * @param transformFilter Filter to allow the client code to update the link found.
     */
    public static void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
            @Nullable String scheme, @Nullable MatchFilter matchFilter,
            @Nullable TransformFilter transformFilter) {
        if (shouldAddLinksFallbackToFramework()) {
            Linkify.addLinks(text, pattern, scheme, matchFilter, transformFilter);
            return;
        }
        addLinks(text, pattern, scheme, null, matchFilter, transformFilter);
    }

    /**
     *  Applies a regex to the text of a TextView turning the matches into
     *  links.  If links are found then UrlSpans are applied to the link
     *  text match areas, and the movement method for the text is changed
     *  to LinkMovementMethod.
     *
     *  @param text TextView whose text is to be marked-up with links.
     *  @param pattern Regex pattern to be used for finding links.
     *  @param defaultScheme The default scheme to be prepended to links if the link does not
     *                       start with one of the <code>schemes</code> given.
     *  @param schemes Array of schemes (eg <code>http://</code>) to check if the link found
     *                 contains a scheme. Passing a null or empty value means prepend defaultScheme
     *                 to all links.
     *  @param matchFilter  The filter that is used to allow the client code additional control
     *                      over which pattern matches are to be converted into links.
     *  @param transformFilter Filter to allow the client code to update the link found.
     */
    public static void addLinks(@NonNull TextView text, @NonNull Pattern pattern,
            @Nullable String defaultScheme, @Nullable String[] schemes,
            @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter) {
        if (shouldAddLinksFallbackToFramework()) {
            Api24Impl.addLinks(text, pattern, defaultScheme, schemes, matchFilter, transformFilter);
            return;
        }
        SpannableString spannable = SpannableString.valueOf(text.getText());

        boolean linksAdded = addLinks(spannable, pattern, defaultScheme, schemes, matchFilter,
                transformFilter);
        if (linksAdded) {
            text.setText(spannable);
            addLinkMovementMethod(text);
        }
    }

    /**
     *  Applies a regex to a Spannable turning the matches into
     *  links.
     *
     *  @param text         Spannable whose text is to be marked-up with links
     *  @param pattern      Regex pattern to be used for finding links
     *  @param scheme       URL scheme string (eg <code>http://</code>) to be
     *                      prepended to the links that do not start with this scheme.
     */
    public static boolean addLinks(@NonNull Spannable text, @NonNull Pattern pattern,
            @Nullable String scheme) {
        if (shouldAddLinksFallbackToFramework()) {
            return Linkify.addLinks(text, pattern, scheme);
        }
        return addLinks(text, pattern, scheme, null, null, null);
    }

    /**
     * Applies a regex to a Spannable turning the matches into
     * links.
     *
     * @param spannable    Spannable whose text is to be marked-up with links
     * @param pattern      Regex pattern to be used for finding links
     * @param scheme       URL scheme string (eg <code>http://</code>) to be
     *                     prepended to the links that do not start with this scheme.
     * @param matchFilter  The filter that is used to allow the client code
     *                     additional control over which pattern matches are
     *                     to be converted into links.
     * @param transformFilter Filter to allow the client code to update the link found.
     *
     * @return True if at least one link is found and applied.
     */
    public static boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
            @Nullable String scheme, @Nullable MatchFilter matchFilter,
            @Nullable TransformFilter transformFilter) {
        if (shouldAddLinksFallbackToFramework()) {
            return Linkify.addLinks(spannable, pattern, scheme, matchFilter, transformFilter);
        }
        return addLinks(spannable, pattern, scheme, null, matchFilter,
                transformFilter);
    }

    /**
     * Applies a regex to a Spannable turning the matches into links.
     *
     * @param spannable Spannable whose text is to be marked-up with links.
     * @param pattern Regex pattern to be used for finding links.
     * @param defaultScheme The default scheme to be prepended to links if the link does not
     *                      start with one of the <code>schemes</code> given.
     * @param schemes Array of schemes (eg <code>http://</code>) to check if the link found
     *                contains a scheme. Passing a null or empty value means prepend defaultScheme
     *                to all links.
     * @param matchFilter  The filter that is used to allow the client code additional control
     *                     over which pattern matches are to be converted into links.
     * @param transformFilter Filter to allow the client code to update the link found.
     *
     * @return True if at least one link is found and applied.
     */
    public static boolean addLinks(@NonNull Spannable spannable, @NonNull Pattern pattern,
            @Nullable  String defaultScheme, @Nullable String[] schemes,
            @Nullable MatchFilter matchFilter, @Nullable TransformFilter transformFilter) {
        if (shouldAddLinksFallbackToFramework()) {
            return Api24Impl.addLinks(spannable, pattern, defaultScheme, schemes, matchFilter,
                    transformFilter);
        }
        final String[] schemesCopy;
        if (defaultScheme == null) defaultScheme = "";
        if (schemes == null || schemes.length < 1) {
            schemes = EMPTY_STRING;
        }

        schemesCopy = new String[schemes.length + 1];
        schemesCopy[0] = defaultScheme.toLowerCase(Locale.ROOT);
        for (int index = 0; index < schemes.length; index++) {
            String scheme = schemes[index];
            schemesCopy[index + 1] = (scheme == null) ? "" : scheme.toLowerCase(Locale.ROOT);
        }

        boolean hasMatches = false;
        Matcher m = pattern.matcher(spannable);

        while (m.find()) {
            int start = m.start();
            int end = m.end();
            String match = m.group(0);
            boolean allowed = true;

            if (matchFilter != null) {
                allowed = matchFilter.acceptMatch(spannable, start, end);
            }

            if (allowed && match != null) {
                String url = makeUrl(match, schemesCopy, m, transformFilter);

                applyLink(url, start, end, spannable);
                hasMatches = true;
            }
        }

        return hasMatches;
    }

    private static boolean shouldAddLinksFallbackToFramework() {
        return Build.VERSION.SDK_INT >= 28;
    }

    private static void addLinkMovementMethod(@NonNull TextView t) {
        MovementMethod m = t.getMovementMethod();

        if (!(m instanceof LinkMovementMethod)) {
            if (t.getLinksClickable()) {
                t.setMovementMethod(LinkMovementMethod.getInstance());
            }
        }
    }

    private static String makeUrl(@NonNull String url, @NonNull String[] prefixes,
            Matcher matcher, @Nullable TransformFilter filter) {
        if (filter != null) {
            url = filter.transformUrl(matcher, url);
        }

        boolean hasPrefix = false;

        for (String prefix : prefixes) {
            if (url.regionMatches(true, 0, prefix, 0, prefix.length())) {
                hasPrefix = true;

                // Fix capitalization if necessary
                if (!url.regionMatches(false, 0, prefix, 0, prefix.length())) {
                    url = prefix + url.substring(prefix.length());
                }

                break;
            }
        }

        if (!hasPrefix && prefixes.length > 0) {
            url = prefixes[0] + url;
        }

        return url;
    }

    @SuppressWarnings("SameParameterValue")
    private static void gatherLinks(ArrayList<LinkSpec> links,
            Spannable s, Pattern pattern, String[] schemes,
            MatchFilter matchFilter, TransformFilter transformFilter) {
        Matcher m = pattern.matcher(s);

        while (m.find()) {
            int start = m.start();
            int end = m.end();
            String match = m.group(0);

            if ((matchFilter == null || matchFilter.acceptMatch(s, start, end)) && match != null) {
                LinkSpec spec = new LinkSpec();
                spec.url = makeUrl(match, schemes, m, transformFilter);
                spec.start = start;
                spec.end = end;

                links.add(spec);
            }
        }
    }

    private static void applyLink(String url, int start, int end, Spannable text) {
        URLSpan span = new URLSpan(url);

        text.setSpan(span, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

    private static void gatherMapLinks(ArrayList<LinkSpec> links, Spannable s) {
        String string = s.toString();
        String address;
        int base = 0;

        try {
            while ((address = findAddress(string)) != null) {
                int start = string.indexOf(address);

                if (start < 0) {
                    break;
                }

                LinkSpec spec = new LinkSpec();
                int length = address.length();
                int end = start + length;

                spec.start = base + start;
                spec.end = base + end;
                string = string.substring(end);
                base += end;

                String encodedAddress;

                try {
                    encodedAddress = URLEncoder.encode(address,"UTF-8");
                } catch (UnsupportedEncodingException e) {
                    continue;
                }

                spec.url = "geo:0,0?q=" + encodedAddress;
                links.add(spec);
            }
        } catch (UnsupportedOperationException e) {
            // findAddress may fail with an unsupported exception on platforms without a WebView.
            // In this case, we will not append anything to the links variable: it would have died
            // in WebView.findAddress.
        }
    }

    @SuppressWarnings("deprecation")
    private static String findAddress(String addr) {
        if (Build.VERSION.SDK_INT >= 28) {
            return WebView.findAddress(addr);
        }
        return FindAddress.findAddress(addr);
    }

    private static void pruneOverlaps(ArrayList<LinkSpec> links, Spannable text) {
        // Append spans added by framework
        URLSpan[] urlSpans = text.getSpans(0, text.length(), URLSpan.class);
        for (URLSpan urlSpan : urlSpans) {
            LinkSpec spec = new LinkSpec();
            spec.frameworkAddedSpan = urlSpan;
            spec.start = text.getSpanStart(urlSpan);
            spec.end = text.getSpanEnd(urlSpan);
            links.add(spec);
        }

        Collections.sort(links, COMPARATOR);

        int len = links.size();
        int i = 0;

        while (i < len - 1) {
            LinkSpec a = links.get(i);
            LinkSpec b = links.get(i + 1);
            int remove = -1;

            if ((a.start <= b.start) && (a.end > b.start)) {
                if (b.end <= a.end) {
                    remove = i + 1;
                } else if ((a.end - a.start) > (b.end - b.start)) {
                    remove = i + 1;
                } else if ((a.end - a.start) < (b.end - b.start)) {
                    remove = i;
                }

                if (remove != -1) {
                    URLSpan span = links.get(remove).frameworkAddedSpan;
                    if (span != null) {
                        text.removeSpan(span);
                    }
                    links.remove(remove);
                    len--;
                    continue;
                }

            }

            i++;
        }
    }

    /**
     * Do not create this static utility class.
     */
    private LinkifyCompat() {}

    private static class LinkSpec {
        URLSpan frameworkAddedSpan;
        String url;
        int start;
        int end;

        LinkSpec() {
        }
    }

    @RequiresApi(24)
    static class Api24Impl {
        private Api24Impl() {
            // This class is not instantiable.
        }

        static void addLinks(TextView text, Pattern pattern, String defaultScheme, String[] schemes,
                MatchFilter matchFilter, TransformFilter transformFilter) {
            Linkify.addLinks(text, pattern, defaultScheme, schemes, matchFilter, transformFilter);
        }

        static boolean addLinks(Spannable spannable, Pattern pattern, String defaultScheme,
                String[] schemes, MatchFilter matchFilter, TransformFilter transformFilter) {
            return Linkify.addLinks(spannable, pattern, defaultScheme, schemes, matchFilter,
                    transformFilter);
        }
    }
}