IniDataWriter.java
/*******************************************************************************
* Copyright 2013 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.writer;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.SortedSet;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.ThreadSafe;
import net.sf.qualitycheck.Check;
import net.sf.uadetector.internal.data.BrowserOperatingSystemMappingComparator;
import net.sf.uadetector.internal.data.Data;
import net.sf.uadetector.internal.data.IdentifiableComparator;
import net.sf.uadetector.internal.data.OrderedPatternComparator;
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.Identifiable;
import net.sf.uadetector.internal.data.domain.OperatingSystem;
import net.sf.uadetector.internal.data.domain.OperatingSystemPattern;
import net.sf.uadetector.internal.data.domain.Robot;
import net.sf.uadetector.internal.util.RegularExpressionConverter;
/**
* This utility is intended to transform an instance of {@code Data} into an <i>UAS data</i> conform XML document and
* allows us to recreate an <code>uas.xml</code>.
*
* @author André Rouél
*/
@ThreadSafe
public final class IniDataWriter {
interface Char {
char EQUALS = '=';
char NEWLINE = '\n';
char QUOTE = '"';
char SEMICOLON = ';';
char SQUARE_BRACKET_CLOSE = ']';
char SQUARE_BRACKET_OPEN = '[';
char WHITESPACE = ' ';
}
interface Tag {
String BROWSER = "browser";
String BROWSER_OS = "browser_os";
String BROWSER_REG = "browser_reg";
String BROWSER_TYPE = "browser_type";
String DEVICE = "device";
String DEVICE_REG = "device_reg";
String OS = "os";
String OS_REG = "os_reg";
String ROBOTS = "robots";
}
private static final String EMPTY = "";
private static void createBrowser(final Browser browser, final StringBuilder builder) {
createKeyValuePair(browser, String.valueOf(browser.getType().getId()), builder);
createKeyValuePair(browser, browser.getFamilyName(), builder);
createKeyValuePair(browser, browser.getUrl(), builder);
createKeyValuePair(browser, browser.getProducer(), builder);
createKeyValuePair(browser, browser.getProducerUrl(), builder);
createKeyValuePair(browser, browser.getIcon(), builder);
createKeyValuePair(browser, browser.getInfoUrl(), builder);
}
private static void createBrowserOperatingSystemMappings(final Data data, final StringBuilder builder) {
final List<BrowserOperatingSystemMapping> mappings = new ArrayList<BrowserOperatingSystemMapping>(
data.getBrowserToOperatingSystemMappings());
Collections.sort(mappings, BrowserOperatingSystemMappingComparator.INSTANCE);
createCategory(Tag.BROWSER_OS, builder);
createComment("browser_id[] = \"OS id\"", builder);
for (final BrowserOperatingSystemMapping mapping : mappings) {
createKeyValuePair(mapping.getBrowserId(), String.valueOf(mapping.getOperatingSystemId()), builder);
}
}
private static void createBrowserPatterns(final Data data, final StringBuilder builder) {
final List<BrowserPattern> patterns = new ArrayList<BrowserPattern>(data.getBrowserPatterns().size());
for (final Entry<Integer, SortedSet<BrowserPattern>> entry : data.getBrowserPatterns().entrySet()) {
patterns.addAll(entry.getValue());
}
Collections.sort(patterns, new OrderedPatternComparator<BrowserPattern>());
createCategory(Tag.BROWSER_REG, builder);
createComment("browser_reg_id[] = \"Browser regstring\"", builder);
createComment("browser_reg_id[] = \"Browser id\"", builder);
for (final BrowserPattern pattern : patterns) {
final String regex = RegularExpressionConverter.convertPatternToPerlRegex(pattern.getPattern());
createKeyValuePair(pattern.getPosition(), regex, builder);
createKeyValuePair(pattern.getPosition(), String.valueOf(pattern.getId()), builder);
}
}
private static void createBrowsers(final Data data, final StringBuilder builder) {
createCategory(Tag.BROWSER, builder);
createComment("browser_id[] = \"Browser type\"", builder);
createComment("browser_id[] = \"Browser Name\"", builder);
createComment("browser_id[] = \"Browser URL\"", builder);
createComment("browser_id[] = \"Browser Company\"", builder);
createComment("browser_id[] = \"Browser Company URL\"", builder);
createComment("browser_id[] = \"Browser ico\"", builder);
createComment("browser_id[] = \"Browser info URL\"", builder);
final List<Browser> browsers = new ArrayList<Browser>(data.getBrowsers());
Collections.sort(browsers, IdentifiableComparator.INSTANCE);
for (final Browser browser : browsers) {
createBrowser(browser, builder);
}
}
private static void createBrowserTypes(final Data data, final StringBuilder builder) {
createCategory(Tag.BROWSER_TYPE, builder);
createComment("browser_type_id[] = \"Browser type\"", builder);
final List<BrowserType> browserTypes = new ArrayList<BrowserType>(data.getBrowserTypes().values());
Collections.sort(browserTypes, IdentifiableComparator.INSTANCE);
for (final BrowserType browserType : browserTypes) {
createKeyValuePair(browserType, browserType.getName(), builder);
}
}
private static void createCategory(@Nonnull final String category, @Nonnull final StringBuilder builder) {
builder.append(Char.SQUARE_BRACKET_OPEN);
builder.append(category);
builder.append(Char.SQUARE_BRACKET_CLOSE);
builder.append(Char.NEWLINE);
}
private static void createComment(@Nonnull final String comment, @Nonnull final StringBuilder builder) {
builder.append(Char.SEMICOLON);
builder.append(Char.WHITESPACE);
builder.append(comment);
builder.append(Char.NEWLINE);
}
private static void createDescription(@Nonnull final Data data, @Nonnull final StringBuilder builder) {
createComment("Data (format ini) for UASparser - http://user-agent-string.info/download/UASparser", builder);
createComment("Version: " + data.getVersion(), builder);
createComment("Checksum:", builder);
createComment("MD5 - http://user-agent-string.info/rpc/get_data.php?format=ini&md5=y", builder);
createComment("SHA1 - http://user-agent-string.info/rpc/get_data.php?format=ini&sha1=y", builder);
builder.append(Char.SEMICOLON);
builder.append(Char.NEWLINE);
}
private static void createDevice(final Device device, final StringBuilder builder) {
createKeyValuePair(device, device.getName(), builder);
createKeyValuePair(device, device.getIcon(), builder);
createKeyValuePair(device, device.getInfoUrl(), builder);
}
private static void createDevicePatterns(final Data data, final StringBuilder builder) {
final List<DevicePattern> patterns = new ArrayList<DevicePattern>(data.getDevicePatterns().size());
for (final Entry<Integer, SortedSet<DevicePattern>> entry : data.getDevicePatterns().entrySet()) {
patterns.addAll(entry.getValue());
}
Collections.sort(patterns, new OrderedPatternComparator<DevicePattern>());
createCategory(Tag.DEVICE_REG, builder);
createComment("device_reg_id[] = \"Device regstring\"", builder);
createComment("device_reg_id[] = \"Device id\"", builder);
for (final DevicePattern pattern : patterns) {
final String regex = RegularExpressionConverter.convertPatternToPerlRegex(pattern.getPattern());
createKeyValuePair(pattern.getPosition(), regex, builder);
createKeyValuePair(pattern.getPosition(), String.valueOf(pattern.getId()), builder);
}
}
private static void createDevices(final Data data, final StringBuilder builder) {
createCategory(Tag.DEVICE, builder);
createComment("device_id[] = \"Device type\"", builder);
createComment("device_id[] = \"Device ico\"", builder);
createComment("device_id[] = \"Device info URL\"", builder);
final List<Device> devices = new ArrayList<Device>(data.getDevices());
Collections.sort(devices, IdentifiableComparator.INSTANCE);
for (final Device device : devices) {
createDevice(device, builder);
}
}
private static void createKeyValuePair(@Nonnull final Identifiable identifiable, @Nonnull final String value,
@Nonnull final StringBuilder builder) {
createKeyValuePair(identifiable.getId(), value, builder);
}
private static void createKeyValuePair(@Nonnull final int id, @Nonnull final String value, @Nonnull final StringBuilder builder) {
builder.append(id);
builder.append(Char.SQUARE_BRACKET_OPEN);
builder.append(Char.SQUARE_BRACKET_CLOSE);
builder.append(Char.WHITESPACE);
builder.append(Char.EQUALS);
builder.append(Char.WHITESPACE);
builder.append(Char.QUOTE);
builder.append(value);
builder.append(Char.QUOTE);
builder.append(Char.NEWLINE);
}
private static void createOperatingSystem(final OperatingSystem operatingSystem, final StringBuilder builder) {
createKeyValuePair(operatingSystem, operatingSystem.getFamily(), builder);
createKeyValuePair(operatingSystem, operatingSystem.getName(), builder);
createKeyValuePair(operatingSystem, operatingSystem.getUrl(), builder);
createKeyValuePair(operatingSystem, operatingSystem.getProducer(), builder);
createKeyValuePair(operatingSystem, operatingSystem.getProducerUrl(), builder);
createKeyValuePair(operatingSystem, operatingSystem.getIcon(), builder);
}
private static void createOperatingSystemPatterns(final Data data, final StringBuilder builder) {
createCategory(Tag.OS_REG, builder);
createComment("os_reg_id[] = \"OS regstring\"", builder);
createComment("os_reg_id[] = \"OS id\"", builder);
final List<OperatingSystemPattern> patterns = new ArrayList<OperatingSystemPattern>(data.getOperatingSystemPatterns().size());
for (final Entry<Integer, SortedSet<OperatingSystemPattern>> entry : data.getOperatingSystemPatterns().entrySet()) {
patterns.addAll(entry.getValue());
}
Collections.sort(patterns, new OrderedPatternComparator<OperatingSystemPattern>());
for (final OperatingSystemPattern pattern : patterns) {
final String regex = RegularExpressionConverter.convertPatternToPerlRegex(pattern.getPattern());
createKeyValuePair(pattern.getPosition(), regex, builder);
createKeyValuePair(pattern.getPosition(), String.valueOf(pattern.getId()), builder);
}
}
private static void createOperatingSystems(final Data data, final StringBuilder builder) {
createCategory(Tag.OS, builder);
createComment("os_id[] = \"OS Family\"", builder);
createComment("os_id[] = \"OS Name\"", builder);
createComment("os_id[] = \"OS URL\"", builder);
createComment("os_id[] = \"OS Company\"", builder);
createComment("os_id[] = \"OS Company URL\"", builder);
createComment("os_id[] = \"OS ico\"", builder);
final List<OperatingSystem> operatingSystems = new ArrayList<OperatingSystem>(data.getOperatingSystems());
Collections.sort(operatingSystems, IdentifiableComparator.INSTANCE);
for (final OperatingSystem operatingSystem : operatingSystems) {
createOperatingSystem(operatingSystem, builder);
}
}
private static void createRobot(final Robot robot, final StringBuilder builder) {
createKeyValuePair(robot, robot.getUserAgentString(), builder);
createKeyValuePair(robot, robot.getFamilyName(), builder);
createKeyValuePair(robot, robot.getName(), builder);
createKeyValuePair(robot, EMPTY, builder);
createKeyValuePair(robot, robot.getProducer(), builder);
createKeyValuePair(robot, robot.getProducerUrl(), builder);
createKeyValuePair(robot, robot.getIcon(), builder);
createKeyValuePair(robot, EMPTY, builder);
createKeyValuePair(robot, robot.getInfoUrl(), builder);
}
private static void createRobots(final Data data, final StringBuilder builder) {
createCategory(Tag.ROBOTS, builder);
createComment("bot_id[] = \"bot useragentstring\"", builder);
createComment("bot_id[] = \"bot Family\"", builder);
createComment("bot_id[] = \"bot Name\"", builder);
createComment("bot_id[] = \"bot URL\"", builder);
createComment("bot_id[] = \"bot Company\"", builder);
createComment("bot_id[] = \"bot Company URL\"", builder);
createComment("bot_id[] = \"bot ico\"", builder);
createComment("bot_id[] = \"bot OS id\"", builder);
createComment("bot_id[] = \"bot info URL\"", builder);
for (final Robot robot : data.getRobots()) {
createRobot(robot, builder);
}
}
/**
* Transforms a given {@code Data} instance into XML and writes it to the passed in {@code OutputStream}.
*
* @param data
* {@code Data} to transform into XML
* @param outputStream
* output stream to write
* @throws IOException
* if the given output stream can not be written
*/
public static void write(@Nonnull final Data data, @Nonnull final OutputStream outputStream) throws IOException {
Check.notNull(data, "data");
Check.notNull(outputStream, "outputStream");
final StringBuilder doc = new StringBuilder(10000);
// description element
createDescription(data, doc);
// data
createRobots(data, doc);
createOperatingSystems(data, doc);
createBrowsers(data, doc);
createBrowserTypes(data, doc);
createBrowserPatterns(data, doc);
createBrowserOperatingSystemMappings(data, doc);
createOperatingSystemPatterns(data, doc);
createDevices(data, doc);
createDevicePatterns(data, doc);
// write the content to output stream
outputStream.write(doc.toString().getBytes("UTF-8"));
}
/**
* <strong>Attention:</strong> This class is not intended to create objects from it.
*/
private IniDataWriter() {
// This class is not intended to create objects from it.
}
}