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

io.github.apkcloud.devicedetector.parser.AbstractParser Maven / Gradle / Ivy

Go to download

通用设备检测库将解析任何UserAgent并检测浏览器、操作系统、使用的设备(桌面、平板、移动、电视、车载、游戏机等等)、品牌和型号。

There is a newer version: 1.0.7
Show newest version
package io.github.apkcloud.devicedetector.parser;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import io.github.apkcloud.devicedetector.ClientHints;

import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 解析器的抽象类
 */
public abstract class AbstractParser {

    /**
     * 待解析的 UserAgent
     */
    protected String userAgent;

    /**
     * 一般为已解析的 ClientHints
     */
    protected ClientHints clientHints = null;

    /**
     * 正则表达式列表中所有项的串联正则表达式
     */
    protected String overAllMatch;

    /**
     * 指示如何检测版本的深度
     * 如果 maxMinorParts 为 0,仅返回主要版本
     */
    protected static int maxMinorParts = 1;

    /**
     * 版本控制常量,用于设置版本控制最大为主要版本
     * 版本示例:3, 5, 6, 200, 123, ...
     */
    public static final int VERSION_TRUNCATION_MAJOR = 0;

    /**
     * 版本控制常量,用于设置版本控制最大为次要版本
     * 版本示例:3.4, 5.6, 6.234, 0.200, 1.23, ...
     */
    public static final int VERSION_TRUNCATION_MINOR = 1;

    /**
     * 版本控制常量,用于设置版本控制最大为路径级别
     * 版本示例:3.4.0, 5.6.344, 6.234.2, 0.200.3, 1.2.3, ...
     */
    public static final int VERSION_TRUNCATION_PATCH = 2;

    /**
     * 版本控制常量,用于设置版本控制为构建版本号
     * 版本示例:3.4.0.12, 5.6.334.0, 6.234.2.3, 0.200.3.1, 1.2.3.0, ...
     */
    public static final int VERSION_TRUNCATION_BUILD = 3;

    /**
     * 版本控制常量,用于设置版本控制为无限制(没有截断)
     */
    public static final int VERSION_TRUNCATION_NONE = -1;

    /**
     * 返回解析器的内部名称
     *
     * @return String
     */
    public abstract String getName();

    public AbstractParser() {
        userAgent = "";
    }

    public AbstractParser(String ua) {
        userAgent = ua;
    }

    public AbstractParser(String ua, ClientHints clientHints) {
        userAgent = ua;
        this.clientHints = clientHints;
    }

    /**
     * 设置 DeviceDetector 应如何返回版本
     *
     * @param type 任何一个 VERSION_TRUNCATION_* 常量
     */
    public static void setVersionTruncation(int type) {
        List list = Arrays.asList(VERSION_TRUNCATION_BUILD, VERSION_TRUNCATION_NONE, VERSION_TRUNCATION_MAJOR, VERSION_TRUNCATION_MINOR, VERSION_TRUNCATION_PATCH);
        if (!list.contains(type)) {
            return;
        }

        maxMinorParts = type;
    }

    /**
     * 设置要解析的 UserAgent
     *
     * @param ua UserAgent
     */
    public void setUserAgent(String ua) {
        userAgent = ua;
    }

    /**
     * 设置 ClientHints
     *
     * @param clientHints ClientHints
     */
    public void setClientHints(ClientHints clientHints) {
        this.clientHints = clientHints;
    }

    /**
     * 返回解析yml文件的结果,yml文件路径定义在fixtureFile
     *
     * @return {@code ArrayList}
     */
    protected  T getRegexes(String fixtureFile, Class elementClass) {
        T regexList;
        InputStream inputStream = getClass().getClassLoader().getResourceAsStream(fixtureFile);
        ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());

        try {
            regexList = objectMapper.readValue(inputStream, objectMapper.getTypeFactory().constructCollectionType(ArrayList.class, elementClass));

            if (inputStream != null) {
                inputStream.close();
            }
        } catch (IOException e) {
            throw new RuntimeException("Could not load " + fixtureFile, e);
        }

        return regexList;
    }

    /**
     * 返回解析yml文件的结果,yml文件路径定义在fixtureFile
     *
     * @return {@code LinkedHashMap}
     */
    protected  T getRegexes(String fixtureFile, Class keyClass, Class valueClass) {
        T regexList;
        InputStream inputStream = getClass().getClassLoader().getResourceAsStream(fixtureFile);
        ObjectMapper objectMapper = new ObjectMapper(new YAMLFactory());

        try {
            regexList = objectMapper.readValue(inputStream, objectMapper.getTypeFactory().constructMapType(LinkedHashMap.class, keyClass, valueClass));

            if (inputStream != null) {
                inputStream.close();
            }
        } catch (IOException e) {
            throw new RuntimeException("Could not load " + fixtureFile, e);
        }

        return regexList;
    }

    /**
     * 在应用 ClientHints 映射后返回提供的名称。

* 这用于将 ClientHints 中提供的名称映射到我们使用的名称。 * * @return String */ protected String applyClientHintMapping(String name, Map> clientHintMapping) { for (Map.Entry> entry : clientHintMapping.entrySet()) { String mappedName = entry.getKey(); List clientHints = entry.getValue(); for (String clientHint : clientHints) { if (clientHint.equalsIgnoreCase(name)) { return mappedName; } } } return name; } /** * 将 UserAgent 与给定的正则表达式进行匹配。 * * @param regex 正则表达式 * @return {@code List | null} * @throws Exception */ protected List matchUserAgent(String regex) throws Exception { // 仅当 UserAgent 以给定的正则表达式开头,或者前面没有字母时才匹配 regex = "(?:^|[^A-Z0-9\\-_]|[^A-Z0-9\\-]_|sprd-|MZ-)(?:" + regex.replace("/", "\\/") + ")"; try { Matcher matcher = Pattern.compile(regex, Pattern.CASE_INSENSITIVE).matcher(userAgent); List resultList; if (matcher.find()) { resultList = new ArrayList<>(); int groupCount = matcher.groupCount(); for (int i = 0; i <= groupCount; i++) { resultList.add(matcher.group(i)); } return resultList; } } catch (Exception exception) { throw new Exception( String.format("%s\nRegex: %s", exception.getMessage(), regex), exception ); } return null; } protected String buildByMatch(String item, List matches) { List search = new ArrayList<>(); List replace = new ArrayList<>(); for (int nb = 1; nb <= matches.size(); nb++) { search.add("$" + nb); try { replace.add(matches.get(nb)); } catch (Exception e) { replace.add(""); } } String result = item; for (int i = 0; i < search.size(); i++) { result = result.replace(search.get(i), replace.get(i)); } return result.trim(); } /** * 使用给定的 versionString 和 matches 构建版本。 *

* 例子:

* $versionString = 'v$2'

* $matches = ['version_1_0_1', '1_0_1']

* 返回值将是 v1.0.1 * * @param versionString 版本字符串 * @param matches 正则匹配结果 * @return String */ protected String buildVersion(String versionString, List matches) { versionString = buildByMatch(versionString, matches); versionString = versionString.replace("_", "."); if (VERSION_TRUNCATION_NONE != maxMinorParts && (versionString.split("\\.").length - 1) > maxMinorParts) { String[] versionParts = versionString.split("\\."); versionParts = Arrays.copyOfRange(versionParts, 0, 1 + maxMinorParts); versionString = String.join(".", versionParts); } return trim(versionString, " ."); } public static String trim(String str, String charsToRemove) { String regexPattern = "^[" + Pattern.quote(charsToRemove) + "]+|[" + Pattern.quote(charsToRemove) + "]+$"; return str.replaceAll(regexPattern, ""); } /** * 对 UserAgent 字符串进行所有正则表达式的组合测试。 *

* getRegexes() 返回的所有正则表达式将被反转并使用 “|” 连接起来, * 之后将拼接后长长的正则表达式与 UserAgent 字符串进行匹配测试。 *

* 此方法可用于在单个正则表达式进行检查之前通过进行大检查来加快检测速度。 * * @return {@code List | null} */ protected List preMatchOverall(String fixtureFile) throws Exception { List> regexes = getRegexes(fixtureFile, Map.class); // 反转所有正则表达式,使通用的正则表达式首先匹配,它已经匹配了大多数模式 Collections.reverse(regexes); StringBuilder regexesStr = new StringBuilder(); for (Map item : regexes) { if (!regexesStr.toString().isEmpty()) { regexesStr.append("|").append(item.get("regex")); } else { regexesStr.append(item.get("regex")); } } overAllMatch = regexesStr.toString(); return matchUserAgent(overAllMatch); } /** * 比较两个字符串是否相等(忽略大小写和删掉空格) * * @param value1 字符串1 * @param value2 字符串2 * @return boolean 比较结果,相等返回 true,否则返回 false */ protected boolean fuzzyCompare(String value1, String value2) { return value1.replaceAll(" ", "").equalsIgnoreCase(value2.replaceAll(" ", "")); } /** * 从 Map 中根据值获取键的辅助方法 */ public static K getKeyByValue(Map map, V value) { for (Map.Entry entry : map.entrySet()) { if (entry.getValue().equals(value)) { return entry.getKey(); } } return null; } /** * 默认情况下,在第一个版本低于第二个时,version_compare() 返回 -1;如果两者相等,返回 0;第二个版本更低时则返回 1。 */ public static int compareVersions(String version1, String version2) { String[] v1Tokens = version1.split("\\."); String[] v2Tokens = version2.split("\\."); int length = Math.max(v1Tokens.length, v2Tokens.length); for (int i = 0; i < length; i++) { int v1Token = i < v1Tokens.length ? Integer.parseInt(v1Tokens[i]) : 0; int v2Token = i < v2Tokens.length ? Integer.parseInt(v2Tokens[i]) : 0; if (v1Token < v2Token) { return -1; } else if (v1Token > v2Token) { return 1; } } return 0; } public static boolean isNullOrEmpty(List list) { return list == null || list.isEmpty(); } public static boolean isNullOrEmpty(Map map) { return map == null || map.isEmpty(); } public static boolean isNullOrEmpty(String s) { return s == null || s.isEmpty(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy