AbstractUserAgentStringParser.java
- /*******************************************************************************
- * Copyright 2012 André Rouél
- *
- * 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 net.sf.uadetector.parser;
- import java.util.Map.Entry;
- import java.util.regex.Matcher;
- import javax.annotation.Nonnull;
- import net.sf.uadetector.DeviceCategory;
- import net.sf.uadetector.ReadableDeviceCategory.Category;
- import net.sf.uadetector.UserAgent;
- import net.sf.uadetector.UserAgentStringParser;
- import net.sf.uadetector.UserAgentType;
- import net.sf.uadetector.VersionNumber;
- import net.sf.uadetector.datastore.DataStore;
- import net.sf.uadetector.internal.data.Data;
- import net.sf.uadetector.internal.data.domain.Browser;
- import net.sf.uadetector.internal.data.domain.BrowserPattern;
- import net.sf.uadetector.internal.data.domain.Device;
- import net.sf.uadetector.internal.data.domain.DevicePattern;
- import net.sf.uadetector.internal.data.domain.OperatingSystem;
- import net.sf.uadetector.internal.data.domain.OperatingSystemPattern;
- import net.sf.uadetector.internal.data.domain.Robot;
- public abstract class AbstractUserAgentStringParser implements UserAgentStringParser {
- /**
- * The number of capturing groups if nothing matches
- */
- private static final int ZERO_MATCHING_GROUPS = 0;
- /**
- * Examines the user agent string whether it is a browser.
- *
- * @param userAgent
- * String of an user agent
- * @param builder
- * Builder for an user agent information
- */
- private static void examineAsBrowser(final UserAgent.Builder builder, final Data data) {
- Matcher matcher;
- VersionNumber version = VersionNumber.UNKNOWN;
- for (final Entry<BrowserPattern, Browser> entry : data.getPatternToBrowserMap().entrySet()) {
- matcher = entry.getKey().getPattern().matcher(builder.getUserAgentString());
- if (matcher.find()) {
- entry.getValue().copyTo(builder);
- // try to get the browser version from the first subgroup
- if (matcher.groupCount() > ZERO_MATCHING_GROUPS) {
- version = VersionNumber.parseVersion(matcher.group(1) != null ? matcher.group(1) : "");
- }
- builder.setVersionNumber(version);
- break;
- }
- }
- }
- /**
- * Examines the user agent string whether it is a robot.
- *
- * @param userAgent
- * String of an user agent
- * @param builder
- * Builder for an user agent information
- * @return {@code true} if it is a robot, otherwise {@code false}
- */
- private static boolean examineAsRobot(final UserAgent.Builder builder, final Data data) {
- boolean isRobot = false;
- VersionNumber version;
- for (final Robot robot : data.getRobots()) {
- if (robot.getUserAgentString().equals(builder.getUserAgentString())) {
- isRobot = true;
- robot.copyTo(builder);
- // try to get the version from the last found group
- version = VersionNumber.parseLastVersionNumber(robot.getName());
- builder.setVersionNumber(version);
- break;
- }
- }
- return isRobot;
- }
- /**
- * Examines the user agent string whether has a specific device category.
- *
- * @param userAgent
- * String of an user agent
- * @param builder
- * Builder for an user agent information
- */
- private static void examineDeviceCategory(final UserAgent.Builder builder, final Data data) {
- // a robot will be classified as 'Other'
- if (UserAgentType.ROBOT == builder.getType()) {
- final DeviceCategory category = findDeviceCategoryByValue(Category.OTHER, data);
- builder.setDeviceCategory(category);
- return;
- }
- // classification depends on matching order
- for (final Entry<DevicePattern, Device> entry : data.getPatternToDeviceMap().entrySet()) {
- final Matcher matcher = entry.getKey().getPattern().matcher(builder.getUserAgentString());
- if (matcher.find()) {
- final Category category = Category.evaluate(entry.getValue().getName());
- final DeviceCategory deviceCategory = findDeviceCategoryByValue(category, data);
- builder.setDeviceCategory(deviceCategory);
- return;
- }
- }
- // an unknown user agent type should lead to an unknown device
- if (UserAgentType.UNKNOWN == builder.getType()) {
- builder.setDeviceCategory(DeviceCategory.EMPTY);
- return;
- }
- // if no pattern is available but the type is Other, Library, Validator or UA Anonymizer
- // than classify it as 'Other'
- if (UserAgentType.OTHER == builder.getType() || UserAgentType.LIBRARY == builder.getType()
- || UserAgentType.VALIDATOR == builder.getType() || UserAgentType.USERAGENT_ANONYMIZER == builder.getType()) {
- final DeviceCategory category = findDeviceCategoryByValue(Category.OTHER, data);
- builder.setDeviceCategory(category);
- return;
- }
- // if no pattern is available but the type is a mobile or WAP browser than classify it as 'Smartphone'
- if (UserAgentType.MOBILE_BROWSER == builder.getType() || UserAgentType.WAP_BROWSER == builder.getType()) {
- final DeviceCategory category = findDeviceCategoryByValue(Category.SMARTPHONE, data);
- builder.setDeviceCategory(category);
- return;
- }
- final DeviceCategory category = findDeviceCategoryByValue(Category.PERSONAL_COMPUTER, data);
- builder.setDeviceCategory(category);
- }
- /**
- * Examines the operating system of the user agent string, if not available.
- *
- * @param userAgent
- * String of an user agent
- * @param builder
- * Builder for an user agent information
- */
- private static void examineOperatingSystem(final UserAgent.Builder builder, final Data data) {
- if (net.sf.uadetector.OperatingSystem.EMPTY.equals(builder.getOperatingSystem())) {
- for (final Entry<OperatingSystemPattern, OperatingSystem> entry : data.getPatternToOperatingSystemMap().entrySet()) {
- final Matcher matcher = entry.getKey().getPattern().matcher(builder.getUserAgentString());
- if (matcher.find()) {
- entry.getValue().copyTo(builder);
- break;
- }
- }
- }
- }
- private static DeviceCategory findDeviceCategoryByValue(@Nonnull final Category category, @Nonnull final Data data) {
- for (final Device device : data.getDevices()) {
- if (category == device.getCategory()) {
- return new DeviceCategory(category, device.getIcon(), device.getInfoUrl(), device.getName());
- }
- }
- return DeviceCategory.EMPTY;
- }
- /**
- * Gets the data store of this parser.
- *
- * @return data store of this parser
- */
- protected abstract DataStore getDataStore();
- @Override
- public String getDataVersion() {
- return getDataStore().getData().getVersion();
- }
- @Override
- public UserAgent parse(final String userAgent) {
- final UserAgent.Builder builder = new UserAgent.Builder(userAgent);
- // work during the analysis always with the same reference of data
- final Data data = getDataStore().getData();
- if (!examineAsRobot(builder, data)) {
- examineAsBrowser(builder, data);
- examineOperatingSystem(builder, data);
- }
- examineDeviceCategory(builder, data);
- return builder.build();
- }
- @Override
- public void shutdown() {
- // nothing to shutdown
- }
- }