DevicePattern.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.domain;
import java.io.Serializable;
import java.util.regex.Pattern;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.annotation.concurrent.Immutable;
import javax.annotation.concurrent.NotThreadSafe;
import net.sf.qualitycheck.Check;
import net.sf.uadetector.internal.util.RegularExpressionConverter;
/**
* The {@code DevicePattern} class represents the detection information for a device specific item.<br>
* <br>
* A {@code DevicePattern} object is immutable, their values cannot be changed after creation.
*
* @author André Rouél
*/
@Immutable
public final class DevicePattern implements Identifiable, OrderedPattern<DevicePattern>, Serializable {
/**
* Factory that creates instances of {@code DevicePattern} via method calls.
*
* @author André Rouél
*/
@NotThreadSafe
public static final class Builder {
/**
* Identification number (ID) of a device pattern
*/
private int id = Integer.MIN_VALUE;
/**
* A compiled representation of a regular expression to detect a device
*/
private Pattern pattern;
/**
* Position of a {@code DevicePattern} (only relevant if there are multiple patterns for a device in a
* {@code SortedSet})
*/
private int position = Integer.MIN_VALUE;
/**
* Builds a new instance of {@code DevicePattern} and returns it.
*
* @return a new instance of {@code DevicePattern}
* @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException
* if one of the needed arguments to build an instance of {@code DevicePattern} is invalid
* @throws net.sf.qualitycheck.exception.IllegalNullArgumentException
* if one of the needed arguments to build an instance of {@code DevicePattern} is invalid
*/
@Nonnull
public DevicePattern build() {
return new DevicePattern(id, pattern, position);
}
/**
* Sets the identification number of a device pattern entry.
*
* @param id
* identification number
* @return this {@code Builder}, for chaining
* @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException
* if the given integer is smaller than {@code 0}
*/
@Nonnull
public Builder setId(@Nonnegative final int id) {
Check.notNegative(id, "id");
this.id = id;
return this;
}
/**
* Sets the identification number (ID) of a device pattern. The given {@code String} is parsed as a decimal
* number.
*
* @param id
* ID of a device pattern as string
* @return this {@code Builder}, for chaining
* @throws net.sf.qualitycheck.exception.IllegalNullArgumentException
* if the given argument is {@code null}
* @throws NumberFormatException
* if the given string is not parsable as integer
* @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException
* if the parsed integer is smaller than {@code 0}
*/
@Nonnull
public Builder setId(@Nonnull final String id) {
Check.notEmpty(id, "id");
this.setId(Integer.parseInt(id.trim()));
return this;
}
/**
* Sets a regular expression for a device pattern.
*
* @param pattern
* compiled representation of a regular expression
* @return this {@code Builder}, for chaining
*/
@Nonnull
public Builder setPattern(@Nonnull final Pattern pattern) {
Check.notNull(pattern, "pattern");
this.pattern = pattern;
return this;
}
/**
* Converts a PERL regular expression in a Java regular expression and sets it in the {@code Builder}.
*
* @param regex
* PERL style regular expression to be converted
* @return this {@code Builder}, for chaining
*/
@Nonnull
public Builder setPerlRegularExpression(@Nonnull final String regex) {
Check.notEmpty(regex, "regex");
setPattern(RegularExpressionConverter.convertPerlRegexToPattern(regex));
return this;
}
/**
* Sets the position of a device pattern in a set of patterns.
*
* @param position
* position of a device pattern
* @return this {@code Builder}, for chaining
* @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException
* if the given integer is smaller than {@code 0}
*/
@Nonnull
public Builder setPosition(@Nonnegative final int position) {
Check.notNegative(position, "position");
this.position = position;
return this;
}
/**
* Sets the position of a device pattern in a set of patterns. The given {@code String} is parsed as a decimal
* number.
*
* @param position
* position of a device pattern as string
* @return this {@code Builder}, for chaining
* @throws net.sf.qualitycheck.exception.IllegalNullArgumentException
* if the given argument is {@code null}
* @throws NumberFormatException
* if the given string is not parsable as integer
* @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException
* if the parsed integer is smaller than {@code 0}
*/
@Nonnull
public Builder setPosition(@Nonnull final String position) {
Check.notEmpty(position, "position");
this.setPosition(Integer.parseInt(position.trim()));
return this;
}
}
private static final long serialVersionUID = 2845531314485836348L;
/**
* Compares to integers.
*
* @param a
* first integer
* @param b
* second integer
* @return {@code -1} if {@code a} is less, {@code 0} if equal, or {@code 1} if greater than {@code b}
*/
private static int compareInt(final int a, final int b) {
int result = 0;
if (a > b) {
result = 1;
} else if (a < b) {
result = -1;
}
return result;
}
/**
* Identification number (ID) of a device pattern
*/
@Nonnegative
private final int id;
/**
* A compiled representation of a regular expression to detect a device
*/
@Nonnull
private final Pattern pattern;
/**
* Position of a {@code DevicePattern} (only relevant if there are multiple patterns for a device in a
* {@code SortedSet})
*/
@Nonnegative
private final int position;
public DevicePattern(@Nonnegative final int id, @Nonnull final Pattern pattern, @Nonnegative final int position) {
Check.notNegative(id, "id");
Check.notNull(pattern, "pattern");
Check.notNegative(position, "position");
this.id = id;
this.pattern = pattern;
this.position = position;
}
/**
* Compares all attributes of this instance with the given instance of a {@code DevicePattern}.
*
* <p>
* This method is <em>consistent with equals</em>.
*
* @param other
* another instance of {@code OperatingSystemPattern}
* @return negative value if one of the attributes of this instance is less, 0 if equal, or positive value if
* greater than the other one
*/
@Override
public int compareTo(final DevicePattern other) {
int result = other == null ? -1 : 0;
if (result == 0) {
result = compareInt(getPosition(), other.getPosition());
if (result == 0) {
result = compareInt(getId(), other.getId());
}
if (result == 0) {
result = getPattern().pattern().compareTo(other.getPattern().pattern());
}
if (result == 0) {
result = compareInt(getPattern().flags(), other.getPattern().flags());
}
}
return result;
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final DevicePattern other = (DevicePattern) obj;
if (id != other.id) {
return false;
}
if (position != other.position) {
return false;
}
if (!pattern.pattern().equals(other.pattern.pattern())) {
return false;
}
if (pattern.flags() != other.pattern.flags()) {
return false;
}
return true;
}
/**
* Gets the identification number (ID) of a device pattern.
*
* @return identification number (ID) of a device pattern
*/
@Override
public int getId() {
return id;
}
@Override
public Pattern getPattern() {
return pattern;
}
@Override
public int getPosition() {
return position;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + position;
result = prime * result + pattern.pattern().hashCode();
result = prime * result + pattern.flags();
return result;
}
@Override
public String toString() {
final StringBuilder builder = new StringBuilder();
builder.append("DevicePattern [id=");
builder.append(id);
builder.append(", pattern=");
builder.append(pattern);
builder.append(", position=");
builder.append(position);
builder.append("]");
return builder.toString();
}
}