All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.abdera.i18n.rfc4646.Range Maven / Gradle / Ivy

There is a newer version: 1.1.3
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  The ASF licenses this file to You
 * 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.  For additional information regarding
 * copyright in this work, please see the NOTICE file in the top level
 * directory of this distribution.
 */
package org.apache.abdera.i18n.rfc4646;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.abdera.i18n.rfc4646.Subtag.Type;

/**
 * A language range used for matching language tags
 */
public class Range extends SubtagSet {

    private static final long serialVersionUID = -6397227794306856431L;
    private final boolean extended;

    /**
     * Create a Language-Range
     * 
     * @param range The language-range
     * @param extended true if this is an extended language range
     */
    public Range(String range, boolean extended) {
        super(parse(range, extended).primary);
        this.extended = extended;
    }

    /**
     * Create a Language-Range
     */
    public Range(String range) {
        this(parse(range).primary);
    }

    /**
     * Create a Language-Range from a Lang tag
     */
    public Range(Lang lang) {
        this(lang.toString());
    }

    /**
     * Create a Language-Range from a Lang tag
     * 
     * @param lang The language tag
     * @param extended true if this is an extended language-range
     */
    public Range(Lang lang, boolean extended) {
        this(lang.toString(), extended);
    }

    Range(Subtag primary) {
        super(primary);
        this.extended = !checkBasic();
    }

    /**
     * Append a subtag to the range
     */
    public Range append(Subtag subtag) {
        Subtag last = null;
        for (Subtag tag : this)
            last = tag;
        last.setNext(subtag);
        return this;
    }

    /**
     * Append a wildcard subtag to the range
     */
    public Range appendWildcard() {
        return append(Subtag.newWildcard());
    }

    /**
     * Copy this range
     */
    public Range clone() {
        return new Range(primary.clone());
    }

    /**
     * Create a basic language-range from this range
     */
    public Range toBasicRange() {
        if (primary.getType() == Subtag.Type.WILDCARD) {
            return new Range("*");
        } else {
            List list = new LinkedList();
            for (Subtag tag : this) {
                if (tag.getType() != Subtag.Type.WILDCARD)
                    list.add(tag.clone());
            }
            Subtag primary = null, current = null;
            for (Subtag tag : list) {
                tag.setNext(null);
                tag.setPrevious(null);
                if (primary == null) {
                    primary = tag;
                    current = primary;
                } else {
                    current.setNext(tag);
                    current = tag;
                }
            }
            return new Range(primary);
        }
    }

    /**
     * True if this range is a basic range
     */
    public boolean isBasic() {
        return !extended;
    }

    private boolean checkBasic() {
        Subtag current = primary.getNext();
        while (current != null) {
            if (current.getType() == Subtag.Type.WILDCARD)
                return false;
            current = current.getNext();
        }
        return true;
    }

    /**
     * True if the lang tag matches this range
     */
    public boolean matches(String lang) {
        return matches(new Lang(lang), extended);
    }

    /**
     * True if the lang tag matches this range
     * 
     * @param lang The language tage
     * @param extended True if extended matching rules should be used
     */
    public boolean matches(String lang, boolean extended) {
        return matches(new Lang(lang), extended);
    }

    /**
     * True if the lang tag matches this range
     */
    public boolean matches(Lang lang) {
        return matches(lang, false);
    }

    /**
     * True if the lang tag matches this range
     * 
     * @param lang The language tage
     * @param extended True if extended matching rules should be used
     */
    public boolean matches(Lang lang, boolean extended) {
        Iterator i = iterator();
        Iterator e = lang.iterator();
        if (isBasic() && !extended) {
            if (primary.getType() == Subtag.Type.WILDCARD)
                return true;
            for (; i.hasNext() && e.hasNext();) {
                Subtag in = i.next();
                Subtag en = e.next();
                if (!in.equals(en))
                    return false;
            }
            return true;
        } else {
            Subtag icurrent = i.next();
            Subtag ecurrent = e.next();
            if (!icurrent.equals(ecurrent))
                return false;

            while (i.hasNext()) {
                icurrent = i.next();
                while (icurrent.getType() == Subtag.Type.WILDCARD && i.hasNext())
                    icurrent = i.next();
                // the range ends in a wildcard so it will match everything beyond this point
                if (icurrent.getType() == Subtag.Type.WILDCARD)
                    return true;
                boolean matched = false;
                while (e.hasNext()) {
                    ecurrent = e.next();
                    if (extended && (ecurrent.getType().ordinal() < icurrent.getType().ordinal()))
                        continue;
                    if (!ecurrent.equals(icurrent))
                        break;
                    else {
                        matched = true;
                        break;
                    }
                }
                if (!matched)
                    return false;
            }
            return true;
        }
    }

    /**
     * Filter the given set of lang tags. Return an array of matching tags
     */
    public Lang[] filter(Lang... lang) {
        List langs = new LinkedList();
        for (Lang l : lang)
            if (matches(l))
                langs.add(l);
        return langs.toArray(new Lang[langs.size()]);
    }

    /**
     * Filter the given set of lang tags. Return an array of matching tags
     */
    public String[] filter(String... lang) {
        List langs = new LinkedList();
        for (String l : lang)
            if (matches(l))
                langs.add(l);
        return langs.toArray(new String[langs.size()]);
    }

    /**
     * Filter the given set of lang tags. Return an array of matching tags
     */
    public static Lang[] filter(String range, Lang... lang) {
        return new Range(range).filter(lang);
    }

    /**
     * Filter the given set of lang tags. Return an array of matching tags
     */
    public static String[] filter(String range, String... lang) {
        return new Range(range).filter(lang);
    }

    /**
     * True if the lang tag matches the range.
     * 
     * @param range The language-range
     * @param lang The language tag
     * @param extended true to use extended match rules
     */
    public static boolean matches(String range, Lang lang, boolean extended) {
        return new Range(range, extended).matches(lang);
    }

    /**
     * True if the lang tag matches the range.
     * 
     * @param range The language-range
     * @param lang The language tag
     * @param extended true to use extended match rules
     */
    public static boolean matches(String range, Lang lang) {
        return new Range(range).matches(lang);
    }

    /**
     * True if the lang tag matches the range.
     * 
     * @param range The language-range
     * @param lang The language tag
     * @param extended true to use extended match rules
     */
    public static boolean matches(String range, String lang, boolean extended) {
        return new Range(range, extended).matches(lang);
    }

    /**
     * True if the lang tag matches the range.
     * 
     * @param range The language-range
     * @param lang The language tag
     * @param extended true to use extended match rules
     */
    public static boolean matches(String range, String lang) {
        return new Range(range).matches(lang);
    }

    // Parsing logic //

    private static final String range = "((?:[a-zA-Z]{1,8}|\\*))((?:[-_](?:[a-zA-Z0-9]{1,8}|\\*))*)";
    private static final String range_component = "[-_]((?:[a-zA-Z0-9]{1,8}|\\*))";
    private static final Pattern p_range = Pattern.compile(range);
    private static final Pattern p_range_component = Pattern.compile(range_component);

    private static final String language =
        "((?:[a-zA-Z]{2,3}(?:[-_](?:[a-zA-Z]{3}|\\*)){0,3})|[a-zA-Z]{4}|[a-zA-Z]{5,8}|\\*)";
    private static final String script = "((?:[-_](?:[a-zA-Z]{4}|\\*))?)";
    private static final String region = "((?:[-_](?:(?:[a-zA-Z]{2})|(?:[0-9]{3})|\\*))?)";
    private static final String variant = "((?:[-_](?:(?:[a-zA-Z0-9]{5,8})|(?:[0-9][a-zA-Z0-9]{3})|\\*))*)";
    private static final String extension = "((?:[-_](?:(?:[a-wy-zA-WY-Z0-9](?:[-_][a-zA-Z0-9]{2,8})+)|\\*))*)";
    private static final String privateuse = "[xX](?:[-_][a-zA-Z0-9]{2,8})+";
    private static final String _privateuse = "((?:[-_](?:" + privateuse + ")+|\\*)?)";
    private static final String langtag = "^" + language + script + region + variant + extension + _privateuse + "$";
    private static final String grandfathered =
        "^(?:art[-_]lojban|cel[-_]gaulish|en[-_]GB[-_]oed|i[-_]ami|i[-_]bnn|i[-_]default|i[-_]enochian|i[-_]hak|i[-_]klingon|i[-_]lux|i[-_]mingo|i[-_]navajo|i[-_]pwn|i[-_]tao||i[-_]tay|i[-_]tsu|no[-_]bok|no[-_]nyn|sgn[-_]BE[-_]fr|sgn[-_]BE[-_]nl|sgn[-_]CH[-_]de|zh[-_]cmn|zh[-_]cmn[-_]Hans|zh[-_]cmn[-_]Hant|zh[-_]gan|zh[-_]guoyu|zh[-_]hakka|zh[-_]min|zh[-_]min[-_]nan|zh[-_]wuu|zh[-_]xiang|zh[-_]yue)$";
    private static final Pattern p_privateuse = Pattern.compile("^" + privateuse + "$");
    private static final Pattern p_grandfathered = Pattern.compile(grandfathered);
    private static final Pattern p_extended_range = Pattern.compile(langtag);

    /**
     * Parse the language-range
     */
    public static Range parse(String range) {
        return parse(range, false);
    }

    /**
     * Parse the language-range
     * 
     * @param range The language-range
     * @param extended true to use extended language rules
     */
    public static Range parse(String range, boolean extended) {
        if (!extended) {
            Subtag primary = null, current = null;
            Matcher m = p_range.matcher(range);
            if (m.find()) {
                String first = m.group(1);
                String therest = m.group(2);
                primary =
                    new Subtag(first.equals("*") ? Subtag.Type.WILDCARD : Subtag.Type.SIMPLE, first
                        .toLowerCase(Locale.US));
                current = primary;
                Matcher n = p_range_component.matcher(therest);
                while (n.find()) {
                    String name = n.group(1).toLowerCase(Locale.US);
                    current = new Subtag(name.equals("*") ? Subtag.Type.WILDCARD : Subtag.Type.SIMPLE, name, current);
                }
            }
            return new Range(primary);
        } else {

            Subtag primary = null;
            Matcher m = p_grandfathered.matcher(range);
            if (m.find()) {
                String[] tags = range.split("[-_]");
                Subtag current = null;
                for (String tag : tags) {
                    if (current == null) {
                        primary = new Subtag(Type.GRANDFATHERED, tag, null);
                        current = primary;
                    } else {
                        current = new Subtag(Type.GRANDFATHERED, tag, current);
                    }
                }
                return new Range(primary);
            }
            m = p_privateuse.matcher(range);
            if (m.find()) {
                String[] tags = range.split("[-_]");
                Subtag current = null;
                for (String tag : tags) {
                    if (current == null) {
                        primary = new Subtag(tag.equals("*") ? Type.WILDCARD : Type.SINGLETON, tag, null);
                        current = primary;
                    } else {
                        current = new Subtag(tag.equals("*") ? Type.WILDCARD : Type.PRIVATEUSE, tag, current);
                    }
                }
                return new Range(primary);
            }
            m = p_extended_range.matcher(range);
            if (m.find()) {
                String langtag = m.group(1);
                String script = m.group(2);
                String region = m.group(3);
                String variant = m.group(4);
                String extension = m.group(5);
                String privateuse = m.group(6);
                Subtag current = null;
                String[] tags = langtag.split("[-_]");
                for (String tag : tags) {
                    if (current == null) {
                        primary = new Subtag(tag.equals("*") ? Type.WILDCARD : Type.PRIMARY, tag);
                        current = primary;
                    } else {
                        current = new Subtag(tag.equals("*") ? Type.WILDCARD : Type.EXTLANG, tag, current);
                    }
                }
                if (script != null && script.length() > 0)
                    current =
                        new Subtag(script.substring(1).equals("*") ? Type.WILDCARD : Type.SCRIPT, script.substring(1),
                                   current);
                if (region != null && region.length() > 0)
                    current =
                        new Subtag(region.substring(1).equals("*") ? Type.WILDCARD : Type.REGION, region.substring(1),
                                   current);
                if (variant != null && variant.length() > 0) {
                    variant = variant.substring(1);
                    tags = variant.split("-");
                    for (String tag : tags)
                        current = new Subtag(tag.equals("*") ? Type.WILDCARD : Type.VARIANT, tag, current);
                }
                if (extension != null && extension.length() > 0) {
                    extension = extension.substring(1);
                    tags = extension.split("-");
                    current = new Subtag(tags[0].equals("*") ? Type.WILDCARD : Type.SINGLETON, tags[0], current);
                    for (int i = 1; i < tags.length; i++) {
                        String tag = tags[i];
                        current =
                            new Subtag(tag.equals("*") ? Type.WILDCARD : tag.length() == 1 ? Type.SINGLETON
                                : Type.EXTENSION, tag, current);
                    }
                }
                if (privateuse != null && privateuse.length() > 0) {
                    privateuse = privateuse.substring(1);
                    tags = privateuse.split("-");
                    current = new Subtag(tags[0].equals("*") ? Type.WILDCARD : Type.SINGLETON, tags[0], current);
                    for (int i = 1; i < tags.length; i++) {
                        current = new Subtag(tags[i].equals("*") ? Type.WILDCARD : Type.PRIVATEUSE, tags[i], current);
                    }
                }
                return new Range(primary);
            }
        }
        throw new IllegalArgumentException("Invalid range");
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy