public Device build(UserAgent userAgent, int confidenceTreshold) {
    ArrayList<Device> foundDevices = new ArrayList<Device>();
    for (String step1Token : orderedRules.keySet()) {
      if (userAgent.getCompleteUserAgent().matches("(?i).*" + step1Token + ".*")) {
        Map<String, String> step1Compliant = (Map<String, String>) orderedRules.get(step1Token);
        for (String step2token : step1Compliant.keySet()) {
          Device d = elaborateTwoStepDeviceWithToken(userAgent, step1Token, step2token);
          if (d != null) {
            if (d.getConfidence() > confidenceTreshold) {
              return d;

            } else {
              if (d.getConfidence() > 0) {
                foundDevices.add(d);
              }
            }
          }
        }
      }
    }
    if (foundDevices.size() > 0) {
      Collections.sort(foundDevices, Collections.reverseOrder());
      return foundDevices.get(0);
    }

    return null;
  }
  private Device elaborateTwoStepDeviceWithToken(
      UserAgent userAgent, String step1Token, String step2Token) {
    int maxLittleTokensDistance = 4;
    int maxBigTokensDistance = 8;
    int confidence;

    String originalToken = step2Token;
    String looseToken = step2Token.replaceAll("[ _/-]", ".?");

    if (userAgent.getCompleteUserAgent().matches("(?i).*" + step2Token + ".*")) {
      confidence = 100;
    } else if (userAgent.getCompleteUserAgent().matches("(?i).*" + looseToken + ".*")) {
      step2Token = looseToken;
      confidence = 90;

    } else {
      return null;
    }

    Matcher result =
        Pattern.compile(
                "(?i)(?:(?:.*"
                    + step1Token
                    + "(.*?)"
                    + step2Token
                    + ".*)|(?:.*"
                    + step2Token
                    + "(.*?)"
                    + step1Token
                    + ".*))")
            .matcher(userAgent.getCompleteUserAgent());
    if (result.matches()) {
      // test for case sensitive match
      Matcher bestResult =
          Pattern.compile(
                  "(?:(?:.*"
                      + step1Token
                      + "(.*?)"
                      + step2Token
                      + ".*)|(?:.*"
                      + step2Token
                      + "(.*?)"
                      + step1Token
                      + ".*))")
              .matcher(userAgent.getCompleteUserAgent());
      if (bestResult.matches()) {
        result = bestResult;

      } else {
        confidence -= 20;
      }

      String betweenTokens = result.group(1);
      String s2 = result.group(2);
      if (s2 != null && (betweenTokens == null || betweenTokens.length() > s2.length())) {
        betweenTokens = s2;
      }
      int betweenTokensLength = betweenTokens.length();

      if (step2Token.length() > 3) {
        if (betweenTokensLength > maxBigTokensDistance) {
          confidence -= 10;
        }

        String deviceId = ((Map<String, String>) orderedRules.get(step1Token)).get(originalToken);

        if (devices.containsKey(deviceId)) {
          Device retDevice = (Device) devices.get(deviceId).clone();
          retDevice.setConfidence(confidence);
          return retDevice;
        }
      }

      if ((betweenTokensLength < maxLittleTokensDistance)
          || (betweenTokensLength < maxBigTokensDistance
              && (step2Token.length() < 6 || !anyLetter.matcher(step2Token).matches()))) {
        if (betweenTokensLength <= 1) {
          if (!looseSymbols.matcher(betweenTokens).matches()) {
            confidence -= 20;
          }

        } else {
          confidence -= 40;
        }

        confidence -= (betweenTokensLength * 4);

        String deviceId = ((Map<String, String>) orderedRules.get(step1Token)).get(originalToken);

        if (devices.containsKey(deviceId)) {
          Device retDevice = (Device) devices.get(deviceId).clone();
          retDevice.setConfidence(confidence);
          return retDevice;
        }
      }
    }

    return null;
  }