content: package io.github.tapcard.emvnfccard.utils;
  
  import java.io.Closeable;
  import java.io.IOException;
  import java.util.Calendar;
  import java.util.Date;
  
  /**
   * Some methods from Apache Commons library
   */
  
  @SuppressWarnings({"PointlessBooleanExpression", "JavadocReference", "SameParameterValue"})
  class CommonsUtils {
  
      /**
       * <p>Eight-bit Unicode Transformation Format.</p>
       * <p>
       * <p>Every implementation of the Java platform is required to support this character encoding.</p>
       */
      static final String UTF_8 = "UTF-8";
  
      /**
       * <p>Checks if a CharSequence is empty ("") or null.</p>
       * <p>
       * <pre>
       * StringUtils.isEmpty(null)      = true
       * StringUtils.isEmpty("")        = true
       * StringUtils.isEmpty(" ")       = false
       * StringUtils.isEmpty("bob")     = false
       * StringUtils.isEmpty("  bob  ") = false
       * </pre>
       * <p>
       * <p>NOTE: This method changed in Lang version 2.0.
       * It no longer trims the CharSequence.
       * That functionality is available in isBlank().</p>
       *
       * @param cs the CharSequence to check, may be null
       * @return {@code true} if the CharSequence is empty or null
       * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence)
       */
      public static boolean isEmpty(CharSequence cs) {
          return cs == null || cs.length() == 0;
      }
  
      /**
       * <p>Deletes all whitespaces from a String as defined by
       * {@link Character#isWhitespace(char)}.</p>
       * <p>
       * <pre>
       * StringUtils.deleteWhitespace(null)         = null
       * StringUtils.deleteWhitespace("")           = ""
       * StringUtils.deleteWhitespace("abc")        = "abc"
       * StringUtils.deleteWhitespace("   ab  c  ") = "abc"
       * </pre>
       *
       * @param str the String to delete whitespace from, may be null
       * @return the String without whitespaces, {@code null} if null String input
       */
      static String deleteWhitespace(String str) {
          if (isEmpty(str)) {
              return str;
          }
          int sz = str.length();
          char[] chs = new char[sz];
          int count = 0;
          for (int i = 0; i < sz; i++) {
              if (!Character.isWhitespace(str.charAt(i))) {
                  chs[count++] = str.charAt(i);
              }
          }
          if (count == sz) {
              return str;
          }
          return new String(chs, 0, count);
      }
  
      static void closeQuietly(Closeable closeable) {
          try {
              if (closeable != null) {
                  closeable.close();
              }
          } catch (IOException ignored) {
          }
      }
  
  
      /**
       * <p>Checks if a CharSequence is whitespace, empty ("") or null.</p>
       * <p>
       * <pre>
       * StringUtils.isBlank(null)      = true
       * StringUtils.isBlank("")        = true
       * StringUtils.isBlank(" ")       = true
       * StringUtils.isBlank("bob")     = false
       * StringUtils.isBlank("  bob  ") = false
       * </pre>
       *
       * @param cs the CharSequence to check, may be null
       * @return {@code true} if the CharSequence is null, empty or whitespace
       * @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence)
       */
      private static boolean isBlank(CharSequence cs) {
          int strLen;
          if (cs == null || (strLen = cs.length()) == 0) {
              return true;
          }
          for (int i = 0; i < strLen; i++) {
              if (Character.isWhitespace(cs.charAt(i)) == false) {
                  return false;
              }
          }
          return true;
      }
  
      /**
       * <p>Checks if a CharSequence is not empty (""), not null and not whitespace only.</p>
       * <p>
       * <pre>
       * StringUtils.isNotBlank(null)      = false
       * StringUtils.isNotBlank("")        = false
       * StringUtils.isNotBlank(" ")       = false
       * StringUtils.isNotBlank("bob")     = true
       * StringUtils.isNotBlank("  bob  ") = true
       * </pre>
       *
       * @param cs the CharSequence to check, may be null
       * @return {@code true} if the CharSequence is
       * not empty and not null and not whitespace
       * @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence)
       */
      public static boolean isNotBlank(CharSequence cs) {
          return !isBlank(cs);
      }
  
      /**
       * <p>Checks if the object is in the given array.</p>
       *
       * <p>The method returns {@code false} if a {@code null} array is passed in.</p>
       *
       * @param array  the array to search through
       * @param objectToFind  the object to find
       * @return {@code true} if the array contains the object
       */
      static boolean contains(Object[] array, Object objectToFind) {
          return indexOf(array, objectToFind) != INDEX_NOT_FOUND;
      }
  
      /**
       * <p>Finds the index of the given object in the array.</p>
       *
       * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
       *
       * @param array  the array to search through for the object, may be {@code null}
       * @param objectToFind  the object to find, may be {@code null}
       * @return the index of the object within the array,
       *  {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
       */
      private static int indexOf(Object[] array, Object objectToFind) {
          return indexOf(array, objectToFind, 0);
      }
  
      /**
       * <p>Finds the index of the given object in the array starting at the given index.</p>
       *
       * <p>This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.</p>
       *
       * <p>A negative startIndex is treated as zero. A startIndex larger than the array
       * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).</p>
       *
       * @param array  the array to search through for the object, may be {@code null}
       * @param objectToFind  the object to find, may be {@code null}
       * @param startIndex  the index to start searching at
       * @return the index of the object within the array starting at the index,
       *  {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input
       */
      private static int indexOf(Object[] array, Object objectToFind, int startIndex) {
          if (array == null) {
              return INDEX_NOT_FOUND;
          }
          if (startIndex < 0) {
              startIndex = 0;
          }
          if (objectToFind == null) {
              for (int i = startIndex; i < array.length; i++) {
                  if (array[i] == null) {
                      return i;
                  }
              }
          } else if (array.getClass().getComponentType().isInstance(objectToFind)) {
              for (int i = startIndex; i < array.length; i++) {
                  if (objectToFind.equals(array[i])) {
                      return i;
                  }
              }
          }
          return INDEX_NOT_FOUND;
      }
  
      /**
       * The index value when an element is not found in a list or array: {@code -1}.
       * This value is returned by methods in this class and can also be used in comparisons with values returned by
       * various method from {@link java.util.List}.
       */
      private static final int INDEX_NOT_FOUND = -1;
  
  
  
      /**
       * <p>Left pad a String with spaces (' ').</p>
       *
       * <p>The String is padded to the size of {@code size}.</p>
       *
       * <pre>
       * StringUtils.leftPad(null, *)   = null
       * StringUtils.leftPad("", 3)     = "   "
       * StringUtils.leftPad("bat", 3)  = "bat"
       * StringUtils.leftPad("bat", 5)  = "  bat"
       * StringUtils.leftPad("bat", 1)  = "bat"
       * StringUtils.leftPad("bat", -1) = "bat"
       * </pre>
       *
       * @param str  the String to pad out, may be null
       * @param size  the size to pad to
       * @return left padded String or original String if no padding is necessary,
       *  {@code null} if null String input
       */
      static String leftPad(String str, int size) {
          return leftPad(str, size, ' ');
      }
  
      /**
       * <p>Left pad a String with a specified character.</p>
       *
       * <p>Pad to a size of {@code size}.</p>
       *
       * <pre>
       * StringUtils.leftPad(null, *, *)     = null
       * StringUtils.leftPad("", 3, 'z')     = "zzz"
       * StringUtils.leftPad("bat", 3, 'z')  = "bat"
       * StringUtils.leftPad("bat", 5, 'z')  = "zzbat"
       * StringUtils.leftPad("bat", 1, 'z')  = "bat"
       * StringUtils.leftPad("bat", -1, 'z') = "bat"
       * </pre>
       *
       * @param str  the String to pad out, may be null
       * @param size  the size to pad to
       * @param padChar  the character to pad with
       * @return left padded String or original String if no padding is necessary,
       *  {@code null} if null String input
       * @since 2.0
       */
      private static String leftPad(String str, int size, char padChar) {
          if (str == null) {
              return null;
          }
          int pads = size - str.length();
          if (pads <= 0) {
              return str; // returns original String when possible
          }
          if (pads > PAD_LIMIT) {
              return leftPad(str, size, String.valueOf(padChar));
          }
          return repeat(padChar, pads).concat(str);
      }
  
      /**
       * <p>Left pad a String with a specified String.</p>
       *
       * <p>Pad to a size of {@code size}.</p>
       *
       * <pre>
       * StringUtils.leftPad(null, *, *)      = null
       * StringUtils.leftPad("", 3, "z")      = "zzz"
       * StringUtils.leftPad("bat", 3, "yz")  = "bat"
       * StringUtils.leftPad("bat", 5, "yz")  = "yzbat"
       * StringUtils.leftPad("bat", 8, "yz")  = "yzyzybat"
       * StringUtils.leftPad("bat", 1, "yz")  = "bat"
       * StringUtils.leftPad("bat", -1, "yz") = "bat"
       * StringUtils.leftPad("bat", 5, null)  = "  bat"
       * StringUtils.leftPad("bat", 5, "")    = "  bat"
       * </pre>
       *
       * @param str  the String to pad out, may be null
       * @param size  the size to pad to
       * @param padStr  the String to pad with, null or empty treated as single space
       * @return left padded String or original String if no padding is necessary,
       *  {@code null} if null String input
       */
      private static String leftPad(String str, int size, String padStr) {
          if (str == null) {
              return null;
          }
          if (isEmpty(padStr)) {
              padStr = " ";
          }
          int padLen = padStr.length();
          int strLen = str.length();
          int pads = size - strLen;
          if (pads <= 0) {
              return str; // returns original String when possible
          }
          if (padLen == 1 && pads <= PAD_LIMIT) {
              return leftPad(str, size, padStr.charAt(0));
          }
  
          if (pads == padLen) {
              return padStr.concat(str);
          } else if (pads < padLen) {
              return padStr.substring(0, pads).concat(str);
          } else {
              char[] padding = new char[pads];
              char[] padChars = padStr.toCharArray();
              for (int i = 0; i < pads; i++) {
                  padding[i] = padChars[i % padLen];
              }
              return new String(padding).concat(str);
          }
      }
  
      /**
       * <p>The maximum size to which the padding constant(s) can expand.</p>
       */
      private static final int PAD_LIMIT = 8192;
  
  
  
      /**
       * <p>Returns padding using the specified delimiter repeated
       * to a given length.</p>
       *
       * <pre>
       * StringUtils.repeat(0, 'e')  = ""
       * StringUtils.repeat(3, 'e')  = "eee"
       * StringUtils.repeat(-2, 'e') = ""
       * </pre>
       *
       * <p>Note: this method doesn't not support padding with
       * <a href="http://www.unicode.org/glossary/#supplementary_character">Unicode Supplementary Characters</a>
       * as they require a pair of {@code char}s to be represented.
       * If you are needing to support full I18N of your applications
       * consider using {@link #repeat(String, int)} instead.
       * </p>
       *
       * @param ch  character to repeat
       * @param repeat  number of times to repeat char, negative treated as zero
       * @return String with repeated character
       * @see #repeat(String, int)
       */
      private static String repeat(char ch, int repeat) {
          char[] buf = new char[repeat];
          for (int i = repeat - 1; i >= 0; i--) {
              buf[i] = ch;
          }
          return new String(buf);
      }
  
  
      /**
       * Constant marker for truncating.
       * @since 3.0
       */
      private static final int MODIFY_TRUNCATE = 0;
      /**
       * Constant marker for rounding.
       * @since 3.0
       */
      private static final int MODIFY_ROUND = 1;
      /**
       * Constant marker for ceiling.
       * @since 3.0
       */
      private static final int MODIFY_CEILING = 2;
  
      /**
       * <p>Truncate this date, leaving the field specified as the most
       * significant field.</p>
       *
       * <p>For example, if you had the datetime of 28 Mar 2002
       * 13:45:01.231, if you passed with HOUR, it would return 28 Mar
       * 2002 13:00:00.000.  If this was passed with MONTH, it would
       * return 1 Mar 2002 0:00:00.000.</p>
       *
       * @param date  the date to work with, not null
       * @param field  the field from {@code Calendar} or <code>SEMI_MONTH</code>
       * @return the different truncated date, not null
       * @throws IllegalArgumentException if the date is <code>null</code>
       * @throws ArithmeticException if the year is over 280 million
       */
      public static Date truncate(Date date, int field) {
          if (date == null) {
              throw new IllegalArgumentException("The date must not be null");
          }
          Calendar gval = Calendar.getInstance();
          gval.setTime(date);
          modify(gval, field, MODIFY_TRUNCATE);
          return gval.getTime();
      }
  
      /**
       * This is half a month, so this represents whether a date is in the top
       * or bottom half of the month.
       */
      public static final int SEMI_MONTH = 1001;
  
  
      private static final int[][] fields = {
              {Calendar.MILLISECOND},
              {Calendar.SECOND},
              {Calendar.MINUTE},
              {Calendar.HOUR_OF_DAY, Calendar.HOUR},
              {Calendar.DATE, Calendar.DAY_OF_MONTH, Calendar.AM_PM
                  /* Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK, Calendar.DAY_OF_WEEK_IN_MONTH */
              },
              {Calendar.MONTH, SEMI_MONTH},
              {Calendar.YEAR},
              {Calendar.ERA}};
  
      /**
       * <p>Internal calculation method.</p>
       *
       * @param val  the calendar, not null
       * @param field  the field constant
       * @param modType  type to truncate, round or ceiling
       * @throws ArithmeticException if the year is over 280 million
       */
      private static void modify(Calendar val, int field, int modType) {
          if (val.get(Calendar.YEAR) > 280000000) {
              throw new ArithmeticException("Calendar value too large for accurate calculations");
          }
  
          if (field == Calendar.MILLISECOND) {
              return;
          }
  
          // ----------------- Fix for LANG-59 ---------------------- START ---------------
          // see http://issues.apache.org/jira/browse/LANG-59
          //
          // Manually truncate milliseconds, seconds and minutes, rather than using
          // Calendar methods.
  
          Date date = val.getTime();
          long time = date.getTime();
          boolean done = false;
  
          // truncate milliseconds
          int millisecs = val.get(Calendar.MILLISECOND);
          if (MODIFY_TRUNCATE == modType || millisecs < 500) {
              time = time - millisecs;
          }
          if (field == Calendar.SECOND) {
              done = true;
          }
  
          // truncate seconds
          int seconds = val.get(Calendar.SECOND);
          if (!done && (MODIFY_TRUNCATE == modType || seconds < 30)) {
              time = time - (seconds * 1000L);
          }
          if (field == Calendar.MINUTE) {
              done = true;
          }
  
          // truncate minutes
          int minutes = val.get(Calendar.MINUTE);
          if (!done && (MODIFY_TRUNCATE == modType || minutes < 30)) {
              time = time - (minutes * 60000L);
          }
  
          // reset time
          if (date.getTime() != time) {
              date.setTime(time);
              val.setTime(date);
          }
          // ----------------- Fix for LANG-59 ----------------------- END ----------------
  
          boolean roundUp = false;
          for (int[] aField : fields) {
              for (int element : aField) {
                  if (element == field) {
                      //This is our field... we stop looping
                      if (modType == MODIFY_CEILING || (modType == MODIFY_ROUND && roundUp)) {
                          if (field == SEMI_MONTH) {
                              //This is a special case that's hard to generalize
                              //If the date is 1, we round up to 16, otherwise
                              //  we subtract 15 days and add 1 month
                              if (val.get(Calendar.DATE) == 1) {
                                  val.add(Calendar.DATE, 15);
                              } else {
                                  val.add(Calendar.DATE, -15);
                                  val.add(Calendar.MONTH, 1);
                              }
  // ----------------- Fix for LANG-440 ---------------------- START ---------------
                          } else if (field == Calendar.AM_PM) {
                              // This is a special case
                              // If the time is 0, we round up to 12, otherwise
                              //  we subtract 12 hours and add 1 day
                              if (val.get(Calendar.HOUR_OF_DAY) == 0) {
                                  val.add(Calendar.HOUR_OF_DAY, 12);
                              } else {
                                  val.add(Calendar.HOUR_OF_DAY, -12);
                                  val.add(Calendar.DATE, 1);
                              }
  // ----------------- Fix for LANG-440 ---------------------- END ---------------
                          } else {
                              //We need at add one to this field since the
                              //  last number causes us to round up
                              val.add(aField[0], 1);
                          }
                      }
                      return;
                  }
              }
              //We have various fields that are not easy roundings
              int offset = 0;
              boolean offsetSet = false;
              //These are special types of fields that require different rounding rules
              switch (field) {
                  case SEMI_MONTH:
                      if (aField[0] == Calendar.DATE) {
                          //If we're going to drop the DATE field's value,
                          //  we want to do this our own way.
                          //We need to subtrace 1 since the date has a minimum of 1
                          offset = val.get(Calendar.DATE) - 1;
                          //If we're above 15 days adjustment, that means we're in the
                          //  bottom half of the month and should stay accordingly.
                          if (offset >= 15) {
                              offset -= 15;
                          }
                          //Record whether we're in the top or bottom half of that range
                          roundUp = offset > 7;
                          offsetSet = true;
                      }
                      break;
                  case Calendar.AM_PM:
                      if (aField[0] == Calendar.HOUR_OF_DAY) {
                          //If we're going to drop the HOUR field's value,
                          //  we want to do this our own way.
                          offset = val.get(Calendar.HOUR_OF_DAY);
                          if (offset >= 12) {
                              offset -= 12;
                          }
                          roundUp = offset >= 6;
                          offsetSet = true;
                      }
                      break;
              }
              if (!offsetSet) {
                  int min = val.getActualMinimum(aField[0]);
                  int max = val.getActualMaximum(aField[0]);
                  //Calculate the offset from the minimum allowed value
                  offset = val.get(aField[0]) - min;
                  //Set roundUp if this is more than half way between the minimum and maximum
                  roundUp = offset > ((max - min) / 2);
              }
              //We need to remove this field
              if (offset != 0) {
                  val.set(aField[0], val.get(aField[0]) - offset);
              }
          }
          throw new IllegalArgumentException("The field " + field + " is not supported");
  
      }
  
  
  }
  