net.tachtler.browscap4j.Browscap4jFileReader.java Source code

Java tutorial

Introduction

Here is the source code for net.tachtler.browscap4j.Browscap4jFileReader.java

Source

package net.tachtler.browscap4j;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.LineIterator;
import org.apache.commons.lang3.CharUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/*******************************************************************************
 * Browscap4jFileReader read the User-Agent-String capabilities from
 * http://browscap.org - browscap.csv file.
 * 
 * @author Klaus Tachtler. <klaus@tachtler.net>
 * 
 *         Homepage : http://www.tachtler.net
 * 
 *         This program is free software; you can redistribute it and/or modify
 *         it under the terms of the GNU General Public License as published by
 *         the Free Software Foundation; either version 2 of the License, or (at
 *         your option) any later version.
 * 
 *         Copyright (c) 2018 by Klaus Tachtler.
 ******************************************************************************/
public class Browscap4jFileReader {

    private static Logger log = LogManager.getLogger();

    private static int browsecap4jMapEntrySize = 0;
    private static Entry<Pattern, Browscap4jPositionBean> browsecap4jMapEntry = null;

    /**
     * Constructor.
     */
    public Browscap4jFileReader() {
        super();
    }

    /**
     * Initialize the Browscap4jDataBean.
     * 
     * @param csvFile
     * @return Browscap4jDataBean
     * @throws IllegalStateException
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static Browscap4jDataBean initBrowscap4jData(File csvFile)
            throws IllegalStateException, FileNotFoundException, IOException {

        log.debug("*csvFile                                : " + csvFile);

        /**
         * Browscap4jDataBean with the LinkedHashMap<Pattern, Browscap4jPositionBean>
         * browscap4jMap and the String browscap4jString as data.
         */
        Browscap4jDataBean browscap4jDataBean = new Browscap4jDataBean();

        /**
         * LinkedHashMap with regular expression pattern as key from the original
         * userAgentString and Browscap4jPositionBean integer variables as value object
         * by determining the offset and the length for each line.
         */
        LinkedHashMap<Pattern, Browscap4jPositionBean> browscap4jMap = new LinkedHashMap<Pattern, Browscap4jPositionBean>();

        /**
         * Generate browscap4jString with all fields filled up with data from
         * browscap.csv to a single String.
         */
        String browscap4jString = null;

        /**
         * StringBuilder for fast concatenation.
         */
        StringBuilder stringBuilder = new StringBuilder();

        /*
         * Check if csvFile is null.
         */
        if (null == csvFile) {
            throw new IllegalStateException("Argument csvFile is null (NOT set).");
        }

        /*
         * Iterate over the csvFile - browscap.csv with all the data and generate a
         * string with all the lines concatenated. Generate a regular expression pattern
         * from the first column of the csvFile - browscap.csv as key and calculate the
         * offset and the length for every single line inside the concatenated string as
         * Browscap4jPositionBean as value.
         */
        LineIterator lineIterator = FileUtils.lineIterator(csvFile, "UTF-8");

        try {
            int offset = 0;
            String[] col = null;

            while (lineIterator.hasNext()) {
                String line = lineIterator.nextLine();

                Browscap4jPositionBean browscap4jPositionBean = new Browscap4jPositionBean(offset,
                        offset + line.length());

                offset += line.length();

                col = line.split("\",\"");

                browscap4jMap.put(Pattern.compile(convertToRegex(col[0].substring(1))), browscap4jPositionBean);

                stringBuilder.append(line);
            }
        } finally {
            LineIterator.closeQuietly(lineIterator);
        }

        /*
         * Generate the String browscap4jString from StringBuilder stringBuilder.
         */
        browscap4jString = stringBuilder.toString();

        /*
         * Debug NOT the Map and the String, because of too much entry's, only the class
         * should be printed out in debug mode, to see if the browscap4jMap and the
         * browscap4jString are NOT null.
         */
        log.debug("*browscap4jMap                          : " + browscap4jMap.getClass());
        log.debug("*browscap4jString                       : " + browscap4jString.getClass());

        browscap4jDataBean.setBrowscap4jMap(browscap4jMap);
        browscap4jDataBean.setBrowscap4jString(browscap4jString);

        return browscap4jDataBean;
    }

    /**
     * Determining the Browscap4jCapabilities with the userAgentString from
     * Browscap4jDataBean and return the result in a browscap4jBean.
     * 
     * @param browscap4jDataBean
     * @param userAgentString
     * @return Browscap4jBean
     */
    public static Browscap4jBean determineBrowscap4jCapabilities(Browscap4jDataBean browscap4jDataBean,
            String userAgentString) {

        /*
         * Debug NOT the browscap4jDataBean, because of too much entry's, only the class
         * should be printed out in debug mode, to see if the browscap4jDataBean is NOT
         * null.
         */
        log.debug("*browscap4jDataBean                     : " + browscap4jDataBean.getClass());

        /*
         * Debug NOT the Map and the String, because of too much entry's, only the class
         * should be printed out in debug mode, to see if the browscap4jMap and the
         * browscap4jString are NOT null.
         */
        log.debug("*browscap4jDataBean.getBrowscap4jMap()  : " + browscap4jDataBean.getBrowscap4jMap().getClass());
        log.debug(
                "*browscap4jString                       : " + browscap4jDataBean.getBrowscap4jString().getClass());

        log.debug("*userAgentString                        : " + userAgentString);

        /**
         * Initialize variables and the target Browscap4jBean for the result.
         */
        browsecap4jMapEntrySize = 0;
        browsecap4jMapEntry = null;
        Browscap4jBean browscap4jBean = null;

        /**
         * Instance browscap4jBean and initialize all values to "" (empty) strings.
         */
        browscap4jBean = new Browscap4jBean();
        browscap4jBean.init();

        log.debug("*browscap4jBean                         : " + browscap4jBean);

        /*
         * If one ore more results where determined, than take the first one. If nothing
         * was found fill up the browscap4jBean with empty values. This search was done
         * by using the stream capabilities of a parallel search by Java 8 (1.8).
         */
        String userAgentStringRegEx = userAgentString.toLowerCase();

        log.debug("*userAgentStringRegEx                   : " + userAgentStringRegEx);

        /*
         * Compare all the pattern entry's inside browsecap4jMapEntry with the Java 8
         * (1.8) parallel stream possibilities while using the stream and the
         * userAgentStringRegEx.
         */
        Map<Pattern, Browscap4jPositionBean> browsecap4jMapEntrys = browscap4jDataBean.getBrowscap4jMap().entrySet()
                .parallelStream().filter(map -> {
                    final Matcher matcher = map.getKey().matcher(userAgentStringRegEx);
                    return matcher.matches();
                }).collect(Collectors.toMap(map -> map.getKey(), map -> map.getValue()));

        log.debug("*browsecap4jMapEntrys                   : " + browsecap4jMapEntrys);

        log.debug("-----------------------------------------");

        /*
         * Sort the result map, to determine the longest key as the best match and put
         * that "entry" into an Entry<Pattern, Browscap4jPositionBean>
         * (browsecap4jMapEntry).
         */
        browsecap4jMapEntrys.entrySet().parallelStream().forEachOrdered(entry -> {

            if (browsecap4jMapEntrySize < entry.getKey().toString().length()) {
                browsecap4jMapEntrySize = entry.getKey().toString().length();
                browsecap4jMapEntry = entry;
            }

            log.debug("entry.getKey().toString()               : " + entry.getKey().toString());
            log.debug("entry.getKey().toString().length()      : " + entry.getKey().toString().length());
            log.debug("-----------------------------------------");

        });

        log.debug("*browsecap4jMapEntry.getKey()           : " + browsecap4jMapEntry.getKey());
        log.debug("*browsecap4jMapEntry.getValue()         : " + browsecap4jMapEntry.getValue());

        /*
         * Return the search result browsecap4jMapEntry if one was not empty. If it was
         * not empty, generate a Browscap4jBean (browscap4jBean) from the
         * browsecap4jMapEntry.getValue() using the Browscap4jPositionBean position
         * values from browscap4jPositionBean. If false generate a browscap4jBean with
         * empty values and the search fields userAgentString and userAgentStringRegEx.
         */
        if (!browsecap4jMapEntry.getKey().toString().isEmpty()
                && !browsecap4jMapEntry.getKey().toString().equals("^.*?$")) {

            Browscap4jPositionBean browscap4jPositionBean = new Browscap4jPositionBean();

            browscap4jPositionBean = browsecap4jMapEntry.getValue();

            String[] result = browscap4jDataBean.getBrowscap4jString()
                    .substring(browscap4jPositionBean.getOffset(), browscap4jPositionBean.getLength())
                    .split("\",\"");

            /*
             * Fill up the former generated browscap4jBean with the fields from csvFile -
             * browscap.csv.
             */
            browscap4jBean.setPropertyName(result[0].substring(1));
            browscap4jBean.setMasterParent(result[1]);
            browscap4jBean.setLiteMode(result[2]);
            browscap4jBean.setParent(result[3]);
            browscap4jBean.setComment(result[4]);
            browscap4jBean.setBrowser(result[5]);
            browscap4jBean.setBrowserType(result[6]);
            browscap4jBean.setBrowserBits(result[7]);
            browscap4jBean.setBrowserMaker(result[8]);
            browscap4jBean.setBrowserModus(result[9]);
            browscap4jBean.setVersion(result[10]);
            browscap4jBean.setMajorVersion(result[11]);
            browscap4jBean.setMinorVersion(result[12]);
            browscap4jBean.setPlatform(result[13]);
            browscap4jBean.setPlatformVersion(result[14]);
            browscap4jBean.setPlatformDescription(result[15]);
            browscap4jBean.setPlatformBits(result[16]);
            browscap4jBean.setPlatformMaker(result[17]);
            browscap4jBean.setAlpha(result[18]);
            browscap4jBean.setBeta(result[19]);
            browscap4jBean.setWin16(result[20]);
            browscap4jBean.setWin32(result[21]);
            browscap4jBean.setWin64(result[22]);
            browscap4jBean.setFrames(result[23]);
            browscap4jBean.setiFrames(result[24]);
            browscap4jBean.setTables(result[25]);
            browscap4jBean.setCookies(result[26]);
            browscap4jBean.setBackgroundSounds(result[27]);
            browscap4jBean.setJavaScript(result[28]);
            browscap4jBean.setVbScript(result[29]);
            browscap4jBean.setJavaApplets(result[30]);
            browscap4jBean.setActiveXControls(result[31]);
            browscap4jBean.setIsMobileDevice(result[32]);
            browscap4jBean.setIsTablet(result[33]);
            browscap4jBean.setIsSyndicationReader(result[34]);
            browscap4jBean.setCrawler(result[35]);
            browscap4jBean.setIsFake(result[36]);
            browscap4jBean.setIsAnonymized(result[37]);
            browscap4jBean.setIsModified(result[38]);
            browscap4jBean.setCssVersion(result[39]);
            browscap4jBean.setAolVersion(result[40]);
            browscap4jBean.setDeviceName(result[41]);
            browscap4jBean.setDeviceMaker(result[42]);
            browscap4jBean.setDeviceType(result[43]);
            browscap4jBean.setDevicePointingMethod(result[44]);
            browscap4jBean.setDeviceCodeName(result[45]);
            browscap4jBean.setDeviceBrandName(result[46]);
            browscap4jBean.setRenderingEngineName(result[47]);
            browscap4jBean.setRenderingEngineVersion(result[48]);
            browscap4jBean.setRenderingEngineDescription(result[49]);
            browscap4jBean.setRenderingEngineMaker(result[50].substring(0, result[50].length() - 1));

            log.debug("Browsercapabilities were found for      : " + userAgentString);
            log.debug("*browsecap4jMapEntry.get().getKey()     : " + browsecap4jMapEntry.getKey());
            log.debug("*browsecap4jMapEntry.get()...getOffset(): " + browscap4jPositionBean.getOffset());
            log.debug("*browsecap4jMapEntry.get()...getLength(): " + browscap4jPositionBean.getLength());

        } else {
            browscap4jBean.init();

            log.debug("Browsercapabilities were NOT found for  : " + userAgentString);
        }

        /*
         * Set userAgentString from request and the userAgentStringRegEx to the
         * browscap4jBean in every case, to know which search string was used.
         */
        browscap4jBean.setUserAgentString(userAgentString);
        browscap4jBean.setUserAgentStringRegEx(userAgentStringRegEx);

        return browscap4jBean;
    }

    /**
     * Format the given String to a Java regular expression formatted String.
     * 
     * @param regexPattern
     * @return regexPattern formatted String
     */
    private static String convertToRegex(String regexPattern) {

        StringBuffer stringBuffer = new StringBuffer();

        /*
         * Add the regular expression anchor at the beginning of the line.
         */
        stringBuffer.append("^");

        /*
         * Convert char by char by following rule set.
         */
        for (final char c : regexPattern.toCharArray()) {
            switch (c) {
            case '*':
                stringBuffer.append(".*?");
                break;
            case '?':
                stringBuffer.append(".");
                break;
            default:
                if (CharUtils.isAsciiAlphanumeric(c) || c == ' ') {
                    stringBuffer.append(c);
                } else {
                    stringBuffer.append("\\").append(c);
                }
            }
        }

        /*
         * Add the regular expression anchor at the end of the line.
         */
        stringBuffer.append("$");

        return stringBuffer.toString().toLowerCase();
    }

}