DevicePattern.java

  1. /*******************************************************************************
  2.  * Copyright 2012 André Rouél
  3.  *
  4.  * Licensed under the Apache License, Version 2.0 (the "License");
  5.  * you may not use this file except in compliance with the License.
  6.  * You may obtain a copy of the License at
  7.  *
  8.  *   http://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  * Unless required by applicable law or agreed to in writing, software
  11.  * distributed under the License is distributed on an "AS IS" BASIS,
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  * See the License for the specific language governing permissions and
  14.  * limitations under the License.
  15.  ******************************************************************************/
  16. package net.sf.uadetector.internal.data.domain;

  17. import java.io.Serializable;
  18. import java.util.regex.Pattern;

  19. import javax.annotation.Nonnegative;
  20. import javax.annotation.Nonnull;
  21. import javax.annotation.concurrent.Immutable;
  22. import javax.annotation.concurrent.NotThreadSafe;

  23. import net.sf.qualitycheck.Check;
  24. import net.sf.uadetector.internal.util.RegularExpressionConverter;

  25. /**
  26.  * The {@code DevicePattern} class represents the detection information for a device specific item.<br>
  27.  * <br>
  28.  * A {@code DevicePattern} object is immutable, their values cannot be changed after creation.
  29.  *
  30.  * @author André Rouél
  31.  */
  32. @Immutable
  33. public final class DevicePattern implements Identifiable, OrderedPattern<DevicePattern>, Serializable {

  34.     /**
  35.      * Factory that creates instances of {@code DevicePattern} via method calls.
  36.      *
  37.      * @author André Rouél
  38.      */
  39.     @NotThreadSafe
  40.     public static final class Builder {

  41.         /**
  42.          * Identification number (ID) of a device pattern
  43.          */
  44.         private int id = Integer.MIN_VALUE;

  45.         /**
  46.          * A compiled representation of a regular expression to detect a device
  47.          */
  48.         private Pattern pattern;

  49.         /**
  50.          * Position of a {@code DevicePattern} (only relevant if there are multiple patterns for a device in a
  51.          * {@code SortedSet})
  52.          */
  53.         private int position = Integer.MIN_VALUE;

  54.         /**
  55.          * Builds a new instance of {@code DevicePattern} and returns it.
  56.          *
  57.          * @return a new instance of {@code DevicePattern}
  58.          * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException
  59.          *             if one of the needed arguments to build an instance of {@code DevicePattern} is invalid
  60.          * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException
  61.          *             if one of the needed arguments to build an instance of {@code DevicePattern} is invalid
  62.          */
  63.         @Nonnull
  64.         public DevicePattern build() {
  65.             return new DevicePattern(id, pattern, position);
  66.         }

  67.         /**
  68.          * Sets the identification number of a device pattern entry.
  69.          *
  70.          * @param id
  71.          *            identification number
  72.          * @return this {@code Builder}, for chaining
  73.          * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException
  74.          *             if the given integer is smaller than {@code 0}
  75.          */
  76.         @Nonnull
  77.         public Builder setId(@Nonnegative final int id) {
  78.             Check.notNegative(id, "id");

  79.             this.id = id;
  80.             return this;
  81.         }

  82.         /**
  83.          * Sets the identification number (ID) of a device pattern. The given {@code String} is parsed as a decimal
  84.          * number.
  85.          *
  86.          * @param id
  87.          *            ID of a device pattern as string
  88.          * @return this {@code Builder}, for chaining
  89.          * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException
  90.          *             if the given argument is {@code null}
  91.          * @throws NumberFormatException
  92.          *             if the given string is not parsable as integer
  93.          * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException
  94.          *             if the parsed integer is smaller than {@code 0}
  95.          */
  96.         @Nonnull
  97.         public Builder setId(@Nonnull final String id) {
  98.             Check.notEmpty(id, "id");

  99.             this.setId(Integer.parseInt(id.trim()));
  100.             return this;
  101.         }

  102.         /**
  103.          * Sets a regular expression for a device pattern.
  104.          *
  105.          * @param pattern
  106.          *            compiled representation of a regular expression
  107.          * @return this {@code Builder}, for chaining
  108.          */
  109.         @Nonnull
  110.         public Builder setPattern(@Nonnull final Pattern pattern) {
  111.             Check.notNull(pattern, "pattern");

  112.             this.pattern = pattern;
  113.             return this;
  114.         }

  115.         /**
  116.          * Converts a PERL regular expression in a Java regular expression and sets it in the {@code Builder}.
  117.          *
  118.          * @param regex
  119.          *            PERL style regular expression to be converted
  120.          * @return this {@code Builder}, for chaining
  121.          */
  122.         @Nonnull
  123.         public Builder setPerlRegularExpression(@Nonnull final String regex) {
  124.             Check.notEmpty(regex, "regex");

  125.             setPattern(RegularExpressionConverter.convertPerlRegexToPattern(regex));
  126.             return this;
  127.         }

  128.         /**
  129.          * Sets the position of a device pattern in a set of patterns.
  130.          *
  131.          * @param position
  132.          *            position of a device pattern
  133.          * @return this {@code Builder}, for chaining
  134.          * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException
  135.          *             if the given integer is smaller than {@code 0}
  136.          */
  137.         @Nonnull
  138.         public Builder setPosition(@Nonnegative final int position) {
  139.             Check.notNegative(position, "position");

  140.             this.position = position;
  141.             return this;
  142.         }

  143.         /**
  144.          * Sets the position of a device pattern in a set of patterns. The given {@code String} is parsed as a decimal
  145.          * number.
  146.          *
  147.          * @param position
  148.          *            position of a device pattern as string
  149.          * @return this {@code Builder}, for chaining
  150.          * @throws net.sf.qualitycheck.exception.IllegalNullArgumentException
  151.          *             if the given argument is {@code null}
  152.          * @throws NumberFormatException
  153.          *             if the given string is not parsable as integer
  154.          * @throws net.sf.qualitycheck.exception.IllegalNegativeArgumentException
  155.          *             if the parsed integer is smaller than {@code 0}
  156.          */
  157.         @Nonnull
  158.         public Builder setPosition(@Nonnull final String position) {
  159.             Check.notEmpty(position, "position");

  160.             this.setPosition(Integer.parseInt(position.trim()));
  161.             return this;
  162.         }

  163.     }

  164.     private static final long serialVersionUID = 2845531314485836348L;

  165.     /**
  166.      * Compares to integers.
  167.      *
  168.      * @param a
  169.      *            first integer
  170.      * @param b
  171.      *            second integer
  172.      * @return {@code -1} if {@code a} is less, {@code 0} if equal, or {@code 1} if greater than {@code b}
  173.      */
  174.     private static int compareInt(final int a, final int b) {
  175.         int result = 0;
  176.         if (a > b) {
  177.             result = 1;
  178.         } else if (a < b) {
  179.             result = -1;
  180.         }
  181.         return result;
  182.     }

  183.     /**
  184.      * Identification number (ID) of a device pattern
  185.      */
  186.     @Nonnegative
  187.     private final int id;

  188.     /**
  189.      * A compiled representation of a regular expression to detect a device
  190.      */
  191.     @Nonnull
  192.     private final Pattern pattern;

  193.     /**
  194.      * Position of a {@code DevicePattern} (only relevant if there are multiple patterns for a device in a
  195.      * {@code SortedSet})
  196.      */
  197.     @Nonnegative
  198.     private final int position;

  199.     public DevicePattern(@Nonnegative final int id, @Nonnull final Pattern pattern, @Nonnegative final int position) {
  200.         Check.notNegative(id, "id");
  201.         Check.notNull(pattern, "pattern");
  202.         Check.notNegative(position, "position");

  203.         this.id = id;
  204.         this.pattern = pattern;
  205.         this.position = position;
  206.     }

  207.     /**
  208.      * Compares all attributes of this instance with the given instance of a {@code DevicePattern}.
  209.      *
  210.      * <p>
  211.      * This method is <em>consistent with equals</em>.
  212.      *
  213.      * @param other
  214.      *            another instance of {@code OperatingSystemPattern}
  215.      * @return negative value if one of the attributes of this instance is less, 0 if equal, or positive value if
  216.      *         greater than the other one
  217.      */
  218.     @Override
  219.     public int compareTo(final DevicePattern other) {
  220.         int result = other == null ? -1 : 0;
  221.         if (result == 0) {
  222.             result = compareInt(getPosition(), other.getPosition());
  223.             if (result == 0) {
  224.                 result = compareInt(getId(), other.getId());
  225.             }
  226.             if (result == 0) {
  227.                 result = getPattern().pattern().compareTo(other.getPattern().pattern());
  228.             }
  229.             if (result == 0) {
  230.                 result = compareInt(getPattern().flags(), other.getPattern().flags());
  231.             }
  232.         }
  233.         return result;
  234.     }

  235.     @Override
  236.     public boolean equals(final Object obj) {
  237.         if (this == obj) {
  238.             return true;
  239.         }
  240.         if (obj == null) {
  241.             return false;
  242.         }
  243.         if (getClass() != obj.getClass()) {
  244.             return false;
  245.         }
  246.         final DevicePattern other = (DevicePattern) obj;
  247.         if (id != other.id) {
  248.             return false;
  249.         }
  250.         if (position != other.position) {
  251.             return false;
  252.         }
  253.         if (!pattern.pattern().equals(other.pattern.pattern())) {
  254.             return false;
  255.         }
  256.         if (pattern.flags() != other.pattern.flags()) {
  257.             return false;
  258.         }
  259.         return true;
  260.     }

  261.     /**
  262.      * Gets the identification number (ID) of a device pattern.
  263.      *
  264.      * @return identification number (ID) of a device pattern
  265.      */
  266.     @Override
  267.     public int getId() {
  268.         return id;
  269.     }

  270.     @Override
  271.     public Pattern getPattern() {
  272.         return pattern;
  273.     }

  274.     @Override
  275.     public int getPosition() {
  276.         return position;
  277.     }

  278.     @Override
  279.     public int hashCode() {
  280.         final int prime = 31;
  281.         int result = 1;
  282.         result = prime * result + id;
  283.         result = prime * result + position;
  284.         result = prime * result + pattern.pattern().hashCode();
  285.         result = prime * result + pattern.flags();
  286.         return result;
  287.     }

  288.     @Override
  289.     public String toString() {
  290.         final StringBuilder builder = new StringBuilder();
  291.         builder.append("DevicePattern [id=");
  292.         builder.append(id);
  293.         builder.append(", pattern=");
  294.         builder.append(pattern);
  295.         builder.append(", position=");
  296.         builder.append(position);
  297.         builder.append("]");
  298.         return builder.toString();
  299.     }

  300. }