/*******************************************************************************
* Copyright 2011 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.jacclog.uasparser.internal.data;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
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.TreeSet;
import net.sf.jacclog.uasparser.internal.data.domain.Browser;
import net.sf.jacclog.uasparser.internal.data.domain.BrowserOperatingSystemMapping;
import net.sf.jacclog.uasparser.internal.data.domain.BrowserPattern;
import net.sf.jacclog.uasparser.internal.data.domain.BrowserType;
import net.sf.jacclog.uasparser.internal.data.domain.OperatingSystem;
import net.sf.jacclog.uasparser.internal.data.domain.OperatingSystemPattern;
import net.sf.jacclog.uasparser.internal.data.domain.Robot;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The <code>Data</code> class represents the data for <i>UASparser</i> from {@link http://user-agent-string.info/}.<br>
* <br>
* A <code>Data</code> object is immutable; their values cannot be changed after creation.
*
* @author André Rouél
*/
public class Data {
/**
* This builder is not thread safe.
*
* @author André Rouél
*/
public static class Builder {
private static final Logger LOG = LoggerFactory.getLogger(Builder.class);
private static void addOperatingSystemToBrowser(final Map<Integer, Browser.Builder> browserBuilders,
final Map<Integer, OperatingSystem> operatingSystems, final Map<Integer, Integer> browserOsMap) {
Browser.Builder browserBuilder;
for (final Map.Entry<Integer, Integer> entry : browserOsMap.entrySet()) {
if (browserBuilders.containsKey(entry.getKey())) {
browserBuilder = browserBuilders.get(entry.getKey());
if (operatingSystems.containsKey(entry.getValue())) {
browserBuilder.setOperatingSystem(operatingSystems.get(entry.getValue()));
} else {
LOG.warn("Can not find an operating system with ID '" + entry.getValue() + "' for browser '"
+ browserBuilder.getProducer() + " " + browserBuilder.getFamily() + "'.");
}
} else {
LOG.warn("Can not find a browser with ID '" + entry.getKey() + "'.");
}
}
}
private static void addPatternToBrowser(final Map<Integer, Browser.Builder> builders,
final Map<Integer, SortedSet<BrowserPattern>> patterns) {
for (final Map.Entry<Integer, Browser.Builder> entry : builders.entrySet()) {
if (patterns.containsKey(entry.getKey())) {
entry.getValue().setPatternSet(patterns.get(entry.getKey()));
} else {
LOG.warn("No pattern available for '" + entry.getValue().getProducer() + " "
+ entry.getValue().getFamily() + "'.");
}
}
}
private static void addPatternToOperatingSystem(final Map<Integer, OperatingSystem.Builder> builders,
final Map<Integer, SortedSet<OperatingSystemPattern>> patterns) {
for (final Map.Entry<Integer, OperatingSystem.Builder> entry : builders.entrySet()) {
if (patterns.containsKey(entry.getKey())) {
entry.getValue().setPatternSet(patterns.get(entry.getKey()));
} else {
LOG.warn("No pattern available for '" + entry.getValue().getProducer() + " "
+ entry.getValue().getFamily() + "'.");
}
}
}
private static void addTypeToBrowser(final Map<Integer, Browser.Builder> builders,
final Map<Integer, BrowserType> types) {
int typeId;
for (final Map.Entry<Integer, Browser.Builder> entry : builders.entrySet()) {
typeId = entry.getValue().getTypeId();
if (types.containsKey(typeId)) {
entry.getValue().setType(types.get(typeId));
} else {
LOG.warn("No type available for '" + entry.getValue().getProducer() + " "
+ entry.getValue().getFamily() + "'.");
}
}
}
private static Set<Browser> buildBrowsers(final Map<Integer, Browser.Builder> browserBuilders) {
final Set<Browser> browsers = new HashSet<Browser>();
for (final Map.Entry<Integer, Browser.Builder> entry : browserBuilders.entrySet()) {
try {
browsers.add(entry.getValue().build());
} catch (final Exception e) {
LOG.error("Can not build browser.", e);
}
}
return browsers;
}
private static Map<Integer, OperatingSystem> buildOperatingSystems(
final Map<Integer, OperatingSystem.Builder> osBuilders) {
final Map<Integer, OperatingSystem> operatingSystems = new HashMap<Integer, OperatingSystem>();
for (final Map.Entry<Integer, OperatingSystem.Builder> entry : osBuilders.entrySet()) {
try {
operatingSystems.put(entry.getKey(), entry.getValue().build());
} catch (final Exception e) {
LOG.error("Can not build operating system.", e);
}
}
return operatingSystems;
}
private static SortedMap<BrowserPattern, Browser> buildPatternBrowserMap(final Set<Browser> browserSet) {
final SortedMap<BrowserPattern, Browser> patternBrowser = new TreeMap<BrowserPattern, Browser>(
ORDERED_PATTERN_COMPARATOR);
for (final Browser browser : browserSet) {
for (final BrowserPattern pattern : browser.getPatternSet()) {
patternBrowser.put(pattern, browser);
}
}
return patternBrowser;
}
private static SortedMap<OperatingSystemPattern, OperatingSystem> buildPatternOperatingSystemMap(
final Set<OperatingSystem> osSet) {
final SortedMap<OperatingSystemPattern, OperatingSystem> map = new TreeMap<OperatingSystemPattern, OperatingSystem>(
ORDERED_PATTERN_COMPARATOR);
for (final OperatingSystem os : osSet) {
for (final OperatingSystemPattern pattern : os.getPatternSet()) {
map.put(pattern, os);
}
}
return map;
}
private static Map<Integer, Integer> convertBrowserOsMapping(
final Set<BrowserOperatingSystemMapping> browserOperatingSystemMappings) {
final Map<Integer, Integer> result = new HashMap<Integer, Integer>();
for (final BrowserOperatingSystemMapping mapping : browserOperatingSystemMappings) {
result.put(mapping.getBrowserId(), mapping.getOperatingSystemId());
}
return result;
}
private static Set<OperatingSystem> convertOperatingSystems(final Map<Integer, OperatingSystem> operatingSystems) {
final Set<OperatingSystem> result = new HashSet<OperatingSystem>();
for (final Entry<Integer, OperatingSystem> entry : operatingSystems.entrySet()) {
result.add(entry.getValue());
}
return result;
}
private final Map<Integer, BrowserType> browserTypes = new HashMap<Integer, BrowserType>();
private final Map<Integer, SortedSet<BrowserPattern>> browserPatterns = new HashMap<Integer, SortedSet<BrowserPattern>>();
private final Map<Integer, SortedSet<OperatingSystemPattern>> operatingSystemPatterns = new HashMap<Integer, SortedSet<OperatingSystemPattern>>();
private final Map<Integer, Browser.Builder> browserBuilders = new HashMap<Integer, Browser.Builder>();
private final Set<Browser> browsers = new HashSet<Browser>();
private final Map<Integer, OperatingSystem.Builder> operatingSystemBuilders = new HashMap<Integer, OperatingSystem.Builder>();
private final Set<OperatingSystem> operatingSystems = new HashSet<OperatingSystem>();
private final Set<Robot> robots = new HashSet<Robot>();
private String version;
private final Set<BrowserOperatingSystemMapping> browserOperatingSystemMappings = new HashSet<BrowserOperatingSystemMapping>();
private static final OrderedPatternComparator ORDERED_PATTERN_COMPARATOR = new OrderedPatternComparator();
public Builder appendBrowser(final Browser browser) {
if (browser == null) {
throw new IllegalArgumentException("Argument 'browser' can not be null.");
}
browsers.add(browser);
return this;
}
public Builder appendBrowserBuilder(final Browser.Builder browserBuilder) {
if (browserBuilder == null) {
throw new IllegalArgumentException("Argument 'browserBuilder' can not be null.");
}
if (browserBuilder.getId() < 0) {
throw new IllegalArgumentException("The ID of argument 'browserBuilder' can not be smaller than 0.");
}
if (browserBuilders.containsKey(browserBuilder.getId())) {
throw new IllegalArgumentException("The pattern '" + browserBuilder.getProducer() + " "
+ browserBuilder.getFamily() + "' is already in the map.");
}
browserBuilders.put(browserBuilder.getId(), browserBuilder);
return this;
}
public Builder appendBrowserOperatingSystemMapping(final BrowserOperatingSystemMapping browserOsMapping) {
if (browserOsMapping == null) {
throw new IllegalArgumentException("Argument 'browserOsMapping' can not be null.");
}
browserOperatingSystemMappings.add(browserOsMapping);
return this;
}
/**
* Appends a browser pattern to the map unless the ID is not already present. If the ID of the pattern is
* already set an <code>IllegalArgumentException</code> will be thrown.
*
* @param pattern
* A pattern for a browser
* @throws IllegalArgumentException
* If the pattern is null
* @throws IllegalArgumentException
* If the ID of the pattern is smaller than null
* @return itself
*/
public Builder appendBrowserPattern(final BrowserPattern pattern) {
if (pattern == null) {
throw new IllegalArgumentException("Argument 'pattern' can not be null.");
}
if (pattern.getId() < 0) {
throw new IllegalArgumentException("The ID of argument 'pattern' can not be smaller than 0.");
}
if (!browserPatterns.containsKey(pattern.getId())) {
browserPatterns.put(pattern.getId(), new TreeSet<BrowserPattern>(ORDERED_PATTERN_COMPARATOR));
}
browserPatterns.get(pattern.getId()).add(pattern);
return this;
}
public Builder appendBrowserType(final BrowserType type) {
if (type == null) {
throw new IllegalArgumentException("Argument 'type' can not be null.");
}
browserTypes.put(type.getId(), type);
return this;
}
public Builder appendOperatingSystem(final OperatingSystem operatingSystem) {
if (operatingSystem == null) {
throw new IllegalArgumentException("Argument 'operatingSystem' can not be null.");
}
operatingSystems.add(operatingSystem);
return this;
}
public Builder appendOperatingSystemBuilder(final OperatingSystem.Builder operatingSystemBuilder) {
if (operatingSystemBuilder == null) {
throw new IllegalArgumentException("Argument 'operatingSystemBuilder' can not be null.");
}
if (operatingSystemBuilder.getId() < 0) {
throw new IllegalArgumentException(
"The ID of argument 'operatingSystemBuilder' can not be smaller than 0.");
}
operatingSystemBuilders.put(operatingSystemBuilder.getId(), operatingSystemBuilder);
return this;
}
/**
* Appends a operating system pattern to the map unless the ID is not already present. If the ID of the pattern
* is already set an <code>IllegalArgumentException</code> will be thrown.
*
* @param pattern
* A pattern for a browser
* @throws IllegalArgumentException
* If the pattern is null
* @throws IllegalArgumentException
* If the ID of the pattern is smaller than null
* @return itself
*/
public Builder appendOperatingSystemPattern(final OperatingSystemPattern pattern) {
if (pattern == null) {
throw new IllegalArgumentException("Argument 'pattern' can not be null.");
}
if (pattern.getId() < 0) {
throw new IllegalArgumentException("The ID of argument 'pattern' can not be smaller than 0.");
}
if (!operatingSystemPatterns.containsKey(pattern.getId())) {
operatingSystemPatterns.put(pattern.getId(), new TreeSet<OperatingSystemPattern>(
ORDERED_PATTERN_COMPARATOR));
}
operatingSystemPatterns.get(pattern.getId()).add(pattern);
return this;
}
public Builder appendRobot(final Robot robot) {
if (robot == null) {
throw new IllegalArgumentException("Argument 'robot' can not be null.");
}
robots.add(robot);
return this;
}
public Data build() {
addTypeToBrowser(browserBuilders, browserTypes);
addPatternToBrowser(browserBuilders, browserPatterns);
addPatternToOperatingSystem(operatingSystemBuilders, operatingSystemPatterns);
final Map<Integer, OperatingSystem> operatingSystems = buildOperatingSystems(operatingSystemBuilders);
addOperatingSystemToBrowser(browserBuilders, operatingSystems,
convertBrowserOsMapping(browserOperatingSystemMappings));
final Set<OperatingSystem> osSet = convertOperatingSystems(operatingSystems);
osSet.addAll(this.operatingSystems);
final Set<Browser> browserSet = buildBrowsers(browserBuilders);
browserSet.addAll(browsers);
final SortedMap<BrowserPattern, Browser> patternBrowserMap = buildPatternBrowserMap(browserSet);
final SortedMap<OperatingSystemPattern, OperatingSystem> patternOsMap = buildPatternOperatingSystemMap(osSet);
return new Data(browserSet, osSet, robots, patternBrowserMap, patternOsMap, version);
}
public Builder setVersion(final String version) {
if (version == null) {
throw new IllegalArgumentException("Argument 'version' can not be null.");
}
this.version = version;
return this;
}
}
/**
* An immutable empty <code>Data</code> object.
*/
public static final Data EMPTY = new Data(new HashSet<Browser>(), new HashSet<OperatingSystem>(),
new HashSet<Robot>(0), new TreeMap<BrowserPattern, Browser>(),
new TreeMap<OperatingSystemPattern, OperatingSystem>(), "");
private final Set<Browser> browsers;
private final Set<OperatingSystem> operatingSystems;
private final Set<Robot> robots;
private final String version;
private final SortedMap<BrowserPattern, Browser> patternBrowserMap;
private final SortedMap<OperatingSystemPattern, OperatingSystem> patternOsMap;
public Data(final Set<Browser> browsers, final Set<OperatingSystem> operatingSystems, final Set<Robot> robots,
final SortedMap<BrowserPattern, Browser> patternBrowserMap,
final SortedMap<OperatingSystemPattern, OperatingSystem> patternOsMap, final String version) {
if (browsers == null) {
throw new IllegalArgumentException("Argument 'browsers' can not be null.");
}
if (operatingSystems == null) {
throw new IllegalArgumentException("Argument 'operatingSystems' can not be null.");
}
if (robots == null) {
throw new IllegalArgumentException("Argument 'robots' can not be null.");
}
if (patternBrowserMap == null) {
throw new IllegalArgumentException("Argument 'patternBrowserMap' can not be null.");
}
if (patternOsMap == null) {
throw new IllegalArgumentException("Argument 'patternOsMap' can not be null.");
}
if (version == null) {
throw new IllegalArgumentException("Argument 'version' can not be null.");
}
this.browsers = browsers;
this.operatingSystems = operatingSystems;
this.patternBrowserMap = patternBrowserMap;
this.patternOsMap = patternOsMap;
this.robots = robots;
this.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 == null) {
if (other.browsers != null)
return false;
} else if (!browsers.equals(other.browsers))
return false;
if (operatingSystems == null) {
if (other.operatingSystems != null)
return false;
} else if (!operatingSystems.equals(other.operatingSystems))
return false;
if (robots == null) {
if (other.robots != null)
return false;
} else if (!robots.equals(other.robots))
return false;
if (version == null) {
if (other.version != null)
return false;
} else if (!version.equals(other.version))
return false;
return true;
}
public Set<Browser> getBrowsers() {
return Collections.unmodifiableSet(browsers);
}
public Set<OperatingSystem> getOperatingSystems() {
return Collections.unmodifiableSet(operatingSystems);
}
public SortedMap<BrowserPattern, Browser> getPatternBrowserMap() {
return patternBrowserMap;
}
public SortedMap<OperatingSystemPattern, OperatingSystem> getPatternOsMap() {
return patternOsMap;
}
public Set<Robot> getRobots() {
return Collections.unmodifiableSet(robots);
}
public String getVersion() {
return version;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((browsers == null) ? 0 : browsers.hashCode());
result = prime * result + ((operatingSystems == null) ? 0 : operatingSystems.hashCode());
result = prime * result + ((robots == null) ? 0 : robots.hashCode());
result = prime * result + ((version == null) ? 0 : version.hashCode());
return result;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("Data [browsers=");
builder.append(browsers);
builder.append(", operatingSystems=");
builder.append(operatingSystems);
builder.append(", robots=");
builder.append(robots);
builder.append(", version=");
builder.append(version);
builder.append("]");
return builder.toString();
}
}