Data.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.internal.data;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicInteger;

import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;

import net.sf.qualitycheck.Check;
import net.sf.uadetector.internal.data.domain.Browser;
import net.sf.uadetector.internal.data.domain.BrowserOperatingSystemMapping;
import net.sf.uadetector.internal.data.domain.BrowserPattern;
import net.sf.uadetector.internal.data.domain.BrowserType;
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;

/**
 * This class represents the detection information of <i>UADetector</i>.
 * <p>
 * An instance of {@code Data} is immutable, their values cannot be changed after creation.
 * 
 * @author André Rouél
 */
@Immutable
public class Data implements Serializable {

	/**
	 * An <i>immutable</i> empty {@code Data} object.
	 */
	public static final Data EMPTY = new Data(new HashSet<Browser>(0), new HashMap<Integer, SortedSet<BrowserPattern>>(0),
			new HashMap<Integer, BrowserType>(0), new TreeMap<BrowserPattern, Browser>(), new HashSet<BrowserOperatingSystemMapping>(0),
			new HashSet<OperatingSystem>(0), new HashMap<Integer, SortedSet<OperatingSystemPattern>>(0),
			new TreeMap<OperatingSystemPattern, OperatingSystem>(), new ArrayList<Robot>(0), new HashSet<Device>(0),
			new HashMap<Integer, SortedSet<DevicePattern>>(0), new TreeMap<DevicePattern, Device>(), "");

	private static final long serialVersionUID = 8522012551928801089L;

	@Nonnull
	private final Map<Integer, SortedSet<BrowserPattern>> browserPatterns;

	@Nonnull
	private final Set<Browser> browsers;

	@Nonnull
	private final Map<Integer, SortedSet<DevicePattern>> devicePatterns;

	@Nonnull
	private final Set<Device> devices;

	@Nonnull
	private final SortedMap<DevicePattern, Device> patternToDeviceMap;

	@Nonnull
	private final Set<BrowserOperatingSystemMapping> browserToOperatingSystemMappings;

	@Nonnull
	private final Map<Integer, BrowserType> browserTypes;

	@Nonnull
	private final Map<Integer, SortedSet<OperatingSystemPattern>> operatingSystemPatterns;

	@Nonnull
	private final Set<OperatingSystem> operatingSystems;

	@Nonnull
	private final SortedMap<BrowserPattern, Browser> patternToBrowserMap;

	@Nonnull
	private final SortedMap<OperatingSystemPattern, OperatingSystem> patternToOperatingSystemMap;

	@Nonnull
	private final List<Robot> robots;

	/**
	 * Version information of the UAS data
	 */
	@Nonnull
	private final String version;

	public Data(@Nonnull final Set<Browser> browsers, @Nonnull final Map<Integer, SortedSet<BrowserPattern>> browserPatterns,
			@Nonnull final Map<Integer, BrowserType> browserTypes, @Nonnull final SortedMap<BrowserPattern, Browser> patternToBrowserMap,
			@Nonnull final Set<BrowserOperatingSystemMapping> browserToOperatingSystemMappings,
			@Nonnull final Set<OperatingSystem> operatingSystems,
			@Nonnull final Map<Integer, SortedSet<OperatingSystemPattern>> operatingSystemPatterns,
			@Nonnull final SortedMap<OperatingSystemPattern, OperatingSystem> patternToOperatingSystemMap,
			@Nonnull final List<Robot> robots, @Nonnull final Set<Device> devices,
			@Nonnull final Map<Integer, SortedSet<DevicePattern>> devicePatterns,
			@Nonnull final SortedMap<DevicePattern, Device> patternToDeviceMap, @Nonnull final String version) {
		Check.notNull(browsers, "browsers");
		Check.notNull(browserPatterns, "browserPatterns");
		Check.notNull(browserTypes, "browserTypes");
		Check.notNull(patternToBrowserMap, "patternToBrowserMap");
		Check.notNull(browserToOperatingSystemMappings, "browserToOperatingSystemMap");
		Check.notNull(operatingSystems, "operatingSystems");
		Check.notNull(operatingSystemPatterns, "operatingSystemPatterns");
		Check.notNull(patternToOperatingSystemMap, "patternToOperatingSystemMap");
		Check.notNull(robots, "robots");
		Check.notNull(devices, "devices");
		Check.notNull(devicePatterns, "devicePatterns");
		Check.notNull(patternToDeviceMap, "patternToDeviceMap");
		Check.notNull(version, "version");

		this.browsers = Collections.unmodifiableSet(new HashSet<Browser>(browsers));
		this.browserPatterns = Collections.unmodifiableMap(new HashMap<Integer, SortedSet<BrowserPattern>>(browserPatterns));
		this.browserTypes = Collections.unmodifiableMap(new HashMap<Integer, BrowserType>(Check.notNull(browserTypes, "browserTypes")));
		this.patternToBrowserMap = Collections.unmodifiableSortedMap(new TreeMap<BrowserPattern, Browser>(patternToBrowserMap));
		this.browserToOperatingSystemMappings = Collections.unmodifiableSet(new HashSet<BrowserOperatingSystemMapping>(
				browserToOperatingSystemMappings));
		this.operatingSystems = Collections.unmodifiableSet(new HashSet<OperatingSystem>(operatingSystems));
		this.operatingSystemPatterns = Collections.unmodifiableMap(new HashMap<Integer, SortedSet<OperatingSystemPattern>>(
				operatingSystemPatterns));
		this.patternToOperatingSystemMap = Collections.unmodifiableSortedMap(new TreeMap<OperatingSystemPattern, OperatingSystem>(
				patternToOperatingSystemMap));
		this.robots = Collections.unmodifiableList(new ArrayList<Robot>(robots));
		this.devices = Collections.unmodifiableSet(new HashSet<Device>(devices));
		this.devicePatterns = Collections.unmodifiableMap(new HashMap<Integer, SortedSet<DevicePattern>>(devicePatterns));
		this.patternToDeviceMap = Collections.unmodifiableSortedMap(new TreeMap<DevicePattern, Device>(patternToDeviceMap));
		this.version = Check.notNull(version, "version");
	}

	@Override
	public boolean equals(final Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (getClass() != obj.getClass()) {
			return false;
		}
		final Data other = (Data) obj;
		if (!browsers.equals(other.browsers)) {
			return false;
		}
		if (!browserPatterns.equals(other.browserPatterns)) {
			return false;
		}
		if (!browserTypes.equals(other.browserTypes)) {
			return false;
		}
		if (!patternToBrowserMap.equals(other.patternToBrowserMap)) {
			return false;
		}
		if (!browserToOperatingSystemMappings.equals(other.browserToOperatingSystemMappings)) {
			return false;
		}
		if (!operatingSystems.equals(other.operatingSystems)) {
			return false;
		}
		if (!operatingSystemPatterns.equals(other.operatingSystemPatterns)) {
			return false;
		}
		if (!patternToOperatingSystemMap.equals(other.patternToOperatingSystemMap)) {
			return false;
		}
		if (!robots.equals(other.robots)) {
			return false;
		}
		if (!devices.equals(other.devices)) {
			return false;
		}
		if (!devicePatterns.equals(other.devicePatterns)) {
			return false;
		}
		if (!patternToDeviceMap.equals(other.patternToDeviceMap)) {
			return false;
		}
		if (!version.equals(other.version)) {
			return false;
		}
		return true;
	}

	@Nonnull
	public Map<Integer, SortedSet<BrowserPattern>> getBrowserPatterns() {
		return browserPatterns;
	}

	@Nonnull
	public Set<Browser> getBrowsers() {
		return browsers;
	}

	@Nonnull
	public Set<BrowserOperatingSystemMapping> getBrowserToOperatingSystemMappings() {
		return browserToOperatingSystemMappings;
	}

	@Nonnull
	public Map<Integer, BrowserType> getBrowserTypes() {
		return browserTypes;
	}

	@Nonnull
	public Map<Integer, SortedSet<DevicePattern>> getDevicePatterns() {
		return devicePatterns;
	}

	@Nonnull
	public Set<Device> getDevices() {
		return devices;
	}

	@Nonnull
	public Map<Integer, SortedSet<OperatingSystemPattern>> getOperatingSystemPatterns() {
		return operatingSystemPatterns;
	}

	@Nonnull
	public Set<OperatingSystem> getOperatingSystems() {
		return operatingSystems;
	}

	@Nonnull
	public SortedMap<BrowserPattern, Browser> getPatternToBrowserMap() {
		return patternToBrowserMap;
	}

	@Nonnull
	public SortedMap<DevicePattern, Device> getPatternToDeviceMap() {
		return patternToDeviceMap;
	}

	@Nonnull
	public SortedMap<OperatingSystemPattern, OperatingSystem> getPatternToOperatingSystemMap() {
		return patternToOperatingSystemMap;
	}

	@Nonnull
	public List<Robot> getRobots() {
		return robots;
	}

	/**
	 * Gets the version of the UAS data which are available within this instance.
	 * 
	 * @return version of UAS data
	 */
	@Nonnull
	public String getVersion() {
		return version;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + browsers.hashCode();
		result = prime * result + browserPatterns.hashCode();
		result = prime * result + browserTypes.hashCode();
		result = prime * result + patternToBrowserMap.hashCode();
		result = prime * result + browserToOperatingSystemMappings.hashCode();
		result = prime * result + operatingSystems.hashCode();
		result = prime * result + operatingSystemPatterns.hashCode();
		result = prime * result + patternToOperatingSystemMap.hashCode();
		result = prime * result + robots.hashCode();
		result = prime * result + devices.hashCode();
		result = prime * result + devicePatterns.hashCode();
		result = prime * result + patternToDeviceMap.hashCode();
		result = prime * result + version.hashCode();
		return result;
	}

	@Nonnull
	public String toStats() {
		final StringBuilder builder = new StringBuilder();
		builder.append("UAS data stats\n");
		builder.append("----------------------------------------------------------------");
		builder.append('\n');
		builder.append("version:\t\t");
		builder.append(version);
		builder.append('\n');
		builder.append("browser:\t\t");
		builder.append(browsers.size());
		builder.append('\n');
		final Map<String, AtomicInteger> browserByType = new HashMap<String, AtomicInteger>();
		for (final Browser browser : browsers) {
			final AtomicInteger counter = browserByType.get(browser.getType().getName());
			if (counter == null) {
				browserByType.put(browser.getType().getName(), new AtomicInteger(1));
			} else {
				counter.incrementAndGet();
			}
		}
		for (final Entry<String, AtomicInteger> entry : browserByType.entrySet()) {
			builder.append('\t');
			builder.append('\t');
			builder.append('\t');
			builder.append(entry.getKey());
			builder.append(":\t");
			builder.append(entry.getValue().get());
			builder.append('\n');
		}
		builder.append("browser patterns:\t");
		builder.append(patternToBrowserMap.size());
		builder.append('\n');
		builder.append("operating systems:\t");
		builder.append(operatingSystems.size());
		builder.append('\n');
		builder.append("os patterns:\t\t");
		builder.append(patternToOperatingSystemMap.size());
		builder.append('\n');
		builder.append("robots:\t\t\t");
		builder.append(robots.size());
		builder.append('\n');
		builder.append("devices:\t");
		builder.append(devices.size());
		builder.append('\n');
		builder.append("device patterns:\t");
		builder.append(patternToDeviceMap.size());
		builder.append('\n');
		builder.append("----------------------------------------------------------------");
		return builder.toString();
	}

	@Override
	public String toString() {
		final StringBuilder builder = new StringBuilder();
		builder.append("Data [browsers=");
		builder.append(browsers);
		builder.append(", browserPatterns=");
		builder.append(browserPatterns);
		builder.append(", browserTypes=");
		builder.append(browserTypes);
		builder.append(", patternToBrowserMap=");
		builder.append(patternToBrowserMap);
		builder.append(", browserToOperatingSystemMap=");
		builder.append(browserToOperatingSystemMappings);
		builder.append(", operatingSystems=");
		builder.append(operatingSystems);
		builder.append(", operatingSystemPatterns=");
		builder.append(operatingSystemPatterns);
		builder.append(", patternToOperatingSystemMap=");
		builder.append(patternToOperatingSystemMap);
		builder.append(", robots=");
		builder.append(robots);
		builder.append(", devices=");
		builder.append(devices);
		builder.append(", devicePatterns=");
		builder.append(devicePatterns);
		builder.append(", patternToDeviceMap=");
		builder.append(patternToDeviceMap);
		builder.append(", version=");
		builder.append(version);
		builder.append("]");
		return builder.toString();
	}

}