org.apache.wicket.protocol.http.ClientProperties.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.wicket.protocol.http.ClientProperties.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * 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.
 */
package org.apache.wicket.protocol.http;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.TimeZone;

import javax.servlet.http.Cookie;

import org.apache.wicket.markup.html.pages.BrowserInfoPage;
import org.apache.wicket.request.IRequestParameters;
import org.apache.wicket.request.cycle.RequestCycle;
import org.apache.wicket.request.http.WebRequest;
import org.apache.wicket.util.io.IClusterable;
import org.apache.wicket.util.string.AppendingStringBuffer;

/**
 * Description of various user agent (browser) properties. To fill the properties with values from
 * the user agent you need to probe the browser using javascript and request header analysis. Wicket
 * provides a default implementation of this in {@link BrowserInfoPage}.
 * <p>
 * A convenient way of letting Wicket do a sneaky redirect to {@link BrowserInfoPage} (and back
 * again) is to put this in your Application's init method:
 * 
 * <pre>
 * getRequestCycleSettings().setGatherExtendedBrowserInfo(true);
 * </pre>
 * 
 * </p>
 * 
 * WARNING: Be sure you think about the dangers of depending on information you pull from the client
 * too much. They may be easily spoofed or inaccurate in other ways, and properties like window and
 * browser size are all too easy to be used naively.
 * 
 * @see BrowserInfoPage
 * @author Frank Bille (frankbille)
 */
public class ClientProperties implements IClusterable {
    private static final long serialVersionUID = 1L;

    private int browserHeight = -1;
    private boolean browserInternetExplorer;
    private boolean browserKonqueror;
    private boolean browserMozilla;
    private boolean browserMozillaFirefox;
    private boolean browserOpera;
    private boolean browserSafari;
    private boolean browserChrome;
    private boolean browserEdge;
    private int browserVersionMajor = -1;
    private int browserVersionMinor = -1;
    private int browserWidth = -1;
    private boolean navigatorCookieEnabled;
    private boolean navigatorJavaEnabled;
    private String navigatorAppCodeName;
    private String navigatorAppName;
    private String navigatorAppVersion;
    private String navigatorLanguage;
    private String navigatorPlatform;
    private String navigatorUserAgent;
    private String remoteAddress;
    private int screenColorDepth = -1;
    private int screenHeight = -1;
    private int screenWidth = -1;
    private String utcDSTOffset;
    private String utcOffset;
    private String hostname;

    private boolean javaScriptEnabled;

    /** Cached timezone for repeating calls to {@link #getTimeZone()} */
    private transient TimeZone timeZone;

    /**
     * @return The browser height at the time it was measured
     */
    public int getBrowserHeight() {
        return browserHeight;
    }

    /**
     * @return The major version number of the browser.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public int getBrowserVersionMajor() {
        return browserVersionMajor;
    }

    /**
     * @return The minor version number of the browser.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public int getBrowserVersionMinor() {
        return browserVersionMinor;
    }

    /**
     * @return The browser width at the time it was measured
     */
    public int getBrowserWidth() {
        return browserWidth;
    }

    /**
     * @return The client's navigator.appCodeName property.
     */
    public String getNavigatorAppCodeName() {
        return navigatorAppCodeName;
    }

    /**
     * @return The client's navigator.appName property.
     */
    public String getNavigatorAppName() {
        return navigatorAppName;
    }

    /**
     * @return The client's navigator.appVersion property.
     */
    public String getNavigatorAppVersion() {
        return navigatorAppVersion;
    }

    /**
     * @return The client's navigator.language (or navigator.userLanguage) property.
     */
    public String getNavigatorLanguage() {
        return navigatorLanguage;
    }

    /**
     * @return The client's navigator.platform property.
     */
    public String getNavigatorPlatform() {
        return navigatorPlatform;
    }

    /**
     * @return The client's navigator.userAgent property.
     */
    public String getNavigatorUserAgent() {
        return navigatorUserAgent;
    }

    /**
     * @return The client's remote/ip address.
     */
    public String getRemoteAddress() {
        return remoteAddress;
    }

    /**
     * @return The clients hostname shown in the browser
     */
    public String getHostname() {
        return hostname;
    }

    /**
     * @return Color depth of the screen in bits (integer).
     */
    public int getScreenColorDepth() {
        return screenColorDepth;
    }

    /**
     * @return Height of the screen in pixels (integer).
     */
    public int getScreenHeight() {
        return screenHeight;
    }

    /**
     * @return Height of the screen in pixels (integer).
     */
    public int getScreenWidth() {
        return screenWidth;
    }

    /**
     * Get the client's time zone if that could be detected.
     * 
     * @return The client's time zone
     */
    public TimeZone getTimeZone() {
        if (timeZone == null) {
            String utc = getUtcOffset();
            if (utc != null) {
                // apparently it is platform dependent on whether you get the
                // offset in a decimal form or not. This parses the decimal
                // form of the UTC offset, taking into account several
                // possibilities
                // such as getting the format in +2.5 or -1.2

                int dotPos = utc.indexOf('.');
                if (dotPos >= 0) {
                    String hours = utc.substring(0, dotPos);
                    String hourPart = utc.substring(dotPos + 1);

                    if (hours.startsWith("+")) {
                        hours = hours.substring(1);
                    }
                    int offsetHours = Integer.parseInt(hours);
                    int offsetMins = (int) (Double.parseDouble(hourPart) * 6);

                    // construct a GMT timezone offset string from the retrieved
                    // offset which can be parsed by the TimeZone class.

                    AppendingStringBuffer sb = new AppendingStringBuffer("GMT");
                    sb.append(offsetHours > 0 ? '+' : '-');
                    sb.append(Math.abs(offsetHours));
                    sb.append(':');
                    if (offsetMins < 10) {
                        sb.append('0');
                    }
                    sb.append(offsetMins);
                    timeZone = TimeZone.getTimeZone(sb.toString());
                } else {
                    int offset = Integer.parseInt(utc);
                    if (offset < 0) {
                        utc = utc.substring(1);
                    }
                    timeZone = TimeZone.getTimeZone("GMT" + ((offset > 0) ? '+' : '-') + utc);
                }

                String dstOffset = getUtcDSTOffset();
                if (timeZone != null && dstOffset != null) {
                    TimeZone dstTimeZone;
                    dotPos = dstOffset.indexOf('.');
                    if (dotPos >= 0) {
                        String hours = dstOffset.substring(0, dotPos);
                        String hourPart = dstOffset.substring(dotPos + 1);

                        if (hours.startsWith("+")) {
                            hours = hours.substring(1);
                        }
                        int offsetHours = Integer.parseInt(hours);
                        int offsetMins = (int) (Double.parseDouble(hourPart) * 6);

                        // construct a GMT timezone offset string from the
                        // retrieved
                        // offset which can be parsed by the TimeZone class.

                        AppendingStringBuffer sb = new AppendingStringBuffer("GMT");
                        sb.append(offsetHours > 0 ? '+' : '-');
                        sb.append(Math.abs(offsetHours));
                        sb.append(':');
                        if (offsetMins < 10) {
                            sb.append('0');
                        }
                        sb.append(offsetMins);
                        dstTimeZone = TimeZone.getTimeZone(sb.toString());
                    } else {
                        int offset = Integer.parseInt(dstOffset);
                        if (offset < 0) {
                            dstOffset = dstOffset.substring(1);
                        }
                        dstTimeZone = TimeZone.getTimeZone("GMT" + ((offset > 0) ? '+' : '-') + dstOffset);
                    }
                    // if the dstTimezone (1 July) has a different offset then
                    // the real time zone (1 January) try to combine the 2.
                    if (dstTimeZone != null && dstTimeZone.getRawOffset() != timeZone.getRawOffset()) {
                        int dstSaving = Math.abs(dstTimeZone.getRawOffset() - timeZone.getRawOffset());
                        String[] availableIDs = TimeZone.getAvailableIDs(
                                dstTimeZone.getRawOffset() < timeZone.getRawOffset() ? dstTimeZone.getRawOffset()
                                        : timeZone.getRawOffset());
                        for (String availableID : availableIDs) {
                            TimeZone zone = TimeZone.getTimeZone(availableID);
                            if (zone.getDSTSavings() == dstSaving) {
                                // this is a best guess... still the start and end of the DST should
                                // be needed to know to be completely correct, or better yet
                                // not just the GMT offset but the TimeZone ID should be transfered
                                // from the browser.
                                timeZone = zone;
                                break;
                            }
                        }
                    }
                }
            }
        }

        return timeZone;
    }

    /**
     * @return The client's time DST offset from UTC in hours (note: if you do this yourself, use
     *         'new Date(new Date().getFullYear(), 0, 6, 0, 0, 0, 0).getTimezoneOffset() / -60'
     *         (note the -)).
     */
    public String getUtcDSTOffset() {
        return utcDSTOffset;
    }

    /**
     * @return The client's time offset from UTC in hours (note: if you do this yourself, use 'new
     *         Date(new Date().getFullYear(), 0, 1, 0, 0, 0, 0).getTimezoneOffset() / -60' (note the
     *         -)).
     */
    public String getUtcOffset() {
        return utcOffset;
    }

    /**
     * Flag indicating support of JavaScript in the browser.
     * 
     * @return True if JavaScript is enabled
     */
    public boolean isJavaScriptEnabled() {
        return javaScriptEnabled;
    }

    /**
     * Flag indicating that the browser is a derivative of the Microsoft Internet Explorer browser
     * platform.
     * 
     * @return True if a derivative of the Microsoft Internet Explorer browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public boolean isBrowserInternetExplorer() {
        return browserInternetExplorer;
    }

    /**
     * Flag indicating that the browser is a derivative of the KDE Konqueror browser platform.
     * 
     * @return True if a derivative of the KDE Konqueror browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public boolean isBrowserKonqueror() {
        return browserKonqueror;
    }

    /**
     * Flag indicating that the browser is a derivative of the Mozilla 1.0-1.8+ browser platform.
     * 
     * @return True if a derivative of the Mozilla 1.0-1.8+ browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public boolean isBrowserMozilla() {
        return browserMozilla;
    }

    /**
     * Flag indicating that the browser is a derivative of the Mozilla Firefox 1.0+ browser
     * platform.
     * 
     * @return True if a derivative of the Mozilla Firefox 1.0+ browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public boolean isBrowserMozillaFirefox() {
        return browserMozillaFirefox;
    }

    /**
     * Flag indicating that the browser is a derivative of the Opera browser platform.
     * 
     * @return True if a derivative of the Opera browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public boolean isBrowserOpera() {
        return browserOpera;
    }

    /**
     * Flag indicating that the browser is a derivative of the Apple Safari browser platform.
     * 
     * @return True if a derivative of the Apple Safari browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public boolean isBrowserSafari() {
        return browserSafari;
    }

    /**
     * Flag indicating that the browser is a derivative of the Chrome browser platform.
     * 
     * @return True if a derivative of the Chrome browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public boolean isBrowserChrome() {
        return browserChrome;
    }

    /**
     * Flag indicating that the browser is a derivative of the Microsoft Edge browser platform.
     *
     * @return True if a derivative of the Microsoft Edge browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public boolean isBrowserEdge() {
        return browserEdge;
    }

    /**
     * 
     * 
     * @return The client's navigator.cookieEnabled property.
     */
    public boolean isNavigatorCookieEnabled() {
        if (!navigatorCookieEnabled && RequestCycle.get() != null) {
            Collection<Cookie> cookies = ((WebRequest) RequestCycle.get().getRequest()).getCookies();
            navigatorCookieEnabled = cookies != null && cookies.size() > 0;
        }
        return navigatorCookieEnabled;
    }

    /**
     * @return The client's navigator.javaEnabled property.
     */
    public boolean isNavigatorJavaEnabled() {
        return navigatorJavaEnabled;
    }

    /**
     * @param browserHeight
     *            The height of the browser
     */
    public void setBrowserHeight(int browserHeight) {
        this.browserHeight = browserHeight;
    }

    /**
     * Flag indicating that the browser is a derivative of the Microsoft Internet Explorer browser
     * platform.
     * 
     * @param browserInternetExplorer
     *            True if a derivative of the Microsoft Internet Explorer browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public void setBrowserInternetExplorer(boolean browserInternetExplorer) {
        this.browserInternetExplorer = browserInternetExplorer;
    }

    /**
     * Flag indicating that the browser is a derivative of the KDE Konqueror browser platform.
     * 
     * @param browserKonqueror
     *            True if a derivative of the KDE Konqueror browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public void setBrowserKonqueror(boolean browserKonqueror) {
        this.browserKonqueror = browserKonqueror;
    }

    /**
     * Flag indicating that the browser is a derivative of the Mozilla 1.0-1.8+ browser platform.
     * 
     * @param browserMozilla
     *            True if a derivative of the Mozilla 1.0-1.8+ browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public void setBrowserMozilla(boolean browserMozilla) {
        this.browserMozilla = browserMozilla;
    }

    /**
     * Flag indicating that the browser is a derivative of the Mozilla Firefox 1.0+ browser
     * platform.
     * 
     * @param browserMozillaFirefox
     *            True if a derivative of the Mozilla Firefox 1.0+ browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public void setBrowserMozillaFirefox(boolean browserMozillaFirefox) {
        this.browserMozillaFirefox = browserMozillaFirefox;
    }

    /**
     * Flag indicating that the browser is a derivative of the Opera browser platform.
     * 
     * @param browserOpera
     *            True if a derivative of the Opera browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public void setBrowserOpera(boolean browserOpera) {
        this.browserOpera = browserOpera;
    }

    /**
     * Flag indicating that the browser is a derivative of the Apple Safari browser platform.
     * 
     * @param browserSafari
     *            True if a derivative of the Apple Safari browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public void setBrowserSafari(boolean browserSafari) {
        this.browserSafari = browserSafari;
    }

    /**
     * Flag indicating that the browser is a derivative of the Chrome browser platform.
     * 
     * @param browserChrome
     *            True if a derivative of the Chrome browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public void setBrowserChrome(boolean browserChrome) {
        this.browserChrome = browserChrome;
    }

    /**
     * Flag indicating that the browser is a derivative of the Microsoft Edge browser platform.
     *
     * @param browserEdge
     *            True if a derivative of the Microsoft Edge browser platform.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public void setBrowserEdge(boolean browserEdge) {
        this.browserEdge = browserEdge;
    }

    /**
     * @param browserVersionMajor
     *            The major version number of the browser.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public void setBrowserVersionMajor(int browserVersionMajor) {
        this.browserVersionMajor = browserVersionMajor;
    }

    /**
     * @param browserVersionMinor
     *            The minor version number of the browser.
     * @deprecated Users are recommended to use third party library for parsing the user agent string.
     * @see <a href="https://cwiki.apache.org/confluence/display/WICKET/Migration+to+Wicket+8.0#MigrationtoWicket8.0-Useragentdetection">Wicket 8 migration entry</a>
     */
    @Deprecated
    public void setBrowserVersionMinor(int browserVersionMinor) {
        this.browserVersionMinor = browserVersionMinor;
    }

    /**
     * @param browserWidth
     *            The browser width
     */
    public void setBrowserWidth(int browserWidth) {
        this.browserWidth = browserWidth;
    }

    /**
     * @param cookiesEnabled
     *            The client's navigator.cookieEnabled property.
     */
    public void setNavigatorCookieEnabled(boolean cookiesEnabled) {
        this.navigatorCookieEnabled = cookiesEnabled;
    }

    /**
     * @param navigatorJavaEnabled
     *            The client's navigator.javaEnabled property.
     */
    public void setNavigatorJavaEnabled(boolean navigatorJavaEnabled) {
        this.navigatorJavaEnabled = navigatorJavaEnabled;
    }

    /**
     * @param navigatorAppCodeName
     *            The client's navigator.appCodeName property.
     */
    public void setNavigatorAppCodeName(String navigatorAppCodeName) {
        this.navigatorAppCodeName = navigatorAppCodeName;
    }

    /**
     * @param navigatorAppName
     *            The client's navigator.appName property.
     */
    public void setNavigatorAppName(String navigatorAppName) {
        this.navigatorAppName = navigatorAppName;
    }

    /**
     * @param navigatorAppVersion
     *            The client's navigator.appVersion property.
     */
    public void setNavigatorAppVersion(String navigatorAppVersion) {
        this.navigatorAppVersion = navigatorAppVersion;
    }

    /**
     * @param navigatorLanguage
     *            The client's navigator.language (or navigator.userLanguage) property.
     */
    public void setNavigatorLanguage(String navigatorLanguage) {
        this.navigatorLanguage = navigatorLanguage;
    }

    /**
     * @param navigatorPlatform
     *            The client's navigator.platform property.
     */
    public void setNavigatorPlatform(String navigatorPlatform) {
        this.navigatorPlatform = navigatorPlatform;
    }

    /**
     * @param navigatorUserAgent
     *            The client's navigator.userAgent property.
     */
    public void setNavigatorUserAgent(String navigatorUserAgent) {
        this.navigatorUserAgent = navigatorUserAgent;
    }

    /**
     * @param remoteAddress
     *            The client's remote/ip address.
     */
    public void setRemoteAddress(String remoteAddress) {
        this.remoteAddress = remoteAddress;
    }

    /**
     * @param hostname
     *            the hostname shown in the browser.
     */
    public void setHostname(String hostname) {
        this.hostname = hostname;
    }

    /**
     * @param screenColorDepth
     *            Color depth of the screen in bits (integer).
     */
    public void setScreenColorDepth(int screenColorDepth) {
        this.screenColorDepth = screenColorDepth;
    }

    /**
     * @param screenHeight
     *            Height of the screen in pixels (integer).
     */
    public void setScreenHeight(int screenHeight) {
        this.screenHeight = screenHeight;
    }

    /**
     * @param screenWidth
     *            Height of the screen in pixels (integer).
     */
    public void setScreenWidth(int screenWidth) {
        this.screenWidth = screenWidth;
    }

    /**
     * Sets time zone.
     * 
     * @param timeZone
     */
    public void setTimeZone(TimeZone timeZone) {
        this.timeZone = timeZone;
    }

    /**
     * @param utcDSTOffset
     */
    public void setUtcDSTOffset(String utcDSTOffset) {
        this.utcDSTOffset = utcDSTOffset;
    }

    /**
     * @param utcOffset
     *            The client's time offset from UTC in minutes (note: if you do this yourself, use
     *            'new Date().getTimezoneOffset() / -60' (note the -)).
     */
    public void setUtcOffset(String utcOffset) {
        this.utcOffset = utcOffset;
    }

    /**
     * @param javaScriptEnabled
     *            is JavaScript supported in the browser
     */
    public void setJavaScriptEnabled(boolean javaScriptEnabled) {
        this.javaScriptEnabled = javaScriptEnabled;
    }

    @Override
    public String toString() {
        StringBuilder b = new StringBuilder();

        Class<?> clazz = getClass();
        while (clazz != Object.class) {
            Field[] fields = clazz.getDeclaredFields();

            for (Field field : fields) {
                // Ignore these fields
                if (Modifier.isStatic(field.getModifiers()) || Modifier.isTransient(field.getModifiers())
                        || field.isSynthetic()) {
                    continue;
                }

                field.setAccessible(true);

                Object value;
                try {
                    value = field.get(this);
                } catch (IllegalArgumentException e) {
                    throw new RuntimeException(e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }

                if (field.getType().equals(Integer.TYPE)) {
                    if (Integer.valueOf(-1).equals(value)) {
                        value = null;
                    }
                }

                if (value != null) {
                    b.append(field.getName());
                    b.append('=');
                    b.append(value);
                    b.append('\n');
                }
            }

            clazz = clazz.getSuperclass();
        }
        return b.toString();
    }

    /**
     * Read parameters.
     * 
     * @param parameters
     *            parameters sent from browser
     */
    public void read(IRequestParameters parameters) {
        setNavigatorAppCodeName(parameters.getParameterValue("navigatorAppCodeName").toString("N/A"));
        setNavigatorAppName(parameters.getParameterValue("navigatorAppName").toString("N/A"));
        setNavigatorAppVersion(parameters.getParameterValue("navigatorAppVersion").toString("N/A"));
        setNavigatorCookieEnabled(parameters.getParameterValue("navigatorCookieEnabled").toBoolean(false));
        setNavigatorJavaEnabled(parameters.getParameterValue("navigatorJavaEnabled").toBoolean(false));
        setNavigatorLanguage(parameters.getParameterValue("navigatorLanguage").toString("N/A"));
        setNavigatorPlatform(parameters.getParameterValue("navigatorPlatform").toString("N/A"));
        setNavigatorUserAgent(parameters.getParameterValue("navigatorUserAgent").toString("N/A"));
        setScreenWidth(parameters.getParameterValue("screenWidth").toInt(-1));
        setScreenHeight(parameters.getParameterValue("screenHeight").toInt(-1));
        setScreenColorDepth(parameters.getParameterValue("screenColorDepth").toInt(-1));
        setUtcOffset(parameters.getParameterValue("utcOffset").toString(null));
        setUtcDSTOffset(parameters.getParameterValue("utcDSTOffset").toString(null));
        setBrowserWidth(parameters.getParameterValue("browserWidth").toInt(-1));
        setBrowserHeight(parameters.getParameterValue("browserHeight").toInt(-1));
        setHostname(parameters.getParameterValue("hostname").toString("N/A"));
    }
}